watchReward.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  1. <template >
  2. <div class="app-container live-page">
  3. <div class="live-page__header">
  4. <h2 class="live-page__title">观看奖励</h2>
  5. </div>
  6. <div class="live-panel">
  7. <div v-loading.fullscreen.lock="loading">
  8. <!-- 提示信息 -->
  9. <div class="tip-message" >
  10. 设置观看奖励,用户达到直播观看时长后可领取奖励
  11. </div>
  12. <!-- 开启观看奖励开关 -->
  13. <div class="reward-switch">
  14. <span class="switch-label">开启观看奖励</span>
  15. <el-switch v-model="watchRewardForm.enabled"></el-switch>
  16. </div>
  17. <!-- 观看奖励设置 -->
  18. <div v-if="watchRewardForm.enabled" class="section-block">
  19. <div class="section-title">观看奖励设置</div>
  20. <!-- 表单内容 -->
  21. <el-form
  22. :model="watchRewardForm"
  23. :rules="rules"
  24. ref="watchRewardForm"
  25. label-width="130px"
  26. >
  27. <!-- 参与条件 -->
  28. <el-form-item label="参与条件" prop="participateCondition">
  29. <el-radio v-model="watchRewardForm.participateCondition" label="1">
  30. 达到指定观看时长
  31. </el-radio>
  32. </el-form-item>
  33. <!-- 观看时长 -->
  34. <el-form-item label="观看时长" prop="watchDuration">
  35. <el-input v-model="watchRewardForm.watchDuration" placeholder="请输入观看时长" class="duration-input">
  36. <template #append>分钟</template>
  37. </el-input>
  38. </el-form-item>
  39. <!-- 实施动作 -->
  40. <el-form-item label="实施动作" prop="action">
  41. <el-select v-model="watchRewardForm.action" placeholder="请选择实施动作" style="width: 300px;">
  42. <el-option
  43. v-for="item in actionOptions"
  44. :key="item.value"
  45. :label="item.label"
  46. :value="item.value"
  47. ></el-option>
  48. </el-select>
  49. </el-form-item>
  50. <!-- 抽奖设置 -->
  51. <template v-if="watchRewardForm.action === '3'">
  52. <el-form-item label="选择抽奖" prop="bigGiftRewardId">
  53. <el-select
  54. v-model="watchRewardForm.bigGiftRewardId"
  55. placeholder="请选择抽奖活动"
  56. style="width: 300px;"
  57. filterable
  58. :loading="bigGiftRewardLoading"
  59. >
  60. <el-option
  61. v-for="item in bigGiftRewardList"
  62. :key="getRewardId(item)"
  63. :label="getRewardName(item)"
  64. :value="getRewardId(item)"
  65. ></el-option>
  66. </el-select>
  67. </el-form-item>
  68. <el-form-item label="奖品内容" v-if="watchRewardForm.bigGiftRewardId">
  69. <template v-if="selectedBigGiftReward">
  70. <el-descriptions :column="2" border size="small" class="reward-desc">
  71. <el-descriptions-item label="抽奖名称">{{ selectedBigGiftReward.name || '-' }}</el-descriptions-item>
  72. <el-descriptions-item label="抽奖ID">{{ selectedBigGiftReward.id }}</el-descriptions-item>
  73. <el-descriptions-item label="期望价值">{{ selectedBigGiftReward.expectedValue != null ? selectedBigGiftReward.expectedValue + ' 元' : '-' }}</el-descriptions-item>
  74. <el-descriptions-item label="状态">{{ formatRewardStatus(selectedBigGiftReward.status) }}</el-descriptions-item>
  75. <el-descriptions-item label="描述" :span="2">{{ selectedBigGiftReward.description || '-' }}</el-descriptions-item>
  76. </el-descriptions>
  77. <el-table
  78. v-if="prizePreviewList.length > 0"
  79. :data="prizePreviewList"
  80. border
  81. size="small"
  82. style="width: 100%; margin-top: 12px;"
  83. max-height="420"
  84. >
  85. <el-table-column label="图标" width="70" align="center">
  86. <template slot-scope="scope">
  87. <el-image
  88. v-if="scope.row.iconUrl"
  89. :src="scope.row.iconUrl"
  90. :preview-src-list="[scope.row.iconUrl]"
  91. fit="cover"
  92. class="prize-icon"
  93. />
  94. <span v-else>-</span>
  95. </template>
  96. </el-table-column>
  97. <el-table-column label="奖品名称" prop="name" min-width="140" show-overflow-tooltip />
  98. <el-table-column label="奖品类型" width="100" align="center">
  99. <template slot-scope="scope">
  100. {{ formatRewardType(scope.row.type) }}
  101. </template>
  102. </el-table-column>
  103. <el-table-column label="数量/金额" width="100" align="center">
  104. <template slot-scope="scope">
  105. {{ scope.row.amount != null ? scope.row.amount : '-' }}
  106. </template>
  107. </el-table-column>
  108. <el-table-column label="概率权重" prop="probability" width="100" align="center" />
  109. </el-table>
  110. <div v-else class="empty-prize-tip">暂无奖品明细</div>
  111. </template>
  112. <div v-else class="empty-prize-tip">已选择抽奖 ID:{{ watchRewardForm.bigGiftRewardId }}(奖品详情加载中或未找到)</div>
  113. </el-form-item>
  114. </template>
  115. <!-- 领取提示语 -->
  116. <!-- <el-form-item label="领取提示语" prop="receivePrompt">-->
  117. <!-- <el-input v-model="watchRewardForm.receivePrompt" placeholder="请输入领取提示语"></el-input>-->
  118. <!-- </el-form-item>-->
  119. <!-- 红包设置 -->
  120. <div v-if="watchRewardForm.action !== '3'">
  121. <div class="section-title">红包设置</div>
  122. <!-- 根据实施动作类型显示不同的表单内容 -->
  123. <template v-if="watchRewardForm.action === '1'">
  124. <!-- 现金红包设置 -->
  125. <!-- 红包发放方式 1固定金额 2随机金额 -->
  126. <el-form-item label="红包发放方式" prop="redPacketType">
  127. <el-radio-group v-model="watchRewardForm.redPacketType">
  128. <el-radio label="1">固定金额</el-radio>
  129. <el-radio label="2">随机金额</el-radio>
  130. </el-radio-group>
  131. </el-form-item>
  132. <!-- 红包金额 -->
  133. <el-form-item label="红包金额" prop="redPacketAmount">
  134. <el-input v-model="watchRewardForm.redPacketAmount" placeholder="请输入红包金额"></el-input>
  135. </el-form-item>
  136. <!-- 红包发放数量 -->
  137. <el-form-item label="红包发放数量" prop="redPacketCount">
  138. <el-input v-model="watchRewardForm.redPacketCount" placeholder="红包数量+28888人数"></el-input>
  139. </el-form-item>
  140. <!-- 红包领取方式 1二维码核销 2微信提现 -->
  141. <el-form-item label="红包领取方式" prop="receiveMethod">
  142. <el-radio-group v-model="watchRewardForm.receiveMethod">
  143. <el-radio label="1">二维码领取</el-radio>
  144. <el-radio label="2">微信发放</el-radio>
  145. </el-radio-group>
  146. </el-form-item>
  147. </template>
  148. <template v-else>
  149. <!-- 积分红包设置 -->
  150. <!-- 积分值 -->
  151. <el-form-item label="积分值" prop="scoreAmount">
  152. <el-input
  153. v-model="watchRewardForm.scoreAmount"
  154. placeholder="请输入积分值" style="width: 300px;"
  155. ></el-input>
  156. </el-form-item>
  157. <!-- 最大领取人数 -->
  158. <!-- <el-form-item label="最大领取人数" prop="maxReceivers">-->
  159. <!-- <el-input-->
  160. <!-- v-model="watchRewardForm.maxReceivers"-->
  161. <!-- placeholder="请输入最大领取人数" style="width: 300px;"-->
  162. <!-- ></el-input>-->
  163. <!-- </el-form-item>-->
  164. </template>
  165. </div>
  166. <!-- 其他设置 -->
  167. <div v-if="watchRewardForm.action !== '3'">
  168. <div class="section-title">其他设置</div>
  169. <template v-if="watchRewardForm.action === '1'">
  170. <!-- 客服引导 1跟进企业微信 2不设置 -->
  171. <el-form-item label="客服引导" prop="showGuide">
  172. <el-radio-group v-model="watchRewardForm.showGuide">
  173. <el-radio label="1">跟进企业微信</el-radio>
  174. <el-radio label="2">不设置</el-radio>
  175. </el-radio-group>
  176. </el-form-item>
  177. <!-- 客服引导语 -->
  178. <el-form-item label="客服引导语" prop="guideText">
  179. <el-input
  180. v-model="watchRewardForm.guideText"
  181. placeholder="请输入客服引导语" style="width: 300px;"
  182. ></el-input>
  183. </el-form-item>
  184. </template>
  185. <template v-else>
  186. <!-- 积分使用引导语 -->
  187. <el-form-item label="积分使用引导语" prop="scoreGuideText">
  188. <el-input
  189. v-model="watchRewardForm.scoreGuideText"
  190. placeholder="请输入积分使用引导语" style="width: 300px;"
  191. ></el-input>
  192. </el-form-item>
  193. <!-- 积分使用引导链接 -->
  194. <el-form-item label="积分使用引导链接" prop="scoreGuideLink">
  195. <el-input
  196. v-model="watchRewardForm.scoreGuideLink"
  197. placeholder="请输入积分使用引导链接" style="width: 300px;"
  198. ></el-input>
  199. </el-form-item>
  200. <!-- 引导语 -->
  201. <!-- <el-form-item label="引导语" prop="guideText">-->
  202. <!-- <el-input-->
  203. <!-- v-model="watchRewardForm.guideText"-->
  204. <!-- placeholder="请输入引导语" style="width: 300px;"-->
  205. <!-- ></el-input>-->
  206. <!-- </el-form-item>-->
  207. </template>
  208. </div>
  209. <!-- 保存按钮 -->
  210. <div class="form-actions">
  211. <el-button type="primary" @click="saveWatchReward">保存</el-button>
  212. </div>
  213. </el-form>
  214. </div>
  215. </div>
  216. </div>
  217. </div>
  218. </template>
  219. <script>
  220. import {addConfig, getConfig, updateConfig} from "@/api/live/liveQuestionLive";
  221. import {listBigGiftReward} from "@/api/live/live";
  222. export default {
  223. data() {
  224. return {
  225. loading: true,
  226. liveId: null,
  227. watchRewardForm: {
  228. id: null,
  229. liveId: null,
  230. // 是否启用观看奖励
  231. enabled: false,
  232. // 参与条件
  233. participateCondition: '1',
  234. // 观看时长
  235. watchDuration: '',
  236. // 实施动作
  237. action: '2',
  238. // 领取提示语
  239. receivePrompt: '',
  240. // 红包发放方式(固定金额/随机金额)
  241. redPacketType: '1',
  242. // 红包金额
  243. redPacketAmount: '',
  244. // 红包发放数量
  245. redPacketCount: '',
  246. // 红包领取方式
  247. receiveMethod: '1',
  248. // 是否显示客服引导
  249. showGuide: '1',
  250. // 客服引导语
  251. guideText: '',
  252. // 积分值
  253. scoreAmount: '',
  254. // // 最大领取人数
  255. // maxReceivers: '',
  256. // 积分使用引导语
  257. scoreGuideText: '',
  258. // 积分使用引导链接
  259. scoreGuideLink: '',
  260. // 大礼品抽奖ID(action=3时仅保存此ID)
  261. bigGiftRewardId: null
  262. },
  263. bigGiftRewardList: [],
  264. bigGiftRewardLoading: false,
  265. // 添加实施动作选项
  266. actionOptions: [
  267. // {
  268. // label: '现金红包',
  269. // value: '1'
  270. // },
  271. {
  272. label: '积分红包',
  273. value: '2'
  274. },
  275. {
  276. label: '抽奖',
  277. value: '3'
  278. }
  279. ],
  280. };
  281. },
  282. computed: {
  283. rules() {
  284. const baseRules = {
  285. participateCondition: [
  286. { required: true, message: '请选择参与条件', trigger: 'blur' }
  287. ],
  288. watchDuration: [
  289. { required: true, message: '请输入观看时长', trigger: 'blur' }
  290. ],
  291. action: [
  292. { required: true, message: '请选择实施动作', trigger: 'blur' }
  293. ]
  294. };
  295. if (this.watchRewardForm.action === '3') {
  296. baseRules.bigGiftRewardId = [
  297. { required: true, message: '请选择抽奖活动', trigger: 'change' }
  298. ];
  299. } else if (this.watchRewardForm.action === '1') {
  300. baseRules.redPacketType = [
  301. { required: true, message: '请选择红包发放方式', trigger: 'blur' }
  302. ];
  303. baseRules.redPacketAmount = [
  304. { required: true, message: '请输入红包金额', trigger: 'blur' }
  305. ];
  306. baseRules.receiveMethod = [
  307. { required: true, message: '请选择红包领取方式', trigger: 'blur' }
  308. ];
  309. baseRules.showGuide = [
  310. { required: true, message: '请选择是否显示客服引导', trigger: 'blur' }
  311. ];
  312. }
  313. return baseRules;
  314. },
  315. selectedBigGiftReward() {
  316. if (!this.watchRewardForm.bigGiftRewardId) {
  317. return null;
  318. }
  319. const rewardId = String(this.watchRewardForm.bigGiftRewardId);
  320. return this.bigGiftRewardList.find(item => String(this.getRewardId(item)) === rewardId) || null;
  321. },
  322. prizePreviewList() {
  323. return this.getPrizeList(this.selectedBigGiftReward);
  324. }
  325. },
  326. watch: {
  327. // 监听路由的 query 参数变化
  328. '$route.query': {
  329. handler(newQuery) {
  330. this.liveId = this.$route.params.liveId
  331. this.watchRewardForm.liveId = this.liveId
  332. if(this.liveId == null) {
  333. return;
  334. }
  335. this.getLiveConfig();
  336. },
  337. // 初始化时立即执行一次
  338. immediate: true
  339. }
  340. },
  341. created() {
  342. this.loadBigGiftRewardList();
  343. },
  344. methods: {
  345. getRewardId(item) {
  346. if (!item) {
  347. return null;
  348. }
  349. return item.id != null ? item.id : (item.rewardId != null ? item.rewardId : item.configId);
  350. },
  351. getRewardName(item) {
  352. if (!item) {
  353. return '';
  354. }
  355. const name = item.name || item.rewardName || item.title || ('抽奖-' + this.getRewardId(item));
  356. if (item.expectedValue != null && item.expectedValue !== '') {
  357. return `${name}(期望${item.expectedValue}元)`;
  358. }
  359. return name;
  360. },
  361. formatRewardStatus(status) {
  362. if (status === 1 || status === '1') {
  363. return '启用';
  364. }
  365. if (status === 0 || status === '0') {
  366. return '停用';
  367. }
  368. return status != null ? status : '-';
  369. },
  370. formatRewardType(type) {
  371. const typeMap = {
  372. '1': '现金',
  373. '2': '芳华币',
  374. '3': '优惠券',
  375. '4': '积分',
  376. '5': '实物商品'
  377. };
  378. return typeMap[String(type)] || (type != null ? type : '-');
  379. },
  380. getPrizeList(reward) {
  381. if (!reward || !reward.actualRewards) {
  382. return [];
  383. }
  384. let list = reward.actualRewards;
  385. if (typeof list === 'string') {
  386. try {
  387. list = JSON.parse(list);
  388. } catch (e) {
  389. return [];
  390. }
  391. }
  392. return Array.isArray(list) ? list : [];
  393. },
  394. loadBigGiftRewardList() {
  395. this.bigGiftRewardLoading = true;
  396. listBigGiftReward({
  397. pageNum: 1,
  398. pageSize: 100
  399. }).then(response => {
  400. this.bigGiftRewardList = response.rows || [];
  401. }).finally(() => {
  402. this.bigGiftRewardLoading = false;
  403. });
  404. },
  405. mergeWatchRewardForm(data) {
  406. const defaults = {
  407. id: null,
  408. liveId: this.liveId,
  409. enabled: false,
  410. participateCondition: '1',
  411. watchDuration: '',
  412. action: '2',
  413. receivePrompt: '',
  414. redPacketType: '1',
  415. redPacketAmount: '',
  416. redPacketCount: '',
  417. receiveMethod: '1',
  418. showGuide: '1',
  419. guideText: '',
  420. scoreAmount: '',
  421. scoreGuideText: '',
  422. scoreGuideLink: '',
  423. bigGiftRewardId: null
  424. };
  425. const merged = Object.assign({}, defaults, data || {});
  426. merged.participateCondition = merged.participateCondition != null ? String(merged.participateCondition) : '1';
  427. merged.action = merged.action != null ? String(merged.action) : '2';
  428. merged.watchDuration = merged.watchDuration != null ? String(merged.watchDuration) : '';
  429. if (merged.bigGiftRewardId != null) {
  430. merged.bigGiftRewardId = Number(merged.bigGiftRewardId);
  431. }
  432. return merged;
  433. },
  434. buildSavePayload() {
  435. const form = this.watchRewardForm;
  436. const common = {
  437. id: form.id,
  438. liveId: form.liveId,
  439. enabled: form.enabled,
  440. participateCondition: form.participateCondition,
  441. watchDuration: form.watchDuration,
  442. action: form.action
  443. };
  444. if (form.action === '3') {
  445. return Object.assign({}, common, {
  446. bigGiftRewardId: form.bigGiftRewardId
  447. });
  448. }
  449. if (form.action === '1') {
  450. return Object.assign({}, form, common);
  451. }
  452. return Object.assign({}, common, {
  453. scoreAmount: form.scoreAmount,
  454. scoreGuideText: form.scoreGuideText,
  455. scoreGuideLink: form.scoreGuideLink
  456. });
  457. },
  458. getLiveConfig(){
  459. getConfig(this.liveId).then(response => {
  460. if(response.code === 200 && response.data != null && response.data.length > 0){
  461. this.watchRewardForm = this.mergeWatchRewardForm(JSON.parse(response.data));
  462. }
  463. this.loading = false
  464. })
  465. },
  466. saveWatchReward() {
  467. this.$refs["watchRewardForm"].validate(valid => {
  468. if (valid) {
  469. const payload = this.buildSavePayload();
  470. if (this.watchRewardForm.id == null) {
  471. addConfig(payload, this.liveId).then(res => {
  472. if (res.code == 200) {
  473. this.msgSuccess("修改成功");
  474. this.getLiveConfig();
  475. }
  476. })
  477. } else {
  478. updateConfig(payload, this.liveId).then(response => {
  479. this.msgSuccess("修改成功");
  480. this.getLiveConfig();
  481. });
  482. }
  483. }
  484. })
  485. },
  486. }
  487. };
  488. </script>
  489. <style scoped>
  490. /* 提示信息样式 */
  491. .tip-message {
  492. padding: 12px 16px;
  493. background-color: #FFF6F2;
  494. border-radius: 4px;
  495. color: #666;
  496. font-size: 14px;
  497. margin-bottom: 20px;
  498. }
  499. /* 开关容器样式 */
  500. .reward-switch {
  501. /* margin-bottom: 20px; */
  502. padding: 20px;
  503. background-color: #fff;
  504. border-radius: 4px;
  505. display: flex;
  506. align-items: center;
  507. }
  508. /* 开关标签样式 */
  509. .reward-switch .switch-label {
  510. margin-right: 10px;
  511. font-size: 14px;
  512. color: #333;
  513. margin-left: 50px;
  514. }
  515. /* 表单区块样式 */
  516. .section-block {
  517. width: 50%;
  518. background-color: #fff;
  519. padding: 20px;
  520. border-radius: 4px;
  521. margin-left: 50px;
  522. margin-bottom: 20px;
  523. }
  524. /* 标题样式 */
  525. .section-block .section-title {
  526. font-size: 14px;
  527. color: #333;
  528. margin-bottom: 20px;
  529. border-left: 4px solid #409EFF;
  530. padding-left: 10px;
  531. line-height: 1;
  532. }
  533. /* 表单样式 */
  534. .reward-form {
  535. margin-top: 20px;
  536. }
  537. /* 表单项样式 */
  538. .reward-form .el-form-item {
  539. margin-bottom: 22px;
  540. padding-left: 50px;
  541. }
  542. .reward-form .el-form-item:last-child {
  543. margin-bottom: 0;
  544. }
  545. /* 表单标签样式 */
  546. .reward-form .el-form-item .el-form-item__label {
  547. color: #606266;
  548. }
  549. /* 输入框统一宽度 */
  550. .reward-form .el-form-item .el-input {
  551. width: 300px;
  552. }
  553. /* 必填项星号样式 */
  554. .reward-form .el-form-item.is-required .el-form-item__label:before {
  555. color: #F56C6C;
  556. }
  557. /* 观看时长输入框样式 */
  558. .reward-form .el-form-item .duration-input {
  559. width: 300px;
  560. }
  561. .reward-form .el-form-item .duration-input .el-input__inner {
  562. text-align: left;
  563. }
  564. /* 保存按钮样式 */
  565. .form-actions {
  566. width: 600px;
  567. text-align: center;
  568. margin-top: 30px;
  569. }
  570. .form-actions .el-button {
  571. padding: 8px 20px;
  572. font-size: 13px;
  573. }
  574. .reward-desc {
  575. width: 100%;
  576. }
  577. .empty-prize-tip {
  578. margin-top: 12px;
  579. color: #909399;
  580. font-size: 13px;
  581. }
  582. .prize-icon {
  583. width: 40px;
  584. height: 40px;
  585. border-radius: 4px;
  586. }
  587. </style>