Jelajahi Sumber

修改完课模板

三七 2 bulan lalu
induk
melakukan
fc172794ba

+ 396 - 77
src/views/course/courseFinishTemp/index.vue

@@ -562,15 +562,99 @@
           <span v-else style="margin-left: 10px;color: #ff4949">否</span>
           <Tip :title="'是否 全选整个销售公司的销售,作用于这个课程小节,当这个小节有客户完课时,发送此设置的内容'" />
         </el-form-item>
-        <el-form-item label="所属销售" prop="companyUserIds" v-if="form.isAllCompanyUser == '2'">
-          <el-select v-model="companyUserIds" remote multiple placeholder="请选择" filterable style="width: 100%;">
-            <el-option
-              v-for="dict in userList"
-              :key="dict.userId"
-              :label="dict.nickName"
-              :value="dict.userId.toString()">
-            </el-option>
-          </el-select>
+        <el-form-item label="选择范围" v-if="form.isAllCompanyUser != 1">
+          <el-radio-group v-model="form.checkRange" @change="logChange">
+            <el-radio
+              v-for="dict in finishTempList"
+              :key="dict.dictValue"
+              :label="parseInt(dict.dictValue)"
+            >{{ dict.dictLabel }}</el-radio
+            >
+          </el-radio-group>
+          <div
+            style="
+              color: #999;
+              font-size: 14px;
+              display: flex;
+              align-items: center;
+            "
+          >
+            <i class="el-icon-info"></i>
+            同时存在的情况下,根据配置顺序决定完课模板的优先级 单选客服>选部门>全选销售公司
+          </div>
+        </el-form-item>
+
+        <el-form-item
+          v-show="form.checkRange == 1"
+          label="所属企微员工"
+          prop="companyUserIds"
+          label-width="100px"
+          v-if="form.isAllCompanyUser != 1"
+        >
+          <div>
+            <el-button
+              size="medium"
+              icon="el-icon-circle-plus-outline"
+              plain
+              @click="handlelistUser"
+            >请选择使用成员</el-button
+            >
+            <el-button
+              size="medium"
+              icon="el-icon-remove-outline"
+              plain
+              @click="handleClearCompanyUser()">清除员工
+            </el-button>
+          </div>
+          <div>
+            <el-tag
+              style="margin-left: 5px"
+              size="medium"
+              :key="list.id"
+              v-for="list in userSelectList"
+              closable
+              :disable-transitions="false"
+              @close="handleClosegroupUser(list)"
+            >
+              {{ list.qwUserId }}({{ list.qwUserName }})({{ list.nickName }})
+            </el-tag>
+          </div>
+          <div
+            style="
+              color: #999;
+              font-size: 14px;
+              display: flex;
+              align-items: center;
+            "
+          >
+            <i class="el-icon-info"></i>
+            选择了企微员工之后,此课程小节的完课 消息
+            只作用于此企业微信员工,不再是整个客服公司的员工【注意:此处不区分主体,只区分销售公司!】
+          </div>
+        </el-form-item>
+        <el-form-item
+          v-show="form.checkRange == 2"
+          label="所属部门"
+          prop="deptId"
+          label-width="100px"
+          v-if="form.isAllCompanyUser != 1"
+        >
+          <el-cascader
+            style="width: 100%"
+            v-model="form.deptArrs"
+            filterable
+            :props="{
+              multiple: true,
+              value: 'id',
+              emitPath: false,
+              checkStrictly: true,
+              expandTrigger:'hover'
+            }"
+            :options="deptTree"
+            @change="handleDeptChange"
+          >
+          </el-cascader>
+
         </el-form-item>
       </el-form>
       <div slot="footer" class="dialog-footer" v-if="formType==1">
@@ -581,6 +665,19 @@
     <el-dialog :title="videoNumOptions.title" :visible.sync="videoNumOptions.open" width="1500px" append-to-body>
       <userVideo ref="QwUserVideo" @videoResult="qwUserVideoResult"></userVideo>
     </el-dialog>
+
+    <!-- 选择成员账号弹窗   -->
+    <el-dialog
+      :title="listUser.title"
+      :visible.sync="listUser.open"
+      width="800px"
+      append-to-body
+    >
+      <qwUserListByCompany
+        ref="QwUserList"
+        @selectUserList="selectUserList"
+      ></qwUserListByCompany>
+    </el-dialog>
   </div>
 </template>
 
@@ -598,9 +695,12 @@ import { courseList, videoList } from '@/api/qw/sop'
 import ImageUpload from '@/views/qw/sop/ImageUpload.vue'
 import Tip from "../../../components/Tip/index.vue";
 import userVideo from "@/views/qw/userVideo/userVideo.vue";
