Pārlūkot izejas kodu

feat:app发课-直播

caoliqin 1 nedēļu atpakaļ
vecāks
revīzija
73fed76122

+ 12 - 17
src/views/app/sop/template/updateSopTemp.vue

@@ -1253,7 +1253,7 @@ import {
 } from "@/api/app/sop/template";
 import {courseList, videoList} from "@/api/app/course";
 import { getPackageOptions,} from "@/api/app/pkg";
-// import { getLiveOptions,} from "@/api/app/live";
+import { getLiveOptions,} from "@/api/app/live";
 import { listRole } from '@/api/app/user/userList';
 import { getTagByIds } from "@/api/app/tag/tagGroup";
 import ImageUpload from "@/views/qw/sop/ImageUpload";
@@ -1410,27 +1410,22 @@ export default {
         courseInfo,
         defaultPkg,
         defaultLive,
-        defaultProduct,
-        defaultMedicines,
-        defaultShortVideo,
         defaultArticle,
         defaultOpenClassVideo,
       ] = await Promise.all([
         this.getCourseList(null, null),
         this.getPkgList(null, null),
         this.getLiveList(),
-        this.getProductList(null, null),
-        this.getMedicinesList(null, null),
-        this.getShortVideoList(null, null),
         this.getArticleList(null, null),
         this.getOpenClassVideoList(null, null),
       ]);
       this.courseList = courseInfo.list;
       this.pkgList = defaultPkg.data;
-      this.liveList = defaultLive.data;
-      this.productList = defaultProduct.data;
-      this.medicinesList = defaultMedicines.data;
-      this.shortVideoList = defaultShortVideo.data;
+      this.liveList = defaultLive.data || defaultLive.rows || [];
+      // 民品/药品/短视频接口暂不放开,避免控制台报错
+      // this.productList = ...
+      // this.medicinesList = ...
+      // this.shortVideoList = ...
       this.articleList = defaultArticle.data;
       this.openClassVideoList = defaultOpenClassVideo.data;
     },
@@ -1457,12 +1452,12 @@ export default {
       };
       return await getPackageOptions(data);
     },
-    // /**
-    //  * 获取直播间下拉
-    //  */
-    // async getLiveList() {
-    //   return await getLiveOptions();
-    // },
+    /**
+     * 获取直播间下拉
+     */
+    async getLiveList() {
+      return await getLiveOptions();
+    },
     // /**
     //  * 获取民品列表
     //  * @param keyword

+ 16 - 13
src/views/app/sop/userLogsInfo/groupSendMessage.vue

@@ -813,7 +813,7 @@ import ImageUpload from "@/views/qw/sop/ImageUpload.vue";
 import {courseList, videoList} from "@/api/app/course";
 import { getPackageOptions,} from "@/api/app/pkg";
 import { groupSendMessage, } from '@/api/app/userLogs'
-// import { getLiveOptions,} from "@/api/app/live";
+import { getLiveOptions,} from "@/api/app/live";
 // import { getMedicinesOptions, } from "@/api/app/medicines";
 // import { getProductOptions, } from "@/api/app/product";
 // import { getShortVideoOptions, } from "@/api/app/shortVideo";
@@ -886,12 +886,12 @@ export default {
     },
   },
   async created() {
-    const disabledContentTypes = ['21', '22', '23', '24'];
+    // 仅屏蔽民品/药品/短视频,直播(21)需要展示
+    const disabledContentTypes = ['22', '23', '24'];
     this.getDicts("app_sop_plugin_settingType").then(response => {
       this.sysQwSopAiContentType = response.data.filter(
         item => !disabledContentTypes.includes(String(item.dictValue))
       );
-      // this.sysQwSopAiContentType = response.data;
     });
     this.getDicts("app_sop_watch_status").then(response => {
       this.sysFsSopWatchStatus = response.data;
@@ -899,16 +899,19 @@ export default {
     const [
       courseInfo,
       defaultPkg,
+      defaultLive,
       defaultArticle,
       defaultOpenClassVideo,
     ] = await Promise.all([
       courseList(),
       this.getPkgList(null, null),
+      this.getLiveList(),
       this.getArticleList(null, null),
       this.getOpenClassVideoList(null, null),
     ]);
     this.courseList = courseInfo.list;
     this.pkgList = defaultPkg.data;
+    this.liveList = defaultLive.data || defaultLive.rows || [];
     this.articleList = defaultArticle.data;
     this.openClassVideoList = defaultOpenClassVideo.data;
     // const [
@@ -989,12 +992,12 @@ export default {
       };
       return await getPackageOptions(data);
     },
-    // /**
-    //  * 获取直播间信息
-    //  */
-    // async getLiveList() {
-    //   return await getLiveOptions();
-    // },
+    /**
+     * 获取直播间信息
+     */
+    async getLiveList() {
+      return await getLiveOptions();
+    },
     // /**
     //  * 获取民品列表
     //  * @param keyword
@@ -1263,7 +1266,7 @@ export default {
     liveChange(content) {
       content.liveTitle = null;
       content.liveImgUrl = null;
-      const selectedLive = this.liveList.find(live => live.liveId === content.liveId);
+      const selectedLive = this.liveList.find(live => parseInt(live.liveId) === parseInt(content.liveId));
       if (selectedLive) {
         content.liveTitle = selectedLive.liveName; // 自动填充标题
         content.liveImgUrl = selectedLive.liveImgUrl; // 自动填充封面
@@ -1630,9 +1633,9 @@ export default {
         if ([20].includes(Number(dcs.contentType)) && this.isEmpty(dcs.packageId)) {
           return this.$message.error("疗法不能为空");
         }
-        // if ([21].includes(Number(dcs.contentType)) && this.isEmpty(dcs.liveId)) {
-        //   return this.$message.error("直播间不能为空");
-        // }
+        if ([21].includes(Number(dcs.contentType)) && this.isEmpty(dcs.liveId)) {
+          return this.$message.error("直播间不能为空");
+        }
         // if ([22].includes(Number(dcs.contentType)) && this.isEmpty(dcs.productId)) {
         //   return this.$message.error("民品不能为空");
         // }

+ 14 - 7
src/views/app/sop/userLogsInfo/sendMsgOpenTool.vue

@@ -257,7 +257,7 @@
                       </div>
 
                     </el-form-item>
-                    <div v-if="item.contentType == 10">
+                    <div v-if="item.contentType == 20">
                       <!--                                           <div >-->
                       <el-card class="box-card">
                         <el-form-item label="直播间" >
@@ -398,6 +398,7 @@ import { sendMsgSopType,} from "@/api/app/userLogsInfo";
 import ImageUpload from "@/views/qw/sop/ImageUpload.vue";
 import {courseList, videoList} from "@/api/qw/sop";
 import {updateTimeSopUserLogs} from "@/api/qw/sopUserLogs";
+import { getLiveOptions } from "@/api/app/live";
 
 
 export default {
@@ -472,6 +473,7 @@ export default {
 
       courseList:[],
       videoList:[],
+      liveList: [],//直播间下拉
       //插件版
       sysQwSopAiContentType:[],
 
@@ -518,6 +520,8 @@ export default {
   },
 
   created() {
+    // 本页内容类别字典:sys_qwSopAi_contentType(直播对应 dictValue=20)
+    // 注意:与群发页 app_sop_plugin_settingType(直播=21)不是同一套字典
     this.getDicts("sys_qwSopAi_contentType").then(response => {
       this.sysQwSopAiContentType = response.data;
     });
@@ -529,6 +533,10 @@ export default {
       this.courseList = response.list;
     });
 
+    getLiveOptions().then(response => {
+      this.liveList = response.data || response.rows || [];
+    });
+
     this.loadSmsTemplates();
   },
   methods: {
@@ -546,10 +554,9 @@ export default {
     },
     liveChange(content) {
       // content.liveId 是选中的直播间 ID(liveId)
-      const selectedLive = this.liveList.find(live => live.liveId === content.liveId);
+      const selectedLive = this.liveList.find(live => parseInt(live.liveId) === parseInt(content.liveId));
       if (selectedLive) {
         // 从选中的直播间对象中提取标题和封面,赋值给当前内容的对应字段
-        // 假设直播间对象中标题字段为 liveTitle,封面字段为 coverImg(根据实际接口字段调整)
         content.miniprogramTitle = selectedLive.liveName || ''; // 自动填充标题
         content.miniprogramPicUrl = selectedLive.liveImgUrl || ''; // 自动填充封面
       } else {
@@ -1028,7 +1035,7 @@ export default {
             result.push(additionalMessage);
           }
         }
-        if (item.contentType == 10) {
+        if (item.contentType == 20) {
           item.miniprogramAppid = 'wx776d6bd6848eec49'
         }
         result.push(item);
@@ -1106,15 +1113,15 @@ export default {
                 return this.$message.error("语音不能为空")
               }
               // 直播间内容验证
-              if (item.contentType == 10 && (item.liveId == null || item.liveId == "")) {
+              if (item.contentType == 20 && (item.liveId == null || item.liveId == "")) {
                 this.$message.error("直播间不能为空")
                 return false;
               }
-              if (item.contentType == 10 && (item.miniprogramTitle == null || item.miniprogramTitle == "")) {
+              if (item.contentType == 20 && (item.miniprogramTitle == null || item.miniprogramTitle == "")) {
                 this.$message.error("标题不能为空")
                 return false;
               }
-              if (item.contentType == 10 && (item.miniprogramPicUrl == null || item.miniprogramPicUrl == "")) {
+              if (item.contentType == 20 && (item.miniprogramPicUrl == null || item.miniprogramPicUrl == "")) {
                 this.$message.error("封面不能为空")
                 return false;
               }

+ 38 - 0
src/views/app/user/CusRoleList.vue

@@ -19,6 +19,7 @@
       :data="roleList" 
       ref="roleList"
       :row-key="row => String(row.id)"
+      :class="{ 'cus-role-single': !multiple }"
       @select="singleSelectHandler"
       @select-all="allSelectHandler"
      >
@@ -59,6 +60,11 @@ export default {
       type: Array,
       default: () => [],
     },
+    // 是否允许多选,false 时只能勾选一个
+    multiple: {
+      type: Boolean,
+      default: true,
+    },
     ignoreIds: {
       type: Array,
       default: () => [],
@@ -186,6 +192,26 @@ export default {
      * @param row 
      */
     singleSelectHandler(selection, row) {
+      // 单选模式:只保留当前行
+      if (!this.multiple) {
+        const isSelected = selection.some(l => l.id == row.id)
+        this.$refs.roleList.clearSelection()
+        if (isSelected) {
+          this.$nextTick(() => {
+            this.$refs.roleList.toggleRowSelection(row, true)
+          })
+        }
+        try {
+          this.$emit('selectionChange', {
+            mode: 0,
+            row,
+            selected: isSelected
+          })
+        } catch (e) {
+          console.warn('当前组件暂未指定selection回调')
+        }
+        return
+      }
       let pm = {
         mode: 0,//0-单行选,1-全选
         row,//操作的行
@@ -207,6 +233,11 @@ export default {
      * @param selection 
      */
     allSelectHandler(selection) {
+      // 单选模式不允许全选
+      if (!this.multiple) {
+        this.$refs.roleList.clearSelection()
+        return
+      }
       let pm = {
         mode: 1,//0-单行选,1-全选
         rows: this.roleList,//操作的所有行
@@ -221,3 +252,10 @@ export default {
   }
 };
 </script>
+
+<style scoped>
+/* 单选模式隐藏表头全选勾选框 */
+.cus-role-single ::v-deep .el-table__header-wrapper .el-checkbox {
+  display: none;
+}
+</style>

+ 10 - 23
src/views/app/user/index.vue

@@ -395,6 +395,7 @@
     <el-dialog :title="bindAIConfig.title" :visible.sync="bindAIConfig.visible" width="1000px" append-to-body :close-on-click-modal="false">
       <CusRoleList
         ref="customerBindRef"
+        :multiple="false"
         @confirm="customerSelectedConfirmCallForBind"
         @selectionChange="customerSelectionChangeCallForBind"
         :selected="bindAIConfig.selected"
@@ -1321,6 +1322,7 @@ export default {
       }
       this.bindAIConfig.title = type == 0 ? '绑定客服' : '解绑客服';
       this.bindAIConfig.type = type;
+      this.bindAIConfig.selected = [];
       this.bindAIConfig.visible = true;
     },
     /**
@@ -1328,41 +1330,26 @@ export default {
      */
     customerSelectionChangeCallForBind(config) {
       let selected = config.selected;
-      //单行操作
+      // 单选模式:最多只保留一个客服
       if (config.mode === 0) {
         let row = config.row;
         if (selected) {
-          if (!this.bindAIConfig.selected.some(s => s.id == row.id)) {
-            this.bindAIConfig.selected.push(row);
-          }
+          this.bindAIConfig.selected = [row];
         } else {
           this.bindAIConfig.selected = this.bindAIConfig.selected.filter(m => m.id != row.id);
         }
+        return;
       }
-      //操作当前页所有行
-      else {
-        let list = config.rows;
-        if (selected) {
-          list.map(l => {
-            if (!this.bindAIConfig.selected.some(s => s.id == l.id)) {
-              this.bindAIConfig.selected.push(l);
-            }
-          });
-        } else {
-          list.map(l => {
-            for (let i = 0; i < this.bindAIConfig.selected.length; i++) {
-              if (l.id == this.bindAIConfig.selected[i].id) {
-                this.bindAIConfig.selected.splice(i, 1);
-              }
-            }
-          })
-        }
-      }
+      // 全选(单选模式下 CusRoleList 已拦截,此处兜底忽略)
     },
     /**
      * 保存绑定的客服
      */
     async customerSelectedConfirmCallForBind() {
+      if (!this.bindAIConfig.selected || this.bindAIConfig.selected.length !== 1) {
+        this.$message.closeAll();
+        return this.$message.warning('请选择一个客服');
+      }
       let tmpParam = {};
       tmpParam['enableFilter'] = this.bindAIConfig.enableFilter;
       tmpParam['roleId'] = this.bindAIConfig.selected.map(item => item.id) || [];

+ 8 - 3
src/views/components/index/statisticsDashboard.vue

@@ -619,7 +619,6 @@ const viewCharOption = {
     bottom: '3%',
     containLabel: true
   },
-  projectFrom:process.env.VUE_APP_PROJECT_FROM,
   xAxis: {
     type: 'category',
     data: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23']
@@ -1014,6 +1013,10 @@ export default {
       userTypeText: process.env.VUE_APP_COURSE_DEFAULT==1?"会员":"企微",
       userType: process.env.VUE_APP_COURSE_DEFAULT,
       dealerChart: null,
+      courseWatchChart: null,
+      thisMonthOrderChart: null,
+      thisMonthRecvChart: null,
+      projectFrom: process.env.VUE_APP_PROJECT_FROM,
       // 分公司数量
       dealderCount: 0,
       // 销售数量
@@ -1106,6 +1109,7 @@ export default {
       this.initThisMonthOrderChart();
       this.initThisMonthRecvChart();
 
+      this.refresh();
 
       // 监听窗口大小变化,重新渲染图表
       window.addEventListener('resize', () => {
@@ -1116,7 +1120,6 @@ export default {
     })
   },
   created() {
-    this.refresh();
     listDept().then(res => {
       this.deptInitOptions = res.data;
       listCompany().then(res => {
@@ -1604,7 +1607,9 @@ export default {
           courseWatchOption.series[1].data = completedUserCountList;
           courseWatchOption.series[2].data = answerUserCountList;
           courseWatchOption.series[3].data = correctUserCountList;
-          this.courseWatchChart.setOption(courseWatchOption)
+          if (this.courseWatchChart) {
+            this.courseWatchChart.setOption(courseWatchOption)
+          }
         }
       })
     },