+import QwUserListByCompany from "@/views/qw/user/qwUserListByCompany.vue";
+import { treeselect } from "../../../api/company/companyDept";
+
 export default {
   name: "CourseFinishTemp",
-  components: {Tip, ImageUpload,userVideo},
+  components: {Tip, ImageUpload,userVideo,QwUserListByCompany},
   data() {
     return {
       videoOptionsLoading: false,
@@ -674,9 +774,22 @@ export default {
         chatSetting: null,
         videoIdSet: null,
         courseIdSet: null,
+        deptArrs: [],
+        checkRange: null,
       },
       // 表单校验
-      rules: {}
+      rules: {},
+      deptTree: [],
+      finishTempList: [],
+      //用来记录上次选择部门的数组
+      prevSelected:[],
+      //选择企业微信成员列表
+      userSelectList: [],
+      //选择成员列表
+      listUser: {
+        title: "",
+        open: false,
+      },
     };
   },
   created() {
@@ -684,7 +797,6 @@ export default {
     this.parentId = this.queryParams.parentId;
     getUserList().then(response => {
       this.userList = response.data;
-      console.log("this.userList", this.userList)
     });
     this.getDicts("sys_company_status").then(response => {
       this.statusOptions = response.data;
@@ -695,6 +807,11 @@ export default {
       this.allowSelect = response.data;
     });
 
+    this.getDicts("sys_qw_finish_temp_range").then((response) => {
+      this.finishTempList = response.data;
+    });
+
+
     this.getDicts("sys_fs_sop_watch_status").then(response => {
       this.sysFsSopWatchStatus = response.data;
     });
@@ -705,8 +822,17 @@ export default {
     courseList().then(response => {
       this.courseList = response.list;
     });
+
+    treeselect().then((res) => {
+      this.deptTree = res.data;
+    });
     this.getList();
   },
+  watch: {
+    userSelectList(newList) {
+      this.form.companyUserIds = newList.map((item) => item.id);
+    },
+  },
   methods: {
     // 处理规则中课程变化
     handleRuleCourseChange(item) {
@@ -922,6 +1048,7 @@ export default {
       this.setting = []
       this.chatSetting = []
       this.resetForm("form");
+      this.prevSelected = [];
     },
     /** 搜索按钮操作 */
     handleQuery() {
@@ -952,48 +1079,22 @@ export default {
       this.videoNumOptions.open = true;
     },
 
-    /**
-     * 查看完课模板
-     */
-    handleSelectDetails(row) {
-
-      this.reset();
-      const id = row.id || this.ids
-      getCourseFinishTemp(id).then(response => {
-        console.log("this.form222 ", response.data)
-        this.form = response.data;
-
-        this.setting = JSON.parse(this.form.setting)
-        this.chatSetting = JSON.parse(this.form.chatSetting)
-        if (response.data.companyUserIds != null) {
-          this.companyUserIds = this.form.companyUserIds.split(",");
-        }
-
-        videoList(this.form.courseId).then(response => {
-          this.videoList = response.list;
-        });
-
-        this.open = true;
-        this.title = "查看完课模板";
-        this.formType = 2;
-
-      });
-    },
     /** 修改按钮操作 */
     handleUpdate(row) {
       this.reset();
       const id = row.id || this.ids
       getCourseFinishTemp(id).then(response => {
-        console.log("this.response.data", response.data)
         this.form = response.data;
-
-        this.setting = JSON.parse(this.form.setting)
-        this.chatSetting = JSON.parse(this.form.chatSetting)
-        if (response.data.companyUserIds != null) {
-          this.companyUserIds = this.form.companyUserIds.split(",");
+        this.prevSelected = this.form.deptArrs;
+        this.setting = JSON.parse(this.form.setting);
+        if (response.data.userSelectList != null) {
+          this.userSelectList = this.form.userSelectList;
+          this.form.checkRange = 1;
         }
-
-        videoList(this.form.courseId).then(response => {
+        if (!!response.data.deptArrs) {
+          this.form.checkRange = 2;
+        }
+        videoList(this.form.courseId).then((response) => {
           this.videoList = response.list;
         });
         this.setting.forEach(item => {
@@ -1008,7 +1109,6 @@ export default {
             });
           }
         });
-
         this.open = true;
         this.title = "修改完课模板";
         this.formType = 1;
@@ -1016,15 +1116,52 @@ export default {
     },
     /** 提交按钮 */
     submitForm() {
-      this.$refs["form"].validate(valid => {
+      this.$refs["form"].validate((valid) => {
         if (valid) {
-
           if (this.form.isAllCompanyUser == null) {
             this.form.isAllCompanyUser = 2;
           }
-          this.form.companyUserIds = this.companyUserIds.toString()
-          this.form.parentId = this.parentId
 
+          if (
+            (this.form.isAllCompanyUser == 2 ||
+              this.form.isAllCompanyUser == null) &&
+            (this.form.companyUserIds == null ||
+              this.form.companyUserIds.length == 0) &&
+            (this.form.deptArrs == null || this.form.deptArrs.length == 0)
+          ) {
+            return this.$message.error("请选择企业微信员工或者部门");
+          }
+          //全选客服清空
+          if (this.form.isAllCompanyUser != 2) {
+            this.form.checkRange = null;
+          }
+
+
+          console.log("sout", this.form);
+
+          if (
+            this.form.companyUserIds != null &&
+            this.form.companyUserIds.length > 0
+          ) {
+            if (this.form.companyUserIds && Array.isArray(this.form.companyUserIds)) {
+              this.form.companyUserIds = this.form.companyUserIds.join(",");
+            }
+          } else {
+            this.form.companyUserIds = null;
+          }
+          //根据选择范围清空另外一个参数
+          if (this.form.checkRange == 1) {
+            // 清空部门id
+            this.form.deptArrs = [];
+            this.form.deptIds = "";
+          } else if (this.form.checkRange == 2) {
+            //清空客服个人id
+            this.form.companyUserIds = "";
+          } else {
+            this.form.deptArrs = [];
+            this.form.deptIds = "";
+            this.form.companyUserIds = "";
+          }
 
           const processedSetting = this.setting.map(item => {
             const newItem = {...item};
@@ -1039,54 +1176,95 @@ export default {
 
           this.form.setting = JSON.stringify(processedSetting);
           this.form.chatSetting = JSON.stringify(this.chatSetting)
+          this.form.parentId = this.parentId
 
           if (this.setting.length <= 0) {
-            return this.$message("请添加规则")
+            return this.$message("请添加规则");
           }
           for (let i = 0; i < this.setting.length; i++) {
-            if (this.setting[i].contentType == 1 && (this.setting[i].value == null || this.setting[i].value == "")) {
-              return this.$message.error("内容不能为空")
+            if (
+              this.setting[i].contentType == 1 &&
+              (this.setting[i].value == null || this.setting[i].value == "")
+            ) {
+              return this.$message.error("内容不能为空");
             }
-            if (this.setting[i].contentType == 2 && (this.setting[i].imgUrl == null || this.setting[i].imgUrl == "")) {
-              return this.$message.error("图片不能为空")
+            if (
+              this.setting[i].contentType == 2 &&
+              (this.setting[i].imgUrl == null || this.setting[i].imgUrl == "")
+            ) {
+              return this.$message.error("图片不能为空");
             }
-            if (this.setting[i].contentType == 3 && (this.setting[i].linkTitle == null || this.setting[i].linkTitle == "")) {
-              return this.$message.error("链接标题不能为空")
+            if (
+              this.setting[i].contentType == 3 &&
+              (this.setting[i].linkTitle == null ||
+                this.setting[i].linkTitle == "")
+            ) {
+              return this.$message.error("链接标题不能为空");
             }
-            if (this.setting[i].contentType == 3 && (this.setting[i].linkDescribe == null || this.setting[i].linkDescribe == "")) {
-              return this.$message.error("链接描述不能为空")
+            if (
+              this.setting[i].contentType == 3 &&
+              (this.setting[i].linkDescribe == null ||
+                this.setting[i].linkDescribe == "")
+            ) {
+              return this.$message.error("链接描述不能为空");
             }
-            if (this.setting[i].contentType == 3 && (this.setting[i].linkImageUrl == null || this.setting[i].linkImageUrl == "")) {
-              return this.$message.error("链接图片不能为空")
+            if (
+              this.setting[i].contentType == 3 &&
+              (this.setting[i].linkImageUrl == null ||
+                this.setting[i].linkImageUrl == "")
+            ) {
+              return this.$message.error("链接图片不能为空");
             }
-            if (this.setting[i].contentType == 3 && this.setting[i].type == 1 && (this.setting[i].linkUrl == null || this.setting[i].linkUrl == "")) {
-              return this.$message.error("链接地址不能为空")
+            if (
+              this.setting[i].contentType == 3 &&
+              this.setting[i].type == 1 &&
+              (this.setting[i].linkUrl == null || this.setting[i].linkUrl == "")
+            ) {
+              return this.$message.error("链接地址不能为空");
             }
-            if (this.setting[i].contentType == 5 && (this.setting[i].fileUrl == null || this.setting[i].fileUrl == "")) {
-              return this.$message.error("文件不能为空")
+            if (
+              this.setting[i].contentType == 5 &&
+              (this.setting[i].fileUrl == null || this.setting[i].fileUrl == "")
+            ) {
+              return this.$message.error("文件不能为空");
             }
-            if (this.setting[i].contentType == 6 && (this.setting[i].videoUrl == null || this.setting[i].videoUrl == "")) {
-              return this.$message.error("视频不能为空")
+            if (
+              this.setting[i].contentType == 6 &&
+              (this.setting[i].videoUrl == null ||
+                this.setting[i].videoUrl == "")
+            ) {
+              return this.$message.error("视频不能为空");
             }
-            if (this.setting[i].contentType == 7 && (this.setting[i].value == null || this.setting[i].value == "")) {
-              return this.$message.error("语音不能为空")
+            if (
+              this.setting[i].contentType == 7 &&
+              (this.setting[i].value == null || this.setting[i].value == "")
+            ) {
+              return this.$message.error("语音不能为空");
             }
           }
 
+          if (this.form.deptArrs != null && this.form.deptArrs.length > 0) {
+            this.form.deptIds = "";
+            this.form.deptArrs.forEach((i) => {
+              this.form.deptIds += i + ",";
+            });
+            this.form.deptIds = this.form.deptIds.slice(0, -1);
+          }
+
           if (this.form.id != null) {
-            updateCourseFinishTemp(this.form).then(response => {
+            updateCourseFinishTemp(this.form).then((response) => {
               this.msgSuccess("修改成功");
               this.open = false;
-              this.getList();
               this.setting = [];
-              this.chatSetting = [];
+              this.userSelectList = [];
+              this.getList();
             });
           } else {
-            addCourseFinishTemp(this.form).then(response => {
+            addCourseFinishTemp(this.form).then((response) => {
               this.msgSuccess("新增成功");
               this.open = false;
               this.setting = [];
-              this.chatSetting = [];
+              this.userSelectList = [];
               this.getList();
             });
           }
@@ -1123,7 +1301,148 @@ export default {
         this.exportLoading = false;
       }).catch(() => {
       });
-    }
+    },
+    logChange(val) {
+      this.$forceUpdate();
+    },
+    //选择群发的企业成员账号
+    handlelistUser() {
+      this.listUser.title = "选择企业成员";
+      this.listUser.open = true;
+    },
+
+    handleClearCompanyUser(){
+      this.userSelectList = [];
+    },
+
+    //删除一些选择了的账号
+    handleClosegroupUser(list) {
+      // 假设 list 对象具有一个 id 属性
+      const index = this.userSelectList.findIndex((t) => t.id === list.id);
+      if (index !== -1) {
+        this.userSelectList.splice(index, 1);
+      }
+    },
+
+    //选择的成员账号列表
+    selectUserList(list) {
+      this.listUser.open = false;
+
+      // 3. 遍历要添加的 list,逐条判断是否存在重复
+      list.forEach((newItem) => {
+        // some() 判断是否存在相同 id
+        const isExist = this.userSelectList.some(
+          (oldItem) => oldItem.id === newItem.id
+        );
+        if (!isExist) {
+          // 不存在重复的,才添加
+          this.userSelectList.push(newItem);
+        }
+      });
+    },
+
+
+    /**
+     * 查看完课模板
+     */
+    handleSelectDetails(row) {
+      this.reset();
+      const id = row.id || this.ids;
+      getCourseFinishTemp(id).then((response) => {
+        console.log("this.form222 ", response.data);
+        this.form = response.data;
+
+        this.setting = JSON.parse(this.form.setting);
+
+        if (response.data.userSelectList != null) {
+          this.userSelectList = this.form.userSelectList;
+          this.form.checkRange = 1;
+        }
+        if (!!response.data.deptArrs) {
+          this.form.checkRange = 2;
+        }
+        if (response.data.companyUserIds != null) {
+          this.userSelectList = this.form.companyUserIds;
+        }
+
+        videoList(this.form.courseId).then((response) => {
+          this.videoList = response.list;
+        });
+
+        this.open = true;
+        this.title = "查看完课模板";
+        this.formType = 2;
+      });
+    },
+
+    //选择部门
+    handleDeptChange(value) {
+      //通过先后数组长短来判定是选择还是反选
+      var isCheck = value.length > this.prevSelected.length? true: false;
+      //获取当前操作的节点
+      var node = this.findChangedNode(value)
+      //拿到节点在原始树形结构中的数据
+      var checkTreeNode = this.findNodeById(this.deptTree,node);
+      //判定当前选择的节点是否涵盖有树枝
+      if(!!checkTreeNode && 'children' in checkTreeNode){
+        //拿到节点下所有子集id
+        var checkChildrenIds  = this.getAllChildrenIds(checkTreeNode);
+        if(isCheck){
+          this.form.deptArrs = [...new Set([...this.form.deptArrs, ...checkChildrenIds])];
+        }else{
+          var tempList =  this.form.deptArrs.filter(item => !checkChildrenIds.includes(item));
+          this.form.deptArrs =[...new Set(tempList)]
+        }
+      }
+      //完成后记录当前选择的部门ids
+      this.prevSelected = this.form.deptArrs;
+    },
+    // 查找变更的节点ID
+    findChangedNode(current) {
+      // 处理初始状态
+      if (this.prevSelected.length === 0 && current.length > 0) {
+        return current[current.length - 1];
+      }
+
+      // 找出当前有而之前没有的节点(新增选中)
+      const added = current.filter(id => !this.prevSelected.includes(id));
+      if (added.length > 0) {
+        return added[0];
+      }
+
+      // 找出之前有而当前没有的节点(取消选中)
+      const removed = this.prevSelected.filter(id => !current.includes(id));
+      if (removed.length > 0) {
+        return removed[0];
+      }
+
+      return null;
+    },
+    // 根据ID查找节点
+    findNodeById(nodes, id) {
+      for (const node of nodes) {
+        if (node.id === id) {
+          return node;
+        }
+        if (node.children && node.children.length > 0) {
+          const found = this.findNodeById(node.children, id);
+          if (found) return found;
+        }
+      }
+      return null;
+    },
+    // 获取节点的所有子节点ID(递归)
+    getAllChildrenIds(node) {
+      let ids = [];
+      if (node.children && node.children.length > 0) {
+        node.children.forEach(child => {
+          ids.push(child.id);
+          // 递归获取子节点的子节点
+          ids = [...ids, ...this.getAllChildrenIds(child)];
+        });
+      }
+      return ids;
+    },
   }
 };
 </script>

+ 1129 - 0
src/views/course/courseFinishTemp/indexOld.vue

@@ -0,0 +1,1129 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="模板名称" prop="name">
+        <el-input
+          v-model="queryParams.name"
+          placeholder="请输入模板名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-select v-model="queryParams.status" placeholder="请选择状态" clearable size="small">
+          <el-option
+            v-for="dict in statusOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="课程" prop="courseId">
+        <el-input
+          v-model="queryParams.courseId"
+          placeholder="请输入课程id"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="小节" prop="videoId">
+        <el-input
+          v-model="queryParams.videoId"
+          placeholder="请输入小节视频id"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          v-hasPermi="['courseFinishTemp:course:add']"
+        >新增
+        </el-button>
+      </el-col>
+<!--      <el-col :span="1.5">-->
+<!--        <el-button-->
+<!--          type="success"-->
+<!--          plain-->
+<!--          icon="el-icon-edit"-->
+<!--          size="mini"-->
+<!--          :disabled="multiple"-->
+<!--          @click="handleUpdate"-->
+<!--          v-hasPermi="['courseFinishTemp:course:edit']"-->
+<!--        >修改状态-->
+<!--        </el-button>-->
+<!--      </el-col>-->
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+          v-hasPermi="['courseFinishTemp:course:remove']"
+        >删除
+        </el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          :loading="exportLoading"
+          @click="handleExport"
+          v-hasPermi="['courseFinishTemp:course:export']"
+        >导出
+        </el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table border v-loading="loading" :data="courseFinishTempList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center"/>
+      <el-table-column label="模板ID" align="center" prop="id"/>
+      <el-table-column label="模板名称" align="center" prop="name"/>
+      <el-table-column label="状态" align="center" prop="status">
+        <template slot-scope="scope">
+          <dict-tag :options="statusOptions" :value="scope.row.status"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="课程名称" align="center" prop="courseName"/>
+      <el-table-column label="小节名称" align="center" prop="videoName"/>
+      <el-table-column label="创建时间" align="center" prop="createTime"/>
+      <el-table-column label="修改时间" align="center" prop="updateTime"/>
+      <el-table-column label="全选销售" align="center" prop="isAllCompanyUser">
+        <template slot-scope="scope">
+          <dict-tag :options="allowSelect" :value="scope.row.isAllCompanyUser"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['courseFinishTemp:course:edit']"
+          >修改
+          </el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-s-promotion"
+            @click="handleSelectDetails(scope.row)"
+            v-hasPermi="['courseFinishTemp:course:query']"
+          >详情
+          </el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['courseFinishTemp:course:remove']"
+          >删除
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改完课模板对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="1000px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="模板名称" prop="name">
+          <el-input v-model="form.name" placeholder="请输入模板名称"/>
+        </el-form-item>
+        <el-form-item label="状态">
+          <el-radio-group v-model="form.status">
+            <el-radio
+              v-for="dict in statusOptions"
+              :key="dict.dictValue"
+              :label="parseInt(dict.dictValue)"
+            >{{ dict.dictLabel }}
+            </el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="选择课程">
+          <el-select v-model="form.courseId" placeholder="请选择课程" style=" margin-right: 10px;" size="mini"
+                     @change="courseChange()">
+            <el-option
+              v-for="dict in courseList"
+              :key="dict.dictValue"
+              :label="dict.dictLabel"
+              :value="parseInt(dict.dictValue)"
+            />
+          </el-select>
+          <el-select v-model="form.videoId" placeholder="请选择小节" size="mini" style=" margin-right: 10px;">
+            <el-option
+              v-for="dict in videoList"
+              :key="dict.dictValue"
+              :label="dict.dictLabel"
+              :value="parseInt(dict.dictValue)"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="规则" prop="setting">
+          <div v-for="(item, index) in setting" :key="index"
+               style="background-color: #fdfdfd; border: 1px solid #e6e6e6; margin-bottom: 20px;">
+            <el-row>
+              <el-col :span="22">
+                <el-form :model="item" label-width="70px">
+                  <el-form-item label="内容类别" style="margin: 2%">
+                    <el-radio-group v-model="item.contentType">
+                      <el-radio :label="item.dictValue" v-for="item in sysQwSopAiContentType">{{ item.dictLabel }}
+                      </el-radio>
+                    </el-radio-group>
+                  </el-form-item>
+                  <el-form-item label="内容" style="margin-bottom: 2%">
+                    <el-input v-if="item.contentType == 1 " v-model="item.value" type="textarea" :rows="3"
+                              placeholder="内容" style="width: 90%;margin-top: 10px;"/>
+
+                    <ImageUpload v-if="item.contentType == 2 " v-model="item.imgUrl" type="image" :num="1" :width="150"
+                                 :height="150"/>
+
+                    <div v-if="item.contentType == 3 ">
+                      <el-card class="box-card">
+                        <el-form-item label="链接标题:" label-width="100px">
+                          <el-input v-model="item.linkTitle" placeholder="请输入链接标题" style="width: 90%;"/>
+                        </el-form-item>
+                        <el-form-item label="链接描述:" label-width="100px">
+                          <el-input type="textarea" :rows="3" v-model="item.linkDescribe" placeholder="请输入链接描述"
+                                    style="width: 90%;margin-top: 1%;"/>
+                        </el-form-item>
+                        <el-form-item label="链接封面:" label-width="100px">
+                          <ImageUpload v-model="item.linkImageUrl" type="image" :num="1" :file-size="2" :width="150"
+                                       :height="150" style="margin-top: 1%;"/>
+                        </el-form-item>
+                        <el-form-item label="链接地址:" label-width="100px">
+                          <el-input v-model="item.linkUrl" placeholder="请输入链接地址" style="width: 90%;"/>
+                        </el-form-item>
+                      </el-card>
+                    </div>
+                    <div v-if="item.contentType == 4">
+                      <el-card class="box-card">
+
+                        <el-form-item label="选择课程">
+                          <el-select
+                            v-model="item.courseId"
+                            placeholder="请选择课程"
+                            style="margin-right: 10px"
+                            size="mini"
+                            @change="handleRuleCourseChange(item)"
+                          >
+                            <el-option
+                              v-for="dict in courseList"
+                              :key="dict.dictValue"
+                              :label="dict.dictLabel"
+                              :value="parseInt(dict.dictValue)"
+                            />
+                          </el-select>
+                          <el-select
+                            v-model="item.videoId"
+                            placeholder="请选择小节"
+                            size="mini"
+                            style="margin-right: 10px"
+                            filterable
+                            remote
+                            :remote-method="(query) => remoteMethodRuleVideo(query, item)"
+                            :loading="videoOptionsLoading"
+                            @change="handleRuleVideoChange(item)"
+                          >
+                            <el-option
+                              v-for="dict in videoOptions || []"
+                              :key="dict.dictValue"
+                              :label="dict.dictLabel"
+                              :value="parseInt(dict.dictValue)"
+                            />
+                          </el-select>
+                        </el-form-item>
+
+                        <el-card class="box-card" style="margin-top: 10px">
+                          <el-form-item label="标题" prop="miniprogramTitle">
+                            <el-input v-model="item.miniprogramTitle" placeholder="请输入小程序消息标题,最长为64字" />
+                          </el-form-item>
+                          <el-form-item label="封面" prop="miniprogramPicUrl">
+                            <ImageUpload v-model="item.miniprogramPicUrl" type="image" :num="10" :width="150" :height="150" />
+                          </el-form-item>
+                        </el-card>
+                      </el-card>
+                    </div>
+                    <div v-if="item.contentType == 5 ">
+
+                      <el-form-item label="上传文件:" prop="fileUrl" label-width="100px">
+                        <el-upload
+                          v-model="item.fileUrl"
+                          class="avatar-uploader"
+                          :action="uploadUrl"
+                          :show-file-list="false"
+                          :on-success="(res, file) => handleAvatarSuccessFile(res, file, item)"
+                          :before-upload="beforeAvatarUploadFile">
+                          <i class="el-icon-plus avatar-uploader-icon"></i>
+                        </el-upload>
+                        <el-link v-if="item.fileUrl" type="primary" :href="downloadUrl(item.fileUrl)" download>
+                          {{ item.fileUrl }}
+                        </el-link>
+                      </el-form-item>
+
+                    </div>
+
+                    <div v-if="item.contentType == 6 ">
+                      <el-form-item label="上传视频:" prop="videoUrl" label-width="100px">
+                        <el-upload
+                          v-model="item.videoUrl"
+                          class="avatar-uploader"
+                          :action="uploadUrl"
+                          :show-file-list="false"
+                          :on-success="(res, file) => handleAvatarSuccessVideo(res, file, item)"
+                          :before-upload="beforeAvatarUploadVideo">
+                          <i class="el-icon-plus avatar-uploader-icon"></i>
+                        </el-upload>
+                        <video v-if="item.videoUrl"
+                               :src="item.videoUrl"
+                               controls style="width: 200px;height: 100px">
+                        </video>
+                      </el-form-item>
+                    </div>
+                    <div v-if="item.contentType == 7 ">
+                      <el-input
+                        v-model="item.value"
+                        type="textarea" :rows="3" maxlength="66" show-word-limit
+                        placeholder="输入要转为语音的内容" style="width: 90%;margin-top: 10px;"
+                        @input="handleInputVideoText(item.value,item)"/>
+                    </div>
+                    <div v-if="item.contentType == 8">
+                      <el-button type="primary"
+                                 style="margin-bottom: 1%"
+                                 @click="hanldeSelectVideoNum(setting,index)">
+                        选择视频号
+                      </el-button>
+                      <el-card class="box-card" v-if="item.coverUrl">
+                        <el-form-item label="封面标题:" label-width="100px">
+                          <el-input v-model="item.nickname"
+                                    style="width: 90%;margin-bottom: 1%" disabled/>
+                        </el-form-item>
+                        <el-form-item label="头像:" label-width="100px">
+                          <el-image
+                            v-if="item.avatar != null"
+                            :src="item.avatar"
+                            :preview-src-list="[item.avatar]"
+                            :style="{ width: '50px', height: '50px' }"
+                          ></el-image>
+                        </el-form-item>
+                        <el-form-item label="封面:" label-width="100px">
+                          <el-image
+                            v-if="item.coverUrl != null"
+                            :src="item.coverUrl"
+                            :preview-src-list="[item.coverUrl]"
+                            :style="{ width: '200px', height: '200px' }"
+                          ></el-image>
+
+                        </el-form-item>
+                        <el-form-item label="简介:" label-width="100px">
+                          <el-input type="textarea" :rows="3"
+                                    v-model="item.desc"
+                                    style="width: 90%;margin-top: 1%;" disabled/>
+                        </el-form-item>
+                        <el-form-item label="视频地址:" label-width="100px"
+                                      style="margin-top: 1%">
+                          <el-input v-model="item.url"
+                                    style="width: 90%;" disabled/>
+                        </el-form-item>
+                      </el-card>
+                    </div>
+
+                  </el-form-item>
+                </el-form>
+              </el-col>
+              <el-col :span="1" :offset="1">
+                <i class="el-icon-delete" @click="delSetList(index, 0)" style="margin-top: 20px;"
+                   v-if="setting.length>1 && formType==1"></i>
+              </el-col>
+            </el-row>
+          </div>
+          <el-link type="primary" class="el-icon-plus" :underline="false" @click='addSetList(0)' v-if="formType==1">
+            添加内容
+          </el-link>
+        </el-form-item>
+        <el-form-item label="群聊恭喜规则" prop="setting">
+          <div v-for="(item, index) in chatSetting" :key="index"
+               style="background-color: #fdfdfd; border: 1px solid #e6e6e6; margin-bottom: 20px;">
+            <el-row>
+              <el-col :span="22">
+                <el-form :model="item" label-width="70px">
+                  <el-form-item label="内容类别" style="margin: 2%">
+                    <el-radio-group v-model="item.contentType">
+                      <el-radio :label="item.dictValue" v-for="item in sysQwSopAiContentType">{{ item.dictLabel }}
+                      </el-radio>
+                    </el-radio-group>
+                  </el-form-item>
+                  <el-form-item label="内容" style="margin-bottom: 2%">
+                    <el-input v-if="item.contentType == 1 " v-model="item.value" type="textarea" :rows="3"
+                              placeholder="内容" style="width: 90%;margin-top: 10px;"/>
+
+                    <ImageUpload v-if="item.contentType == 2 " v-model="item.imgUrl" type="image" :num="1" :width="150"
+                                 :height="150"/>
+
+                    <div v-if="item.contentType == 3 ">
+                      <el-card class="box-card">
+                        <el-form-item label="链接标题:" label-width="100px">
+                          <el-input v-model="item.linkTitle" placeholder="请输入链接标题" style="width: 90%;"/>
+                        </el-form-item>
+                        <el-form-item label="链接描述:" label-width="100px">
+                          <el-input type="textarea" :rows="3" v-model="item.linkDescribe" placeholder="请输入链接描述"
+                                    style="width: 90%;margin-top: 1%;"/>
+                        </el-form-item>
+                        <el-form-item label="链接封面:" label-width="100px">
+                          <ImageUpload v-model="item.linkImageUrl" type="image" :num="1" :file-size="2" :width="150"
+                                       :height="150" style="margin-top: 1%;"/>
+                        </el-form-item>
+                        <el-form-item label="链接地址:" label-width="100px">
+                          <el-input v-model="item.linkUrl" placeholder="请输入链接地址" style="width: 90%;"/>
+                        </el-form-item>
+                      </el-card>
+                    </div>
+                    <div v-if="item.contentType == 4">
+                      <el-card class="box-card">
+
+                        <el-form-item label="选择课程">
+                          <el-select
+                            v-model="item.courseId"
+                            placeholder="请选择课程"
+                            style="margin-right: 10px"
+                            size="mini"
+                            @change="handleRuleCourseChange(item)"
+                          >
+                            <el-option
+                              v-for="dict in courseList"
+                              :key="dict.dictValue"
+                              :label="dict.dictLabel"
+                              :value="parseInt(dict.dictValue)"
+                            />
+                          </el-select>
+                          <el-select
+                            v-model="item.videoId"
+                            placeholder="请选择小节"
+                            size="mini"
+                            style="margin-right: 10px"
+                            filterable
+                            remote
+                            :remote-method="(query) => remoteMethodRuleVideo(query, item)"
+                            :loading="videoOptionsLoading"
+                            @change="handleRuleVideoChange(item)"
+                          >
+                            <el-option
+                              v-for="dict in videoOptions || []"
+                              :key="dict.dictValue"
+                              :label="dict.dictLabel"
+                              :value="parseInt(dict.dictValue)"
+                            />
+                          </el-select>
+                        </el-form-item>
+
+                        <el-card class="box-card" style="margin-top: 10px">
+                          <el-form-item label="标题" prop="miniprogramTitle">
+                            <el-input v-model="item.miniprogramTitle" placeholder="请输入小程序消息标题,最长为64字" />
+                          </el-form-item>
+                          <el-form-item label="封面" prop="miniprogramPicUrl">
+                            <ImageUpload v-model="item.miniprogramPicUrl" type="image" :num="10" :width="150" :height="150" />
+                          </el-form-item>
+                        </el-card>
+                      </el-card>
+                    </div>
+                    <div v-if="item.contentType == 5 ">
+
+                      <el-form-item label="上传文件:" prop="fileUrl" label-width="100px">
+                        <el-upload
+                          v-model="item.fileUrl"
+                          class="avatar-uploader"
+                          :action="uploadUrl"
+                          :show-file-list="false"
+                          :on-success="(res, file) => handleAvatarSuccessFile(res, file, item)"
+                          :before-upload="beforeAvatarUploadFile">
+                          <i class="el-icon-plus avatar-uploader-icon"></i>
+                        </el-upload>
+                        <el-link v-if="item.fileUrl" type="primary" :href="downloadUrl(item.fileUrl)" download>
+                          {{ item.fileUrl }}
+                        </el-link>
+                      </el-form-item>
+
+                    </div>
+
+                    <div v-if="item.contentType == 6 ">
+                      <el-form-item label="上传视频:" prop="videoUrl" label-width="100px">
+                        <el-upload
+                          v-model="item.videoUrl"
+                          class="avatar-uploader"
+                          :action="uploadUrl"
+                          :show-file-list="false"
+                          :on-success="(res, file) => handleAvatarSuccessVideo(res, file, item)"
+                          :before-upload="beforeAvatarUploadVideo">
+                          <i class="el-icon-plus avatar-uploader-icon"></i>
+                        </el-upload>
+                        <video v-if="item.videoUrl"
+                               :src="item.videoUrl"
+                               controls style="width: 200px;height: 100px">
+                        </video>
+                      </el-form-item>
+                    </div>
+                    <div v-if="item.contentType == 7 ">
+                      <el-input
+                        v-model="item.value"
+                        type="textarea" :rows="3" maxlength="66" show-word-limit
+                        placeholder="输入要转为语音的内容" style="width: 90%;margin-top: 10px;"
+                        @input="handleInputVideoText(item.value,item)"/>
+                    </div>
+                    <div v-if="item.contentType == 8">
+                      <el-button type="primary"
+                                 style="margin-bottom: 1%"
+                                 @click="hanldeSelectVideoNum(setting,index)">
+                        选择视频号
+                      </el-button>
+                      <el-card class="box-card" v-if="item.coverUrl">
+                        <el-form-item label="封面标题:" label-width="100px">
+                          <el-input v-model="item.nickname"
+                                    style="width: 90%;margin-bottom: 1%" disabled/>
+                        </el-form-item>
+                        <el-form-item label="头像:" label-width="100px">
+                          <el-image
+                            v-if="item.avatar != null"
+                            :src="item.avatar"
+                            :preview-src-list="[item.avatar]"
+                            :style="{ width: '50px', height: '50px' }"
+                          ></el-image>
+                        </el-form-item>
+                        <el-form-item label="封面:" label-width="100px">
+                          <el-image
+                            v-if="item.coverUrl != null"
+                            :src="item.coverUrl"
+                            :preview-src-list="[item.coverUrl]"
+                            :style="{ width: '200px', height: '200px' }"
+                          ></el-image>
+
+                        </el-form-item>
+                        <el-form-item label="简介:" label-width="100px">
+                          <el-input type="textarea" :rows="3"
+                                    v-model="item.desc"
+                                    style="width: 90%;margin-top: 1%;" disabled/>
+                        </el-form-item>
+                        <el-form-item label="视频地址:" label-width="100px"
+                                      style="margin-top: 1%">
+                          <el-input v-model="item.url"
+                                    style="width: 90%;" disabled/>
+                        </el-form-item>
+                      </el-card>
+                    </div>
+
+                  </el-form-item>
+                </el-form>
+              </el-col>
+              <el-col :span="1" :offset="1">
+                <i class="el-icon-delete" @click="delSetList(index,1)" style="margin-top: 20px;"
+                   v-if="setting.length>1 && formType==1"></i>
+              </el-col>
+            </el-row>
+          </div>
+          <el-link type="primary" class="el-icon-plus" :underline="false" @click='addSetList(1)' v-if="formType==1">
+            添加内容
+          </el-link>
+        </el-form-item>
+        <el-form-item label="全选销售" prop="isAllCompanyUser">
+          <el-switch
+            v-model="form.isAllCompanyUser"
+            active-color="#13ce66"
+            inactive-color="#ff4949"
+            :active-value="1"
+            :inactive-value="2">
+          </el-switch>
+          <span v-if="form.isAllCompanyUser == '1'" style="margin-left: 10px;color: #13ce66">是</span>
+          <span v-else style="margin-left: 10px;color: #ff4949">否</span>
+          <Tip :title="'是否 全选整个销售公司的销售,作用于这个课程小节,当这个小节有客户完课时,发送此设置的内容'" />
+        </el-form-item>
+        <el-form-item label="所属销售" prop="companyUserIds" v-if="form.isAllCompanyUser == '2'">
+          <el-select v-model="companyUserIds" remote multiple placeholder="请选择" filterable style="width: 100%;">
+            <el-option
+              v-for="dict in userList"
+              :key="dict.userId"
+              :label="dict.nickName"
+              :value="dict.userId.toString()">
+            </el-option>
+          </el-select>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer" v-if="formType==1">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+    <el-dialog :title="videoNumOptions.title" :visible.sync="videoNumOptions.open" width="1500px" append-to-body>
+      <userVideo ref="QwUserVideo" @videoResult="qwUserVideoResult"></userVideo>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import {
+  addCourseFinishTemp,
+  delCourseFinishTemp,
+  exportCourseFinishTemp,
+  getCourseFinishTemp,
+  listCourseFinishTemp,
+  updateCourseFinishTemp
+} from '@/api/course/courseFinishTemp'
+import { getUserList } from '@/api/company/companyUser'
+import { courseList, videoList } from '@/api/qw/sop'
+import ImageUpload from '@/views/qw/sop/ImageUpload.vue'
+import Tip from "../../../components/Tip/index.vue";
+import userVideo from "@/views/qw/userVideo/userVideo.vue";
+export default {
+  name: "CourseFinishTemp",
+  components: {Tip, ImageUpload,userVideo},
+  data() {
+    return {
+      videoOptionsLoading: false,
+      videoOptions: [],
+      videoLoading: false,
+      videoNumOptions: {
+        title: '选择视频号',
+        open: false,
+        content: null,
+        contentIndex: null,
+      },
+      //上传语音的遮罩层
+      voiceLoading: false,
+      uploadUrl: process.env.VUE_APP_BASE_API + "/common/uploadOSS2",
+      uploadUrlByVoice: process.env.VUE_APP_BASE_API + "/common/uploadOSSByHOOKVoice",
+      companyUserIds: [],
+      userList: [],
+      // 状态字典
+      statusOptions: [],
+      allowSelect: [],
+      courseList: [],
+      videoList: [],
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      formType: 1,
+      // 总条数
+      total: 0,
+      // 完课模板表格数据
+      courseFinishTempList: [],
+      //插件版
+      sysQwSopAiContentType: [],
+
+      sysFsSopWatchStatus: [],
+
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        name: null,
+        status: null,
+        setting: null,
+        chatSetting: null,
+        companyId: null,
+        courseId: null,
+        videoId: null,
+        companyUserIds: null,
+        isDel: null
+      },
+      // 模板表格数据
+      setting: [],
+      parentId: "",
+      chatSetting: [],
+      // 表单参数
+      form: {
+        companyUserIds: [],
+        setting: null,
+        chatSetting: null,
+        videoIdSet: null,
+        courseIdSet: null,
+      },
+      // 表单校验
+      rules: {}
+    };
+  },
+  created() {
+    this.queryParams.parentId = this.$route.params && this.$route.params.parentId;
+    this.parentId = this.queryParams.parentId;
+    getUserList().then(response => {
+      this.userList = response.data;
+      console.log("this.userList", this.userList)
+    });
+    this.getDicts("sys_company_status").then(response => {
+      this.statusOptions = response.data;
+    });
+
+    //复用一下
+    this.getDicts("sys_qw_allow_select").then(response => {
+      this.allowSelect = response.data;
+    });
+
+    this.getDicts("sys_fs_sop_watch_status").then(response => {
+      this.sysFsSopWatchStatus = response.data;
+    });
+
+    this.getDicts("sys_qwSopAi_contentType").then(response => {
+      this.sysQwSopAiContentType = response.data;
+    });
+    courseList().then(response => {
+      this.courseList = response.list;
+    });
+    this.getList();
+  },
+  methods: {
+    // 处理规则中课程变化
+    handleRuleCourseChange(item) {
+      // 为当前规则项单独加载视频列表
+      videoList(item.courseId).then((response) => {
+        // 只保存视频列表,不保存整个响应对象
+        this.videoOptions = response.list;
+
+        this.$set(item, 'videoId', null); // Reset video selection when course changes
+
+        // 自动设置封面为课程封面
+        const selectedCourse = this.courseList.find(
+          course => parseInt(course.dictValue) === item.courseId
+        );
+        if (selectedCourse) {
+          this.$set(item, 'miniprogramPicUrl', selectedCourse.dictImgUrl);
+        }
+      });
+    },
+
+
+    // 处理规则中视频变化
+    handleRuleVideoChange(item) {
+      if (!item.videoId) return;
+
+      // 自动设置标题为视频标题
+      const selectedVideo = (this.videoOptions || []).find(
+        video => parseInt(video.dictValue) === item.videoId
+      );
+      if (selectedVideo) {
+        this.$set(item, 'miniprogramTitle', selectedVideo.dictLabel);
+      }
+    },
+
+    // 远程搜索规则中的视频
+    remoteMethodRuleVideo(query, item) {
+      if (!item.courseId) {
+        this.$message.warning('请先选择课程');
+        this.videoOptions = [];
+        reject();
+        return;
+      }
+
+      this.videoOptionsLoading = true;
+      const data = query ? { title: query } : {};
+
+      videoList(item.courseId, data).then((response) => {
+        this.videoOptions =  response.list;
+        this.videoOptionsLoading =  false;
+        resolve(response);
+      }).catch((error) => {
+        this.videoOptionsLoading = false;
+        reject(error);
+      });
+    },
+    remoteMethodVideo(query) {
+      if (!this.form.courseId) {
+        this.$message.warning('请先选择课程');
+        this.videoList = []; // 清空小节列表
+        return;
+      }
+      if (query !== '') {
+        this.videoLoading = true;
+        // 这里调用接口搜索小节,假设 videoList 方法支持搜索参数
+        var data = {
+          title:query
+        }
+        videoList(this.form.courseId, data).then((response) => {
+          this.videoList = response.list;
+          this.videoLoading = false;
+        });
+      } else {
+        // 如果查询为空,则加载全部
+        videoList(this.form.courseId).then((response) => {
+          this.videoList = response.list;
+        });
+      }
+    },
+    qwUserVideoResult(val) {
+
+      // 根据选中的内容,将返回的数据更新到相应的表单项
+      const content = this.videoNumOptions.content;
+      const setList = content[this.videoNumOptions.contentIndex];
+      setList.nickname = val.nickname;
+      setList.avatar = val.avatar;
+      setList.coverUrl = val.coverUrl;
+      setList.thumbUrl = val.thumbUrl;
+      setList.desc = val.desc;
+      setList.url = val.url;
+      setList.extras = val.extras;
+      setList.videoId = val.id;
+      console.info(setList)
+
+      this.videoNumOptions.open = false;
+
+    },
+    courseChange() {
+
+      videoList(this.form.courseId).then(response => {
+        this.videoList = response.list;
+      });
+    },
+    /** 查询完课模板列表 */
+    getList() {
+      this.loading = true;
+      listCourseFinishTemp(this.queryParams).then(response => {
+        this.courseFinishTempList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    handleAvatarSuccessFile(res, file, item) {
+      if (res.code === 200) {
+        // 使用 $set 确保响应式更新
+        this.$set(item, 'fileUrl', res.url);
+      } else {
+        this.msgError(res.msg);
+      }
+    },
+
+    beforeAvatarUploadFile(file) {
+      const isLt1M = file.size / 1024 / 1024 < 10;
+      if (!isLt1M) {
+        this.$message.error('上传大小不能超过 10MB!');
+      }
+      return isLt1M;
+    },
+    //下载文件
+    downloadUrl(materialUrl) {
+      // 直接返回文件 URL
+      return materialUrl;
+    },
+    handleInputVideoText(value, content) {
+      // 允许的字符:中文、英文(大小写)、数字和指定标点符号(,。!?)
+      const regex = /^[\u4e00-\u9fa5,。!?,!?]+$/;
+
+      // 删除不符合条件的字符
+      const filteredValue = value.split('').filter(char => regex.test(char)).join('');
+
+      this.$set(content, 'value', filteredValue);
+
+    },
+    handleAvatarSuccessVideo(res, file, item) {
+      if (res.code == 200) {
+        // 使用 $set 确保响应式更新
+        this.$set(item, 'videoUrl', res.url);
+      } else {
+        this.msgError(res.msg);
+      }
+    },
+
+    beforeAvatarUploadVideo(file) {
+      const isLt30M = file.size / 1024 / 1024 < 10;
+      const isMP4 = file.type === 'video/mp4';
+
+      if (!isMP4) {
+        this.$message.error('仅支持上传 MP4 格式的视频文件!');
+        return false;
+      }
+
+      if (!isLt30M) {
+        this.$message.error('上传大小不能超过 10MB!');
+        return false;
+      }
+
+      return true;
+    },
+
+    delSetList(index, type) {
+      if (type == 0) {
+        this.setting.splice(index, 1)
+      } else {
+        this.chatSetting.splice(index, 1)
+      }
+    },
+    addSetList(type) {
+      const newSetting = {
+        contentType: '1',
+        value: '',
+      };
+      // 将新设置项添加到 content.setting 数组中
+      if (type == 0) {
+        this.setting.push(newSetting);
+      } else {
+        this.chatSetting.push(newSetting);
+      }
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        name: null,
+        type: 1,
+        status: 1,
+        setting: [],
+        chatSetting: [],
+        companyId: null,
+        createBy: null,
+        createTime: null,
+        courseId: null,
+        videoId: null,
+        companyUserIds: null,
+        updateTime: null,
+        isDel: null,
+        isAllCompanyUser: null,
+      };
+      this.companyUserIds = []
+      this.setting = []
+      this.chatSetting = []
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length !== 1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加完课模板";
+    },
+    //选择视频号
+    hanldeSelectVideoNum(content, index) {
+      this.videoNumOptions.content = content;
+      this.videoNumOptions.contentIndex = index;
+      this.videoNumOptions.open = true;
+    },
+
+    /**
+     * 查看完课模板
+     */
+    handleSelectDetails(row) {
+
+      this.reset();
+      const id = row.id || this.ids
+      getCourseFinishTemp(id).then(response => {
+        console.log("this.form222 ", response.data)
+        this.form = response.data;
+
+        this.setting = JSON.parse(this.form.setting)
+        this.chatSetting = JSON.parse(this.form.chatSetting)
+        if (response.data.companyUserIds != null) {
+          this.companyUserIds = this.form.companyUserIds.split(",");
+        }
+
+        videoList(this.form.courseId).then(response => {
+          this.videoList = response.list;
+        });
+
+        this.open = true;
+        this.title = "查看完课模板";
+        this.formType = 2;
+
+      });
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id || this.ids
+      getCourseFinishTemp(id).then(response => {
+        console.log("this.response.data", response.data)
+        this.form = response.data;
+
+        this.setting = JSON.parse(this.form.setting)
+        this.chatSetting = JSON.parse(this.form.chatSetting)
+        if (response.data.companyUserIds != null) {
+          this.companyUserIds = this.form.companyUserIds.split(",");
+        }
+
+        videoList(this.form.courseId).then(response => {
+          this.videoList = response.list;
+        });
+        this.setting.forEach(item => {
+          if (item.contentType == 4 && item.courseId) {
+            // 初始化 videoOptions
+            this.videoOptions = [];
+            this.videoOptionsLoading = false;
+
+            // 获取课程对应的小节列表
+            videoList(item.courseId).then((videoResponse) => {
+              this.videoOptions = videoResponse.list;
+            });
+          }
+        });
+
+        this.open = true;
+        this.title = "修改完课模板";
+        this.formType = 1;
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+
+          if (this.form.isAllCompanyUser == null) {
+            this.form.isAllCompanyUser = 2;
+          }
+          this.form.companyUserIds = this.companyUserIds.toString()
+          this.form.parentId = this.parentId
+
+
+          const processedSetting = this.setting.map(item => {
+            const newItem = {...item};
+            if (newItem.videoOptions) {
+              delete newItem.videoOptions;
+            }
+            if (newItem.videoLoading !== undefined) {
+              delete newItem.videoLoading;
+            }
+            return newItem;
+          });
+
+          this.form.setting = JSON.stringify(processedSetting);
+          this.form.chatSetting = JSON.stringify(this.chatSetting)
+
+          if (this.setting.length <= 0) {
+            return this.$message("请添加规则")
+          }
+          for (let i = 0; i < this.setting.length; i++) {
+            if (this.setting[i].contentType == 1 && (this.setting[i].value == null || this.setting[i].value == "")) {
+              return this.$message.error("内容不能为空")
+            }
+            if (this.setting[i].contentType == 2 && (this.setting[i].imgUrl == null || this.setting[i].imgUrl == "")) {
+              return this.$message.error("图片不能为空")
+            }
+            if (this.setting[i].contentType == 3 && (this.setting[i].linkTitle == null || this.setting[i].linkTitle == "")) {
+              return this.$message.error("链接标题不能为空")
+            }
+            if (this.setting[i].contentType == 3 && (this.setting[i].linkDescribe == null || this.setting[i].linkDescribe == "")) {
+              return this.$message.error("链接描述不能为空")
+            }
+            if (this.setting[i].contentType == 3 && (this.setting[i].linkImageUrl == null || this.setting[i].linkImageUrl == "")) {
+              return this.$message.error("链接图片不能为空")
+            }
+            if (this.setting[i].contentType == 3 && this.setting[i].type == 1 && (this.setting[i].linkUrl == null || this.setting[i].linkUrl == "")) {
+              return this.$message.error("链接地址不能为空")
+            }
+            if (this.setting[i].contentType == 5 && (this.setting[i].fileUrl == null || this.setting[i].fileUrl == "")) {
+              return this.$message.error("文件不能为空")
+            }
+            if (this.setting[i].contentType == 6 && (this.setting[i].videoUrl == null || this.setting[i].videoUrl == "")) {
+              return this.$message.error("视频不能为空")
+            }
+            if (this.setting[i].contentType == 7 && (this.setting[i].value == null || this.setting[i].value == "")) {
+              return this.$message.error("语音不能为空")
+            }
+          }
+
+          if (this.form.id != null) {
+            updateCourseFinishTemp(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+              this.setting = [];
+              this.chatSetting = [];
+            });
+          } else {
+            addCourseFinishTemp(this.form).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.setting = [];
+              this.chatSetting = [];
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除完课模板编号为"' + ids + '"的数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function () {
+        return delCourseFinishTemp(ids);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("删除成功");
+      }).catch(() => {
+      });
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有完课模板数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        this.exportLoading = true;
+        return exportCourseFinishTemp(queryParams);
+      }).then(response => {
+        this.download(response.msg);
+        this.exportLoading = false;
+      }).catch(() => {
+      });
+    }
+  }
+};
+</script>

+ 463 - 87
src/views/course/courseFinishTempParent/index.vue

@@ -37,17 +37,17 @@
           v-hasPermi="['course:courseFinishTempParent:add']"
         >新增</el-button>
       </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="success"
-          plain
-          icon="el-icon-edit"
-          size="mini"
-          :disabled="single"
-          @click="handleUpdate"
-          v-hasPermi="['course:courseFinishTempParent:edit']"
-        >修改</el-button>
-      </el-col>
+<!--      <el-col :span="1.5">-->
+<!--        <el-button-->
+<!--          type="success"-->
+<!--          plain-->
+<!--          icon="el-icon-edit"-->
+<!--          size="mini"-->
+<!--          :disabled="single"-->
+<!--          @click="handleUpdate"-->
+<!--          v-hasPermi="['course:courseFinishTempParent:edit']"-->
+<!--        >修改</el-button>-->
+<!--      </el-col>-->
       <el-col :span="1.5">
         <el-button
           type="danger"
@@ -83,13 +83,13 @@
       </el-table-column>
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
         <template slot-scope="scope">
-          <el-button
-            size="mini"
-            type="text"
-            icon="el-icon-edit"
-            @click="handleUpdate(scope.row)"
-            v-hasPermi="['course:courseFinishTempParent:edit']"
-          >修改</el-button>
+<!--          <el-button-->
+<!--            size="mini"-->
+<!--            type="text"-->
+<!--            icon="el-icon-edit"-->
+<!--            @click="handleUpdate(scope.row)"-->
+<!--            v-hasPermi="['course:courseFinishTempParent:edit']"-->
+<!--          >修改</el-button>-->
           <el-button
             size="mini"
             type="text"
@@ -515,15 +515,99 @@
           <span v-if="form.isAllCompanyUser == '1'" style="margin-left: 10px;color: #13ce66">是</span>
           <span v-else style="margin-left: 10px;color: #ff4949">否</span>
         </el-form-item>
-        <el-form-item label="所属销售" prop="companyUserIds" v-if="!form.id && form.isAllCompanyUser == '2'">
-          <el-select v-model="companyUserIds" remote multiple placeholder="请选择" filterable style="width: 100%;">
-            <el-option
-              v-for="dict in userList"
-              :key="dict.userId"
-              :label="dict.nickName"
-              :value="dict.userId.toString()">
-            </el-option>
-          </el-select>
+        <el-form-item label="选择范围" v-if="!form.id && form.isAllCompanyUser != 1">
+          <el-radio-group v-model="form.checkRange" @change="logChange">
+            <el-radio
+              v-for="dict in finishTempList"
+              :key="dict.dictValue"
+              :label="parseInt(dict.dictValue)"
+            >{{ dict.dictLabel }}</el-radio
+            >
+          </el-radio-group>
+          <div
+            style="
+              color: #999;
+              font-size: 14px;
+              display: flex;
+              align-items: center;
+            "
+          >
+            <i class="el-icon-info"></i>
+            同时存在的情况下,根据配置顺序决定完课模板的优先级 单选客服>选部门>全选销售公司
+          </div>
+        </el-form-item>
+
+        <el-form-item
+          v-show="!form.id && form.checkRange == 1"
+          label="所属企微员工"
+          prop="companyUserIds"
+          label-width="100px"
+          v-if="form.isAllCompanyUser != 1"
+        >
+          <div>
+            <el-button
+              size="medium"
+              icon="el-icon-circle-plus-outline"
+              plain
+              @click="handlelistUser"
+            >请选择使用成员</el-button
+            >
+            <el-button
+              size="medium"
+              icon="el-icon-remove-outline"
+              plain
+              @click="handleClearCompanyUser()">清除员工
+            </el-button>
+          </div>
+          <div>
+            <el-tag
+              style="margin-left: 5px"
+              size="medium"
+              :key="list.id"
+              v-for="list in userSelectList"
+              closable
+              :disable-transitions="false"
+              @close="handleClosegroupUser(list)"
+            >
+              {{ list.qwUserId }}({{ list.qwUserName }})({{ list.nickName }})
+            </el-tag>
+          </div>
+          <div
+            style="
+              color: #999;
+              font-size: 14px;
+              display: flex;
+              align-items: center;
+            "
+          >
+            <i class="el-icon-info"></i>
+            选择了企微员工之后,此课程小节的完课 消息
+            只作用于此企业微信员工,不再是整个客服公司的员工【注意:此处不区分主体,只区分销售公司!】
+          </div>
+        </el-form-item>
+        <el-form-item
+          v-show="!form.id && form.checkRange == 2"
+          label="所属部门"
+          prop="deptId"
+          label-width="100px"
+          v-if="form.isAllCompanyUser != 1"
+        >
+          <el-cascader
+            style="width: 100%"
+            v-model="form.deptArrs"
+            filterable
+            :props="{
+              multiple: true,
+              value: 'id',
+              emitPath: false,
+              checkStrictly: true,
+              expandTrigger:'hover'
+            }"
+            :options="deptTree"
+            @change="handleDeptChange"
+          >
+          </el-cascader>
+
         </el-form-item>
       </el-form>
       <div slot="footer" class="dialog-footer">
@@ -534,6 +618,19 @@
     <el-dialog :title="videoNumOptions.title" :visible.sync="videoNumOptions.open" width="1500px" append-to-body>
       <userVideo ref="QwUserVideo" @videoResult="qwUserVideoResult"></userVideo>
     </el-dialog>
+
+    <!-- 选择成员账号弹窗   -->
+    <el-dialog
+      :title="listUser.title"
+      :visible.sync="listUser.open"
+      width="800px"
+      append-to-body
+    >
+      <qwUserListByCompany
+        ref="QwUserList"
+        @selectUserList="selectUserList"
+      ></qwUserListByCompany>
+    </el-dialog>
   </div>
 </template>
 
@@ -543,11 +640,13 @@ import {courseList, videoList} from '@/api/qw/sop'
 import ImageUpload from '@/views/qw/sop/ImageUpload.vue'
 import { getUserList } from '@/api/company/companyUser'
 import userVideo from "@/views/qw/userVideo/userVideo.vue";
-
+import QwUserListByCompany from "@/views/qw/user/qwUserListByCompany.vue";
+import { treeselect } from "../../../api/company/companyDept";
+import {addCourseFinishTemp, updateCourseFinishTemp} from "../../../api/course/courseFinishTemp";
 
 export default {
   name: "CourseFinishTempParent",
-  components: { ImageUpload ,userVideo},
+  components: { ImageUpload ,userVideo,QwUserListByCompany},
   data() {
     return {
       videoOptionsLoading: false,
@@ -601,7 +700,14 @@ export default {
         courseId: null,
       },
       // 表单参数
-      form: {companyUserIds: [],},
+      form: {
+        companyUserIds: [],
+        setting: null,
+        videoIdSet: null,
+        courseIdSet: null,
+        deptArrs: [],
+        checkRange: null,
+      },
       // 表单校验
       rules: {
         name:[
@@ -610,7 +716,19 @@ export default {
         courseId:[
           { required: true, message: "课程不能为空", trigger: "blur" }
         ],
-      }
+      },
+      deptTree: [],
+      finishTempList: [],
+      //用来记录上次选择部门的数组
+      prevSelected:[],
+      //选择企业微信成员列表
+      userSelectList: [],
+      //选择成员列表
+      listUser: {
+        title: "",
+        open: false,
+      },
+
     };
   },
   created() {
@@ -628,14 +746,31 @@ export default {
     this.getDicts("sys_qwSopAi_contentType").then(response => {
       this.sysQwSopAiContentType = response.data;
     });
+
+    this.getDicts("sys_qw_finish_temp_range").then((response) => {
+      this.finishTempList = response.data;
+    });
+
+
     getUserList().then(response => {
       this.userList = response.data;
     });
     courseList().then(response => {
       this.courseList = response.list;
     });
+
+    treeselect().then((res) => {
+      this.deptTree = res.data;
+    });
+
     this.getList();
   },
+
+  watch: {
+    userSelectList(newList) {
+      this.form.companyUserIds = newList.map((item) => item.id);
+    },
+  },
   methods: {
     // 处理规则中课程变化
     handleRuleCourseChange(item) {
@@ -744,7 +879,11 @@ export default {
         companyUserIds: null,
         remark: null
       };
+      this.userSelectList = [];
+      this.setting = [];
       this.resetForm("form");
+      this.prevSelected = [];
+
     },
     /** 搜索按钮操作 */
     handleQuery() {
@@ -805,72 +944,167 @@ export default {
     },
     /** 提交按钮 */
     submitForm() {
-      this.$refs["form"].validate(valid => {
+      this.$refs["form"].validate((valid) => {
         if (valid) {
-          if (this.form.id != null) {
-            updateCourseFinishTempParent(this.form).then(response => {
-              this.msgSuccess("修改成功");
-              this.open = false;
-              this.getList();
-            });
+          if (this.form.isAllCompanyUser == null) {
+            this.form.isAllCompanyUser = 2;
+          }
+
+          if (
+            (this.form.isAllCompanyUser == 2 ||
+              this.form.isAllCompanyUser == null) &&
+            (this.form.companyUserIds == null ||
+              this.form.companyUserIds.length == 0) &&
+            (this.form.deptArrs == null || this.form.deptArrs.length == 0)
+          ) {
+            return this.$message.error("请选择企业微信员工或者部门");
+          }
+          //全选客服清空
+          if (this.form.isAllCompanyUser != 2) {
+            this.form.checkRange = null;
+          }
+
+
+          console.log("sout", this.form);
+
+          if (
+            this.form.companyUserIds != null &&
+            this.form.companyUserIds.length > 0
+          ) {
+            if (this.form.companyUserIds && Array.isArray(this.form.companyUserIds)) {
+              this.form.companyUserIds = this.form.companyUserIds.join(",");
+            }
+          } else {
+            this.form.companyUserIds = null;
+          }
+          //根据选择范围清空另外一个参数
+          if (this.form.checkRange == 1) {
+            // 清空部门id
+            this.form.deptArrs = [];
+            this.form.deptIds = "";
+          } else if (this.form.checkRange == 2) {
+            //清空客服个人id
+            this.form.companyUserIds = "";
           } else {
-            if (this.form.isAllCompanyUser == null) {
-              this.form.isAllCompanyUser = 2;
+            this.form.deptArrs = [];
+            this.form.deptIds = "";
+            this.form.companyUserIds = "";
+          }
+
+          const processedSetting = this.setting.map(item => {
+            const newItem = {...item};
+            if (newItem.videoOptions) {
+              delete newItem.videoOptions;
             }
-            this.form.companyUserIds = this.companyUserIds.toString()
-
-
-            const processedSetting = this.setting.map(item => {
-              const newItem = {...item};
-              if (newItem.videoOptions) {
-                delete newItem.videoOptions;
-              }
-              if (newItem.videoLoading !== undefined) {
-                delete newItem.videoLoading;
-              }
-              return newItem;
-            });
+            if (newItem.videoLoading !== undefined) {
+              delete newItem.videoLoading;
+            }
+            return newItem;
+          });
 
-            this.form.setting = JSON.stringify(processedSetting);
-            this.form.chatSetting = JSON.stringify(this.chatSetting)
+          this.form.setting = JSON.stringify(processedSetting);
+          this.form.chatSetting = JSON.stringify(this.chatSetting)
 
-            if (this.setting.length <= 0) {
-              return this.$message("请添加规则")
+          if (this.setting.length <= 0) {
+            return this.$message("请添加规则");
+          }
+          for (let i = 0; i < this.setting.length; i++) {
+            if (
+              this.setting[i].contentType == 1 &&
+              (this.setting[i].value == null || this.setting[i].value == "")
+            ) {
+              return this.$message.error("内容不能为空");
+            }
+            if (
+              this.setting[i].contentType == 2 &&
+              (this.setting[i].imgUrl == null || this.setting[i].imgUrl == "")
+            ) {
+              return this.$message.error("图片不能为空");
+            }
+            if (
+              this.setting[i].contentType == 3 &&
+              (this.setting[i].linkTitle == null ||
+                this.setting[i].linkTitle == "")
+            ) {
+              return this.$message.error("链接标题不能为空");
+            }
+            if (
+              this.setting[i].contentType == 3 &&
+              (this.setting[i].linkDescribe == null ||
+                this.setting[i].linkDescribe == "")
+            ) {
+              return this.$message.error("链接描述不能为空");
+            }
+            if (
+              this.setting[i].contentType == 3 &&
+              (this.setting[i].linkImageUrl == null ||
+                this.setting[i].linkImageUrl == "")
+            ) {
+              return this.$message.error("链接图片不能为空");
             }
-            for (let i = 0; i < this.setting.length; i++) {
-              if (this.setting[i].contentType == 1 && (this.setting[i].value == null || this.setting[i].value == "")) {
-                return this.$message.error("内容不能为空")
-              }
-              if (this.setting[i].contentType == 2 && (this.setting[i].imgUrl == null || this.setting[i].imgUrl == "")) {
-                return this.$message.error("图片不能为空")
-              }
-              if (this.setting[i].contentType == 3 && (this.setting[i].linkTitle == null || this.setting[i].linkTitle == "")) {
-                return this.$message.error("链接标题不能为空")
-              }
-              if (this.setting[i].contentType == 3 && (this.setting[i].linkDescribe == null || this.setting[i].linkDescribe == "")) {
-                return this.$message.error("链接描述不能为空")
-              }
-              if (this.setting[i].contentType == 3 && (this.setting[i].linkImageUrl == null || this.setting[i].linkImageUrl == "")) {
-                return this.$message.error("链接图片不能为空")
-              }
-              if (this.setting[i].contentType == 3 && this.setting[i].type == 1 && (this.setting[i].linkUrl == null || this.setting[i].linkUrl == "")) {
-                return this.$message.error("链接地址不能为空")
-              }
-              if (this.setting[i].contentType == 5 && (this.setting[i].fileUrl == null || this.setting[i].fileUrl == "")) {
-                return this.$message.error("文件不能为空")
-              }
-              if (this.setting[i].contentType == 6 && (this.setting[i].videoUrl == null || this.setting[i].videoUrl == "")) {
-                return this.$message.error("视频不能为空")
-              }
-              if (this.setting[i].contentType == 7 && (this.setting[i].value == null || this.setting[i].value == "")) {
-                return this.$message.error("语音不能为空")
-              }
+            if (
+              this.setting[i].contentType == 3 &&
+              this.setting[i].type == 1 &&
+              (this.setting[i].linkUrl == null || this.setting[i].linkUrl == "")
+            ) {
+              return this.$message.error("链接地址不能为空");
             }
-            addCourseFinishTempParent(this.form).then(response => {
-              // this.loading = true
+            if (
+              this.setting[i].contentType == 5 &&
+              (this.setting[i].fileUrl == null || this.setting[i].fileUrl == "")
+            ) {
+              return this.$message.error("文件不能为空");
+            }
+            if (
+              this.setting[i].contentType == 6 &&
+              (this.setting[i].videoUrl == null ||
+                this.setting[i].videoUrl == "")
+            ) {
+              return this.$message.error("视频不能为空");
+            }
+            if (
+              this.setting[i].contentType == 7 &&
+              (this.setting[i].value == null || this.setting[i].value == "")
+            ) {
+              return this.$message.error("语音不能为空");
+            }
+          }
+
+          if (this.form.deptArrs != null && this.form.deptArrs.length > 0) {
+            this.form.deptIds = "";
+            this.form.deptArrs.forEach((i) => {
+              this.form.deptIds += i + ",";
+            });
+            this.form.deptIds = this.form.deptIds.slice(0, -1);
+          }
+
+
+          let loadingRock = this.$loading({
+            lock: true,
+            text: '正在执行中请稍后~~请不要刷新页面!!',
+            spinner: 'el-icon-loading',
+            background: 'rgba(0, 0, 0, 0.7)'
+          });
+
+          if (this.form.id != null) {
+            updateCourseFinishTempParent(this.form).then((response) => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.setting = [];
+              this.userSelectList = [];
+              this.getList();
+            }).finally(res=>{
+              loadingRock.close();
+            });
+          } else {
+            addCourseFinishTempParent(this.form).then((response) => {
               this.msgSuccess("新增成功");
               this.open = false;
+              this.setting = [];
+              this.userSelectList = [];
               this.getList();
+            }).finally(res=>{
+              loadingRock.close();
             });
           }
         }
@@ -986,6 +1220,148 @@ export default {
     jump(id){
       this.$router.push('/qw/conversion/courseFinishTemp/' + id)
     },
+
+    logChange(val) {
+      this.$forceUpdate();
+    },
+    //选择群发的企业成员账号
+    handlelistUser() {
+      this.listUser.title = "选择企业成员";
+      this.listUser.open = true;
+    },
+
+    handleClearCompanyUser(){
+      this.userSelectList = [];
+    },
+
+    //删除一些选择了的账号
+    handleClosegroupUser(list) {
+      // 假设 list 对象具有一个 id 属性
+      const index = this.userSelectList.findIndex((t) => t.id === list.id);
+      if (index !== -1) {
+        this.userSelectList.splice(index, 1);
+      }
+    },
+
+    //选择的成员账号列表
+    selectUserList(list) {
+      this.listUser.open = false;
+
+      // 3. 遍历要添加的 list,逐条判断是否存在重复
+      list.forEach((newItem) => {
+        // some() 判断是否存在相同 id
+        const isExist = this.userSelectList.some(
+          (oldItem) => oldItem.id === newItem.id
+        );
+        if (!isExist) {
+          // 不存在重复的,才添加
+          this.userSelectList.push(newItem);
+        }
+      });
+    },
+
+
+    /**
+     * 查看完课模板
+     */
+    handleSelectDetails(row) {
+      this.reset();
+      const id = row.id || this.ids;
+      getCourseFinishTempParent(id).then((response) => {
+        console.log("this.form222 ", response.data);
+        this.form = response.data;
+
+        this.setting = JSON.parse(this.form.setting);
+
+        if (response.data.userSelectList != null) {
+          this.userSelectList = this.form.userSelectList;
+          this.form.checkRange = 1;
+        }
+        if (!!response.data.deptArrs) {
+          this.form.checkRange = 2;
+        }
+        if (response.data.companyUserIds != null) {
+          this.userSelectList = this.form.companyUserIds;
+        }
+
+        videoList(this.form.courseId).then((response) => {
+          this.videoList = response.list;
+        });
+
+        this.open = true;
+        this.title = "查看完课模板";
+        this.formType = 2;
+      });
+    },
+
+    //选择部门
+    handleDeptChange(value) {
+      //通过先后数组长短来判定是选择还是反选
+      var isCheck = value.length > this.prevSelected.length? true: false;
+      //获取当前操作的节点
+      var node = this.findChangedNode(value)
+      //拿到节点在原始树形结构中的数据
+      var checkTreeNode = this.findNodeById(this.deptTree,node);
+      //判定当前选择的节点是否涵盖有树枝
+      if(!!checkTreeNode && 'children' in checkTreeNode){
+        //拿到节点下所有子集id
+        var checkChildrenIds  = this.getAllChildrenIds(checkTreeNode);
+        if(isCheck){
+          this.form.deptArrs = [...new Set([...this.form.deptArrs, ...checkChildrenIds])];
+        }else{
+          var tempList =  this.form.deptArrs.filter(item => !checkChildrenIds.includes(item));
+          this.form.deptArrs =[...new Set(tempList)]
+        }
+      }
+      //完成后记录当前选择的部门ids
+      this.prevSelected = this.form.deptArrs;
+    },
+    // 查找变更的节点ID
+    findChangedNode(current) {
+      // 处理初始状态
+      if (this.prevSelected.length === 0 && current.length > 0) {
+        return current[current.length - 1];
+      }
+
+      // 找出当前有而之前没有的节点(新增选中)
+      const added = current.filter(id => !this.prevSelected.includes(id));
+      if (added.length > 0) {
+        return added[0];
+      }
+
+      // 找出之前有而当前没有的节点(取消选中)
+      const removed = this.prevSelected.filter(id => !current.includes(id));
+      if (removed.length > 0) {
+        return removed[0];
+      }
+
+      return null;
+    },
+    // 根据ID查找节点
+    findNodeById(nodes, id) {
+      for (const node of nodes) {
+        if (node.id === id) {
+          return node;
+        }
+        if (node.children && node.children.length > 0) {
+          const found = this.findNodeById(node.children, id);
+          if (found) return found;
+        }
+      }
+      return null;
+    },
+    // 获取节点的所有子节点ID(递归)
+    getAllChildrenIds(node) {
+      let ids = [];
+      if (node.children && node.children.length > 0) {
+        node.children.forEach(child => {
+          ids.push(child.id);
+          // 递归获取子节点的子节点
+          ids = [...ids, ...this.getAllChildrenIds(child)];
+        });
+      }
+      return ids;
+    },
   },
 };
 </script>

+ 991 - 0
src/views/course/courseFinishTempParent/indexOld.vue

@@ -0,0 +1,991 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="名称" prop="name">
+        <el-input
+          v-model="queryParams.name"
+          placeholder="请输入名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="课程" prop="courseId">
+        <el-select v-model="queryParams.courseId" clearable placeholder="请选择课程" style=" margin-right: 10px;" size="mini">
+          <el-option
+            v-for="dict in courseList"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="parseInt(dict.dictValue)"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          v-hasPermi="['course:courseFinishTempParent:add']"
+        >新增</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="success"
+          plain
+          icon="el-icon-edit"
+          size="mini"
+          :disabled="single"
+          @click="handleUpdate"
+          v-hasPermi="['course:courseFinishTempParent:edit']"
+        >修改</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+          v-hasPermi="['course:courseFinishTempParent:remove']"
+        >删除</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          :loading="exportLoading"
+          @click="handleExport"
+          v-hasPermi="['course:courseFinishTempParent:export']"
+        >导出</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table border v-loading="loading" :data="courseFinishTempParentList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="名称" align="center" prop="name" />
+      <el-table-column label="课程" align="center" prop="courseId">
+        <template slot-scope="scope">
+          <el-tag v-for="dict in courseList" v-if="dict.dictValue == scope.row.courseId">{{dict.dictLabel}}</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['course:courseFinishTempParent:edit']"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            @click="jump(scope.row.id)"
+          >模板列表</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['course:courseFinishTempParent:remove']"
+          >删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改完课模板对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="1000px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="名称" prop="name">
+          <el-input v-model="form.name" placeholder="请输入名称" />
+        </el-form-item>
+        <el-form-item label="状态" v-if="!form.id">
+          <el-radio-group v-model="form.status">
+            <el-radio
+              v-for="dict in statusOptions"
+              :key="dict.dictValue"
+              :label="parseInt(dict.dictValue)"
+            >{{ dict.dictLabel }}
+            </el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="课程" prop="courseId">
+          <el-select v-model="form.courseId" :disabled="form.id != null" placeholder="请选择课程" style=" margin-right: 10px;" size="mini">
+            <el-option
+              v-for="dict in courseList"
+              :key="dict.dictValue"
+              :label="dict.dictLabel"
+              :value="parseInt(dict.dictValue)"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="规则" prop="setting" v-if="!form.id">
+          <div v-for="(item, index) in setting" :key="index"
+               style="background-color: #fdfdfd; border: 1px solid #e6e6e6; margin-bottom: 20px;">
+            <el-row>
+              <el-col :span="22">
+                <el-form :model="item" label-width="70px">
+                  <el-form-item label="内容类别" style="margin: 2%">
+                    <el-radio-group v-model="item.contentType">
+                      <el-radio :label="item.dictValue" v-for="item in sysQwSopAiContentType">{{ item.dictLabel }}
+                      </el-radio>
+                    </el-radio-group>
+                  </el-form-item>
+                  <el-form-item label="内容" style="margin-bottom: 2%">
+                    <el-input v-if="item.contentType == 1 " v-model="item.value" type="textarea" :rows="3"
+                              placeholder="内容" style="width: 90%;margin-top: 10px;"/>
+
+                    <ImageUpload v-if="item.contentType == 2 " v-model="item.imgUrl" type="image" :num="1" :width="150"
+                                 :height="150"/>
+
+                    <div v-if="item.contentType == 3 ">
+                      <el-card class="box-card">
+                        <el-form-item label="链接标题:" label-width="100px">
+                          <el-input v-model="item.linkTitle" placeholder="请输入链接标题" style="width: 90%;"/>
+                        </el-form-item>
+                        <el-form-item label="链接描述:" label-width="100px">
+                          <el-input type="textarea" :rows="3" v-model="item.linkDescribe" placeholder="请输入链接描述"
+                                    style="width: 90%;margin-top: 1%;"/>
+                        </el-form-item>
+                        <el-form-item label="链接封面:" label-width="100px">
+                          <ImageUpload v-model="item.linkImageUrl" type="image" :num="1" :file-size="2" :width="150"
+                                       :height="150" style="margin-top: 1%;"/>
+                        </el-form-item>
+                        <el-form-item label="链接地址:" label-width="100px">
+                          <el-input v-model="item.linkUrl" placeholder="请输入链接地址" style="width: 90%;"/>
+                        </el-form-item>
+                      </el-card>
+                    </div>
+                    <div v-if="item.contentType == 4">
+                      <el-card class="box-card">
+
+                        <el-form-item label="选择课程">
+                          <el-select
+                            v-model="item.courseId"
+                            placeholder="请选择课程"
+                            style="margin-right: 10px"
+                            size="mini"
+                            @change="handleRuleCourseChange(item)"
+                          >
+                            <el-option
+                              v-for="dict in courseList"
+                              :key="dict.dictValue"
+                              :label="dict.dictLabel"
+                              :value="parseInt(dict.dictValue)"
+                            />
+                          </el-select>
+                          <el-select
+                            v-model="item.videoId"
+                            placeholder="请选择小节"
+                            size="mini"
+                            style="margin-right: 10px"
+                            filterable
+                            remote
+                            :remote-method="(query) => remoteMethodRuleVideo(query, item)"
+                            :loading="videoOptionsLoading"
+                            @change="handleRuleVideoChange(item)"
+                          >
+                            <el-option
+                              v-for="dict in videoOptions || []"
+                              :key="dict.dictValue"
+                              :label="dict.dictLabel"
+                              :value="parseInt(dict.dictValue)"
+                            />
+                          </el-select>
+                        </el-form-item>
+
+                        <el-card class="box-card" style="margin-top: 10px">
+                          <el-form-item label="标题" prop="miniprogramTitle">
+                            <el-input v-model="item.miniprogramTitle" placeholder="请输入小程序消息标题,最长为64字" />
+                          </el-form-item>
+                          <el-form-item label="封面" prop="miniprogramPicUrl">
+                            <ImageUpload v-model="item.miniprogramPicUrl" type="image" :num="10" :width="150" :height="150" />
+                          </el-form-item>
+                        </el-card>
+                      </el-card>
+                    </div>
+                    <div v-if="item.contentType == 5 ">
+
+                      <el-form-item label="上传文件:" prop="fileUrl" label-width="100px">
+                        <el-upload
+                          v-model="item.fileUrl"
+                          class="avatar-uploader"
+                          :action="uploadUrl"
+                          :show-file-list="false"
+                          :on-success="(res, file) => handleAvatarSuccessFile(res, file, item)"
+                          :before-upload="beforeAvatarUploadFile">
+                          <i class="el-icon-plus avatar-uploader-icon"></i>
+                        </el-upload>
+                        <el-link v-if="item.fileUrl" type="primary" :href="downloadUrl(item.fileUrl)" download>
+                          {{ item.fileUrl }}
+                        </el-link>
+                      </el-form-item>
+
+                    </div>
+
+                    <div v-if="item.contentType == 6 ">
+                      <el-form-item label="上传视频:" prop="videoUrl" label-width="100px">
+                        <el-upload
+                          v-model="item.videoUrl"
+                          class="avatar-uploader"
+                          :action="uploadUrl"
+                          :show-file-list="false"
+                          :on-success="(res, file) => handleAvatarSuccessVideo(res, file, item)"
+                          :before-upload="beforeAvatarUploadVideo">
+                          <i class="el-icon-plus avatar-uploader-icon"></i>
+                        </el-upload>
+                        <video v-if="item.videoUrl"
+                               :src="item.videoUrl"
+                               controls style="width: 200px;height: 100px">
+                        </video>
+                      </el-form-item>
+                    </div>
+                    <div v-if="item.contentType == 7 ">
+                      <el-input
+                        v-model="item.value"
+                        type="textarea" :rows="3" maxlength="66" show-word-limit
+                        placeholder="输入要转为语音的内容" style="width: 90%;margin-top: 10px;"
+                        @input="handleInputVideoText(item.value,item)"/>
+                    </div>
+                    <div v-if="item.contentType == 8">
+                      <el-button type="primary"
+                                 style="margin-bottom: 1%"
+                                 @click="hanldeSelectVideoNum(setting,index)">
+                        选择视频号
+                      </el-button>
+                      <el-card class="box-card" v-if="item.coverUrl">
+                        <el-form-item label="封面标题:" label-width="100px">
+                          <el-input v-model="item.nickname"
+                                    style="width: 90%;margin-bottom: 1%" disabled/>
+                        </el-form-item>
+                        <el-form-item label="头像:" label-width="100px">
+                          <el-image
+                            v-if="item.avatar != null"
+                            :src="item.avatar"
+                            :preview-src-list="[item.avatar]"
+                            :style="{ width: '50px', height: '50px' }"
+                          ></el-image>
+                        </el-form-item>
+                        <el-form-item label="封面:" label-width="100px">
+                          <el-image
+                            v-if="item.coverUrl != null"
+                            :src="item.coverUrl"
+                            :preview-src-list="[item.coverUrl]"
+                            :style="{ width: '200px', height: '200px' }"
+                          ></el-image>
+
+                        </el-form-item>
+                        <el-form-item label="简介:" label-width="100px">
+                          <el-input type="textarea" :rows="3"
+                                    v-model="item.desc"
+                                    style="width: 90%;margin-top: 1%;" disabled/>
+                        </el-form-item>
+                        <el-form-item label="视频地址:" label-width="100px"
+                                      style="margin-top: 1%">
+                          <el-input v-model="item.url"
+                                    style="width: 90%;" disabled/>
+                        </el-form-item>
+                      </el-card>
+                    </div>
+
+                  </el-form-item>
+                </el-form>
+              </el-col>
+              <el-col :span="1" :offset="1">
+                <i class="el-icon-delete" @click="delSetList(index, 0)" style="margin-top: 20px;"
+                   v-if="setting.length>1"></i>
+              </el-col>
+            </el-row>
+          </div>
+          <el-link type="primary" class="el-icon-plus" :underline="false" @click='addSetList(0)'>
+            添加内容
+          </el-link>
+        </el-form-item>
+        <el-form-item label="群聊恭喜规则" prop="setting" v-if="!form.id">
+          <div v-for="(item, index) in chatSetting" :key="index"
+               style="background-color: #fdfdfd; border: 1px solid #e6e6e6; margin-bottom: 20px;">
+            <el-row>
+              <el-col :span="22">
+                <el-form :model="item" label-width="70px">
+                  <el-form-item label="内容类别" style="margin: 2%">
+                    <el-radio-group v-model="item.contentType">
+                      <el-radio :label="item.dictValue" v-for="item in sysQwSopAiContentType">{{ item.dictLabel }}
+                      </el-radio>
+                    </el-radio-group>
+                  </el-form-item>
+                  <el-form-item label="内容" style="margin-bottom: 2%">
+                    <el-input v-if="item.contentType == 1 " v-model="item.value" type="textarea" :rows="3"
+                              placeholder="内容" style="width: 90%;margin-top: 10px;"/>
+
+                    <ImageUpload v-if="item.contentType == 2 " v-model="item.imgUrl" type="image" :num="1" :width="150"
+                                 :height="150"/>
+
+                    <div v-if="item.contentType == 3 ">
+                      <el-card class="box-card">
+                        <el-form-item label="链接标题:" label-width="100px">
+                          <el-input v-model="item.linkTitle" placeholder="请输入链接标题" style="width: 90%;"/>
+                        </el-form-item>
+                        <el-form-item label="链接描述:" label-width="100px">
+                          <el-input type="textarea" :rows="3" v-model="item.linkDescribe" placeholder="请输入链接描述"
+                                    style="width: 90%;margin-top: 1%;"/>
+                        </el-form-item>
+                        <el-form-item label="链接封面:" label-width="100px">
+                          <ImageUpload v-model="item.linkImageUrl" type="image" :num="1" :file-size="2" :width="150"
+                                       :height="150" style="margin-top: 1%;"/>
+                        </el-form-item>
+                        <el-form-item label="链接地址:" label-width="100px">
+                          <el-input v-model="item.linkUrl" placeholder="请输入链接地址" style="width: 90%;"/>
+                        </el-form-item>
+                      </el-card>
+                    </div>
+                    <div v-if="item.contentType == 4">
+                      <el-card class="box-card">
+
+                        <el-form-item label="选择课程">
+                          <el-select
+                            v-model="item.courseId"
+                            placeholder="请选择课程"
+                            style="margin-right: 10px"
+                            size="mini"
+                            @change="handleRuleCourseChange(item)"
+                          >
+                            <el-option
+                              v-for="dict in courseList"
+                              :key="dict.dictValue"
+                              :label="dict.dictLabel"
+                              :value="parseInt(dict.dictValue)"
+                            />
+                          </el-select>
+                          <el-select
+                            v-model="item.videoId"
+                            placeholder="请选择小节"
+                            size="mini"
+                            style="margin-right: 10px"
+                            filterable
+                            remote
+                            :remote-method="(query) => remoteMethodRuleVideo(query, item)"
+                            :loading="videoOptionsLoading"
+                            @change="handleRuleVideoChange(item)"
+                          >
+                            <el-option
+                              v-for="dict in videoOptions || []"
+                              :key="dict.dictValue"
+                              :label="dict.dictLabel"
+                              :value="parseInt(dict.dictValue)"
+                            />
+                          </el-select>
+                        </el-form-item>
+
+                        <el-card class="box-card" style="margin-top: 10px">
+                          <el-form-item label="标题" prop="miniprogramTitle">
+                            <el-input v-model="item.miniprogramTitle" placeholder="请输入小程序消息标题,最长为64字" />
+                          </el-form-item>
+                          <el-form-item label="封面" prop="miniprogramPicUrl">
+                            <ImageUpload v-model="item.miniprogramPicUrl" type="image" :num="10" :width="150" :height="150" />
+                          </el-form-item>
+                        </el-card>
+                      </el-card>
+                    </div>
+                    <div v-if="item.contentType == 5 ">
+
+                      <el-form-item label="上传文件:" prop="fileUrl" label-width="100px">
+                        <el-upload
+                          v-model="item.fileUrl"
+                          class="avatar-uploader"
+                          :action="uploadUrl"
+                          :show-file-list="false"
+                          :on-success="(res, file) => handleAvatarSuccessFile(res, file, item)"
+                          :before-upload="beforeAvatarUploadFile">
+                          <i class="el-icon-plus avatar-uploader-icon"></i>
+                        </el-upload>
+                        <el-link v-if="item.fileUrl" type="primary" :href="downloadUrl(item.fileUrl)" download>
+                          {{ item.fileUrl }}
+                        </el-link>
+                      </el-form-item>
+
+                    </div>
+
+                    <div v-if="item.contentType == 6 ">
+                      <el-form-item label="上传视频:" prop="videoUrl" label-width="100px">
+                        <el-upload
+                          v-model="item.videoUrl"
+                          class="avatar-uploader"
+                          :action="uploadUrl"
+                          :show-file-list="false"
+                          :on-success="(res, file) => handleAvatarSuccessVideo(res, file, item)"
+                          :before-upload="beforeAvatarUploadVideo">
+                          <i class="el-icon-plus avatar-uploader-icon"></i>
+                        </el-upload>
+                        <video v-if="item.videoUrl"
+                               :src="item.videoUrl"
+                               controls style="width: 200px;height: 100px">
+                        </video>
+                      </el-form-item>
+                    </div>
+                    <div v-if="item.contentType == 7 ">
+                      <el-input
+                        v-model="item.value"
+                        type="textarea" :rows="3" maxlength="66" show-word-limit
+                        placeholder="输入要转为语音的内容" style="width: 90%;margin-top: 10px;"
+                        @input="handleInputVideoText(item.value,item)"/>
+                    </div>
+                    <div v-if="item.contentType == 8">
+                      <el-button type="primary"
+                                 style="margin-bottom: 1%"
+                                 @click="hanldeSelectVideoNum(setting,index)">
+                        选择视频号
+                      </el-button>
+                      <el-card class="box-card" v-if="item.coverUrl">
+                        <el-form-item label="封面标题:" label-width="100px">
+                          <el-input v-model="item.nickname"
+                                    style="width: 90%;margin-bottom: 1%" disabled/>
+                        </el-form-item>
+                        <el-form-item label="头像:" label-width="100px">
+                          <el-image
+                            v-if="item.avatar != null"
+                            :src="item.avatar"
+                            :preview-src-list="[item.avatar]"
+                            :style="{ width: '50px', height: '50px' }"
+                          ></el-image>
+                        </el-form-item>
+                        <el-form-item label="封面:" label-width="100px">
+                          <el-image
+                            v-if="item.coverUrl != null"
+                            :src="item.coverUrl"
+                            :preview-src-list="[item.coverUrl]"
+                            :style="{ width: '200px', height: '200px' }"
+                          ></el-image>
+
+                        </el-form-item>
+                        <el-form-item label="简介:" label-width="100px">
+                          <el-input type="textarea" :rows="3"
+                                    v-model="item.desc"
+                                    style="width: 90%;margin-top: 1%;" disabled/>
+                        </el-form-item>
+                        <el-form-item label="视频地址:" label-width="100px"
+                                      style="margin-top: 1%">
+                          <el-input v-model="item.url"
+                                    style="width: 90%;" disabled/>
+                        </el-form-item>
+                      </el-card>
+                    </div>
+
+                  </el-form-item>
+                </el-form>
+              </el-col>
+              <el-col :span="1" :offset="1">
+                <i class="el-icon-delete" @click="delSetList(index,1)" style="margin-top: 20px;"
+                   v-if="setting.length>1"></i>
+              </el-col>
+            </el-row>
+          </div>
+          <el-link type="primary" class="el-icon-plus" :underline="false" @click='addSetList(1)'>
+            添加内容
+          </el-link>
+        </el-form-item>
+        <el-form-item label="全选销售" prop="isAllCompanyUser" v-if="!form.id">
+          <el-switch
+            v-model="form.isAllCompanyUser"
+            active-color="#13ce66"
+            inactive-color="#ff4949"
+            :active-value="1"
+            :inactive-value="2">
+          </el-switch>
+          <span v-if="form.isAllCompanyUser == '1'" style="margin-left: 10px;color: #13ce66">是</span>
+          <span v-else style="margin-left: 10px;color: #ff4949">否</span>
+        </el-form-item>
+        <el-form-item label="所属销售" prop="companyUserIds" v-if="!form.id && form.isAllCompanyUser == '2'">
+          <el-select v-model="companyUserIds" remote multiple placeholder="请选择" filterable style="width: 100%;">
+            <el-option
+              v-for="dict in userList"
+              :key="dict.userId"
+              :label="dict.nickName"
+              :value="dict.userId.toString()">
+            </el-option>
+          </el-select>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+    <el-dialog :title="videoNumOptions.title" :visible.sync="videoNumOptions.open" width="1500px" append-to-body>
+      <userVideo ref="QwUserVideo" @videoResult="qwUserVideoResult"></userVideo>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listCourseFinishTempParent, getCourseFinishTempParent, delCourseFinishTempParent, addCourseFinishTempParent, updateCourseFinishTempParent, exportCourseFinishTempParent } from "@/api/course/courseFinishTempParent";
+import {courseList, videoList} from '@/api/qw/sop'
+import ImageUpload from '@/views/qw/sop/ImageUpload.vue'
+import { getUserList } from '@/api/company/companyUser'
+import userVideo from "@/views/qw/userVideo/userVideo.vue";
+
+
+export default {
+  name: "CourseFinishTempParent",
+  components: { ImageUpload ,userVideo},
+  data() {
+    return {
+      videoOptionsLoading: false,
+      videoOptions: [],
+      videoLoading: false,
+      videoNumOptions: {
+        title: '选择视频号',
+        open: false,
+        content: null,
+        contentIndex: null,
+      },
+      voiceLoading: false,
+      uploadUrl: process.env.VUE_APP_BASE_API + "/common/uploadOSS2",
+      uploadUrlByVoice: process.env.VUE_APP_BASE_API + "/common/uploadOSSByHOOKVoice",
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 完课模板表格数据
+      courseFinishTempParentList: [],
+      companyUserIds: [],
+      courseList: [],
+      userList: [],
+      // 状态字典
+      statusOptions: [],
+      allowSelect: [],
+      sysFsSopWatchStatus: [],
+      sysQwSopAiContentType: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 模板表格数据
+      setting: [],
+      chatSetting: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        name: null,
+        courseId: null,
+      },
+      // 表单参数
+      form: {companyUserIds: [],},
+      // 表单校验
+      rules: {
+        name:[
+          { required: true, message: "名称不能为空", trigger: "blur" }
+        ],
+        courseId:[
+          { required: true, message: "课程不能为空", trigger: "blur" }
+        ],
+      }
+    };
+  },
+  created() {
+    this.getDicts("sys_company_status").then(response => {
+      this.statusOptions = response.data;
+    });//复用一下
+    this.getDicts("sys_qw_allow_select").then(response => {
+      this.allowSelect = response.data;
+    });
+
+    this.getDicts("sys_fs_sop_watch_status").then(response => {
+      this.sysFsSopWatchStatus = response.data;
+    });
+
+    this.getDicts("sys_qwSopAi_contentType").then(response => {
+      this.sysQwSopAiContentType = response.data;
+    });
+    getUserList().then(response => {
+      this.userList = response.data;
+    });
+    courseList().then(response => {
+      this.courseList = response.list;
+    });
+    this.getList();
+  },
+  methods: {
+    // 处理规则中课程变化
+    handleRuleCourseChange(item) {
+      // 为当前规则项单独加载视频列表
+      videoList(item.courseId).then((response) => {
+        // 只保存视频列表,不保存整个响应对象
+        this.videoOptions = response.list;
+
+        this.$set(item, 'videoId', null); // Reset video selection when course changes
+
+        // 自动设置封面为课程封面
+        const selectedCourse = this.courseList.find(
+          course => parseInt(course.dictValue) === item.courseId
+        );
+        if (selectedCourse) {
+          this.$set(item, 'miniprogramPicUrl', selectedCourse.dictImgUrl);
+        }
+      });
+    },
+
+
+    // 处理规则中视频变化
+    handleRuleVideoChange(item) {
+      if (!item.videoId) return;
+
+      // 自动设置标题为视频标题
+      const selectedVideo = (this.videoOptions || []).find(
+        video => parseInt(video.dictValue) === item.videoId
+      );
+      if (selectedVideo) {
+        this.$set(item, 'miniprogramTitle', selectedVideo.dictLabel);
+      }
+    },
+
+    // 远程搜索规则中的视频
+    remoteMethodRuleVideo(query, item) {
+      if (!item.courseId) {
+        this.$message.warning('请先选择课程');
+        this.videoOptions = [];
+        reject();
+        return;
+      }
+
+      this.videoOptionsLoading = true;
+      const data = query ? { title: query } : {};
+
+      videoList(item.courseId, data).then((response) => {
+        this.videoOptions =  response.list;
+        this.videoOptionsLoading =  false;
+        resolve(response);
+      }).catch((error) => {
+        this.videoOptionsLoading = false;
+        reject(error);
+      });
+    },
+    remoteMethodVideo(query) {
+      if (!this.form.courseId) {
+        this.$message.warning('请先选择课程');
+        this.videoList = []; // 清空小节列表
+        return;
+      }
+      if (query !== '') {
+        this.videoLoading = true;
+        // 这里调用接口搜索小节,假设 videoList 方法支持搜索参数
+        var data = {
+          title:query
+        }
+        videoList(this.form.courseId, data).then((response) => {
+          this.videoList = response.list;
+          this.videoLoading = false;
+        });
+      } else {
+        // 如果查询为空,则加载全部
+        videoList(this.form.courseId).then((response) => {
+          this.videoList = response.list;
+        });
+      }
+    },
+    /** 查询完课模板列表 */
+    getList() {
+      this.loading = true;
+      listCourseFinishTempParent(this.queryParams).then(response => {
+        this.courseFinishTempParentList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        status: 1,
+        name: null,
+        setting: [],
+        chatSetting: [],
+        courseId: null,
+        createTime: null,
+        createBy: null,
+        updateBy: null,
+        updateTime: null,
+        companyUserIds: null,
+        remark: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加完课模板";
+    },
+    //选择视频号
+    hanldeSelectVideoNum(content, index) {
+      this.videoNumOptions.content = content;
+      this.videoNumOptions.contentIndex = index;
+      this.videoNumOptions.open = true;
+    },
+
+    qwUserVideoResult(val) {
+
+      // 根据选中的内容,将返回的数据更新到相应的表单项
+      const content = this.videoNumOptions.content;
+      const setList = content[this.videoNumOptions.contentIndex];
+      setList.nickname = val.nickname;
+      setList.avatar = val.avatar;
+      setList.coverUrl = val.coverUrl;
+      setList.thumbUrl = val.thumbUrl;
+      setList.desc = val.desc;
+      setList.url = val.url;
+      setList.extras = val.extras;
+      setList.videoId = val.id;
+      console.info(setList)
+
+      this.videoNumOptions.open = false;
+
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id || this.ids
+      getCourseFinishTempParent(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改完课模板";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != null) {
+            updateCourseFinishTempParent(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            if (this.form.isAllCompanyUser == null) {
+              this.form.isAllCompanyUser = 2;
+            }
+            this.form.companyUserIds = this.companyUserIds.toString()
+
+
+            const processedSetting = this.setting.map(item => {
+              const newItem = {...item};
+              if (newItem.videoOptions) {
+                delete newItem.videoOptions;
+              }
+              if (newItem.videoLoading !== undefined) {
+                delete newItem.videoLoading;
+              }
+              return newItem;
+            });
+
+            this.form.setting = JSON.stringify(processedSetting);
+            this.form.chatSetting = JSON.stringify(this.chatSetting)
+
+            if (this.setting.length <= 0) {
+              return this.$message("请添加规则")
+            }
+            for (let i = 0; i < this.setting.length; i++) {
+              if (this.setting[i].contentType == 1 && (this.setting[i].value == null || this.setting[i].value == "")) {
+                return this.$message.error("内容不能为空")
+              }
+              if (this.setting[i].contentType == 2 && (this.setting[i].imgUrl == null || this.setting[i].imgUrl == "")) {
+                return this.$message.error("图片不能为空")
+              }
+              if (this.setting[i].contentType == 3 && (this.setting[i].linkTitle == null || this.setting[i].linkTitle == "")) {
+                return this.$message.error("链接标题不能为空")
+              }
+              if (this.setting[i].contentType == 3 && (this.setting[i].linkDescribe == null || this.setting[i].linkDescribe == "")) {
+                return this.$message.error("链接描述不能为空")
+              }
+              if (this.setting[i].contentType == 3 && (this.setting[i].linkImageUrl == null || this.setting[i].linkImageUrl == "")) {
+                return this.$message.error("链接图片不能为空")
+              }
+              if (this.setting[i].contentType == 3 && this.setting[i].type == 1 && (this.setting[i].linkUrl == null || this.setting[i].linkUrl == "")) {
+                return this.$message.error("链接地址不能为空")
+              }
+              if (this.setting[i].contentType == 5 && (this.setting[i].fileUrl == null || this.setting[i].fileUrl == "")) {
+                return this.$message.error("文件不能为空")
+              }
+              if (this.setting[i].contentType == 6 && (this.setting[i].videoUrl == null || this.setting[i].videoUrl == "")) {
+                return this.$message.error("视频不能为空")
+              }
+              if (this.setting[i].contentType == 7 && (this.setting[i].value == null || this.setting[i].value == "")) {
+                return this.$message.error("语音不能为空")
+              }
+            }
+            addCourseFinishTempParent(this.form).then(response => {
+              // this.loading = true
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除完课模板编号为"' + ids + '"的数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return delCourseFinishTempParent(ids);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有完课模板数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportCourseFinishTempParent(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    },
+
+
+    handleAvatarSuccessFile(res, file, item) {
+      if (res.code === 200) {
+        // 使用 $set 确保响应式更新
+        this.$set(item, 'fileUrl', res.url);
+      } else {
+        this.msgError(res.msg);
+      }
+    },
+
+    beforeAvatarUploadFile(file) {
+      const isLt1M = file.size / 1024 / 1024 < 10;
+      if (!isLt1M) {
+        this.$message.error('上传大小不能超过 10MB!');
+      }
+      return isLt1M;
+    },
+    //下载文件
+    downloadUrl(materialUrl) {
+      // 直接返回文件 URL
+      return materialUrl;
+    },
+    handleInputVideoText(value, content) {
+      // 允许的字符:中文、英文(大小写)、数字和指定标点符号(,。!?)
+      const regex = /^[\u4e00-\u9fa5,。!?,!?]+$/;
+
+      // 删除不符合条件的字符
+      const filteredValue = value.split('').filter(char => regex.test(char)).join('');
+
+      this.$set(content, 'value', filteredValue);
+
+    },
+    handleAvatarSuccessVideo(res, file, item) {
+      if (res.code == 200) {
+        // 使用 $set 确保响应式更新
+        this.$set(item, 'videoUrl', res.url);
+      } else {
+        this.msgError(res.msg);
+      }
+    },
+
+    beforeAvatarUploadVideo(file) {
+      const isLt30M = file.size / 1024 / 1024 < 10;
+      const isMP4 = file.type === 'video/mp4';
+
+      if (!isMP4) {
+        this.$message.error('仅支持上传 MP4 格式的视频文件!');
+        return false;
+      }
+
+      if (!isLt30M) {
+        this.$message.error('上传大小不能超过 10MB!');
+        return false;
+      }
+
+      return true;
+    },
+
+    delSetList(index, type) {
+      if (type == 0) {
+        this.setting.splice(index, 1)
+      } else {
+        this.chatSetting.splice(index, 1)
+      }
+    },
+    addSetList(type) {
+      const newSetting = {
+        contentType: '1',
+        value: '',
+      };
+      // 将新设置项添加到 content.setting 数组中
+      if (type == 0) {
+        this.setting.push(newSetting);
+      } else {
+        this.chatSetting.push(newSetting);
+      }
+    },
+    jump(id){
+      this.$router.push('/qw/conversion/courseFinishTemp/' + id)
+    },
+  },
+};
+</script>

+ 1 - 0
src/views/qw/qwUserVoiceLogTotal/index.vue

@@ -620,6 +620,7 @@ export default {
           if (tag.tagId) { // 确保 tagId 存在
             this.queryParams.tagIds.push(tag.tagId);
           }
+
         });
         //this.queryParams.tagIds=this.queryParams.tagIds.join(",");
       }else {

+ 138 - 0
src/views/qw/user/qwUserListByCompany.vue

@@ -30,6 +30,7 @@
       </el-form-item>
 
       <el-form-item>
+        <el-button type="danger" icon="el-icon-circle-check" size="mini" @click="handleAddAllQwDept">快捷添加企微部门员工</el-button>
         <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">刷新/重置</el-button>
       </el-form-item>
@@ -55,11 +56,66 @@
       :limit.sync="queryParams.pageSize"
       @pagination="handlePaginationChange"
     />
+
+    <el-dialog title="快捷部门选择" :visible.sync="handleAddAllQwDeptOpen" width="700px"  append-to-body >
+
+      <el-form :model="queryParamsDept" ref="queryFormDept" :inline="true" v-show="showSearch" label-width="100px" @submit.prevent="handleQueryDept">
+
+        <el-form-item label="部门昵称" prop="deptName">
+          <el-input
+            v-model="queryParamsDept.deptName"
+            placeholder="请输入部门昵称"
+            clearable
+            size="small"
+          />
+        </el-form-item>
+
+        <el-form-item>
+          <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQueryDept">搜索</el-button>
+          <el-button icon="el-icon-refresh" size="mini" @click="resetQueryDept">刷新/重置</el-button>
+        </el-form-item>
+      </el-form>
+
+      <el-table border v-loading="loading" :data="deptList" @selection-change="handleSelectionChangeDept">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="部门id" align="center" prop="id" width="160"/>
+        <el-table-column label="部门编号" align="center" prop="deptId" width="160"/>
+        <el-table-column label="部门名称" align="center" prop="deptName" width="150"/>
+        <el-table-column label="操作" align="center"  width="100px" fixed="right">
+          <template slot-scope="scope">
+            <el-button
+              size="mini"
+              type="text"
+              style="color: blue;"
+              @click="handleChangeDept(scope.row)"
+              v-hasPermi="['qw:sop:remove']"
+            >选择此部门
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <div style="margin-top: 30px;display: flex;justify-content: center">
+        <el-button type="warning" icon="el-icon-search" @click="confirmSelectDept">确定选择</el-button>
+      </div>
+
+      <pagination
+        v-show="totalDept>0"
+        :total="totalDept"
+        :page.sync="queryParamsDept.pageNum"
+        :limit.sync="queryParamsDept.pageSize"
+        @pagination="getDeptList"
+      />
+    </el-dialog>
+
+
   </div>
 </template>
 
 <script>
 import { listUser} from "@/api/qw/user";
+import {getQwUserByDept} from "../../../api/qw/user";
+import {listCompanyDept} from "../../../api/qw/qwDept";
 
 
 export default {
@@ -82,6 +138,7 @@ export default {
       showSearch: true,
       // 总条数
       total: 0,
+      totalDept: 0,
       // 企微用户表格数据
       userList: [],
       // 弹出层标题
@@ -98,6 +155,15 @@ export default {
         corpId: null,
         nickName: null
       },
+      handleAddAllQwDeptOpen:false,
+      deptList:null,
+      selectDeptList: [],
+      queryParamsDept:{
+        pageNum: 1,
+        pageSize: 10,
+        corpId:null,
+        deptName:null,
+      },
       // 表单参数
       form: {},
 
@@ -111,6 +177,78 @@ export default {
   },
   methods: {
 
+    handleChangeDept(row){
+
+      getQwUserByDept({ deptIds: [row.deptId],corpId:this.queryParams.corpId}).then(response => {
+
+        // 合并responseIds到selectUsers并去重
+        this.selectUsers=response.data
+
+        setTimeout(() => {
+          this.handleAddAllQwDeptOpen=false;
+          this.$emit("selectUserList",this.selectUsers);
+          this.resetSelect();
+        }, 200);
+
+      });
+    },
+
+    /** 重置按钮操作 */
+    resetQueryDept() {
+      this.resetForm("queryFormDept");
+      this.handleQueryDept();
+    },
+
+    handleAddAllQwDept(){
+
+      this.getDeptList()
+      this.handleAddAllQwDeptOpen = true;
+    },
+
+    getDeptList(){
+      this.queryParamsDept.corpId=this.queryParams.corpId;
+      listCompanyDept(this.queryParamsDept).then(res=>{
+        this.deptList=res.rows;
+        this.totalDept = res.total;
+      })
+    },
+
+
+    handleQueryDept() {
+      this.queryParamsDept.pageNum = 1;
+      this.getDeptList();
+    },
+
+
+    handleSelectionChangeDept(selection){
+
+      // 合并选中项
+      this.selectDeptList =selection.map(item => item.deptId)
+
+      // 更新 single 和 multiple
+      this.single = this.selectDeptList.length !== 1;
+      this.multiple = !this.selectDeptList.length;
+
+    },
+
+
+    confirmSelectDept(){
+
+      getQwUserByDept({ deptIds: this.selectDeptList,corpId:row.corpId}).then(response => {
+        // 合并responseIds到selectUsers并去重
+        this.selectUsers=response.data
+
+        setTimeout(() => {
+          this.handleAddAllQwDeptOpen=false;
+          this.$emit("selectUserList",this.selectUsers);
+          this.resetSelect();
+        }, 200);
+
+      });
+
+    },
+
+
     /** 查询企微用户列表 */
     getList() {
       this.loading = true;