Pārlūkot izejas kodu

SOP模板迁移

吴树波 1 mēnesi atpakaļ
vecāks
revīzija
9306b5e66e

+ 8 - 0
src/api/course/userCourseVideo.js

@@ -32,6 +32,14 @@ export function addUserCourseVideo(data) {
     data: data
   })
 }
+// 新增课堂视频
+export function updates(data) {
+  return request({
+    url: '/course/userCourseVideo/updates',
+    method: 'post',
+    data: data
+  })
+}
 
 // 修改课堂视频
 export function updateUserCourseVideo(data) {

+ 54 - 0
src/api/qw/material.js

@@ -0,0 +1,54 @@
+import request from '@/utils/request'
+
+// 查询素材库列表
+export function listMaterial(query) {
+  return request({
+    url: '/qw/material/list',
+    method: 'get',
+    params: query
+  })
+}
+
+
+// 查询素材库详细
+export function getMaterial(materialId) {
+  return request({
+    url: '/qw/material/' + materialId,
+    method: 'get'
+  })
+}
+
+// 新增素材库
+export function addMaterial(data) {
+  return request({
+    url: '/qw/material',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改素材库
+export function updateMaterial(data) {
+  return request({
+    url: '/qw/material',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除素材库
+export function delMaterial(materialId) {
+  return request({
+    url: '/qw/material/' + materialId,
+    method: 'delete'
+  })
+}
+
+// 导出素材库
+export function exportMaterial(query) {
+  return request({
+    url: '/qw/material/export',
+    method: 'get',
+    params: query
+  })
+}

+ 53 - 0
src/api/qw/materialGroup.js

@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 查询企业微信素材分组列表
+export function listMaterialGroup(query) {
+  return request({
+    url: '/qw/materialGroup/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询企业微信素材分组详细
+export function getMaterialGroup(materialGroupId) {
+  return request({
+    url: '/qw/materialGroup/' + materialGroupId,
+    method: 'get'
+  })
+}
+
+// 新增企业微信素材分组
+export function addMaterialGroup(data) {
+  return request({
+    url: '/qw/materialGroup',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改企业微信素材分组
+export function updateMaterialGroup(data) {
+  return request({
+    url: '/qw/materialGroup',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除企业微信素材分组
+export function delMaterialGroup(materialGroupId) {
+  return request({
+    url: '/qw/materialGroup/' + materialGroupId,
+    method: 'delete'
+  })
+}
+
+// 导出企业微信素材分组
+export function exportMaterialGroup(query) {
+  return request({
+    url: '/qw/materialGroup/export',
+    method: 'get',
+    params: query
+  })
+}

+ 123 - 0
src/api/qw/sop.js

@@ -0,0 +1,123 @@
+import request from '@/utils/request'
+
+// 查询企微sop列表
+export function listSop(query) {
+  return request({
+    url: '/qw/sop/list',
+    method: 'get',
+    params: query
+  })
+}
+// 查询企微sop模板列表
+export function listAiChatSop(query) {
+  return request({
+    url: '/qw/sop/listAiChatSop',
+    method: 'get',
+    params: query
+  })
+}
+
+
+export function courseList() {
+  return request({
+    url: '/qw/sop/courseList',
+    method: 'get',
+  })
+}
+export function videoList(id) {
+  return request({
+    url: '/qw/sop/videoList/' + id,
+    method: 'get'
+  })
+}
+// 查询企微sop详细
+export function getSop(id) {
+  return request({
+    url: '/qw/sop/' + id,
+    method: 'get'
+  })
+}
+
+// 新增企微sop
+export function addSop(data) {
+  return request({
+    url: '/qw/sop',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改企微sop
+export function updateSop(data) {
+  return request({
+    url: '/qw/sop',
+    method: 'put',
+    data: data
+  })
+}
+//修改企微sop自动创建时间
+export function updateAutoSopTime(data) {
+  return request({
+    url: '/qw/sop/updateAutoSopTime',
+    method: 'post',
+    data: data
+  })
+}
+
+//修改状态
+export function updateSopStatus(data) {
+  return request({
+    url: '/qw/sop/updateSopStatus',
+    method: 'post',
+    data: data
+  })
+}
+// 删除企微sop
+export function delSop(id) {
+  return request({
+    url: '/qw/sop/' + id,
+    method: 'delete'
+  })
+}
+
+//批量执行企微sop
+export function executeSop(ids) {
+  return request({
+    url: '/qw/sop/executeSop/' + ids,
+    method: 'put'
+  })
+}
+//批量执行企微sop先改状态
+export function updateStatus(ids) {
+  return request({
+    url: '/qw/sop/updateStatus/' + ids,
+    method: 'get'
+  })
+}
+// 导出企微sop
+export function exportSop(query) {
+  return request({
+    url: '/qw/sop/export',
+    method: 'get',
+    params: query
+  })
+}
+
+// 导出企微sop
+export function getSopVoiceList(params) {
+  return request({
+    url: '/qw/sop/getSopVoiceList',
+    method: 'get',
+    params
+  })
+}
+
+
+// 修改企微sop
+export function updateSopQwUser(data) {
+  return request({
+    url: '/qw/sop/updateSopQwUser',
+    method: 'post',
+    data: data
+  })
+}

+ 126 - 0
src/api/qw/sopTemp.js

@@ -0,0 +1,126 @@
+import request from '@/utils/request'
+
+// 查询sop模板列表
+export function listSopTemp(query) {
+  return request({
+    url: '/qw/sopTemp/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询sop模板详细
+export function getSopTemp(id) {
+  return request({
+    url: '/qw/sopTemp/' + id,
+    method: 'get'
+  })
+}
+export function dayListFun(id) {
+  return request({
+    url: '/qw/sopTemp/dayList',
+    method: 'get',
+    params: {id}
+  })
+}
+// 查询sop模板详细
+export function sortDay(list) {
+  return request({
+    url: '/qw/sopTemp/sortDay',
+    method: 'post',
+    data: list
+  })
+}
+// 查询sop模板详细
+export function selectRulesInfo(id, name, dayNum) {
+  return request({
+    url: '/qw/sopTemp/selectRulesInfo',
+    method: 'get',
+    params: {id, name, dayNum}
+  })
+}
+// 查询sop模板详细
+export function delRules(id, name, dayNum) {
+  return request({
+    url: '/qw/sopTemp/delRules',
+    method: 'get',
+    params: {id, name, dayNum}
+  })
+}
+
+// 新增sop模板
+export function addSopTemp(data) {
+  return request({
+    url: '/qw/sopTemp',
+    method: 'post',
+    data: data
+  })
+}
+// 新增sop模板
+export function addTemp(data) {
+  return request({
+    url: '/qw/sopTemp/add',
+    method: 'post',
+    data: data
+  })
+}
+
+// 新增sop模板
+export function copyTemplate(data) {
+  return request({
+    url: '/qw/sopTemp/copyTemplate',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改sop模板
+export function updateTemp(data) {
+  return request({
+    url: '/qw/sopTemp/update',
+    method: 'post',
+    data: data
+  })
+}
+// 修改sop模板
+export function addOrUpdateSetting(data) {
+  return request({
+    url: '/qw/sopTemp/addOrUpdateSetting',
+    method: 'post',
+    data: data
+  })
+}
+// 修改sop模板
+export function updateSopTemp(data) {
+  return request({
+    url: '/qw/sopTemp',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除sop模板
+export function delSopTemp(id) {
+  return request({
+    url: '/qw/sopTemp/' + id,
+    method: 'delete'
+  })
+}
+
+
+export function shareSopTemp(data) {
+  return request({
+    url: '/qw/sopTemp/shareTemp',
+    method: 'post',
+    data: data
+  })
+}
+
+// 导出sop模板
+export function exportSopTemp(query) {
+  return request({
+    url: '/qw/sopTemp/export',
+    method: 'get',
+    params: query
+  })
+}

+ 53 - 0
src/api/qw/userVideo.js

@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 查询企业微信的视频号列表
+export function listQwUserVideo(query) {
+  return request({
+    url: '/qw/userVideo/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询企业微信的视频号详细
+export function getQwUserVideo(id) {
+  return request({
+    url: '/qw/userVideo/' + id,
+    method: 'get'
+  })
+}
+
+// 新增企业微信的视频号
+export function addQwUserVideo(data) {
+  return request({
+    url: '/qw/userVideo',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改企业微信的视频号
+export function updateQwUserVideo(data) {
+  return request({
+    url: '/qw/userVideo',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除企业微信的视频号
+export function delQwUserVideo(id) {
+  return request({
+    url: '/qw/userVideo/' + id,
+    method: 'delete'
+  })
+}
+
+// 导出企业微信的视频号
+export function exportQwUserVideo(query) {
+  return request({
+    url: '/qw/userVideo/export',
+    method: 'get',
+    params: query
+  })
+}

+ 28 - 3
src/router/index.js

@@ -118,9 +118,34 @@ export const constantRoutes = [
         meta: { title: '修改生成配置' }
       }
     ]
-  }
-   
- 
+  },
+  {
+    path: '/qw/sopTempe',
+    component: Layout,
+    hidden: true,
+    children: [
+      {
+        path: 'updateSopTemp/:id/:type(\\d+)', // 确保 :type 的正则匹配数字
+        component: (resolve) => require(['@/views/qw/sopTemp/updateSopTemp'], resolve),
+        name: 'updateSopTemp',
+        meta: { title: '改动SOP模板', activeMenu: '/qw/addSopTemp' }
+      },
+      {
+        path: 'updateTemp/:id/:type(\\d+)', // 确保 :type 的正则匹配数字
+        component: () => import('@/views/qw/sopTemp/updateTemp'),
+        name: 'updateTemp',
+        meta: { title: '改动SOP模板', activeMenu: '/qw/updateTemp' }
+      },
+      {
+        path: 'updateAiChatTemp/:id/:type(\\d+)', // 确保 :type 的正则匹配数字
+        component: () => import('@/views/qw/sopTemp/updateAiChatTemp'),
+        name: 'updateAiChatTemp',
+        meta: { title: '改动SOP模板', activeMenu: '/qw/addSopTemp' }
+      }
+    ]
+  },
+
+
 ]
 
 export default new Router({

+ 89 - 2
src/views/components/course/userCourseCatalogDetails.vue

@@ -28,6 +28,15 @@
           @click="handleAdd"
         >新增目录</el-button>
       </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          :disabled="!ids || ids.length <= 0"
+          size="mini"
+          @click="openUpdates"
+        >修改时间</el-button>
+      </el-col>
       <el-col :span="1.5">
         <el-button
           type="danger"
@@ -93,6 +102,27 @@
         <el-form-item label="课程排序" prop="courseSort">
           <el-input-number v-model="form.courseSort" :min="1" ></el-input-number>
         </el-form-item>
+        <el-form-item label="看课时间" prop="timeRange">
+          <el-time-picker
+            is-range
+            v-model="form.timeRange"
+            range-separator="至"
+            start-placeholder="开始时间"
+            value-format="HH:mm:ss"
+            end-placeholder="结束时间"
+            placeholder="选择时间范围">
+          </el-time-picker>
+        </el-form-item>
+        <el-form-item label="领取红包时间" prop="lastJoinTime">
+          <el-time-picker
+            v-model="form.lastJoinTime"
+            :selectableRange="form.timeRange"
+            value-format="HH:mm:ss"
+            placeholder="选择时间范围">
+          </el-time-picker>
+          <p style="color: red;margin: 0;font-size: 12px">超过领取红包时间,只允许看课,不允许领取红包</p>
+        </el-form-item>
+
         <el-form-item label="视频缩略图" prop="thumbnail">
           <el-upload
             v-model="form.thumbnail"
@@ -163,6 +193,34 @@
         <el-button @click="cancel">取 消</el-button>
       </div>
     </el-dialog>
+    <el-dialog :title="title" :visible.sync="updateBatchData.open" width="1000px" append-to-body >
+      <el-form ref="form" :model="updateBatchData.form" label-width="110px">
+        <el-form-item label="看课时间" prop="timeRange">
+          <el-time-picker
+            is-range
+            v-model="updateBatchData.form.timeRange"
+            range-separator="至"
+            start-placeholder="开始时间"
+            value-format="HH:mm:ss"
+            end-placeholder="结束时间"
+            placeholder="选择时间范围">
+          </el-time-picker>
+        </el-form-item>
+        <el-form-item label="领取红包时间" prop="lastJoinTime">
+          <el-time-picker
+            v-model="updateBatchData.form.lastJoinTime"
+            :selectableRange="updateBatchData.form.timeRange"
+            value-format="HH:mm:ss"
+            placeholder="选择时间范围">
+          </el-time-picker>
+          <p style="color: red;margin: 0;font-size: 12px">超过领取红包时间,只允许看课,不允许领取红包</p>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="updateBatch">确 定</el-button>
+        <el-button @click="updateBatchData.open = false">取 消</el-button>
+      </div>
+    </el-dialog>
     <el-dialog :title="questionBank.title" :visible.sync="questionBank.open" width="800px" append-to-body >
       <question-bank ref="questionBank" @questionBankResult="questionBankResult" ></question-bank>
     </el-dialog>
@@ -171,7 +229,7 @@
 </template>
 
 <script>
-import { getSort,getVideoListByCourseId,delUserCourseVideo,getUserCourseVideo,addUserCourseVideo,updateUserCourseVideo } from "@/api/course/userCourseVideo";
+import { getSort,getVideoListByCourseId,delUserCourseVideo,getUserCourseVideo,addUserCourseVideo,updateUserCourseVideo, updates } from "@/api/course/userCourseVideo";
 import {getSignature,uploadHuaWeiVod,uploadHuaWeiObs} from "@/api/common"
 import  QuestionBank from  "@/views/course/courseQuestionBank/QuestionBank.vue";
 import TcVod from 'vod-js-sdk-v6'
@@ -242,6 +300,10 @@ import VideoUpload from "@/components/VideoUpload/index.vue";
         multiple: true,
           // 表单参数
         form: {},
+        updateBatchData: {
+          open: false,
+          form:{}
+        },
         // 表单校验
         rules: {
           title: [
@@ -451,7 +513,7 @@ import VideoUpload from "@/components/VideoUpload/index.vue";
       },
       // 多选框选中数据
       handleSelectionChange(selection) {
-        this.ids = selection.map(item => item.courseId)
+        this.ids = selection.map(item => item.videoId)
         this.single = selection.length!==1
         this.multiple = !selection.length
       },
@@ -483,6 +545,10 @@ import VideoUpload from "@/components/VideoUpload/index.vue";
           if (this.form.packageJson!=null){
             this.packageList = JSON.parse(this.form.packageJson);
           }
+
+          if(response.data.viewStartTime != null && response.data.viewEndTime != null){
+            this.form.timeRange = [response.data.viewStartTime, response.data.viewEndTime]
+          }
           setTimeout(() => {
             this.$refs.videoUpload.resetUpload();
           }, 500);
@@ -504,6 +570,10 @@ import VideoUpload from "@/components/VideoUpload/index.vue";
               });
               return
             }
+            if(this.form.timeRange != null && this.form.timeRange.length === 2){
+              this.form.viewStartTime = this.form.timeRange[0];
+              this.form.viewEndTime = this.form.timeRange[1];
+            }
             if(this.form.duration==null){
               this.$message({
                 message: '未识别到视频时长请稍等。。。',
@@ -540,6 +610,23 @@ import VideoUpload from "@/components/VideoUpload/index.vue";
           }
         });
       },
+      openUpdates(){
+        this.updateBatchData.form = {};
+        this.updateBatchData.open = true;
+      },
+      /** 提交按钮 */
+      updateBatch() {
+        this.updateBatchData.form.ids = this.ids;
+        if(this.updateBatchData.form.timeRange != null && this.updateBatchData.form.timeRange.length === 2){
+          this.updateBatchData.form.viewStartTime = this.updateBatchData.form.timeRange[0];
+          this.updateBatchData.form.viewEndTime = this.updateBatchData.form.timeRange[1];
+        }
+          updates(this.updateBatchData.form).then(response => {
+            this.msgSuccess("修改成功");
+            this.updateBatchData.open = false;
+            this.getList();
+          });
+      },
       /** 删除按钮操作 */
       handleDelete(row) {
         const videoIds = row.videoId || this.ids;

+ 261 - 225
src/views/course/userCourse/index.vue

@@ -2,23 +2,24 @@
   <div class="app-container">
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
       <el-form-item label="课堂分类" prop="cateId">
-        <el-select v-model="queryParams.cateId" placeholder="请选择" clearable size="small"  @change="getQuerySubCateList(queryParams.cateId)">
+        <el-select v-model="queryParams.cateId" placeholder="请选择" clearable size="small"
+                   @change="getQuerySubCateList(queryParams.cateId)">
           <el-option
-              v-for="dict in categoryOptions"
-              :key="dict.dictValue"
-              :label="dict.dictLabel"
-              :value="dict.dictValue"
-            />
+            v-for="dict in categoryOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
         </el-select>
       </el-form-item>
       <el-form-item label="课堂子分类" prop="subCateId">
         <el-select v-model="queryParams.subCateId" placeholder="请选择" clearable size="small">
           <el-option
-              v-for="dict in querySubCateOptions"
-              :key="dict.dictValue"
-              :label="dict.dictLabel"
-              :value="dict.dictValue"
-            />
+            v-for="dict in querySubCateOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
         </el-select>
       </el-form-item>
       <el-form-item label="课堂名称" prop="courseName">
@@ -33,11 +34,11 @@
       <el-form-item label="课堂类型" prop="isPrivate">
         <el-select v-model="queryParams.isPrivate" placeholder="请选择" clearable size="small">
           <el-option
-              v-for="dict in courseTypeOptions"
-              :key="dict.dictValue"
-              :label="dict.dictLabel"
-              :value="dict.dictValue"
-            />
+            v-for="dict in courseTypeOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
         </el-select>
       </el-form-item>
       <el-form-item>
@@ -55,7 +56,8 @@
           size="mini"
           @click="handleAdd"
           v-hasPermi="['course:userCourse:add']"
-        >新增</el-button>
+        >新增
+        </el-button>
       </el-col>
       <el-col :span="1.5">
         <el-button
@@ -66,7 +68,8 @@
           :disabled="single"
           @click="handleUpdate"
           v-hasPermi="['course:userCourse:edit']"
-        >修改</el-button>
+        >修改
+        </el-button>
       </el-col>
       <el-col :span="1.5">
         <el-button
@@ -77,7 +80,8 @@
           :disabled="multiple"
           @click="handleDelete"
           v-hasPermi="['course:userCourse:remove']"
-        >删除</el-button>
+        >删除
+        </el-button>
       </el-col>
       <el-col :span="1.5">
         <el-button
@@ -88,11 +92,12 @@
           :loading="exportLoading"
           @click="handleExport"
           v-hasPermi="['course:userCourse:export']"
-        >导出</el-button>
+        >导出
+        </el-button>
       </el-col>
       <el-col :span="1.5">
         <el-button
-        v-if="queryParams.isShow==0"
+          v-if="queryParams.isShow==0"
           type="success"
           plain
           icon="el-icon-edit"
@@ -100,7 +105,8 @@
           :disabled="multiple"
           @click="putOn"
           v-hasPermi="['course:userCourse:putOn']"
-        >上架</el-button>
+        >上架
+        </el-button>
       </el-col>
       <el-col :span="1.5">
         <el-button
@@ -112,7 +118,8 @@
           :disabled="multiple"
           @click="pullOff"
           v-hasPermi="['course:userCourse:pullOff']"
-        >下架</el-button>
+        >下架
+        </el-button>
       </el-col>
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
@@ -122,9 +129,9 @@
       <el-tab-pane label="待上架" name="0"></el-tab-pane>
     </el-tabs>
     <el-table height="600" border v-loading="loading" :data="userCourseList" @selection-change="handleSelectionChange">
-      <el-table-column type="selection" width="55" align="center" />
-      <el-table-column label="课程ID" align="center" prop="courseId" />
-      <el-table-column label="所属项目" align="center" prop="projectName" />
+      <el-table-column type="selection" width="55" align="center"/>
+      <el-table-column label="课程ID" align="center" prop="courseId"/>
+      <el-table-column label="所属项目" align="center" prop="projectName"/>
       <el-table-column label="封面图片" align="center" prop="imgUrl" width="120">
         <template slot-scope="scope">
           <el-popover
@@ -132,7 +139,7 @@
             title=""
             trigger="hover"
           >
-            <img slot="reference" :src="scope.row.imgUrl" width="100" >
+            <img slot="reference" :src="scope.row.imgUrl" width="100">
             <img :src="scope.row.imgUrl" style="max-width: 300px;">
           </el-popover>
         </template>
@@ -149,20 +156,21 @@
           </el-popover>
         </template>
       </el-table-column>
-      <el-table-column label="课堂名称" align="center" show-overflow-tooltip prop="courseName" />
-      <el-table-column label="排序" align="center" prop="sort" />
-      <el-table-column label="分类名称" align="center" prop="cateName" />
-      <el-table-column label="子分类名称" align="center" prop="subCateName" />
-      <el-table-column label="原价" align="center" prop="price" />
-      <el-table-column label="售价" align="center" prop="sellPrice" />
-      <el-table-column label="单节积分" align="center" prop="integral" />
+      <el-table-column label="课堂名称" align="center" show-overflow-tooltip prop="courseName"/>
+      <el-table-column label="排序" align="center" prop="sort"/>
+      <el-table-column label="分类名称" align="center" prop="cateName"/>
+      <el-table-column label="子分类名称" align="center" prop="subCateName"/>
+      <el-table-column label="原价" align="center" prop="price"/>
+      <el-table-column label="售价" align="center" prop="sellPrice"/>
+      <el-table-column label="单节积分" align="center" prop="integral"/>
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
         <template slot-scope="scope">
           <el-button
             size="mini"
             type="text"
             @click="handleCatalog(scope.row)"
-          >目录管理</el-button>
+          >目录管理
+          </el-button>
           <el-button
             size="mini"
             type="text"
@@ -178,14 +186,16 @@
             icon="el-icon-edit"
             @click="handleUpdate(scope.row)"
             v-hasPermi="['course:userCourse:edit']"
-          >修改</el-button>
+          >修改
+          </el-button>
           <el-button
             size="mini"
             type="text"
             icon="el-icon-delete"
             @click="handleDelete(scope.row)"
             v-hasPermi="['course:userCourse:remove']"
-          >删除</el-button>
+          >删除
+          </el-button>
         </template>
       </el-table-column>
     </el-table>
@@ -213,21 +223,22 @@
             </el-select>
           </el-form-item>
         </el-row>
-        <el-row >
+        <el-row>
           <el-col :span="8">
             <el-form-item label="课堂名称" prop="courseName">
-              <el-input v-model="form.courseName" placeholder="请输入课堂名称" />
+              <el-input v-model="form.courseName" placeholder="请输入课堂名称"/>
             </el-form-item>
           </el-col>
           <el-col :span="8">
             <el-form-item label="课堂分类" prop="cateId">
-              <el-select v-model="form.cateId" placeholder="请选择" clearable size="small"  @change="getSubCateList(form.cateId)">
+              <el-select v-model="form.cateId" placeholder="请选择" clearable size="small"
+                         @change="getSubCateList(form.cateId)">
                 <el-option
-                    v-for="dict in categoryOptions"
-                    :key="dict.dictValue"
-                    :label="dict.dictLabel"
-                    :value="dict.dictValue"
-                  />
+                  v-for="dict in categoryOptions"
+                  :key="dict.dictValue"
+                  :label="dict.dictLabel"
+                  :value="dict.dictValue"
+                />
               </el-select>
             </el-form-item>
           </el-col>
@@ -235,11 +246,11 @@
             <el-form-item label="课堂子分类" prop="subCateId">
               <el-select v-model="form.subCateId" placeholder="请选择" clearable size="small">
                 <el-option
-                    v-for="dict in subCategoryOptions"
-                    :key="dict.dictValue"
-                    :label="dict.dictLabel"
-                    :value="dict.dictValue"
-                  />
+                  v-for="dict in subCategoryOptions"
+                  :key="dict.dictValue"
+                  :label="dict.dictLabel"
+                  :value="dict.dictValue"
+                />
               </el-select>
             </el-form-item>
           </el-col>
@@ -258,15 +269,16 @@
             </el-form-item>
           </el-col> -->
           <el-col :span="8">
-            <el-form-item label="关联达人" prop="talentId" >
-              <el-select v-model="form.talentId" remote filterable clearable reserve-keyword placeholder="输入手机号搜索" :remote-method="talentMethod" >
+            <el-form-item label="关联达人" prop="talentId">
+              <el-select v-model="form.talentId" remote filterable clearable reserve-keyword
+                         placeholder="输入手机号搜索" :remote-method="talentMethod">
                 <el-option
                   v-for="item in talentList"
                   :key="item.talentId"
                   :label="item.nickName +'#'+item.phone"
                   :value="item.talentId">
                   <span style="float: left">{{ item.talentId }}</span>
-                  <span style="margin-left: 30px ;">{{item.nickName}}</span>
+                  <span style="margin-left: 30px ;">{{ item.nickName }}</span>
                   <span style="margin-left: 30px">{{ item.phone }}</span>
                 </el-option>
               </el-select>
@@ -276,15 +288,15 @@
         <el-row>
           <el-col :span="24">
             <el-form-item label="课堂简介" prop="description">
-              <el-input v-model="form.description" type="textarea" :rows="2" placeholder="请输入课堂简介" />
+              <el-input v-model="form.description" type="textarea" :rows="2" placeholder="请输入课堂简介"/>
             </el-form-item>
           </el-col>
         </el-row>
         <el-form-item label="课程封面" prop="imgUrl">
-          <ImageUpload v-model="form.imgUrl" type="image" :num="10" :width="150" :height="150" />
+          <ImageUpload v-model="form.imgUrl" type="image" :num="10" :width="150" :height="150"/>
         </el-form-item>
         <el-form-item label="小封面" prop="imgUrl">
-          <ImageUpload v-model="form.secondImg" type="image" :num="10" :width="150" :height="150" />
+          <ImageUpload v-model="form.secondImg" type="image" :num="10" :width="150" :height="150"/>
         </el-form-item>
         <el-row>
           <el-col :span="12">
@@ -300,20 +312,30 @@
             </el-form-item>
           </el-col>
         </el-row>
+        <el-row>
+          <el-col :span="8">
+            <el-form-item label="发课时间" prop="sendTime">
+              <el-time-picker
+                v-model="form.sendTime"
+              >
+              </el-time-picker>
+            </el-form-item>
+          </el-col>
+        </el-row>
         <el-row>
           <el-col :span="8">
             <el-form-item label="排序" prop="sort">
-              <el-input-number v-model="form.sort"  :min="0"  label="排序"></el-input-number>
+              <el-input-number v-model="form.sort" :min="0" label="排序"></el-input-number>
             </el-form-item>
           </el-col>
           <el-col :span="8">
             <el-form-item label="播放量" prop="views">
-              <el-input-number v-model="form.views" :min="0"  label="浏览量"></el-input-number>
+              <el-input-number v-model="form.views" :min="0" label="浏览量"></el-input-number>
             </el-form-item>
           </el-col>
           <el-col :span="8">
             <el-form-item label="点赞量" prop="likes">
-              <el-input-number v-model="form.likes" :min="0"   label="点赞量"></el-input-number>
+              <el-input-number v-model="form.likes" :min="0" label="点赞量"></el-input-number>
             </el-form-item>
           </el-col>
         </el-row>
@@ -321,17 +343,17 @@
 
           <el-col :span="8">
             <el-form-item label="收藏数" prop="favoriteNum">
-              <el-input-number v-model="form.favoriteNum" :min="0"   label="收藏数"></el-input-number>
+              <el-input-number v-model="form.favoriteNum" :min="0" label="收藏数"></el-input-number>
             </el-form-item>
           </el-col>
           <el-col :span="8">
             <el-form-item label="分享数" prop="shares">
-              <el-input-number v-model="form.shares" :min="0"   label="分享数"></el-input-number>
+              <el-input-number v-model="form.shares" :min="0" label="分享数"></el-input-number>
             </el-form-item>
           </el-col>
           <el-col :span="8">
             <el-form-item label="热度值" prop="hotNum">
-              <el-input-number v-model="form.hotNum" :min="0"   label="热度值"></el-input-number>
+              <el-input-number v-model="form.hotNum" :min="0" label="热度值"></el-input-number>
             </el-form-item>
           </el-col>
         </el-row>
@@ -339,21 +361,21 @@
           <el-col :span="8">
             <el-form-item label="状态" prop="isShow">
               <el-radio-group v-model="form.isShow">
-                <el-radio :label="item.dictValue" v-for="item in specShowOptions" >{{item.dictLabel}}</el-radio>
+                <el-radio :label="item.dictValue" v-for="item in specShowOptions">{{ item.dictLabel }}</el-radio>
               </el-radio-group>
             </el-form-item>
           </el-col>
           <el-col :span="8">
             <el-form-item label="是否推荐" prop="isTui">
               <el-radio-group v-model="form.isTui">
-                <el-radio :label="item.dictValue" v-for="item in orOptions" >{{item.dictLabel}}</el-radio>
+                <el-radio :label="item.dictValue" v-for="item in orOptions">{{ item.dictLabel }}</el-radio>
               </el-radio-group>
             </el-form-item>
           </el-col>
           <el-col :span="8">
             <el-form-item label="是否精选" prop="isBest">
               <el-radio-group v-model="form.isBest">
-                <el-radio :label="item.dictValue" v-for="item in orOptions" >{{item.dictLabel}}</el-radio>
+                <el-radio :label="item.dictValue" v-for="item in orOptions">{{ item.dictLabel }}</el-radio>
               </el-radio-group>
             </el-form-item>
           </el-col>
@@ -362,21 +384,21 @@
           <el-col :span="8">
             <el-form-item label="是否自动播放" prop="isAutoPlay">
               <el-radio-group v-model="form.isAutoPlay">
-                <el-radio :label="item.dictValue" v-for="item in orOptions" >{{item.dictLabel}}</el-radio>
+                <el-radio :label="item.dictValue" v-for="item in orOptions">{{ item.dictLabel }}</el-radio>
               </el-radio-group>
             </el-form-item>
           </el-col>
           <el-col :span="8">
             <el-form-item label="是否允许快进" prop="isFast">
               <el-radio-group v-model="form.isFast">
-                <el-radio :label="item.dictValue" v-for="item in orOptions" >{{item.dictLabel}}</el-radio>
+                <el-radio :label="item.dictValue" v-for="item in orOptions">{{ item.dictLabel }}</el-radio>
               </el-radio-group>
             </el-form-item>
           </el-col>
           <el-col :span="8">
             <el-form-item label="是否积分兑换" prop="isIntegral">
               <el-radio-group v-model="form.isIntegral">
-                <el-radio :label="item.dictValue" v-for="item in orOptions" >{{item.dictLabel}}</el-radio>
+                <el-radio :label="item.dictValue" v-for="item in orOptions">{{ item.dictLabel }}</el-radio>
               </el-radio-group>
             </el-form-item>
           </el-col>
@@ -385,45 +407,45 @@
           <el-col :span="8">
             <el-form-item label="是否逐级播放" prop="isNext">
               <el-radio-group v-model="form.isNext">
-                <el-radio :label="item.dictValue" v-for="item in orOptions" >{{item.dictLabel}}</el-radio>
+                <el-radio :label="item.dictValue" v-for="item in orOptions">{{ item.dictLabel }}</el-radio>
               </el-radio-group>
             </el-form-item>
           </el-col>
           <el-col :span="8">
             <el-form-item label="是否私域" prop="isPrivate">
               <el-radio-group v-model="form.isPrivate">
-                <el-radio :label="item.dictValue" v-for="item in orOptions" >{{item.dictLabel}}</el-radio>
+                <el-radio :label="item.dictValue" v-for="item in orOptions">{{ item.dictLabel }}</el-radio>
               </el-radio-group>
             </el-form-item>
           </el-col>
         </el-row>
         <el-row>
           <el-col :span="8">
-            <el-form-item label="课程原价" prop="price" >
-              <el-input v-model="form.price" placeholder="请输入课程原价" />
+            <el-form-item label="课程原价" prop="price">
+              <el-input v-model="form.price" placeholder="请输入课程原价"/>
             </el-form-item>
           </el-col>
           <el-col :span="8">
-            <el-form-item label="课程售价" prop="sellPrice" >
-              <el-input v-model="form.sellPrice" placeholder="请输入课程售价" />
+            <el-form-item label="课程售价" prop="sellPrice">
+              <el-input v-model="form.sellPrice" placeholder="请输入课程售价"/>
             </el-form-item>
           </el-col>
           <el-col :span="8">
             <el-form-item label="单节所需积分" prop="integral" v-if="form.isIntegral==1">
-              <el-input v-model="form.integral" placeholder="请输入单节所需积分" />
+              <el-input v-model="form.integral" placeholder="请输入单节所需积分"/>
             </el-form-item>
           </el-col>
         </el-row>
-		<el-form-item label="关联公司" prop="tags">
-		  <el-select v-model="companyIds" multiple placeholder="请选择公司" filterable clearable style="width: 90%;">
-		    <el-option
-		      v-for="dict in companyOptions"
-		      :key="dict.dictValue"
-		      :label="dict.dictLabel"
-		      :value="dict.dictValue"
-		    />
-		  </el-select>
-		</el-form-item>
+        <el-form-item label="关联公司" prop="tags">
+          <el-select v-model="companyIds" multiple placeholder="请选择公司" filterable clearable style="width: 90%;">
+            <el-option
+              v-for="dict in companyOptions"
+              :key="dict.dictValue"
+              :label="dict.dictLabel"
+              :value="dict.dictValue"
+            />
+          </el-select>
+        </el-form-item>
       </el-form>
       <div slot="footer" class="dialog-footer">
         <el-button type="primary" @click="submitForm">确 定</el-button>
@@ -431,53 +453,64 @@
       </div>
     </el-dialog>
     <el-drawer
-        :with-header="false"
-        size="75%"
-         :title="show.title" :visible.sync="show.open" append-to-body>
-     <userCourseCatalogDetails  ref="userCourseCatalogDetails" />
-   </el-drawer>
+      :with-header="false"
+      size="75%"
+      :title="show.title" :visible.sync="show.open" append-to-body>
+      <userCourseCatalogDetails ref="userCourseCatalogDetails"/>
+    </el-drawer>
   </div>
 </template>
 
 <script>
-import { listUserCourse, getUserCourse, delUserCourse, addUserCourse, updateUserCourse, exportUserCourse,updateIsShow,putOn,pullOff } from "@/api/course/userCourse";
+import {
+  listUserCourse,
+  getUserCourse,
+  delUserCourse,
+  addUserCourse,
+  updateUserCourse,
+  exportUserCourse,
+  updateIsShow,
+  putOn,
+  pullOff
+} from "@/api/course/userCourse";
 import Treeselect from "@riophae/vue-treeselect";
 import "@riophae/vue-treeselect/dist/vue-treeselect.css";
 import Editor from '@/components/Editor/wang';
 import ImageUpload from '@/components/ImageUpload/index';
-import { listBySearch} from "@/api/course/userTalent";
+import {listBySearch} from "@/api/course/userTalent";
 import userCourseCatalogDetails from '../../components/course/userCourseCatalogDetails.vue';
-import { getAllCourseCategoryList,getCatePidList ,getCateListByPid} from "@/api/course/userCourseCategory";
+import {getAllCourseCategoryList, getCatePidList, getCateListByPid} from "@/api/course/userCourseCategory";
 import {allList} from "@/api/company/company";
+
 export default {
   name: "UserCourse",
   components: {
     Treeselect,
-    Editor,ImageUpload,userCourseCatalogDetails
+    Editor, ImageUpload, userCourseCatalogDetails
   },
   data() {
     return {
-      talentParam:{
-        phone:null,
-        talentId:null
+      talentParam: {
+        phone: null,
+        talentId: null
       },
-      talentList:[],
-      show:{
-        title:"目录管理",
-        open:false
+      talentList: [],
+      show: {
+        title: "目录管理",
+        open: false
       },
-      activeName:"1",
-      projectOptions:[],
-      tagsOptions:[],
-      tags:[],
-	  companyIds:[],
-      courseTypeOptions:[],
+      activeName: "1",
+      projectOptions: [],
+      tagsOptions: [],
+      tags: [],
+      companyIds: [],
+      courseTypeOptions: [],
       orOptions: [],
       specShowOptions: [],
       specTypeOptions: [],
-      categoryOptions:[],
-      subCategoryOptions:[],
-      querySubCateOptions:[],
+      categoryOptions: [],
+      subCategoryOptions: [],
+      querySubCateOptions: [],
       // 遮罩层
       loading: true,
       // 导出遮罩层
@@ -494,7 +527,7 @@ export default {
       total: 0,
       // 课程表格数据
       userCourseList: [],
-	  companyOptions:[],
+      companyOptions: [],
       // 弹出层标题
       title: "",
       // 是否显示弹出层
@@ -504,7 +537,7 @@ export default {
         pageNum: 1,
         pageSize: 10,
         cateId: null,
-        subCateId:null,
+        subCateId: null,
         title: null,
         imgUrl: null,
         userId: null,
@@ -525,49 +558,49 @@ export default {
       // 表单校验
       rules: {
         courseName: [
-          { required: true, message: "课堂名称不能为空", trigger: "blur" }
+          {required: true, message: "课堂名称不能为空", trigger: "blur"}
         ],
         imgUrl: [
-          { required: true, message: "封面图片不能为空", trigger: "blur" }
+          {required: true, message: "封面图片不能为空", trigger: "blur"}
         ],
         isTui: [
-          { required: true, message: "是否推荐不能为空", trigger: "blur" }
+          {required: true, message: "是否推荐不能为空", trigger: "blur"}
         ],
         isBest: [
-          { required: true, message: "是否精选不能为空", trigger: "blur" }
+          {required: true, message: "是否精选不能为空", trigger: "blur"}
         ],
         isFast: [
-          { required: true, message: "是否允许快进不能为空", trigger: "blur" }
+          {required: true, message: "是否允许快进不能为空", trigger: "blur"}
         ],
         isAutoPlay: [
-          { required: true, message: "是否自动播放不能为空", trigger: "blur" }
+          {required: true, message: "是否自动播放不能为空", trigger: "blur"}
         ],
         sort: [
-          { required: true, message: "排序不能为空", trigger: "blur" }
+          {required: true, message: "排序不能为空", trigger: "blur"}
         ],
         views: [
-          { required: true, message: "播放量不能为空", trigger: "blur" }
+          {required: true, message: "播放量不能为空", trigger: "blur"}
         ],
         likes: [
-          { required: true, message: "点赞数不能为空", trigger: "blur" }
+          {required: true, message: "点赞数不能为空", trigger: "blur"}
         ],
         favoriteNum: [
-          { required: true, message: "收藏数不能为空", trigger: "blur" }
+          {required: true, message: "收藏数不能为空", trigger: "blur"}
         ],
         shares: [
-          { required: true, message: "分享数不能为空", trigger: "blur" }
+          {required: true, message: "分享数不能为空", trigger: "blur"}
         ],
         isIntegral: [
-          { required: true, message: "是否允许积分兑换不能为空", trigger: "blur" }
+          {required: true, message: "是否允许积分兑换不能为空", trigger: "blur"}
         ],
         isShow: [
-          { required: true, message: "上架状态不能为空", trigger: "blur" }
+          {required: true, message: "上架状态不能为空", trigger: "blur"}
         ],
         isPrivate: [
-          { required: true, message: "公私域不能为空", trigger: "blur" }
+          {required: true, message: "公私域不能为空", trigger: "blur"}
         ],
         integral: [
-          { required: true, message: "小节兑换积分不能为空", trigger: "blur" }
+          {required: true, message: "小节兑换积分不能为空", trigger: "blur"}
         ],
       }
     };
@@ -575,7 +608,7 @@ export default {
   created() {
     this.getList();
     getCatePidList().then(response => {
-        this.categoryOptions = response.data;
+      this.categoryOptions = response.data;
     });
 
     // this.getTreeselect();
@@ -597,15 +630,15 @@ export default {
     this.getDicts("sys_company_or").then(response => {
       this.orOptions = response.data;
     });
-	allList().then(response => {
-	    this.companyOptions = response.rows;
-	});
+    allList().then(response => {
+      this.companyOptions = response.rows;
+    });
   },
   methods: {
-    selectTalent(){
+    selectTalent() {
 
     },
-    talentMethod(query){
+    talentMethod(query) {
       if (query !== '') {
         this.talentParam.phone = query;
         listBySearch(this.talentParam).then(response => {
@@ -613,44 +646,44 @@ export default {
         });
       }
     },
-    getSubCateList(pid){
-      this.form.subCateId=null;
-      if(pid == ''){
-        this.subCategoryOptions=[];
+    getSubCateList(pid) {
+      this.form.subCateId = null;
+      if (pid == '') {
+        this.subCategoryOptions = [];
         return
       }
       getCateListByPid(pid).then(response => {
         this.subCategoryOptions = response.data;
       });
     },
-    getQuerySubCateList(pid){
-      this.queryParams.subCateId=null;
-      if(pid == ''){
-        this.querySubCateOptions=[];
+    getQuerySubCateList(pid) {
+      this.queryParams.subCateId = null;
+      if (pid == '') {
+        this.querySubCateOptions = [];
         return
       }
-      this.queryParams.subCateId=null;
+      this.queryParams.subCateId = null;
       getCateListByPid(pid).then(response => {
         this.querySubCateOptions = response.data;
       });
     },
     handleShow(row) {
       var isShowValue = row.isShow === 0 ? 1 : 0;
-      var course = { courseId: row.courseId, isShow: isShowValue };
+      var course = {courseId: row.courseId, isShow: isShowValue};
       updateIsShow(course).then(response => {
         this.msgSuccess("修改成功");
         this.getList();
       });
     },
-    handleCatalog(row){
+    handleCatalog(row) {
       const courseId = row.courseId;
-      this.show.open=true;
-        setTimeout(() => {
-             this.$refs.userCourseCatalogDetails.getDetails(courseId,row.courseName,row.isPrivate);
-        }, 200);
+      this.show.open = true;
+      setTimeout(() => {
+        this.$refs.userCourseCatalogDetails.getDetails(courseId, row.courseName, row.isPrivate);
+      }, 200);
     },
     handleClick(tab, event) {
-      this.queryParams.isShow=tab.name;
+      this.queryParams.isShow = tab.name;
       this.getList();
     },
     /** 转换课堂分类数据结构 */
@@ -668,7 +701,7 @@ export default {
       getAllCourseCategoryList().then(response => {
         this.categoryOptions = [];
         const data = this.handleTree(response.data, "cateId", "pid");
-        this.categoryOptions=data;
+        this.categoryOptions = data;
       });
     },
     /** 查询课程列表 */
@@ -690,10 +723,10 @@ export default {
       this.form = {
         courseId: null,
         cateId: null,
-        subCateId:null,
+        subCateId: null,
         title: null,
         imgUrl: null,
-        secondImg:null,
+        secondImg: null,
         userId: null,
         sort: null,
         createTime: null,
@@ -701,26 +734,26 @@ export default {
         status: 0,
         isVip: null,
         isAutoPlay: "1",
-        isIntegral:"0",
+        isIntegral: "0",
         isShow: "1",
-        isFast:"1",
-        isTui:"1",
-        isBest:"1",
-        isNext:"1",
-        isPrivate:"1",
+        isFast: "1",
+        isTui: "1",
+        isBest: "1",
+        isNext: "1",
+        isPrivate: "1",
         views: 100000,
         duration: null,
         description: null,
         hotRanking: null,
         integral: null,
         price: null,
-        likes:100000,
-        shares:100000,
-        favoriteNum:100000,
-        hotNum:100000
+        likes: 100000,
+        shares: 100000,
+        favoriteNum: 100000,
+        hotNum: 100000,
       };
-      this.tags=[];
-      this.subCategoryOptions=[]
+      this.tags = [];
+      this.subCategoryOptions = []
       this.resetForm("form");
     },
     /** 搜索按钮操作 */
@@ -731,26 +764,26 @@ export default {
     /** 重置按钮操作 */
     resetQuery() {
       this.resetForm("queryForm");
-      this.queryParams.isShow=this.activeName
+      this.queryParams.isShow = this.activeName
       this.handleQuery();
     },
     // 多选框选中数据
     handleSelectionChange(selection) {
       this.ids = selection.map(item => item.courseId)
-      this.single = selection.length!==1
+      this.single = selection.length !== 1
       this.multiple = !selection.length
     },
     /** 新增按钮操作 */
     handleAdd() {
       this.reset();
-      this.talentList=[];
+      this.talentList = [];
       this.open = true;
       this.title = "添加课程";
     },
     /** 修改按钮操作 */
     handleUpdate(row) {
       this.reset();
-      this.talentList=[];
+      this.talentList = [];
       const courseId = row.courseId || this.ids
       getUserCourse(courseId).then(response => {
         this.form = response.data;
@@ -759,10 +792,10 @@ export default {
           this.subCategoryOptions = response.data;
         });
         // this.form.courseType = response.data.courseType.toString();
-        if(response.data.project!=null){
+        if (response.data.project != null) {
           this.form.project = response.data.project.toString();
         }
-        if(response.data.tags!=null){
+        if (response.data.tags != null) {
           this.tags = response.data.tags.split(",")
         }
         this.form.isAutoPlay = response.data.isAutoPlay.toString();
@@ -774,11 +807,11 @@ export default {
         this.form.isNext = response.data.isNext.toString();
         this.form.isPrivate = response.data.isPrivate.toString();
         this.talentParam.talentId = response.data.talentId;
-		if(this.form.companyIds!=null){
-			this.companyIds=((this.form.companyIds).split(",").map(Number))
-		}else{
-			this.companyIds=[]
-		}
+        if (this.form.companyIds != null) {
+          this.companyIds = ((this.form.companyIds).split(",").map(Number))
+        } else {
+          this.companyIds = []
+        }
 
         listBySearch(this.talentParam).then(response => {
           this.talentList = response.data;
@@ -791,13 +824,12 @@ export default {
     submitForm() {
       this.$refs["form"].validate(valid => {
         if (valid) {
-          if(this.tags.length>0){
-              this.form.tags=this.tags.toString();
-            }
-            else{
-              this.form.tags=null
-            }
-			this.form.companyIds=this.companyIds.toString()
+          if (this.tags.length > 0) {
+            this.form.tags = this.tags.toString();
+          } else {
+            this.form.tags = null
+          }
+          this.form.companyIds = this.companyIds.toString()
           if (this.form.courseId != null) {
             updateUserCourse(this.form).then(response => {
               this.msgSuccess("修改成功");
@@ -818,62 +850,66 @@ export default {
     handleDelete(row) {
       const courseIds = row.courseId || this.ids;
       this.$confirm('是否确认删除课程编号为"' + courseIds + '"的数据项?', "警告", {
-          confirmButtonText: "确定",
-          cancelButtonText: "取消",
-          type: "warning"
-        }).then(function() {
-          return delUserCourse(courseIds);
-        }).then(() => {
-          this.getList();
-          this.msgSuccess("删除成功");
-        }).catch(() => {});
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function () {
+        return delUserCourse(courseIds);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("删除成功");
+      }).catch(() => {
+      });
     },
     /** 导出按钮操作 */
     handleExport() {
       const queryParams = this.queryParams;
       this.$confirm('是否确认导出所有课程数据项?', "警告", {
-          confirmButtonText: "确定",
-          cancelButtonText: "取消",
-          type: "warning"
-        }).then(() => {
-          this.exportLoading = true;
-          return exportUserCourse(queryParams);
-        }).then(response => {
-          this.download(response.msg);
-          this.exportLoading = false;
-        }).catch(() => {});
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        this.exportLoading = true;
+        return exportUserCourse(queryParams);
+      }).then(response => {
+        this.download(response.msg);
+        this.exportLoading = false;
+      }).catch(() => {
+      });
     },
     putOn() {
-      const courseIds =this.ids;
-      if(courseIds==null||courseIds==""){
-         return this.$message("未选择课程");
+      const courseIds = this.ids;
+      if (courseIds == null || courseIds == "") {
+        return this.$message("未选择课程");
       }
       this.$confirm('是否确认批量上架课程?', "警告", {
-          confirmButtonText: "确定",
-          cancelButtonText: "取消",
-          type: "warning"
-        }).then(function() {
-          return putOn(courseIds);
-        }).then(() => {
-          this.getList();
-          this.msgSuccess("上架成功");
-        }).catch(function() {});
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function () {
+        return putOn(courseIds);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("上架成功");
+      }).catch(function () {
+      });
     },
     pullOff() {
-      const courseIds =this.ids;
-      if(courseIds==null||courseIds==""){
-         return this.$message("未选择课程");
+      const courseIds = this.ids;
+      if (courseIds == null || courseIds == "") {
+        return this.$message("未选择课程");
       }
       this.$confirm('是否确认批量下架课程?', "警告", {
-          confirmButtonText: "确定",
-          cancelButtonText: "取消",
-          type: "warning"
-        }).then(function() {
-          return pullOff(courseIds);
-        }).then(() => {
-          this.getList();
-          this.msgSuccess("下架成功");
-        }).catch(function() {});
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function () {
+        return pullOff(courseIds);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("下架成功");
+      }).catch(function () {
+      });
     }
   }
 };

+ 10 - 10
src/views/course/userCoursePeriod/index.vue

@@ -148,7 +148,6 @@
         </el-row>
 
         <el-table v-loading="loading" :data="periodList">
-          <el-table-column label="所属训练营" align="center" prop="trainingCampName" />
           <el-table-column label="营期名称" align="center" prop="periodName" />
           <el-table-column label="公司名称" align="center" prop="companyName" />
           <el-table-column label="开营开始时间" align="center" prop="periodStartingTime" width="180" />
@@ -192,7 +191,7 @@
     </el-container>
 
     <!-- 添加或修改会员营期对话框-->
-    <el-dialog :title="title" :visible.sync="open" width="700px" append-to-body>
+    <el-drawer :title="title" :visible.sync="open" width="700px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
         <el-form-item label="营期名称" prop="periodName">
           <el-input v-model="form.periodName" placeholder="请输入营期名称" />
@@ -269,10 +268,10 @@
         <el-button type="primary" @click="submitForm">确 定</el-button>
         <el-button @click="cancel">取 消</el-button>
       </div>
-    </el-dialog>
+    </el-drawer>
 
     <!-- 添加训练营对话框 -->
-    <el-dialog :title="campForm.trainingCampId ? '修改训练营' : '新建训练营'" :visible.sync="campDialogVisible" width="500px" append-to-body>
+    <el-drawer :title="campForm.trainingCampId ? '修改训练营' : '新建训练营'" :visible.sync="campDialogVisible" width="500px" append-to-body>
       <el-form ref="campForm" :model="campForm" :rules="campRules" label-width="100px">
         <el-form-item label="训练营名称" prop="trainingCampName">
           <el-input v-model="campForm.trainingCampName" placeholder="请输入训练营名称" />
@@ -282,7 +281,7 @@
         <el-button type="primary" @click="submitCampForm">确 定</el-button>
         <el-button @click="cancelCampForm">取 消</el-button>
       </div>
-    </el-dialog>
+    </el-drawer>
 
     <!-- 添加或修改会员营期对话框-->
     <el-dialog title="课程管理" :visible.sync="course.open" width="90%" top="10px" append-to-body style="padding-bottom: 10px">
@@ -728,7 +727,7 @@ export default {
     handleEditCamp(item) {
       this.resetCampForm();
       this.leftLoading = true;
-      
+
       // 获取最新的训练营数据
       const trainingCampId = item.trainingCampId;
       // 应该调用获取训练营详情的API
@@ -771,10 +770,10 @@ export default {
         if (valid) {
           // 显示加载中
           this.leftLoading = true;
-          
+
           // 准备提交的数据
           const submitData = JSON.parse(JSON.stringify(this.campForm));
-          
+
           // 判断是新增还是修改
           if (submitData.trainingCampId) {
             // 修改训练营
@@ -782,7 +781,7 @@ export default {
               if (response.code === 200) {
                 this.$message.success('修改训练营成功');
                 this.campDialogVisible = false;
-                
+
                 // 更新列表中的数据
                 const index = this.campList.findIndex(camp => camp.trainingCampId === submitData.trainingCampId);
                 if (index !== -1) {
@@ -792,7 +791,7 @@ export default {
                     this.selectCamp(index);
                   }
                 }
-                
+
                 // 重新加载训练营列表
                 this.getLeftList();
               } else {
@@ -833,6 +832,7 @@ export default {
     },
     /** 选中训练营 */
     selectCamp(index) {
+      if(index == null || index == undefined) return;
       this.activeCampIndex = index;
       // 加载对应的训练营营期数据
       const selectedCamp = this.campList[index];

+ 427 - 0
src/views/qw/friendMaterial/index.vue

@@ -0,0 +1,427 @@
+<template>
+  <div v-if="type == 'image'">
+    <ul v-for="(item,index) in value" :key="index" class="el-upload-list el-upload-list--picture-card">
+      <li tabindex="0" class="el-upload-list__item is-ready" :style="'width: '+width+'px;height: '+height+'px'">
+        <div>
+          <img :src="item" alt="" class="el-upload-list__item-thumbnail">
+          <span class="el-upload-list__item-actions">
+            <span v-if="index != 0" class="el-upload-list__item-preview" @click="moveMaterial(index,'up')">
+              <i class="el-icon-back" />
+            </span>
+            <span class="el-upload-list__item-preview" @click="zoomMaterial(index)">
+              <i class="el-icon-view" />
+            </span>
+            <span class="el-upload-list__item-delete" @click="deleteMaterial(index)">
+              <i class="el-icon-delete" />
+            </span>
+            <span v-if="index != value.length-1" class="el-upload-list__item-preview" @click="moveMaterial(index,'down')">
+              <i class="el-icon-right" />
+            </span>
+          </span>
+        </div>
+      </li>
+    </ul>
+    <div v-if="num > value.length" tabindex="0" class="el-upload el-upload--picture-card" :style="'width: '+width+'px;height: '+height+'px;'+'line-height:'+height+'px;'" @click="toSeleteMaterial">
+      <i class="el-icon-plus" />
+    </div>
+    <!-- 查看 -->
+    <el-dialog
+      append-to-body
+      :visible.sync="dialogVisible"
+      width="35%"
+    >
+      <img :src="materialUrl" alt="" style="width: 100%">
+    </el-dialog>
+    <!-- 素材列表 -->
+    <el-dialog
+      title="朋友圈图片素材库"
+      append-to-body
+      :visible.sync="listDialogVisible"
+      width="70%"
+    >
+      <el-container>
+        <el-aside width="unset">
+          <div style="margin-bottom: 10px">
+            <el-button
+              class="el-icon-plus"
+              size="small"
+              @click="materialgroupAdd()"
+            >
+              添加分组
+            </el-button>
+          </div>
+          <div class="group-list">
+            <div class="group-item" v-for="(group) in materialGroupList">
+                <el-button  @click="selectGroup(group)" type="primary" plain >{{group.materialGroupName}}</el-button>
+            </div>
+          </div>
+        </el-aside>
+        <el-main>
+          <el-card>
+            <div slot="header">
+              <el-row>
+                <el-col :span="12">
+                  <span>{{ materialGroup.materialGroupName }}</span>
+                  <span v-if="materialGroup.materialGroupId >0">
+                    <el-button size="small" type="text" class="el-icon-edit" style="margin-left: 10px;" @click="materialgroupEdit(materialGroup)">重命名</el-button>
+                    <el-button size="small" type="text" class="el-icon-delete" style="margin-left: 10px;color: red" @click="materialgroupDelete(materialGroup)">删除</el-button>
+                  </span>
+                </el-col>
+                <el-col :span="12" style="text-align: right;">
+                  <el-upload
+                    :action="uploadUrl"
+                    :file-list="[]"
+                    :on-progress="handleProgress"
+                    :before-upload="beforeUpload"
+                    :on-success="handleSuccess"
+                    :data="{type: 1}"
+                    multiple
+                  >
+                    <el-button size="small" type="primary">批量上传</el-button>
+                  </el-upload>
+                </el-col>
+              </el-row>
+            </div>
+            <div v-loading="tableLoading">
+              <el-alert
+                v-if="tableData.length <= 0"
+                title="暂无数据"
+                type="info"
+                :closable="false"
+                center
+                show-icon
+              />
+              <el-row :gutter="5">
+                <el-checkbox-group v-model="urls" :max="num - value.length">
+                  <el-col v-for="(item,index) in tableData" :key="index" :span="4">
+                    <el-card :body-style="{ padding: '5px' }">
+                      <el-image
+                        style="width: 100%;height: 100px"
+                        :src="item.materialUrl"
+                        fit="contain"
+                        :preview-src-list="[item.materialUrl]"
+                        :z-index="9999"
+                      />
+                      <div>
+                        <el-checkbox class="material-name" :label="item.materialUrl">
+                          选择
+                        </el-checkbox>
+                        <el-row>
+                          <el-col :span="24" class="col-do">
+                            <el-button type="text" size="medium" @click="materialDel(item)">删除</el-button>
+                          </el-col>
+                        </el-row>
+
+                      </div>
+                    </el-card>
+                  </el-col>
+                </el-checkbox-group>
+              </el-row>
+               <pagination
+                  v-show="total>0"
+                  :total="total"
+                  :page.sync="queryParams.pageNum"
+                  :limit.sync="queryParams.pageSize"
+                  @pagination="getMaterialList"
+                />
+
+            </div>
+          </el-card>
+        </el-main>
+      </el-container>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="listDialogVisible = false">取 消</el-button>
+        <el-button type="primary" @click="submit">确 定</el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import {addMaterial, delMaterial, listMaterial,} from "@/api/qw/material";
+import {addMaterialGroup, delMaterialGroup, listMaterialGroup, updateMaterialGroup} from "@/api/qw/materialGroup";
+
+export default {
+  name: 'ImageSelect',
+  props: {
+    // 素材数据
+    value: {
+      type: Array,
+      default() {
+        return []
+      }
+    },
+    // 素材类型
+    type: {
+      type: String
+    },
+    // 素材限制选择数量,最多9个
+    num: {
+      type: Number,
+      default() {
+        return 9
+      }
+    },
+    // 宽度
+    width: {
+      type: Number,
+      default() {
+        return 150
+      }
+    },
+    // 宽度
+    height: {
+      type: Number,
+      default() {
+        return 150
+      }
+    }
+  },
+  data() {
+    return {
+      uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
+      dialogVisible: false,
+      materialUrl: '',
+      listDialogVisible: false,
+      //素材组列表
+      materialGroupList: [],
+      materialGroupLoading: false,
+      //选择的某个素材组
+      materialGroup:{},
+      tableData: [],
+      resultNumber: 0,
+      total: 0,
+      //查询素材列表
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        groupType:2,
+        materialGroupName: null,
+        materialGroupId:null,
+        materialUrl: null,
+        createUserId: null,
+      },
+      tableLoading: false,
+      urls: []
+    }
+  },
+  mounted(){
+    this.getAllMaterialGroup();
+  },
+  methods: {
+    //查询素材组下的素材列表
+    selectGroup(item){
+      this.materialGroup=item;
+      this.queryParams.materialGroupId=item.materialGroupId;
+      this.getMaterialList();
+    },
+    //添加分组
+    materialgroupAdd() {
+      const that = this
+      this.$prompt('请输入分组名', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消'
+      }).then(({ value }) => {
+        addMaterialGroup({
+          materialGroupName: value,
+          groupType:2
+        }).then(function() {
+          that.materialGroup={};
+          that.getAllMaterialGroup();
+        })
+      }).catch(() => {
+
+      })
+    },
+    //删除素材组
+    materialgroupDelete(materialgroupObj) {
+      const that = this
+      this.$confirm('是否确认删除该分组?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(function() {
+        delMaterialGroup(materialgroupObj.materialGroupId)
+          .then(function() {
+            that.materialGroup={};
+            that.getAllMaterialGroup()
+          })
+      })
+      this.resetGroup();
+      this.getAllMaterialGroup();
+    },
+    //修改素材组名称
+    materialgroupEdit(materialgroupObj) {
+      const that = this
+      this.$prompt('请输入分组名', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        inputValue: materialgroupObj.materialGroupName
+      }).then(({ value }) => {
+        updateMaterialGroup({
+          materialGroupId: materialgroupObj.materialGroupId,
+          materialGroupName: value,
+          groupType:2,
+        }).then(function() {
+          that.materialGroup={};
+          that.getAllMaterialGroup()
+        })
+      }).catch(() => {
+
+      })
+    },
+    //获取所有素材分组
+    getAllMaterialGroup() {
+      this.materialGroupLoading = true;
+      listMaterialGroup({groupType:2}).then(response => {
+        this.materialGroupList = response.rows
+        this.materialGroupLoading = false;
+      });
+    },
+    resetGroup(){
+      this.queryParams= {
+        pageNum: 1,
+          pageSize: 10,
+          groupType:2,
+          materialGroupName: null,
+          materialGroupId:null,
+          materialUrl: null,
+          createUserId: null,
+      };
+    },
+    //查询分组 下的素材
+    getMaterialList() {
+      this.tableLoading = true;
+      listMaterial(this.queryParams).then(response => {
+        this.tableData = response.rows;
+        this.total = response.total;
+        this.tableLoading = false;
+      });
+    },
+    //移动素材
+    moveMaterial(index, type) {
+      if (type == 'up') {
+        const tempOption = this.value[index - 1]
+        this.$set(this.value, index - 1, this.value[index])
+        this.$set(this.value, index, tempOption)
+      }
+      if (type == 'down') {
+        const tempOption = this.value[index + 1]
+        this.$set(this.value, index + 1, this.value[index])
+        this.$set(this.value, index, tempOption)
+      }
+    },
+    //缩小图片
+    zoomMaterial(index) {
+      this.dialogVisible = true
+      this.materialUrl = this.value[index]
+    },
+    //删除素材
+    deleteMaterial(index) {
+      const that = this
+      this.$confirm('是否确认删除?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(function() {
+        that.value.splice(index, 1)
+        that.urls = []
+      })
+    },
+
+    toSeleteMaterial() {
+      this.listDialogVisible = true
+      this.getAllMaterialGroup()
+      this.getMaterialList();
+    },
+    //删除素材
+    materialDel(item) {
+      const that = this
+      this.$confirm('是否确认删除该素材?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(function() {
+        delMaterial(item.materialId)
+          .then(function() {
+            that.queryParams.pageNum=0;
+            that.getMaterialList();
+          })
+      })
+    },
+
+    //上传进度
+    handleProgress(event, file, fileList) {
+    },
+    //上传成功后
+    handleSuccess(response, file, fileList) {
+      const that = this
+      addMaterial({
+        materialType: 'image',
+        materialGroupId: this.queryParams.materialGroupId,
+        materialName: file.name,
+        materialUrl: response.url,
+        groupType:2,
+      }).then(() => {
+        this.resultNumber++
+        if (fileList.length === this.resultNumber) {
+          that.getMaterialList()
+          this.resultNumber = 0
+        }
+      })
+    },
+    //上传之前
+    beforeUpload(file) {
+      const isPic =
+        file.type === 'image/jpeg' ||
+        file.type === 'image/png' ||
+        file.type === 'image/gif' ||
+        file.type === 'image/jpg'
+      const isLt2M = file.size / 1024 / 1024 < 10
+      if (!isPic) {
+        this.$message.error('上传图片只能是 JPG、JPEG、PNG、GIF 格式!')
+        return false
+      }
+      if (!isLt2M) {
+        this.$message.error('上传图片大小不能超过 10MB!')
+      }
+      return isPic && isLt2M
+    },
+    //提交
+    submit() {
+      this.urls.forEach(item => {
+        console.log("item",item)
+        console.log("this.value",this.value)
+        console.log("this.value.length",this.value.length)
+
+        this.$set(this.value, this.value.length, item)
+      })
+      this.listDialogVisible = false
+    }
+  }
+}
+</script>
+
+<style rel="stylesheet/scss" lang="scss" scoped>
+  ::v-deep .el-icon-circle-close{
+    color: red;
+  }
+  .material-name{
+    padding: 8px 0px;
+  }
+  .col-do{
+    text-align: center;
+  }
+  .button-do{
+    padding: unset!important;
+    font-size: 12px;
+  }
+  .group-list{
+    display: flex;
+    flex-direction:column;
+    align-items: flex-start;
+  }
+  .group-item{
+    margin: 5px;
+  }
+</style>

+ 205 - 0
src/views/qw/sop/ImageUpload.vue

@@ -0,0 +1,205 @@
+<template>
+  <div class="component-upload-image">
+    <el-upload
+      :action="uploadUrl"
+      list-type="picture-card"
+      :on-success="handleUploadSuccess"
+      :before-upload="handleBeforeUpload"
+      :limit="limit"
+      :on-error="handleUploadError"
+      :on-exceed="handleExceed"
+      name="file"
+      :on-remove="handleRemove"
+      :show-file-list="true"
+      :file-list="fileList"
+      :on-preview="handlePictureCardPreview"
+      :class="{hide: this.fileList.length >= this.limit}"
+    >
+      <i class="el-icon-plus"></i>
+    </el-upload>
+
+    <el-dialog
+      :visible.sync="dialogVisible"
+      title="预览"
+      width="800"
+      append-to-body
+    >
+      <img
+        :src="dialogImageUrl"
+        style="display: block; max-width: 100%; margin: 0 auto"
+      />
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { getToken } from "@/utils/auth";
+
+export default {
+  name: "ImageUpload",
+  props: {
+    value: [String, Object, Array],
+    // 图片数量限制
+    limit: {
+      type: Number,
+      default: 1,
+    },
+    // 大小限制(MB)
+    fileSize: {
+       type: Number,
+       default: 1,
+    },
+    // 文件类型, 例如['png', 'jpg', 'jpeg']
+    fileType: {
+      type: Array,
+      default: () => ["png", "jpg", "jpeg"],
+    },
+    // 是否显示提示
+    isShowTip: {
+      type: Boolean,
+      default: true
+    }
+  },
+  data() {
+    return {
+      dialogImageUrl: "",
+      dialogVisible: false,
+      hideUpload: false,
+      baseUrl:"",
+      uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSSByHOOKImage",
+      headers: {
+        Authorization: "Bearer " + getToken(),
+      },
+      fileList: []
+    };
+  },
+  watch: {
+    value: {
+      handler(val) {
+        if (val) {
+          // 首先将值转为数组
+          const list = Array.isArray(val) ? val : this.value.split(',');
+          // 然后将数组转为对象数组
+          this.fileList = list.map(item => {
+            if (typeof item === "string") {
+              if (item.indexOf(this.baseUrl) === -1) {
+                  item = { name: item, url: item };
+              } else {
+                  item = { name: item, url: item };
+              }
+            }
+            return item;
+          });
+        } else {
+          this.fileList = [];
+          return [];
+        }
+      },
+      deep: true,
+      immediate: true
+    }
+  },
+  computed: {
+    // 是否显示提示
+    showTip() {
+      return this.isShowTip && (this.fileType || this.fileSize);
+    },
+  },
+  methods: {
+    // 删除图片
+    handleRemove(file, fileList) {
+      const findex = this.fileList.map(f => f.name).indexOf(file.name);
+      if(findex > -1) {
+        this.fileList.splice(findex, 1);
+        this.$emit("input", this.listToString(this.fileList));
+      }
+    },
+    // 上传成功回调
+    handleUploadSuccess(res,file) {
+      this.fileList.push({ name: res.url, url: res.url });
+      this.$emit("input", this.listToString(this.fileList));
+      this.loading.close();
+    },
+    // 上传前loading加载
+    handleBeforeUpload(file) {
+      let isImg = false;
+      if (this.fileType.length) {
+        let fileExtension = "";
+        if (file.name.lastIndexOf(".") > -1) {
+          fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
+        }
+        isImg = this.fileType.some(type => {
+          if (file.type.indexOf(type) > -1) return true;
+          if (fileExtension && fileExtension.indexOf(type) > -1) return true;
+          return false;
+        });
+      } else {
+        isImg = file.type.indexOf("image") > -1;
+      }
+
+      if (!isImg) {
+        this.$message.error(
+          `文件格式不正确, 请上传${this.fileType.join("/")}图片格式文件!`
+        );
+        return false;
+      }
+      if (this.fileSize) {
+        const isLt = file.size / 1024 / 1024 < this.fileSize;
+        if (!isLt) {
+          this.$message.error(`上传头像图片大小不能超过 ${this.fileSize} MB!`);
+          return false;
+        }
+      }
+      this.loading = this.$loading({
+        lock: true,
+        text: "上传中",
+        background: "rgba(0, 0, 0, 0.7)",
+      });
+    },
+    // 文件个数超出
+    handleExceed() {
+      this.$message.error(`上传文件数量不能超过 ${this.limit} 个!`);
+    },
+    // 上传失败
+    handleUploadError() {
+      this.$message({
+        type: "error",
+        message: "上传失败",
+      });
+      this.loading.close();
+    },
+    // 预览
+    handlePictureCardPreview(file) {
+      console.log(file)
+      this.dialogImageUrl = file.url;
+      this.dialogVisible = true;
+    },
+    // 对象转成指定字符串分隔
+    listToString(list, separator) {
+      let strs = "";
+      separator = separator || ",";
+      for (let i in list) {
+        strs += list[i].url.replace(this.baseUrl, "") + separator;
+      }
+      return strs != '' ? strs.substr(0, strs.length - 1) : '';
+    }
+  }
+};
+</script>
+<style scoped lang="scss">
+// .el-upload--picture-card 控制加号部分
+::v-deep.hide .el-upload--picture-card {
+    display: none;
+}
+// 去掉动画效果
+::v-deep .el-list-enter-active,
+::v-deep .el-list-leave-active {
+    transition: all 0s;
+}
+
+::v-deep .el-list-enter, .el-list-leave-active {
+    opacity: 0;
+    transform: translateY(0);
+}
+</style>
+

+ 331 - 0
src/views/qw/sopTemp/addAiChatTemp.vue

@@ -0,0 +1,331 @@
+<template>
+  <div class="app-container">
+    <div style="margin: 30px;">sop规则【新客对话】模板</div>
+    <div style="margin-top: 10px;margin-left: 50px;margin-right: 100px;margin-bottom: 60px;">
+      <el-form ref="formRef" :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="dict.dictValue"
+            >{{ dict.dictLabel }}
+            </el-radio>
+          </el-radio-group>
+        </el-form-item>
+
+        <el-form-item label="推送方式">
+          <el-radio-group v-model="form.sendType">
+            <el-radio
+              v-for="dict in sysQwSopType.filter(item => parseInt(item.dictValue) === 4)"
+              :key="dict.dictValue"
+              :label="parseInt(dict.dictValue)"
+            >{{ dict.dictLabel }}
+            </el-radio>
+          </el-radio-group>
+        </el-form-item>
+
+        <!--        <el-form-item label="间隔天数" prop="gap">
+                  <el-input-number v-model="form.gap" :min="1" label="间隔天数"></el-input-number>
+                </el-form-item> -->
+
+        <el-form-item label="排序" prop="sort">
+          <el-input-number v-model="form.sort" :min="0" label="排序"></el-input-number>
+        </el-form-item>
+
+        <el-form-item label="规则" prop="setting">
+          <el-timeline>
+            <el-timeline-item
+              :timestamp="'第'+(1+(form.gap*index))+'天'"
+              :color="'#0bbd87'"
+              placement="top"
+              v-for="(item, index) in setting"
+              :key="index"
+              style="margin-top: 10px;">
+              <el-row>
+                <el-col :span="22">
+                  <div style="background-color: #fbfbfb;padding: 15px; border: 1px solid #e6e6e6; margin-bottom: 20px;">
+                    <el-form :model="item" label-width="80px">
+                      <el-form-item label="规则">
+                        <div v-for="(content, contentIndex) in item.content"
+                             :key="contentIndex"
+                             style="background-color: #fdfdfd;padding: 15px; border: 1px solid #e6e6e6; margin-bottom: 20px;">
+                          <el-row>
+                            <el-col :span="22">
+                              <el-form :model="content" label-width="70px">
+                                <!-- 第一天不显示时间选择 -->
+                                <el-form-item label="时间" prop="time" v-if="index > 0">
+                                  <el-time-picker
+                                    class="custom-input"
+                                    v-model="content.time"
+                                    value-format="HH:mm"
+                                    format="HH:mm"
+                                    :picker-options="{ selectableRange: '00:00:00 - 23:59:59' }"
+                                    placeholder="时间"
+                                    style="width: 100px;height: 20px;">
+                                  </el-time-picker>
+                                </el-form-item>
+
+
+                                <div v-for="(setList, setIndex) in content.setting"
+                                     :key="setIndex"
+                                     style="background-color: #fdfdfd; border: 1px solid #e6e6e6; margin-bottom: 20px;">
+                                  <el-row>
+                                    <el-col :span="22">
+                                      <el-form :model="setList" label-width="70px">
+                                        <el-form-item label="添加客服" prop="intervalTime" style="margin: 2%">
+                                          <el-input-number
+                                            v-model="setList.intervalTime"
+                                            :min="1"
+                                            :max="1440"
+                                            style="width:100;margin-top: 10px;"
+                                          >
+                                          </el-input-number>
+                                          <span class="tip">单位:分钟,最大1440分钟(24小时)</span>
+                                        </el-form-item>
+                                        <el-form-item label="内容" style="margin: 2%">
+                                          <el-input
+                                            v-model="setList.value"
+                                            type="textarea"
+                                            :rows="3"
+                                            placeholder="内容"
+                                            style="width: 90%;margin-top: 10px;"/>
+                                        </el-form-item>
+                                        <el-form-item label="交流状态" style="margin: 2%">
+                                          <el-select v-model="setList.talkType" placeholder="更改交流状态" size="mini"
+                                                     style=" margin-right: 10px;" clearable>
+                                            <el-option label="非首次交流" value="非首次交流"></el-option>
+                                            <el-option label="首次交流1" value="首次交流1"></el-option>
+                                            <el-option label="首次交流2" value="首次交流2"></el-option>
+                                            <el-option label="交流状态1" value="交流状态1"></el-option>
+                                            <el-option label="交流状态2" value="交流状态2"></el-option>
+                                            <el-option label="交流状态3" value="交流状态3"></el-option>
+                                          </el-select>
+                                        </el-form-item>
+                                      </el-form>
+                                    </el-col>
+                                    <el-col :span="1" :offset="1">
+                                      <i class="el-icon-delete"
+                                         @click="delSetList(index,contentIndex,setIndex)"
+                                         style="margin-top: 20px;"
+                                         v-if="content.setting.length>1"></i>
+                                    </el-col>
+                                  </el-row>
+                                </div>
+                                <el-link type="primary"
+                                         class="el-icon-plus"
+                                         :underline="false"
+                                         @click='addSetList(contentIndex,item.content)'>添加内容
+                                </el-link>
+                              </el-form>
+                            </el-col>
+                            <el-col :span="1" :offset="1">
+                              <i class="el-icon-delete"
+                                 @click="delContent(index,contentIndex)"
+                                 style="margin-top: 20px;"
+                                 v-if="item.content.length>1"></i>
+                            </el-col>
+                          </el-row>
+                        </div>
+                        <el-link type="primary"
+                                 v-if="index > 0"
+                                 class="el-icon-plus"
+                                 :underline="false"
+                                 @click='addContent(index)'>添加规则
+                        </el-link>
+                      </el-form-item>
+                    </el-form>
+                  </div>
+                </el-col>
+                <el-col :span="1" :offset="1">
+                  <i class="el-icon-delete"
+                     @click="delSetting(index)"
+                     v-if="setting.length>1"></i>
+                </el-col>
+              </el-row>
+            </el-timeline-item>
+          </el-timeline>
+          <!--          <el-link type="primary"
+                             class="el-icon-plus"
+                             :underline="false"
+                             @click='addSetting()'>添加天数</el-link> -->
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer" style="float: right;">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import {addSopTemp} from "@/api/qw/sopTemp";
+
+export default {
+  name: "AddAiChatTemp",
+  data() {
+    return {
+      // 状态字典
+      statusOptions: [],
+      // 添加推送方式字典
+      sysQwSopType: [],
+      // 表单数据
+      form: {
+        name: null,
+        setting: null,
+        status: "1",
+        sort: 1,
+        gap: 1,
+        sendType: 4, // AI对话类型,固定值
+      },
+      // 规则设置
+      setting: [],
+      // 表单校验
+      rules: {
+        name: [
+          {required: true, message: '名称不能为空', trigger: 'blur'}
+        ],
+        status: [
+          {required: true, message: '状态不能为空', trigger: 'blur'}
+        ],
+        sort: [
+          {required: true, message: '排序不能为空', trigger: 'blur'}
+        ],
+        gap: [
+          {required: true, message: '间隔天数不能为空', trigger: 'blur'}
+        ]
+      }
+    };
+  },
+  created() {
+    this.getDicts("sys_company_status").then(response => {
+      this.statusOptions = response.data;
+    });
+    // 获取推送方式字典
+    this.getDicts("sys_qw_sop_type").then(response => {
+      this.sysQwSopType = response.data;
+    });
+    this.getList();
+  },
+  methods: {
+    getList() {
+      this.setting.push({
+        name: null,
+        content: [{
+          type: 1,
+          contentType: '1',
+          setting: [{
+            intervalTime: 5,
+            contentType: '1',
+            value: ""
+          }]
+        }]
+      });
+    },
+
+    addSetList(contentIndex, content) {
+      content[contentIndex].setting.push({
+        intervalTime: 5,
+        contentType: '1',
+        value: ""
+      });
+    },
+
+    delSetList(index, contentIndex, setIndex) {
+      this.setting[index].content[contentIndex].setting.splice(setIndex, 1);
+    },
+
+    addContent(index) {
+      this.setting[index].content.push({
+        type: 1,
+        contentType: '1',
+        // 默认5分钟
+        setting: [{
+          intervalTime: 5,
+          contentType: '1',
+          value: ""
+        }]
+      });
+    },
+
+    delContent(index, contentIndex) {
+      this.setting[index].content.splice(contentIndex, 1);
+    },
+
+    addSetting() {
+      this.setting.push({
+        name: null,
+        content: [{
+          type: 1,
+          contentType: '1',
+          setting: [{
+            intervalTime: 5, // 默认5分钟
+            contentType: '1',
+            value: ""
+          }]
+        }]
+      });
+    },
+
+    delSetting(index) {
+      this.setting.splice(index, 1);
+    },
+
+    submitForm() {
+      this.$refs["formRef"].validate(valid => {
+        if (valid) {
+          // 验证内容
+          for (let day of this.setting) {
+            for (let content of day.content) {
+              if (content.time === undefined && this.setting.indexOf(day) > 0) {
+                return this.$message.error("时间不能为空");
+              }
+              for (let set of content.setting) {
+                if (!set.intervalTime) {
+                  return this.$message.error("间隔时间不能为空");
+                }
+                if (!set.value) {
+                  return this.$message.error("内容不能为空");
+                }
+              }
+            }
+          }
+
+          this.form.setting = JSON.stringify(this.setting);
+          addSopTemp(this.form).then(response => {
+            this.$message.success("新增成功");
+            window.location.replace('/qw/conversion/sopTemp')
+            this.reset();
+          });
+        }
+      });
+    },
+
+    cancel() {
+      this.$router.push('/qw/conversion/sopTemp');
+    }
+  }
+};
+</script>
+
+<style scoped>
+.custom-input /deep/ .el-input__inner {
+  height: 20px;
+  text-align: center;
+}
+
+.custom-input /deep/ .el-input__icon {
+  line-height: 10px;
+}
+
+.tip {
+  color: #909399;
+  font-size: 12px;
+  margin-left: 10px;
+}
+</style>

+ 981 - 0
src/views/qw/sopTemp/addSopTemp.vue

@@ -0,0 +1,981 @@
+<template>
+  <div class="app-container">
+
+    <div style="margin: 30px;" v-if="this.form.sendType == 1 "> sop规则【企微接口】模板</div>
+    <div style="margin: 30px;" v-if="this.form.sendType == 2 "> sop规则【AI插件】模板</div>
+
+    <div style="margin-top: 10px;margin-left: 50px;margin-right: 100px;margin-bottom: 60px;">
+      <el-form ref="form" :model="form" :rules="rules" label-width="100px">
+        <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="dict.dictValue"
+            >{{dict.dictLabel}}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="间隔天数" prop="gap">
+          <el-input-number v-model="form.gap"  :min="1" label="间隔天数"></el-input-number>
+        </el-form-item>
+        <el-form-item label="排序" prop="sort">
+          <el-input-number v-model="form.sort"  :min="0" label="排序"></el-input-number>
+        </el-form-item>
+        <el-form-item label="规则" prop="setting">
+          <el-timeline >
+            <el-timeline-item :timestamp="'第'+(1+(form.gap*index))+'天'" :color="'#0bbd87'" placement="top" v-for="(item, index) in setting"  style="margin-top: 10px;">
+              <el-row>
+                <el-col :span="22">
+                  <div style="background-color: #fbfbfb;padding: 15px;  border: 1px solid #e6e6e6; margin-bottom: 20px;">
+                    <el-form :model="item"  label-width="80px">
+
+                      <el-form-item label="内容名称"  style="height: 50px;">
+                        <el-input v-model="item.name" placeholder="内容名称,仅内部可见" />
+                      </el-form-item>
+
+                      <el-form-item label="规则"  >
+                        <div v-for="(content, contentIndex) in item.content" style="background-color: #fdfdfd;padding: 15px;  border: 1px solid #e6e6e6; margin-bottom: 20px;">
+
+                          <el-row>
+
+                            <el-col el-col :span="22" >
+                              <el-form :model="content" label-width="70px">
+                                <el-form-item label="时间"  prop="time"  >
+                                  <el-time-picker
+                                    class="custom-input"
+                                    v-model="content.time"
+                                    value-format="HH:mm"
+                                    format="HH:mm"
+                                    :picker-options="{ selectableRange: '00:21:00 - 23:59:59' }"
+                                    placeholder="时间"
+                                    style="width: 100px;height: 20px;" >
+                                  </el-time-picker>
+                                </el-form-item>
+                                <el-form-item label="消息类别"  >
+                                  <el-radio-group v-model="content.type" @change="() => content.contentType = '1'" >
+                                    <el-radio
+                                      :label="1"
+
+                                    >普通</el-radio>
+                                    <el-radio
+                                      :label="2"
+
+                                    >课程</el-radio>
+                                    <el-radio
+                                      :label="3"
+
+                                    >订单</el-radio>
+                                    <el-radio
+                                      :label="4"
+
+                                    >AI触达</el-radio>
+                                  </el-radio-group>
+                                </el-form-item>
+
+                                <el-form-item label="课程"  v-if="content.type == 2 ">
+                                  <el-select  v-model="content.courseId" placeholder="请选择课程" style=" margin-right: 10px;" size="mini"  @change="courseChange(content,index,contentIndex)">
+                                    <el-option
+                                      v-for="dict in courseList"
+                                      :key="dict.dictValue"
+                                      :label="dict.dictLabel"
+                                      :value="parseInt(dict.dictValue)"
+                                    />
+                                  </el-select>
+                                  <el-select  v-model="content.videoId" placeholder="请选择小节" size="mini" style=" margin-right: 10px;" @change="videoIdChange(content,index,contentIndex)" >
+                                    <el-option
+                                      v-for="dict in videoList[index][contentIndex]"
+                                      :key="dict.dictValue"
+                                      :label="dict.dictLabel"
+                                      :value="parseInt(dict.dictValue)"
+                                    />
+                                  </el-select>
+                                  <el-select  v-model="content.courseType" placeholder="请选择消息类型" size="mini" style=" margin-right: 10px;" v-if="content.type != 4 ">
+                                    <el-option
+                                      v-for="dict in sysFsSopWatchStatus"
+                                      :key="dict.dictValue"
+                                      :label="dict.dictLabel"
+                                      :value="parseInt(dict.dictValue)"
+                                    />
+                                  </el-select>
+
+
+
+
+                                </el-form-item>
+                                <el-form-item label="Ai触达"  v-if="content.type == 4 ">
+                                  <el-select  v-model="content.aiTouch" placeholder="请选择Ai触达类型" size="mini" style=" margin-right: 10px;" v-if="content.type == 4 ">
+                                    <el-option label="非首次交流" value="非首次交流"></el-option>
+                                    <el-option label="首次交流1" value="首次交流1"></el-option>
+                                    <el-option label="首次交流2" value="首次交流2"></el-option>
+                                    <el-option label="交流状态1" value="交流状态1"></el-option>
+                                    <el-option label="交流状态2" value="交流状态2"></el-option>
+                                    <el-option label="交流状态3" value="交流状态3"></el-option>
+                                  </el-select>
+                                </el-form-item>
+
+
+
+                                <!--                                <el-form-item label="内容类型"  >-->
+                                <div v-for="(setList, setIndex) in content.setting" :key="setIndex" style="background-color: #fdfdfd; border: 1px solid #e6e6e6; margin-bottom: 20px;" v-if="content.type != 4 ">
+                                  <el-row>
+                                    <el-col :span="22">
+                                      <el-form :model="setList" label-width="70px">
+                                        <el-form-item label="内容类别" style="margin: 2%">
+                                          <div v-if="form.sendType == 1 ">
+                                            <el-radio-group  v-model="setList.contentType" >
+                                              <el-radio
+                                                v-for="item in sysQwSopContentType"
+                                                :label="item.dictValue"
+                                                :disabled="item.dictValue === '1' && content.setting.some(s => s.contentType == '1')">
+                                                {{ item.dictLabel }}
+                                              </el-radio>
+                                            </el-radio-group>
+                                          </div>
+                                          <div v-if="form.sendType == 2 ">
+                                            <!--                                              <div v-if="content.type == 2 ">-->
+                                            <!--                                                <el-radio-group  v-model="setList.contentType" @change="handleContentTypeChange(setList,index,setIndex)">-->
+                                            <!--                                                  <el-radio  :label="item.dictValue" v-for="item in sysQwSopSettingType">{{item.dictLabel}}</el-radio>-->
+                                            <!--                                                </el-radio-group>-->
+                                            <!--                                              </div>-->
+                                            <!--                                              <div v-else>-->
+                                            <el-radio-group  v-model="setList.contentType" @change="handleContentTypeChange(content,index,contentIndex,setIndex)">
+                                              <el-radio   :label="item.dictValue" v-for="item in sysQwSopAiContentType">{{item.dictLabel}}</el-radio>
+                                            </el-radio-group>
+                                            <!--                                              </div>-->
+
+                                          </div>
+                                        </el-form-item>
+                                        <el-form-item label="内容"  >
+                                          <el-input v-if="setList.contentType == 1 " v-model="setList.value" type="textarea" :rows="3" placeholder="内容" style="width: 90%;margin-top: 10px;"/>
+
+                                          <ImageUpload v-if="setList.contentType == 2 " v-model="setList.imgUrl" type="image" :num="1"  :width="150" :height="150" />
+
+                                          <div v-if="setList.contentType == 3 ">
+                                            <el-card class="box-card">
+                                              <el-form-item label="链接标题:"  label-width="100px">
+                                                <el-input v-model="setList.linkTitle" placeholder="请输入链接标题" style="width: 90%;"/>
+                                              </el-form-item>
+                                              <el-form-item label="链接描述:"   label-width="100px" >
+                                                <el-input type="textarea" :rows="3" v-model="setList.linkDescribe" placeholder="请输入链接描述" style="width: 90%;margin-top: 1%;"/>
+                                              </el-form-item>
+                                              <el-form-item label="链接封面:"   label-width="100px">
+                                                <ImageUpload v-model="setList.linkImageUrl" type="image" :num="1" :file-size="2" :width="150" :height="150" style="margin-top: 1%;" />
+                                              </el-form-item>
+                                              <div v-if="content.type!=2" style="margin-top: 1%">
+                                                <el-form-item label="链接地址:"  label-width="100px" >
+                                                  <el-input v-model="setList.linkUrl" placeholder="请输入链接地址" style="width: 90%;"/>
+                                                </el-form-item>
+                                              </div>
+                                              <div v-if="content.type==2">
+                                                <el-form-item label="链接地址:"  label-width="100px" >
+                                                  <el-tag type="warning" v-model="setList.isBindUrl=1">选择的课程小节 即为卡片链接地址</el-tag>
+                                                </el-form-item>
+                                              </div>
+                                            </el-card>
+                                          </div>
+
+                                          <div v-if="setList.contentType == 4">
+                                            <el-card class="box-card">
+                                              <el-form-item label="标题" prop="miniprogramTitle">
+                                                <el-input v-model="setList.miniprogramTitle" placeholder="请输入小程序消息标题,最长为64字"  />
+                                              </el-form-item>
+                                              <el-form-item label="封面" prop="miniprogramPicUrl">
+                                                <ImageUpload v-model="setList.miniprogramPicUrl"  type="image" :num="10" :width="150" :height="150" />
+                                              </el-form-item>
+                                              <el-form-item label="appid" prop="miniprogramAppid" v-show="false" >
+                                                <el-input v-model="setList.miniprogramAppid='wx73f85f8d62769119' " disabled />
+                                              </el-form-item>
+                                              <el-form-item label="page路径" prop="miniprogramPage" v-show="false" label-width="100px" style="margin-left: -30px">
+                                                <el-input v-model="setList.miniprogramPage" placeholder="小程序消息打开后的路径" disabled />
+                                              </el-form-item>
+                                            </el-card>
+                                          </div>
+
+                                          <div v-if="setList.contentType == 5 ">
+
+                                            <el-form-item label="上传文件:" prop="fileUrl" label-width="100px">
+                                              <el-upload
+                                                v-model="setList.fileUrl"
+                                                class="avatar-uploader"
+                                                :action="uploadUrl"
+                                                :show-file-list="false"
+                                                :on-success="(res, file) => handleAvatarSuccessFile(res, file, setList)"
+                                                :before-upload="beforeAvatarUploadFile">
+                                                <i class="el-icon-plus avatar-uploader-icon"></i>
+                                              </el-upload>
+                                              <el-link v-if="setList.fileUrl" type="primary" :href="downloadUrl(setList.fileUrl)" download>
+                                                {{setList.fileUrl}}
+                                              </el-link>
+                                            </el-form-item>
+
+                                          </div>
+
+                                          <div v-if="setList.contentType == 6 ">
+                                            <el-form-item label="上传视频:" prop="videoUrl" label-width="100px">
+                                              <el-upload
+                                                v-model="setList.videoUrl"
+                                                class="avatar-uploader"
+                                                :action="uploadUrl"
+                                                :show-file-list="false"
+                                                :on-success="(res, file) => handleAvatarSuccessVideo(res, file, setList)"
+                                                :before-upload="beforeAvatarUploadVideo">
+                                                <i class="el-icon-plus avatar-uploader-icon"></i>
+                                              </el-upload>
+                                              <video v-if="setList.videoUrl"
+                                                     :src="setList.videoUrl"
+                                                     controls style="width: 200px;height: 100px">
+                                              </video>
+                                            </el-form-item>
+                                          </div>
+                                          <div v-if="setList.contentType == 7 ">
+                                              <el-input
+                                                        v-model="setList.value"
+                                                        type="textarea" :rows="3" maxlength="66" show-word-limit
+                                                        placeholder="输入要转为语音的内容" style="width: 90%;margin-top: 10px;"
+                                                        @input="handleInputVideoText(setList.value,setList)"/>
+                                          </div>
+                                          <div v-if="setList.contentType == 8 ">
+                                            <el-button type="primary" style="margin-bottom: 1%" @click="hanldeSelectVideoNum(content,index,contentIndex,setIndex)">选择视频号</el-button>
+                                            <el-card class="box-card" v-if="setList.coverUrl">
+                                              <el-form-item label="封面标题:"  label-width="100px">
+                                                <el-input v-model="setList.nickname" style="width: 90%;margin-bottom: 1%" disabled/>
+                                              </el-form-item>
+                                              <el-form-item label="头像:"   label-width="100px" >
+                                                <el-image
+                                                  v-if="setList.avatar != null"
+                                                  :src="setList.avatar"
+                                                  :preview-src-list="[setList.avatar]"
+                                                  :style="{ width: '50px', height: '50px' }"
+                                                ></el-image>
+                                              </el-form-item>
+                                              <el-form-item label="封面:"   label-width="100px" >
+                                                <el-image
+                                                  v-if="setList.coverUrl != null"
+                                                  :src="setList.coverUrl"
+                                                  :preview-src-list="[setList.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="setList.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="setList.url" style="width: 90%;" disabled />
+                                              </el-form-item >
+
+                                            </el-card>
+                                          </div>
+
+                                        </el-form-item>
+
+                                        <el-form-item label="添加短链" v-if="content.type == 2 && setList.contentType == 1 "  >
+                                          <el-tooltip content="请先根据课程选定课程小节之后再添加" effect="dark" :disabled="!!content.videoId">
+                                            <el-switch
+                                              v-model="setList.isBindUrl"
+                                              :disabled="!content.videoId"
+                                              active-color="#13ce66"
+                                              inactive-color="#DCDFE6"
+                                              active-value="1"
+                                              inactive-value="2">
+                                            </el-switch>
+                                          </el-tooltip>
+
+                                          <span v-if="setList.isBindUrl == '1'" style="margin-left: 10px; color: #13ce66">添加URL</span>
+                                          <span v-if="setList.isBindUrl == '2'" style="margin-left: 10px; color: #b1b4ba">不加URL</span>
+                                        </el-form-item>
+
+                                        <el-form-item label="课节过期时间" v-if="content.type == 2 && setList.isBindUrl == '1' && setList.contentType != 2  && setList.contentType != 5  && setList.contentType != 6 && setList.contentType != 8 " style="margin-top: 1%">
+                                          <el-row>
+                                            <el-input-number  v-model="setList.expiresDays"  :min="0" :max="100" ></el-input-number>
+                                            (天)
+                                          </el-row>
+                                          <el-row>
+                                            <span class="tip">填写0或不填时,默认为系统配置的默认时间</span>
+                                          </el-row>
+                                        </el-form-item>
+
+                                      </el-form>
+                                    </el-col>
+                                    <el-col :span="1" :offset="1">
+                                      <i class="el-icon-delete" @click="delSetList(index,contentIndex,setIndex)" style="margin-top: 20px;" v-if="content.setting.length>1"></i>
+                                    </el-col>
+                                  </el-row>
+
+                                </div>
+                                <el-link type="primary" class="el-icon-plus" :underline="false" @click='addSetList(contentIndex,item.content)' v-if="content.type != 4 ">添加内容</el-link>
+                                <!--                                </el-form-item>-->
+
+                              </el-form>
+
+                            </el-col>
+                            <el-col :span="1" :offset="1">
+                              <i class="el-icon-delete" @click="delContent(index,contentIndex)" style="margin-top: 20px;" v-if="item.content.length>1"></i>
+                            </el-col>
+                          </el-row>
+                        </div>
+                        <el-link type="primary" class="el-icon-plus" :underline="false" @click='addContent(index)'>添加规则</el-link>
+                      </el-form-item>
+                    </el-form>
+                  </div>
+                </el-col>
+                <el-col :span="1" :offset="1">
+                  <i class="el-icon-delete-solid" @click="delSetting(index)"  v-if="setting.length>1"></i>
+                </el-col>
+              </el-row>
+            </el-timeline-item>
+
+
+          </el-timeline>
+          <el-link type="primary" class="el-icon-plus" :underline="false" @click='addSetting()'>添加天数</el-link>
+        </el-form-item>
+
+      </el-form>
+      <div slot="footer" class="dialog-footer" style="float: right;">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </div>
+
+
+    <el-dialog :title="videoNumOptions.title" :visible.sync="videoNumOptions.open" style="width: 1500px;height: 100%" append-to-body>
+      <userVideo ref="QwUserVideo" @videoResult="qwUserVideoResult" ></userVideo>
+    </el-dialog>
+
+  </div>
+</template>
+
+<script>
+import {addSopTemp, delSopTemp, exportSopTemp, getSopTemp, listSopTemp, updateSopTemp} from "@/api/qw/sopTemp";
+import {courseList, videoList} from "@/api/qw/sop";
+import ImageUpload from "@/views/qw/sop/ImageUpload";
+import FriendMaterial from "@/views/qw/friendMaterial/index.vue";
+import userVideo from "@/views/qw/userVideo/userVideo.vue";
+
+export default {
+  name: "addSopTemp",
+  components: { ImageUpload,FriendMaterial,userVideo},
+  data() {
+    return {
+
+      //图片放大
+      dialogVisible: false,
+      dialogImageUrl:null,
+
+      uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS2",
+      uploadUrlByVoice:process.env.VUE_APP_BASE_API+"/common/uploadOSSByHOOKVoice",
+      //上传语音的遮罩层
+      voiceLoading :false,
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      sysFsSopWatchStatus: [],
+      //消息内容类型 企微版
+      sysQwSopContentType:[],
+      //插件版
+      sysQwSopAiContentType:[],
+      //链接版类别
+      sysQwSopSettingType:[],
+
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // sop模板表格数据
+      setting: [],
+      courseList:[],
+      videoList:[],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 状态字典
+      statusOptions: [],
+      // 查询参数
+      form: {
+        name: null,
+        setting: null,
+        status: "1",
+        sort: 1,
+        companyId: null,
+        gap:1,
+      },
+      videoNumOptions:{
+        title:'选择视频号',
+        open:false,
+        content:null,
+        contentIndex:null,
+        setIndex:null,
+      },
+      // 表单校验
+      rules: {
+        name: [
+          { required: true, message: '名称不能为空', trigger: 'blur' }
+        ],
+        status: [
+          { required: true, message: '状态不能为空', trigger: 'blur' }
+        ],
+        sort: [
+          { required: true, message: '排序不能为空', trigger: 'blur' }
+        ],
+        gap: [
+          { required: true, message: '间隔天数不能为空', trigger: 'blur' }
+        ],
+      },
+      contentRules:{
+        time:[{ required: true, message: '时间不能为空', trigger: 'blur' }],
+      }
+    };
+  },
+  created() {
+
+    this.getDicts("sys_company_status").then(response => {
+      this.statusOptions = response.data;
+    });
+    courseList().then(response => {
+      this.courseList = response.list;
+    });
+    this.getDicts("sys_fs_sop_watch_status").then(response => {
+      this.sysFsSopWatchStatus = response.data;
+    });
+
+    this.getDicts("sys_qwSopAi_contentType").then(response => {
+      this.sysQwSopAiContentType = response.data;
+    });
+
+    this.getDicts("sys_qwSop_contentType").then(response => {
+      this.sysQwSopContentType = response.data;
+    });
+    this.getDicts("sys_qwSop_settingType").then(response => {
+      this.sysQwSopSettingType = response.data;
+    });
+
+
+    this.form.sendType = this.$route.params && this.$route.params.command;
+
+    setTimeout(() => {
+      this.getList();
+    }, 200);
+
+  },
+  methods: {
+    handleInputVideoText(value,content){
+      // 允许的字符:中文、英文(大小写)、数字和指定标点符号(,。!?)
+      const regex = /^[\u4e00-\u9fa5,。!?,!?]+$/;
+
+      // 删除不符合条件的字符
+      const filteredValue = value.split('').filter(char => regex.test(char)).join('');
+
+      this.$set(content, 'value', filteredValue);
+
+    },
+    addSetList(contentIndex, content) {
+
+      if (this.form.sendType == 1) {
+        for (let i = 0; i < content.length; i++) {
+
+          if (content[i].setting.length > 9) {
+            return this.$message.error("因为企微接口限制,企微模板一条消息只能设置最多~9个内容!!")
+          } else {
+
+            // 动态生成新的设置项
+            const newSetting = content[i].setting.some(item => item.contentType === '1')
+              ? {contentType: '2', imgUrl: ''}  // 如果存在 contentType = '1',添加 contentType = '2'
+              : {contentType: '1', value: ''};  // 如果不存在 contentType = '1',添加 contentType = '1'
+
+            // 将新设置项添加到 content[i].setting 数组中
+            content[i].setting.push(newSetting);
+          }
+
+        }
+      } else {
+
+        const newSetting = {
+          contentType: '1',
+          value: '',
+        };
+
+        // 将新设置项添加到 content.setting 数组中
+        content[contentIndex].setting.push(newSetting);
+
+      }
+
+    },
+    delSetList(index, contentIndex, setIndex) {
+      this.setting[index].content[contentIndex].setting.splice(setIndex, 1)
+    },
+
+    addContent(index) {
+
+      if (this.setting[index].content.length > 0 && this.form.sendType == 1) {
+        return this.$message.error("因为企微接口限制,企微模板一天只能设置一条消息~")
+      } else {
+        this.setting[index].content.push({type: 1, contentType: '1', setting: [{contentType: '1', value: "",}]})
+        this.videoList[index].push([])
+      }
+
+    },
+
+    delContent(index, contentIndex) {
+      this.setting[index].content.splice(contentIndex, 1)
+      this.videoList[index].splice(contentIndex, 1)
+    },
+    addSetting() {
+
+      this.setting.push({name: null, content: [{type: 1, contentType: '1', setting: [{contentType: '1', value: "",}]}]})
+      this.videoList.push([])
+
+    },
+    delSetting(index) {
+      this.setting.splice(index, 1)
+      this.videoList.splice(index, 1)
+    },
+    //上传文件
+    handleAvatarSuccessFile(res, file, content) {
+      if (res.code === 200) {
+        // 使用 $set 确保响应式更新
+        this.$set(content, '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;
+    },
+
+    //选择视频号
+    hanldeSelectVideoNum(content,index,contentIndex,setIndex){
+      this.videoNumOptions.content = content;
+      this.videoNumOptions.contentIndex = contentIndex;
+      this.videoNumOptions.setIndex = setIndex;
+        this.videoNumOptions.open=true;
+    },
+
+    qwUserVideoResult(val){
+
+      // 根据选中的内容,将返回的数据更新到相应的表单项
+      const content = this.videoNumOptions.content;
+      const setList = content.setting[this.videoNumOptions.setIndex];
+
+      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;
+
+      this.videoNumOptions.open=false;
+    },
+
+    handleAvatarSuccessVideo(res, file, content) {
+      if (res.code == 200) {
+        // 使用 $set 确保响应式更新
+        this.$set(content, 'videoUrl', res.url);
+      } else {
+        this.msgError(res.msg);
+      }
+    },
+    handleAvatarSuccessVoice(res, file, content) {
+      if (res.code == 200) {
+
+        // 创建 Audio 对象加载音频
+        const audio = new Audio(res.mp3Url);
+        audio.addEventListener('loadedmetadata', () => {
+          // 获取音频时长
+          this.$set(content, 'voiceDuration', Math.ceil(audio.duration));
+        });
+        // 使用 $set 确保响应式更新
+        this.$set(content, 'voiceUrl', res.silkUrl);
+        this.$set(content, 'mp3Url', res.mp3Url);
+      } else {
+        this.msgError(res.msg);
+      }
+      this.voiceLoading = false;
+    },
+    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;
+    },
+    beforeAvatarUploadVoice(file) {
+      return new Promise((resolve, reject) => {
+        const isLt10M = file.size / 1024 / 1024 < 10; // 假设语音文件大小限制为10MB
+        const isVoiceType = ['audio/mp3', 'audio/mpeg', 'audio/wav', 'audio/x-wav'].includes(file.type);
+
+        if (!isVoiceType) {
+          this.$message.error('仅支持上传 MP3, WAV, X-WAV 格式的语音文件!');
+          return reject(false); // 不允许继续上传
+        }
+
+        if (!isLt10M) {
+          this.$message.error('上传大小不能超过 10MB!');
+          return reject(false); // 不允许继续上传
+        }
+
+        // 使用 FileReader 读取文件
+        const reader = new FileReader();
+        reader.onload = (event) => {
+          const audio = new Audio(event.target.result);
+          audio.addEventListener('loadedmetadata', () => {
+            // 获取时长并保存
+            if (Math.ceil(audio.duration) > 30) {
+              this.$message.error('音频时长不能超过30秒!');
+              this.voiceLoading = false;
+              return reject(false); // 不允许继续上传
+            }
+            resolve(true); // 允许上传
+          });
+        };
+
+        reader.onerror = () => {
+          this.$message.error('无法读取音频文件!请上传正确的音频文件');
+          reject(false); // 不允许继续上传
+        };
+        this.voiceLoading = true;
+        reader.readAsDataURL(file); // 开始读取文件
+      });
+    },
+
+    handleClosegroupUser(list) {
+      const index = this.userSelectList.findIndex(t => t === list);
+      if (index !== -1) {
+        this.userSelectList.splice(index, 1);
+      }
+    },
+    courseChange(content, index, countIndex) {
+      this.$set(content, 'videoId', null);
+      let selectedCourse;
+      if (content.courseId != null) {
+        // 查找选中的课程对应的 label
+        selectedCourse = this.courseList.find(course => parseInt(course.dictValue) === content.courseId);
+      }
+
+      for (let i = 0; i < content.setting.length; i++) {
+        //如果是链接的才上
+        if (selectedCourse && content.type == 2 && content.courseId != null) {
+
+          if (content.setting[i].contentType == 3){
+            //响应式直接给链接的标题/封面上值
+              this.$set(content.setting[i], 'linkTitle', selectedCourse.dictLabel);
+              this.$set(content.setting[i], 'linkImageUrl', selectedCourse.dictImgUrl);
+          }
+
+          if (content.setting[i].contentType == 4){
+            this.$set(content.setting[i], 'miniprogramPicUrl', selectedCourse.dictImgUrl);
+          }
+
+
+        }
+      }
+      videoList(content.courseId).then(response => {
+
+        this.videoList[index].splice(countIndex, 1, response.list);
+
+      });
+    },
+
+    handleContentTypeChange(content, index, countIndex, setIndex) {
+
+      //如果是链接的才上
+      if (content.courseId != null && content.type == 2) {
+        const selectedCourse = this.courseList.find(course => parseInt(course.dictValue) === content.courseId);
+        for (let i = 0; i < content.setting.length; i++) {
+          //响应式直接给链接的标题/封面上值
+          if (selectedCourse  && content.type == 2 && content.courseId != null) {
+
+            if (content.setting[i].contentType == 3){
+              this.$set(content.setting[i], 'linkTitle', selectedCourse.dictLabel);
+              this.$set(content.setting[i], 'linkImageUrl', selectedCourse.dictImgUrl);
+            }
+            if (this.setting[i].contentType == 4){
+              this.$set(content.setting[i], 'miniprogramPicUrl', selectedCourse.dictImgUrl);
+            }
+
+          }
+
+        }
+
+      }
+      if (content.videoId != null && content.type == 2) {
+        // 查找选中的课节对应的 label
+        const selectedVideo = this.videoList[index][countIndex].find(course => parseInt(course.dictValue) === content.videoId);
+
+        for (let i = 0; i < content.setting.length; i++) {
+          //响应式直接给链接的描述上值
+          if (selectedVideo  && content.type == 2 && content.videoId != null) {
+            if (content.setting[i].contentType == 3){
+              this.$set(content.setting[i], 'linkDescribe', selectedVideo.dictLabel);
+            }
+            if (this.setting[i].contentType == 4){
+              this.$set(content.setting[i], 'miniprogramTitle', selectedVideo.dictLabel);
+            }
+          }
+        }
+      }
+
+
+    },
+    videoIdChange(content, index, countIndex) {
+      //选择了课程小节则 默认绑上
+
+      let selectedVideo;
+      // 查找选中的课程对应的 label
+      if (content.videoId != null) {
+        selectedVideo = this.videoList[index][countIndex].find(course => parseInt(course.dictValue) === content.videoId);
+      }
+      for (let i = 0; i < content.setting.length; i++) {
+        //课程消息-文本内容
+        if (content.setting[i].contentType == 1 && content.type == 2) {
+          this.$set(content.setting[i], 'isBindUrl', '1');
+        }
+        //如果是链接的才上
+        if (selectedVideo && content.type == 2 && content.videoId != null) {
+          //响应式直接给链接的描述上值
+          if (content.setting[i].contentType == 3 ){
+
+              this.$set(content.setting[i], 'linkDescribe', selectedVideo.dictLabel);
+          }
+          if (content.setting[i].contentType == 4){
+              this.$set(content.setting[i], 'miniprogramTitle', selectedVideo.dictLabel);
+          }
+
+        }
+      }
+
+    },
+    /** 查询sop模板列表 */
+    getList() {
+      this.loading = true;
+      listSopTemp(this.queryParams).then(response => {
+        this.sopTempList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.$store.dispatch("tagsView/delView", this.$route);
+      this.$router.replace('/qw/conversion/sopTemp')
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        name: null,
+        setting: null,
+        status: "0",
+        sort: null,
+        createTime: null,
+        createBy: null,
+        companyId: 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 = "添加sop模板";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id || this.ids
+      getSopTemp(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改sop模板";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+
+        if (valid) {
+          this.form.setting = JSON.stringify(this.setting)
+
+          if (this.setting.length <= 0) {
+            return this.$message("请添加规则")
+          }
+
+          for (let i = 0; i < this.setting.length; i++) {
+
+
+            for (let j = 0; j < this.setting[i].content.length; j++) {
+
+              if (this.setting[i].name == null || this.setting[i].name == "") {
+                return this.$message.error("内容名称不能为空")
+              }
+              if (this.setting[i].content[j].time == null || this.setting[i].content[j].time == "") {
+                return this.$message.error("时间不能为空")
+              }
+              if (this.setting[i].content[j].type != 4) {
+                for (let k = 0; k < this.setting[i].content[j].setting.length; k++) {
+                  if (this.setting[i].content[j].setting[k].contentType == 1 && (this.setting[i].content[j].setting[k].value == null || this.setting[i].content[j].setting[k].value == "")) {
+                    return this.$message.error("内容不能为空")
+                  }
+                  if (this.setting[i].content[j].setting[k].contentType == 2 && (this.setting[i].content[j].setting[k].imgUrl == null || this.setting[i].content[j].setting[k].imgUrl == "")) {
+                    return this.$message.error("图片不能为空")
+                  }
+                  if (this.setting[i].content[j].setting[k].contentType == 3 && (this.setting[i].content[j].setting[k].linkTitle == null || this.setting[i].content[j].setting[k].linkTitle == "")) {
+                    return this.$message.error("链接标题不能为空")
+                  }
+                  if (this.setting[i].content[j].setting[k].contentType == 3 && (this.setting[i].content[j].setting[k].linkDescribe == null || this.setting[i].content[j].setting[k].linkDescribe == "")) {
+                    return this.$message.error("链接描述不能为空")
+                  }
+                  if (this.setting[i].content[j].setting[k].contentType == 3 && (this.setting[i].content[j].setting[k].linkImageUrl == null || this.setting[i].content[j].setting[k].linkImageUrl == "")) {
+                    return this.$message.error("链接图片不能为空")
+                  }
+                  if (this.setting[i].content[j].setting[k].contentType == 3 && this.setting[i].content[j].setting[k].type == 1 && (this.setting[i].content[j].setting[k].linkUrl == null || this.setting[i].content[j].setting[k].linkUrl == "")) {
+                    return this.$message.error("链接地址不能为空")
+                  }
+
+                  if (this.setting[i].content[j].setting[k].contentType == 4 && (this.setting[i].content[j].setting[k].miniprogramTitle == null || this.setting[i].content[j].setting[k].miniprogramTitle == "")) {
+                    return this.$message.error("小程序消息标题不能为空")
+                  }
+                  if (this.setting[i].content[j].setting[k].contentType == 4 && (this.setting[i].content[j].setting[k].miniprogramPicUrl == null || this.setting[i].content[j].setting[k].miniprogramPicUrl == "")) {
+                    return this.$message.error("小程序封面地址不能为空")
+                  }
+                  if (this.setting[i].content[j].setting[k].contentType == 5 && (this.setting[i].content[j].setting[k].fileUrl == null || this.setting[i].content[j].setting[k].fileUrl == "")) {
+                    return this.$message.error("文件不能为空")
+                  }
+                  if (this.setting[i].content[j].setting[k].contentType == 6 && (this.setting[i].content[j].setting[k].videoUrl == null || this.setting[i].content[j].setting[k].videoUrl == "")) {
+                    return this.$message.error("视频不能为空")
+                  }
+                  if (this.setting[i].content[j].setting[k].contentType == 7 && (this.setting[i].content[j].setting[k].value == null || this.setting[i].content[j].setting[k].value == "")) {
+                    return this.$message.error("语音文本不能为空")
+                  }
+                  if (this.setting[i].content[j].setting[k].contentType == 8 && (this.setting[i].content[j].setting[k].url == null || this.setting[i].content[j].setting[k].url == "")) {
+                    return this.$message.error("视频号信息不能为空")
+                  }
+                }
+              } else {
+                if (this.setting[i].content[j].aiTouch == null || this.setting[i].content[j].aiTouch == '') {
+                  return this.$message.error("AI触达不能为空")
+                }
+              }
+            }
+
+
+          }
+
+          if (this.form.id != null) {
+            updateSopTemp(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.$store.dispatch("tagsView/delView", this.$route);
+              // this.$router.replace('/qw/conversion/sopTemp')
+              window.location.replace('/qw/conversion/sopTemp')
+              this.reset();
+
+            });
+          } else {
+
+            addSopTemp(this.form).then(response => {
+              this.msgSuccess("新增成功");
+              this.$store.dispatch("tagsView/delView", this.$route);
+              // this.$router.replace('/qw/conversion/sopTemp')
+              window.location.replace('/qw/conversion/sopTemp')
+              this.reset();
+
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除sop模板编号为"' + ids + '"的数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function () {
+        return delSopTemp(ids);
+      }).then(() => {
+        this.getList();
+
+
+        this.msgSuccess("删除成功");
+      }).catch(() => {
+      });
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有sop模板数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        this.exportLoading = true;
+        return exportSopTemp(queryParams);
+      }).then(response => {
+        this.download(response.msg);
+        this.exportLoading = false;
+      }).catch(() => {
+      });
+    }
+  }
+};
+</script>
+
+<style scoped>
+.custom-input /deep/ .el-input__inner {
+  height: 20px;
+  text-align: center;
+}
+
+.custom-input /deep/ .el-input__icon {
+  line-height: 10px;
+}
+</style>

+ 734 - 0
src/views/qw/sopTemp/addSopTempOld.vue

@@ -0,0 +1,734 @@
+<template>
+  <div class="app-container">
+
+    <div style="margin: 30px;" v-if="this.form.sendType == 1 "> sop规则【企微接口】模板</div>
+    <div style="margin: 30px;" v-if="this.form.sendType == 2 "> sop规则【AI插件】模板</div>
+
+    <div style="margin-top: 10px;margin-left: 50px;margin-right: 100px;margin-bottom: 60px;">
+      <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="dict.dictValue"
+            >{{dict.dictLabel}}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="间隔天数" prop="gap">
+          <el-input-number v-model="form.gap"  :min="1" label="间隔天数"></el-input-number>
+        </el-form-item>
+        <el-form-item label="排序" prop="sort">
+          <el-input-number v-model="form.sort"  :min="0" label="排序"></el-input-number>
+        </el-form-item>
+        <el-form-item label="规则" prop="setting">
+          <el-timeline >
+            <el-timeline-item :timestamp="'第'+(1+(form.gap*index))+'天'" :color="'#0bbd87'" placement="top" v-for="(item, index) in setting"  style="margin-top: 10px;">
+            <el-row>
+              <el-col :span="22">
+                <div style="background-color: #fbfbfb;padding: 15px;  border: 1px solid #e6e6e6; margin-bottom: 20px;">
+                  <el-form :model="item"  label-width="80px">
+
+                    <el-form-item label="内容名称"  style="height: 50px;">
+                       <el-input v-model="item.name" placeholder="内容名称,仅内部可见" />
+                    </el-form-item>
+
+                    <el-form-item label="规则"  >
+                       <div v-for="(content, contentIndex) in item.content" style="background-color: #fdfdfd;padding: 15px;  border: 1px solid #e6e6e6; margin-bottom: 20px;">
+
+                         <el-row>
+
+                           <el-col el-col :span="22" >
+                              <el-form :model="content" label-width="70px">
+                                <el-form-item label="时间"  prop="time"  >
+                                   <el-time-picker
+                                     class="custom-input"
+                                     v-model="content.time"
+                                     value-format="HH:mm"
+                                     format="HH:mm"
+                                     :picker-options="{ selectableRange: '00:00:00 - 23:59:59' }"
+                                     placeholder="时间"
+                                     style="width: 100px;height: 20px;" >
+                                   </el-time-picker>
+                                </el-form-item>
+                                <el-form-item label="消息类别"  >
+                                   <el-radio-group v-model="content.type" @change="() => content.contentType = '1'" >
+                                       <el-radio
+                                         :label="1"
+
+                                       >普通</el-radio>
+                                       <el-radio
+                                         :label="2"
+
+                                       >课程</el-radio>
+                                       <el-radio
+                                         :label="3"
+
+                                       >订单</el-radio>
+                                   </el-radio-group>
+                                </el-form-item>
+
+                                <el-form-item label="课程"  v-if="content.type == 2 ">
+                                   <el-select  v-model="content.courseId" placeholder="请选择课程" style=" margin-right: 10px;" size="mini"  @change="courseChange(content,index,contentIndex)">
+                                     <el-option
+                                         v-for="dict in courseList"
+                                         :key="dict.dictValue"
+                                         :label="dict.dictLabel"
+                                         :value="parseInt(dict.dictValue)"
+                                       />
+                                   </el-select>
+                                   <el-select  v-model="content.videoId" placeholder="请选择小节" size="mini" style=" margin-right: 10px;" @change="videoIdChange(content,index,contentIndex)" >
+                                     <el-option
+                                         v-for="dict in videoList[index][contentIndex]"
+                                         :key="dict.dictValue"
+                                         :label="dict.dictLabel"
+                                         :value="parseInt(dict.dictValue)"
+                                       />
+                                   </el-select>
+                                   <el-select  v-model="content.courseType" placeholder="请选择消息类型" size="mini" style=" margin-right: 10px;">
+                                     <el-option
+                                         v-for="dict in sysFsSopWatchStatus"
+                                         :key="dict.dictValue"
+                                         :label="dict.dictLabel"
+                                         :value="parseInt(dict.dictValue)"
+                                       />
+                                   </el-select>
+
+                                </el-form-item>
+                                <el-form-item label="内容类别"  >
+
+<!--                                  <div v-for="(list, setIndex) in content.setting ">-->
+<!--                                    <el-row>-->
+<!--                                      <el-col el-col :span="22" >-->
+<!--                          -->
+<!--                                      </el-col>-->
+<!--                                      <el-col :span="1" :offset="1">-->
+<!--                                        <i class="el-icon-delete" @click="delContent(index,contentIndex)" style="margin-top: 20px;" v-if="item.content.length>1"></i>-->
+<!--                                      </el-col>-->
+<!--                                    </el-row>-->
+
+<!--                                  </div>-->
+
+                                  <div v-if="form.sendType == 1 ">
+                                    <el-radio-group  v-model="content.contentType" >
+                                      <el-radio  :label="item.dictValue" v-for="item in sysQwSopContentType">{{item.dictLabel}}</el-radio>
+                                    </el-radio-group>
+                                  </div>
+                                  <div v-if="form.sendType == 2 ">
+                                    <div v-if="content.type == 2 ">
+                                      <el-radio-group  v-model="content.contentType" @change="handleContentTypeChange(content,index,contentIndex)">
+                                        <el-radio  :label="item.dictValue" v-for="item in sysQwSopSettingType">{{item.dictLabel}}</el-radio>
+                                      </el-radio-group>
+                                    </div>
+                                    <div v-else>
+                                      <el-radio-group  v-model="content.contentType" @change="handleContentTypeChange(content,index,contentIndex)">
+                                        <el-radio   :label="item.dictValue" v-for="item in sysQwSopAiContentType">{{item.dictLabel}}</el-radio>
+                                      </el-radio-group>
+                                    </div>
+
+                                  </div>
+                                </el-form-item>
+                                <el-form-item label="内容"  >
+                                   <el-input v-if="content.contentType == 1 " v-model="content.value" type="textarea" :rows="3" placeholder="内容" style="width: 90%;margin-top: 10px;"/>
+
+                                    <ImageUpload v-if="content.contentType == 2 " v-model="content.imgUrl" type="image" :num="1" :width="150" :height="150" />
+
+                                    <div v-if="content.contentType == 3 ">
+                                      <el-card class="box-card">
+                                        <el-form-item label="链接标题:"  label-width="100px">
+                                          <el-input v-model="content.linkTitle" placeholder="请输入链接标题" style="width: 90%;"/>
+                                        </el-form-item>
+                                        <el-form-item label="链接描述:"   label-width="100px" >
+                                          <el-input type="textarea" :rows="3" v-model="content.linkDescribe" placeholder="请输入链接描述" style="width: 90%;margin-top: 1%;"/>
+                                        </el-form-item>
+                                        <el-form-item label="链接封面:"   label-width="100px">
+                                          <ImageUpload v-model="content.linkImageUrl" type="image" :num="1" :width="150" :height="150" style="margin-top: 1%;" />
+                                      </el-form-item>
+                                    <div v-if="content.type!=2" style="margin-top: 1%">
+                                    <el-form-item label="链接地址:"  label-width="100px" >
+                                      <el-input v-model="content.linkUrl" placeholder="请输入链接地址" style="width: 90%;"/>
+                                    </el-form-item>
+                                    </div>
+                                    <div v-if="content.type==2">
+                                    <el-form-item label="链接地址:"  label-width="100px" >
+                                      <el-tag type="warning" v-model="content.isBindUrl=1">选择的课程小节 即为卡片链接地址</el-tag>
+                                    </el-form-item>
+                                    </div>
+                                    </el-card>
+                                    </div>
+                                    <div v-if="content.contentType == 5 ">
+
+                                      <el-form-item label="上传文件:" prop="fileUrl" label-width="100px">
+                                        <el-upload
+                                        v-model="content.fileUrl"
+                                        class="avatar-uploader"
+                                        :action="uploadUrl"
+                                        :show-file-list="false"
+                                        :on-success="(res, file) => handleAvatarSuccessFile(res, file, content)"
+                                        :before-upload="beforeAvatarUploadFile">
+                                        <i class="el-icon-plus avatar-uploader-icon"></i>
+                                        </el-upload>
+                                        <el-link v-if="content.fileUrl" type="primary" :href="downloadUrl(content.fileUrl)" download>
+                                        {{content.fileUrl}}
+                                      </el-link>
+                                      </el-form-item>
+
+                                    </div>
+
+                                    <div v-if="content.contentType == 6 ">
+                                      <el-form-item label="上传视频:" prop="videoUrl" label-width="100px">
+                                        <el-upload
+                                        v-model="content.videoUrl"
+                                        class="avatar-uploader"
+                                        :action="uploadUrl"
+                                        :show-file-list="false"
+                                        :on-success="(res, file) => handleAvatarSuccessVideo(res, file, content)"
+                                        :before-upload="beforeAvatarUploadVideo">
+                                        <i class="el-icon-plus avatar-uploader-icon"></i>
+                                        </el-upload>
+                                        <video v-if="content.videoUrl"
+                                        :src="content.videoUrl"
+                                        controls style="width: 200px;height: 100px">
+                                        </video>
+                                      </el-form-item>
+                                    </div>
+                                    <div v-if="content.contentType == 7 ">
+                                      <el-form-item label="上传语音:" prop="videoUrl" label-width="100px">
+                                        <el-upload
+                                          v-model="content.voiceUrl"
+                                          class="avatar-uploader"
+                                          :action="uploadUrl"
+                                          :show-file-list="false"
+                                          :on-success="(res, file) => handleAvatarSuccessVoice(res, file, content)"
+                                          :before-upload="beforeAvatarUploadVoice">
+                                          <i class="el-icon-plus avatar-uploader-icon"></i>
+                                        </el-upload>
+                                        <audio  v-if="content.voiceUrl"
+                                               :src="content.voiceUrl"
+                                               controls style="width: 300px;height: 50px">
+                                        </audio >
+                                      </el-form-item>
+                                    </div>
+
+
+                                </el-form-item>
+
+                                <el-form-item label="添加短链" v-if="content.type == 2 && content.contentType == 1 "  >
+                                  <el-tooltip content="请先根据课程选定课程小节之后再添加" effect="dark" :disabled="!!content.videoId">
+                                    <el-switch
+                                      v-model="content.isBindUrl"
+                                      :disabled="!content.videoId"
+                                      active-color="#13ce66"
+                                      inactive-color="#DCDFE6"
+                                      active-value="1"
+                                      inactive-value="2">
+                                    </el-switch>
+                                  </el-tooltip>
+
+                                  <span v-if="content.isBindUrl == '1'" style="margin-left: 10px; color: #13ce66">添加URL</span>
+                                  <span v-if="content.isBindUrl == '2'" style="margin-left: 10px; color: #b1b4ba">不加URL</span>
+                                </el-form-item>
+
+                                <el-form-item label="课节过期时间" v-if="content.type == 2 && content.isBindUrl == '1' && content.contentType != 2  && content.contentType != 5  && content.contentType != 6 " style="margin-top: 1%">
+                                  <el-row>
+                                    <el-input-number  v-model="content.expiresDays"  :min="0" :max="100" ></el-input-number>
+                                    (天)
+                                  </el-row>
+                                  <el-row>
+                                    <span class="tip">填写0或不填时,默认为系统配置的默认时间</span>
+                                  </el-row>
+                                </el-form-item>
+
+                              </el-form>
+
+                           </el-col>
+                           <el-col :span="1" :offset="1">
+                             <i class="el-icon-delete" @click="delContent(index,contentIndex)" style="margin-top: 20px;" v-if="item.content.length>1"></i>
+                           </el-col>
+                         </el-row>
+                       </div>
+                       <el-link type="primary" class="el-icon-plus" :underline="false" @click='addContent(index)'>添加规则</el-link>
+                    </el-form-item>
+                  </el-form>
+                </div>
+              </el-col>
+              <el-col :span="1" :offset="1">
+                 <i class="el-icon-delete-solid" @click="delSetting(index)"  v-if="setting.length>1"></i>
+              </el-col>
+            </el-row>
+          </el-timeline-item>
+
+
+          </el-timeline>
+           <el-link type="primary" class="el-icon-plus" :underline="false" @click='addSetting()'>添加天数</el-link>
+        </el-form-item>
+
+      </el-form>
+      <div slot="footer" class="dialog-footer" style="float: right;">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { listSopTemp, getSopTemp, delSopTemp, addSopTemp, updateSopTemp, exportSopTemp } from "@/api/qw/sopTemp";
+import { courseList,videoList } from "@/api/qw/sop";
+import ImageUpload from "@/views/qw/sop/ImageUpload";
+import FriendMaterial from "@/views/qw/friendMaterial/index.vue";
+export default {
+  name: "SopTemp",
+      components: { ImageUpload,FriendMaterial},
+  data() {
+    return {
+      uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      sysFsSopWatchStatus: [],
+      //消息内容类型 企微版
+      sysQwSopContentType:[],
+      //插件版
+      sysQwSopAiContentType:[],
+      //类别
+      sysQwSopSettingType:[],
+      courseList:[],
+      videoList:[],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // sop模板表格数据
+      setting: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 状态字典
+      statusOptions: [],
+      // 查询参数
+      form: {
+        name: null,
+        setting: null,
+        status: "1",
+        sort: 1,
+        companyId: null,
+        gap:1,
+      },
+      // 表单校验
+      rules: {
+        name: [
+          { required: true, message: '名称不能为空', trigger: 'blur' }
+        ],
+        status: [
+          { required: true, message: '状态不能为空', trigger: 'blur' }
+        ],
+        sort: [
+          { required: true, message: '排序不能为空', trigger: 'blur' }
+        ],
+        gap: [
+          { required: true, message: '间隔天数不能为空', trigger: 'blur' }
+        ],
+      },
+      contentRules:{
+        time:[{ required: true, message: '时间不能为空', trigger: 'blur' }],
+      }
+    };
+  },
+  created() {
+
+    this.getDicts("sys_company_status").then(response => {
+      this.statusOptions = response.data;
+    });
+    courseList().then(response => {
+      this.courseList = response.list;
+    });
+    this.getDicts("sys_fs_sop_watch_status").then(response => {
+      this.sysFsSopWatchStatus = response.data;
+    });
+
+    this.getDicts("sys_qwSopAi_contentType").then(response => {
+      this.sysQwSopAiContentType = response.data;
+    });
+
+    this.getDicts("sys_qwSop_contentType").then(response => {
+      this.sysQwSopContentType = response.data;
+    });
+    this.getDicts("sys_qwSop_settingType").then(response => {
+      this.sysQwSopSettingType = response.data;
+    });
+
+
+    this.form.sendType= this.$route.params && this.$route.params.command;
+
+    setTimeout(() => {
+      this.getList();
+    }, 200);
+
+  },
+  methods: {
+
+    addContent(index){
+
+      if (this.setting[index].content.length>0 && this.form.sendType==1){
+        return this.$message.error("因为企微接口限制,企微模板一天只能设置一条消息~")
+      }else {
+        this.setting[index].content.push({type:1,contentType:'1'})
+        this.videoList[index].push([])
+      }
+
+    },
+    delContent(index,contentIndex){
+      this.setting[index].content.splice(contentIndex,1)
+      this.videoList[index].splice(contentIndex,1)
+    },
+    addSetting(){
+
+      this.setting.push({name:null,content:[{type:1,contentType:'1'}]})
+      this.videoList.push([])
+
+    },
+    delSetting(index){
+      this.setting.splice(index,1)
+       this.videoList.splice(index,1)
+    },
+    //上传文件
+    handleAvatarSuccessFile(res, file, content) {
+      if (res.code === 200) {
+        // 使用 $set 确保响应式更新
+        this.$set(content, 'fileUrl', res.url);
+      } else {
+        this.msgError(res.msg);
+      }
+    },
+    beforeAvatarUploadFile(file){
+      const isLt1M = file.size / 1024 / 1024 < 30;
+      if (!isLt1M) {
+        this.$message.error('上传大小不能超过 30MB!');
+      }
+      return isLt1M;
+    },
+
+    //下载文件
+    downloadUrl(materialUrl) {
+      // 直接返回文件 URL
+      return materialUrl;
+    },
+
+    handleAvatarSuccessVideo(res, file, content) {
+      if(res.code==200){
+        // 使用 $set 确保响应式更新
+        this.$set(content, 'videoUrl', res.url);
+      }
+      else{
+        this.msgError(res.msg);
+      }
+    },
+    handleAvatarSuccessVoice(res, file, content){
+      if(res.code==200){
+        // 使用 $set 确保响应式更新
+        this.$set(content, 'voiceUrl', res.url);
+      }
+      else{
+        this.msgError(res.msg);
+      }
+    },
+    beforeAvatarUploadVideo(file){
+      const isLt30M = file.size / 1024 / 1024 < 50;
+      const isMP4 = file.type === 'video/mp4';
+
+      if (!isMP4) {
+        this.$message.error('仅支持上传 MP4 格式的视频文件!');
+        return false;
+      }
+
+      if (!isLt30M) {
+        this.$message.error('上传大小不能超过 50MB!');
+        return false;
+      }
+
+      return true;
+    },
+
+    beforeAvatarUploadVoice(file){
+      const isLt10M = file.size / 1024 / 1024 < 10; // 假设语音文件大小限制为10MB
+      const isVoiceType = ['audio/mp3'].includes(file.type); // 支持的语音文件类型
+
+      if (!isVoiceType) {
+        this.$message.error('仅支持上传 MP3 格式的语音文件!');
+        return false;
+      }
+
+      if (!isLt10M) {
+        this.$message.error('上传大小不能超过 10MB!');
+        return false;
+      }
+
+      return true;
+    },
+
+    handleClosegroupUser(list){
+      const index = this.userSelectList.findIndex(t => t === list);
+      if (index !== -1) {
+        this.userSelectList.splice(index, 1);
+      }
+    },
+    courseChange(content,index,countIndex){
+
+      this.$set(content, 'videoId', null);
+
+      //如果是链接的才上
+      if (content.contentType==3 && content.type==2 && content.courseId!=null){
+
+        // 查找选中的课程对应的 label
+        const selectedCourse = this.courseList.find(course => parseInt(course.dictValue) === content.courseId);
+
+        //响应式直接给链接的标题/封面上值
+        if (selectedCourse) {
+          this.$set(content, 'linkTitle', selectedCourse.dictLabel);
+          this.$set(content, 'linkImageUrl', selectedCourse.dictImgUrl);
+        }
+      }
+
+      videoList(content.courseId).then(response => {
+
+        this.videoList[index].splice(countIndex, 1, response.list);
+
+      });
+    },
+
+    handleContentTypeChange(content,index,countIndex){
+
+      //如果是链接的才上
+      if (content.contentType==3 && content.type==2 ) {
+
+        if (content.courseId!=null) {
+            // 查找选中的课程对应的 label
+            const selectedCourse = this.courseList.find(course => parseInt(course.dictValue) === content.courseId);
+
+            //响应式直接给链接的标题/封面上值
+            if (selectedCourse) {
+              this.$set(content, 'linkTitle', selectedCourse.dictLabel);
+              this.$set(content, 'linkImageUrl', selectedCourse.dictImgUrl);
+            }
+        }
+        if (content.videoId!=null){
+            // 查找选中的课程对应的 label
+            const selectedVideo = this.videoList[index][countIndex].find(course => parseInt(course.dictValue) === content.videoId);
+
+            //响应式直接给链接的描述上值
+            if (selectedVideo) {
+              this.$set(content, 'linkDescribe', selectedVideo.dictLabel);
+            }
+        }
+
+      }
+    },
+    videoIdChange(content,index,countIndex){
+
+      //选择了课程小节则 默认绑上
+      this.$set(content,'isBindUrl','1');
+
+      //如果是链接的才上
+      if (content.contentType==3 && content.type==2 && content.videoId!=null ) {
+
+        // 查找选中的课程对应的 label
+        const selectedVideo = this.videoList[index][countIndex].find(course => parseInt(course.dictValue) === content.videoId);
+
+        //响应式直接给链接的描述上值
+        if (selectedVideo) {
+          this.$set(content, 'linkDescribe', selectedVideo.dictLabel);
+        }
+      }
+    },
+    /** 查询sop模板列表 */
+    getList() {
+      this.loading = true;
+      listSopTemp(this.queryParams).then(response => {
+        this.sopTempList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.$store.dispatch("tagsView/delView", this.$route);
+      this.$router.replace('/qw/conversion/sopTemp')
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        name: null,
+        setting: null,
+        status: "0",
+        sort: null,
+        createTime: null,
+        createBy: null,
+        companyId: 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 = "添加sop模板";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id || this.ids
+      getSopTemp(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改sop模板";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+
+        if (valid) {
+          this.form.setting=JSON.stringify(this.setting)
+
+          if(this.setting.length<=0){
+            return this.$message("请添加规则")
+          }
+          for (let i = 0; i < this.setting.length; i++) {
+
+            for (let j = 0; j < this.setting[i].content.length; j++) {
+
+              if(this.setting[i].name==null||this.setting[i].name==""){
+                return this.$message.error("内容名称不能为空")
+              }
+              if(this.setting[i].content[j].time==null||this.setting[i].content[j].time==""){
+                return this.$message.error("时间不能为空")
+
+              }
+              if (this.setting[i].content[j].contentType==1 && (this.setting[i].content[j].value==null|| this.setting[i].content[j].value=="")){
+                return this.$message.error("内容不能为空")
+              }
+              if (this.setting[i].content[j].contentType==2 && (this.setting[i].content[j].imgUrl==null|| this.setting[i].content[j].imgUrl=="")){
+                return this.$message.error("图片不能为空")
+              }
+              if (this.setting[i].content[j].contentType==3 && (this.setting[i].content[j].linkTitle==null|| this.setting[i].content[j].linkTitle=="")){
+                return this.$message.error("链接标题不能为空")
+              }
+              if (this.setting[i].content[j].contentType==3 && (this.setting[i].content[j].linkDescribe==null|| this.setting[i].content[j].linkDescribe=="")){
+                return this.$message.error("链接描述不能为空")
+              }
+              if (this.setting[i].content[j].contentType==3 && (this.setting[i].content[j].linkImageUrl==null|| this.setting[i].content[j].linkImageUrl=="")){
+                return this.$message.error("链接图片不能为空")
+              }
+              if (this.setting[i].content[j].contentType==3 && this.setting[i].content[j].type==1 && (this.setting[i].content[j].linkUrl==null|| this.setting[i].content[j].linkUrl=="")){
+                return this.$message.error("链接地址不能为空")
+              }
+              if (this.setting[i].content[j].contentType==5 && (this.setting[i].content[j].fileUrl==null|| this.setting[i].content[j].fileUrl=="")){
+                return this.$message.error("文件不能为空")
+              }
+              if (this.setting[i].content[j].contentType==6 && (this.setting[i].content[j].videoUrl==null|| this.setting[i].content[j].videoUrl=="")){
+                return this.$message.error("视频不能为空")
+              }
+              if (this.setting[i].content[j].contentType==7 && (this.setting[i].content[j].voiceUrl==null|| this.setting[i].content[j].voiceUrl=="")){
+                return this.$message.error("语音不能为空")
+              }
+            }
+
+          }
+
+          if (this.form.id != null) {
+            updateSopTemp(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.$store.dispatch("tagsView/delView", this.$route);
+              // this.$router.replace('/qw/conversion/sopTemp')
+              window.location.replace('/qw/conversion/sopTemp')
+              this.reset();
+
+            });
+          }
+          else {
+
+            addSopTemp(this.form).then(response => {
+              this.msgSuccess("新增成功");
+              this.$store.dispatch("tagsView/delView", this.$route);
+              // this.$router.replace('/qw/conversion/sopTemp')
+              window.location.replace('/qw/conversion/sopTemp')
+              this.reset();
+
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除sop模板编号为"' + ids + '"的数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return delSopTemp(ids);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有sop模板数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportSopTemp(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    }
+  }
+};
+</script>
+
+<style scoped>
+.custom-input /deep/ .el-input__inner {
+  height: 20px;
+  text-align:center;
+}
+.custom-input /deep/ .el-input__icon {
+  line-height: 10px;
+}
+</style>

+ 194 - 0
src/views/qw/sopTemp/addTemp.vue

@@ -0,0 +1,194 @@
+<template>
+  <div class="app-container">
+
+    <div style="margin: 30px;" v-if="this.form.sendType == 1 "> sop规则【企微接口】模板</div>
+    <div style="margin: 30px;" v-if="this.form.sendType == 2 "> sop规则【AI插件】模板</div>
+
+    <div style="margin-top: 10px;margin-left: 50px;margin-right: 100px;margin-bottom: 60px;">
+      <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="dict.dictValue"
+            >{{dict.dictLabel}}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="间隔天数" prop="gap">
+          <el-input-number v-model="form.gap"  :min="1" label="间隔天数"></el-input-number>
+        </el-form-item>
+        <el-form-item label="排序" prop="sort">
+          <el-input-number v-model="form.sort"  :min="0" label="排序"></el-input-number>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer" style="float: right;">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import {addTemp} from "@/api/qw/sopTemp";
+import ImageUpload from "@/views/qw/sop/ImageUpload";
+import FriendMaterial from "@/views/qw/friendMaterial/index.vue";
+import userVideo from "@/views/qw/userVideo/userVideo.vue";
+
+export default {
+  name: "addSopTemp",
+  components: { ImageUpload,FriendMaterial,userVideo},
+  data() {
+    return {
+
+      //图片放大
+      dialogVisible: false,
+      dialogImageUrl:null,
+
+      uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS2",
+      uploadUrlByVoice:process.env.VUE_APP_BASE_API+"/common/uploadOSSByHOOKVoice",
+      //上传语音的遮罩层
+      voiceLoading :false,
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      sysFsSopWatchStatus: [],
+      //消息内容类型 企微版
+      sysQwSopContentType:[],
+      //插件版
+      sysQwSopAiContentType:[],
+      //链接版类别
+      sysQwSopSettingType:[],
+
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // sop模板表格数据
+      setting: [],
+      courseList:[],
+      videoList:[],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 状态字典
+      statusOptions: [],
+      // 查询参数
+      form: {
+        name: null,
+        setting: null,
+        status: "1",
+        sort: 1,
+        companyId: null,
+        gap:1,
+      },
+      videoNumOptions:{
+        title:'选择视频号',
+        open:false,
+        content:null,
+        contentIndex:null,
+        setIndex:null,
+      },
+      // 表单校验
+      rules: {
+        name: [
+          { required: true, message: '名称不能为空', trigger: 'blur' }
+        ],
+        status: [
+          { required: true, message: '状态不能为空', trigger: 'blur' }
+        ],
+        sort: [
+          { required: true, message: '排序不能为空', trigger: 'blur' }
+        ],
+        gap: [
+          { required: true, message: '间隔天数不能为空', trigger: 'blur' }
+        ],
+      },
+      contentRules:{
+        time:[{ required: true, message: '时间不能为空', trigger: 'blur' }],
+      }
+    };
+  },
+  created() {
+
+    this.getDicts("sys_company_status").then(response => {
+      this.statusOptions = response.data;
+    });
+    this.form.sendType = this.$route.params && this.$route.params.command;
+    setTimeout(() => {
+      this.getList();
+    }, 200);
+
+  },
+  methods: {
+    // 取消按钮
+    cancel() {
+      this.$store.dispatch("tagsView/delView", this.$route);
+      this.$router.replace('/qw/conversion/sopTemp')
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        name: null,
+        setting: null,
+        status: "0",
+        sort: null,
+        createTime: null,
+        createBy: null,
+        companyId: null,
+      };
+      this.resetForm("form");
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != null) {
+            updateTemp(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.$store.dispatch("tagsView/delView", this.$route);
+              // this.$router.replace('/qw/conversion/sopTemp')
+              window.location.replace('/qw/conversion/sopTemp')
+              this.reset();
+            });
+          } else {
+            addTemp(this.form).then(response => {
+              this.msgSuccess("新增成功");
+              this.$store.dispatch("tagsView/delView", this.$route);
+              // this.$router.replace('/qw/conversion/sopTemp')
+              window.location.replace('/qw/conversion/sopTemp')
+              this.reset();
+            });
+          }
+        }
+      });
+    },
+  }
+};
+</script>
+
+<style scoped>
+.custom-input /deep/ .el-input__inner {
+  height: 20px;
+  text-align: center;
+}
+
+.custom-input /deep/ .el-input__icon {
+  line-height: 10px;
+}
+</style>

+ 610 - 0
src/views/qw/sopTemp/index.vue

@@ -0,0 +1,610 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="销售公司" prop="companyId">
+        <el-select filterable  v-model="queryParams.companyId" clearable placeholder="请选择公司名" size="small">
+          <el-option
+            v-for="item in companys"
+            :key="item.companyId"
+            :label="item.companyName"
+            :value="item.companyId"
+          />
+        </el-select>
+      </el-form-item>
+      <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="sendType">
+        <el-select v-model="queryParams.sendType" placeholder="请选择类型" clearable size="small">
+          <el-option
+            v-for="dict in sysQwSopType"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="排序" prop="sort">
+        <el-input
+          v-model="queryParams.sort"
+          placeholder="请输入排序"
+          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="['qw:sopTemp:add']"-->
+        <!--        >新增</el-button>-->
+
+        <el-dropdown
+          @command="handleCommand"
+          trigger="click"
+          placement="bottom-start"
+        >
+          <el-dropdown-menu slot="dropdown" style="width: 120px;">
+            <el-dropdown-item
+              v-for="option in sysQwSopType"
+              :key="option.dictValue"
+              :command="option.dictValue"
+            >
+              <i :class="option.iconClass" style="margin-right: 10px;"></i>
+              {{ option.dictLabel }}
+            </el-dropdown-item>
+          </el-dropdown-menu>
+          <span class="el-dropdown-link">
+              <el-button type="primary" icon="el-icon-plus" plain size="mini">
+                新增模板
+              </el-button>
+            </span>
+        </el-dropdown>
+      </el-col>
+
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+          v-hasPermi="['qw:sopTemp: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="['qw:sopTemp:export']"
+        >导出
+        </el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" border :data="sopTempList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center"/>
+      <el-table-column label="模板编号" align="center" prop="id"/>
+      <el-table-column label="模板编号" align="center">
+        <template slot-scope="scope">
+          <el-tag v-for="item in companys" v-if="scope.row.companyId == item.companyId">{{item.companyName}}</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="模板标题" align="center" prop="name"/>
+      <el-table-column label="模板类型" align="center" prop="sendType">
+        <template slot-scope="scope">
+          <dict-tag :options="sysQwSopType" :value="scope.row.sendType"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="间隔天数" align="center" prop="gap"/>
+      <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="createTime"/>
+      <el-table-column label="修改时间" align="center" prop="updateTime"/>
+      <el-table-column label="排序" align="center" prop="sort"/>
+
+      <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-connection"
+            @click="copyTemplate(scope.row)"
+            v-hasPermi="['qw:sopTemp:edit']"
+          >复制模板
+          </el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-s-promotion"
+            @click="handleQueryDetails(scope.row)"
+            v-hasPermi="['qw:sopTemp:list']"
+          >详情
+          </el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['qw:sopTemp:edit']"
+          >修改
+          </el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate2(scope.row)"
+            v-hasPermi="['qw:sopTemp:edit']"
+          >管理规则
+          </el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['qw:sopTemp: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="800px" 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="销售公司" prop="companyId">
+          <el-select filterable  v-model="form.companyId" placeholder="请选择公司名" size="small">
+            <el-option
+              v-for="item in companys"
+              :key="item.companyId"
+              :label="item.companyName"
+              :value="item.companyId"
+            />
+          </el-select>
+        </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="dict.dictValue"
+            >{{ dict.dictLabel }}
+            </el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="所属项目" prop="project" v-if="form.sendType != 5">
+          <el-select v-model="form.project" placeholder="请选择项目" filterable clearable size="small">
+            <el-option
+              v-for="dict in projectOptions"
+              :key="dict.dictValue"
+              :label="dict.dictLabel"
+              :value="dict.dictValue"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="课程" prop="courseId" v-if="form.sendType == 5 && !form.id">
+          <el-select v-model="form.courseId"placeholder="请选择课程" style=" margin-right: 10px;" size="mini" filterable>
+            <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="gap">
+          <el-input-number v-model="form.gap" :min="1" label="间隔天数"></el-input-number>
+        </el-form-item>
+        <el-form-item label="排序" prop="sort">
+          <el-input-number v-model="form.sort" :min="0" label="排序"></el-input-number>
+        </el-form-item>
+        <el-form-item label="每天发送次数" prop="num" v-if="form.sendType == 5 && !form.id">
+          <el-input-number v-model="form.num" :min="1" label="每天发送次数"></el-input-number>
+        </el-form-item>
+        <el-form-item label="发送时间" v-if="form.sendType == 5 && !form.id">
+            <el-time-picker
+              class="custom-input"
+              v-for="index in form.num"
+              v-model="form.timeList[index]"
+              value-format="HH:mm"
+              format="HH:mm"
+              :picker-options="{ selectableRange: '01:01:00 - 23:59:59' }"
+              placeholder="时间"
+              style="width: 100px;height: 20px;margin-left: 10px;margin-top: 10px">
+            </el-time-picker>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer" style="float: right;">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import {
+  addTemp,
+  copyTemplate,
+  delSopTemp,
+  exportSopTemp,
+  getSopTemp,
+  listSopTemp,
+  shareSopTemp,
+  updateTemp
+} from "@/api/qw/sopTemp";
+import { getCompanyList } from "@/api/company/company";
+import {courseList} from "@/api/qw/sop";
+
+export default {
+  name: "SopTemp",
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      companysloading: false,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      courseList: [],
+      //选中的公司
+      companys: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      companyTotal: 0,
+      total: 0,
+      sendType: 0,
+      // sop模板表格数据
+      sopTempList: [],
+      sysQwSopType: [],
+      companyList: [],
+      projectOptions: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      command: 0,
+      // 状态字典
+      statusOptions: [],
+
+      shareOptions: {
+        title: '分享模板',
+        open: false,
+        templateId: null,
+      },
+      queryCompanyParams: {
+        pageNum: 1,
+        pageSize: 10,
+        companyName: null,
+        status: null,
+      },
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        name: null,
+        setting: null,
+        status: '1',
+        sort: null,
+        companyId: null
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        name: [
+          {required: true, message: '名称不能为空', trigger: 'blur'}
+        ],
+        status: [
+          {required: true, message: '状态不能为空', trigger: 'blur'}
+        ],
+        sort: [
+          {required: true, message: '排序不能为空', trigger: 'blur'}
+        ],
+        gap: [
+          {required: true, message: '间隔天数不能为空', trigger: 'blur'}
+        ],
+      },
+      contentRules: {
+        time: [{required: true, message: '时间不能为空', trigger: 'blur'}],
+      }
+    };
+  },
+  created() {
+    this.getList();
+    this.getDicts("sys_company_status").then(response => {
+      this.statusOptions = response.data;
+    });
+
+    this.getDicts("sys_course_project").then(response => {
+      this.projectOptions = response.data;
+    });
+
+    this.getDicts("sys_qw_sop_type").then(response => {
+      this.sysQwSopType = response.data;
+    });
+
+    courseList().then(response => {
+      this.courseList = response.list;
+    });
+
+    getCompanyList().then(response => {
+      this.companys = response.data;
+    });
+  },
+  methods: {
+
+    handleCompanyQuery() {
+      this.queryCompanyParams.pageNum = 1;
+    },
+    resetCompanyQuery() {
+      this.resetForm("queryCompanyForm");
+      this.handleCompanyQuery();
+    },
+    /** 查询sop模板列表 */
+    getList() {
+      this.loading = true;
+      listSopTemp(this.queryParams).then(response => {
+        this.sopTempList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        gap: 1,
+        sendType: this.sendType,
+        sort: 0,
+        num: 1,
+        timeList:[],
+        status: "1",
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+
+    handleCommand(command) {
+      // if (command==4) {
+      //   this.$router.push('/qw/sopTemp/addAiChatTemp')
+      // }else{
+      this.sendType = command;
+      this.title = "新增";
+      this.reset();
+      this.open = true;
+      // this.$router.push('/qw/sopTemp/addTemp/'+command)
+      // }
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length !== 1
+      this.multiple = !selection.length
+    },
+    //销售公司多选
+    handleSelectionCompany(selection) {
+      this.companys = selection.map(item => item.companyId)
+      this.single = selection.length !== 1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    // handleAdd() {
+    //   this.$router.push('/qw/sopTemp/addSopTemp')
+    // },
+    /**
+     * 查看详情
+     */
+    handleQueryDetails(row) {
+      // if (row.sendType==4) {
+      //   this.$router.push(`/qw/sopTemp/updateAiChatTemp/${row.id}/3`)
+      // }else{
+      this.$router.push(`/qw/sopTemp/updateSopTemp/${row.id}/3`)
+      // }
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      // if (row.sendType==4) {
+      //   this.$router.push(`/qw/sopTemp/updateAiChatTemp/${row.id}/1`)
+      // }else{
+      this.getInfo(row.id, 1);
+      // this.$router.push(`/qw/sopTemp/updateTemp/${row.id}/1`)
+      // }
+
+    },
+    /** 修改按钮操作 */
+    handleUpdate2(row) {
+      // if (row.sendType==4) {
+      //   this.$router.push(`/qw/sopTemp/updateAiChatTemp/${row.id}/1`)
+      // }else{
+      let url = `/qw/sopTempe/updateSopTemp/${row.id}/1`;
+      console.info(url)
+      this.$router.push(url)
+      // }
+    },
+
+    /** 分享模板 */
+    shareTemplate(row) {
+      this.shareOptions.open = true;
+      this.shareOptions.templateId = row.id;
+    },
+    /**
+     * 复制模板
+     */
+    copyTemplate(row) {
+      // if(row.sendType==4){
+      //   this.$router.push(`/qw/sopTemp/updateAiChatTemp/${row.id}/2`)
+      // }else{
+      this.getInfo(row.id, 2);
+      // this.$router.push(`/qw/sopTemp/updateSopTemp/${row.id}/2`)
+      // }
+    },
+    getInfo(id, command) {
+      getSopTemp(id).then(response => {
+        this.command = command;
+        if (command == 2) {
+          this.title = "复制";
+        }
+        if (command == 1) {
+          this.title = "修改";
+        }
+        this.form = response.data;
+        this.open = true;
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      delete this.form.rules
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          const loading = this.$loading({
+            lock: true,
+            text: 'Loading',
+            spinner: 'el-icon-loading',
+            background: 'rgba(0, 0, 0, 0.7)'
+          });
+          if (this.command == 2) {
+            copyTemplate(this.form).then(response => {
+              this.msgSuccess("复制成功");
+              this.open = false;
+              loading.close();
+              this.getList();
+            });
+          } else {
+            if (this.form.id != null) {
+              updateTemp(this.form).then(response => {
+                this.msgSuccess("修改成功");
+                this.open = false;
+                loading.close();
+                this.getList();
+              });
+            } else {
+              addTemp(this.form).then(response => {
+                this.msgSuccess("新增成功");
+                this.open = false;
+                loading.close();
+                this.getList();
+              });
+            }
+          }
+        }
+      });
+    },
+
+    handleShareTemplate() {
+      const companyIds = this.companys;
+      let templateId = this.shareOptions.templateId;
+      this.$confirm("确定将模板分享给 公司编号为:" + companyIds + "的公司?", "提醒", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function () {
+        return shareSopTemp({companyIds: companyIds, templeId: templateId});
+      }).then(() => {
+        this.companys = [];
+        this.shareOptions.open = false;
+        this.getList();
+        this.msgSuccess("分享成功");
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除sop模板编号为"' + ids + '"的数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function () {
+        return delSopTemp(ids);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("删除成功");
+      }).catch(() => {
+      });
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有sop模板数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        this.exportLoading = true;
+        return exportSopTemp(queryParams);
+      }).then(response => {
+        this.download(response.msg);
+        this.exportLoading = false;
+      }).catch(() => {
+      });
+    }
+  }
+};
+</script>

+ 177 - 0
src/views/qw/sopTemp/sopTemp.vue

@@ -0,0 +1,177 @@
+<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>
+        <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-table v-loading="loading" :data="sopTempList" ref="sopTempList" >
+      <el-table-column label="id" align="center" prop="id" />
+      <el-table-column label="模板标题" align="center" prop="name" />
+      <el-table-column label="每天首条消息是否发课" align="center" prop="name" v-if="cuoser">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.cuoser" type="success">是</el-tag>
+          <el-tag v-if="!scope.row.cuoser" type="danger">否</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="medium"
+            type="primary"
+            plain
+            icon="el-icon-edit"
+            @click="handleSopTemp(scope.row)"
+            v-hasPermi="['qw:sopTemp:edit']"
+          >选择此模板</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getResetList"
+    />
+  </div>
+</template>
+
+<script>
+import { listSopTemp } from "@/api/qw/sopTemp";
+
+export default {
+  name: "SopTempComments",
+  data() {
+    return {
+      isUpdateSop:null,
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // sop模板表格数据
+      sopTempList: [],
+      sysQwSopType:[],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      cuoser: false,
+      // 状态字典
+      statusOptions: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        name: null,
+        sendType:null,
+        status:1,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      }
+    };
+  },
+  created() {
+
+    this.getDicts("sys_company_status").then(response => {
+      this.statusOptions = response.data;
+    });
+
+    this.getDicts("sys_qw_sop_type").then(response => {
+      this.sysQwSopType = response.data;
+    });
+  },
+  methods: {
+    /** 查询sop模板列表 */
+    getList(sendType,isUpdate,cuoser) {
+      if(cuoser == null){
+        cuoser = false;
+      }
+      console.log(isUpdate)
+      this.isUpdateSop = isUpdate;
+      this.cuoser = cuoser;
+      this.loading = true;
+      this.queryParams.sendType= sendType;
+      this.queryParams.cuoser= cuoser;
+      listSopTemp(this.queryParams).then(response => {
+        this.sopTempList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+
+    getResetList(){
+      this.loading = true;
+      listSopTemp(this.queryParams).then(response => {
+        this.sopTempList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    handleSopTemp(val){
+      if (this.isUpdateSop === 1){
+        console.log("进入sop修改模板方法")
+        this.$emit("submitUpdateTemp",val)
+        this.$refs.sopTempList.clearSelection();
+      }else {
+        this.$emit("sopTemp",val)
+        this.$refs.sopTempList.clearSelection();
+        this.open=false;
+      }
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        pageNum: 1,
+        pageSize: 10,
+        name: null,
+        status:1,
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getResetList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+
+
+  }
+};
+</script>

+ 327 - 0
src/views/qw/sopTemp/updateAiChatTemp.vue

@@ -0,0 +1,327 @@
+<template>
+  <div class="app-container">
+    <div style="margin: 30px;">sop规则【AI对话】模板</div>
+    <div style="margin-top: 10px;margin-left: 50px;margin-right: 100px;margin-bottom: 60px;">
+      <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="dict.dictValue"
+            >{{dict.dictLabel}}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="排序" prop="sort">
+          <el-input-number v-model="form.sort" :min="0" label="排序"></el-input-number>
+        </el-form-item>
+        <el-form-item label="规则" prop="setting">
+          <el-timeline>
+            <el-timeline-item
+              :timestamp="'第'+(1+(form.gap*index))+'天'"
+              :color="'#0bbd87'"
+              placement="top"
+              v-for="(item, index) in setting"
+              :key="index"
+              style="margin-top: 10px;">
+              <el-row>
+                <el-col :span="22">
+                  <div style="background-color: #fbfbfb;padding: 15px; border: 1px solid #e6e6e6; margin-bottom: 20px;">
+                    <el-form :model="item" label-width="80px">
+                      <el-form-item label="规则">
+                        <div v-for="(content, contentIndex) in item.content"
+                             :key="contentIndex"
+                             style="background-color: #fdfdfd;padding: 15px; border: 1px solid #e6e6e6; margin-bottom: 20px;">
+                          <el-row>
+                            <el-col :span="22">
+                              <el-form :model="content" label-width="70px">
+                                <!-- 第一天不显示时间选择 -->
+                                <el-form-item label="时间" prop="time" v-if="index > 0">
+                                  <el-time-picker
+                                    class="custom-input"
+                                    v-model="content.time"
+                                    value-format="HH:mm"
+                                    format="HH:mm"
+                                    :picker-options="{ selectableRange: '00:00:00 - 23:59:59' }"
+                                    placeholder="时间"
+                                    style="width: 100px;height: 20px;">
+                                  </el-time-picker>
+                                </el-form-item>
+
+
+                                <div v-for="(setList, setIndex) in content.setting"
+                                     :key="setIndex"
+                                     style="background-color: #fdfdfd; border: 1px solid #e6e6e6; margin-bottom: 20px;">
+                                  <el-row>
+                                    <el-col :span="22">
+                                      <el-form :model="setList" label-width="70px">
+										  <el-form-item label="间隔时间" prop="intervalTime" style="margin: 2%">
+										    <el-input-number
+										      v-model="setList.intervalTime"
+										      :min="1"
+										      :max="1440"
+										      style="width:100;margin-top: 10px;"
+										      >
+										    </el-input-number>
+										    <span class="tip">单位:分钟,最大1440分钟(24小时)</span>
+										  </el-form-item>
+                                        <el-form-item label="内容" style="margin: 2%">
+                                          <el-input
+                                            v-model="setList.value"
+                                            type="textarea"
+                                            :rows="3"
+                                            placeholder="内容"
+                                            style="width: 90%;margin-top: 10px;"/>
+                                        </el-form-item>
+										<el-form-item label="交流状态" style="margin: 2%" >
+										  <el-select  v-model="setList.talkType" placeholder="更改交流状态" size="mini" style=" margin-right: 10px;" clearable>
+											<el-option label="非首次交流" value="非首次交流"></el-option>
+											<el-option label="首次交流1" value="首次交流1"></el-option>
+											<el-option label="首次交流2" value="首次交流2"></el-option>
+										    <el-option label="交流状态1" value="交流状态1"></el-option>
+										    <el-option label="交流状态2" value="交流状态2"></el-option>
+										    <el-option label="交流状态3" value="交流状态3"></el-option>
+										  </el-select>
+										</el-form-item>
+                    </el-form>
+                        </el-col>
+                            <el-col :span="1" :offset="1">
+                              <i class="el-icon-delete"
+                                 @click="delSetList(index,contentIndex,setIndex)"
+                                 style="margin-top: 20px;"
+                                 v-if="content.setting.length>1 && (formType==1 || formType==2)"></i>
+                            </el-col>
+                      </el-row>
+                    </div>
+                                <el-link type="primary"
+                                        class="el-icon-plus"
+                                        :underline="false"
+                                        @click='addSetList(contentIndex,item.content)' v-if="(formType==1 || formType==2)">添加内容</el-link>
+                              </el-form>
+                            </el-col>
+                            <el-col :span="1" :offset="1">
+                              <i class="el-icon-delete"
+                                 @click="delContent(index,contentIndex)"
+                                 style="margin-top: 20px;"
+                                 v-if="item.content.length>1 && (formType==1 || formType==2)"></i>
+                            </el-col>
+                          </el-row>
+                        </div>
+                        <el-link type="primary"
+								                v-if="index > 0 && (formType==1 || formType==2)"
+                                class="el-icon-plus"
+                                :underline="false"
+                                @click='addContent(index)' >添加规则</el-link>
+                      </el-form-item>
+                    </el-form>
+                  </div>
+                </el-col>
+                <el-col :span="1" :offset="1">
+                  <i class="el-icon-delete"
+                     @click="delSetting(index)"
+                     v-if="setting.length>1 && (formType==1 || formType==2) "></i>
+                </el-col>
+              </el-row>
+            </el-timeline-item>
+          </el-timeline>
+<!--          <el-link type="primary"
+                   class="el-icon-plus"
+                   :underline="false"
+                   @click='addSetting()'>添加天数</el-link> -->
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer" style="float: right;"  v-if="this.formType==1 || this.formType==2 ">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { addSopTemp ,getSopTemp,updateSopTemp} from "@/api/qw/sopTemp";
+
+export default {
+  name: "AddAiChatTemp",
+  data() {
+    return {
+      // 状态字典
+      statusOptions: [],
+      // 添加推送方式字典
+      sysQwSopType: [],
+      // 表单数据
+      form: {
+        name: null,
+        setting: null,
+        status: "1",
+        sort: 1,
+        gap: 1,
+        sendType: 3, // AI对话类型,固定值
+      },
+      //1 修改 2 复制 3 查看
+      formType:null,
+
+      // 规则设置
+      setting: [],
+      // 表单校验
+      rules: {
+        name: [
+          { required: true, message: '名称不能为空', trigger: 'blur' }
+        ],
+        status: [
+          { required: true, message: '状态不能为空', trigger: 'blur' }
+        ],
+        sort: [
+          { required: true, message: '排序不能为空', trigger: 'blur' }
+        ],
+        gap: [
+          { required: true, message: '间隔天数不能为空', trigger: 'blur' }
+        ]
+      }
+    };
+  },
+  created() {
+    this.getDicts("sys_company_status").then(response => {
+      this.statusOptions = response.data;
+    });
+    // 获取推送方式字典
+    this.getDicts("sys_qw_sop_type").then(response => {
+      this.sysQwSopType = response.data;
+    });
+	const id = this.$route.params && this.$route.params.id;
+	this.formType = this.$route.params && this.$route.params.type;
+	this.handleUpdate(id);
+
+  },
+  methods: {
+
+	handleUpdate(id) {
+	  getSopTemp(id).then(response => {
+		this.form = response.data;
+	    this.setting = JSON.parse(response.data.setting);
+	  });
+	},
+    addSetList(contentIndex, content) {
+      content[contentIndex].setting.push({
+		intervalTime: 5,
+        contentType: '1',
+        value: ""
+      });
+    },
+
+    delSetList(index, contentIndex, setIndex) {
+      this.setting[index].content[contentIndex].setting.splice(setIndex, 1);
+    },
+
+    addContent(index) {
+      this.setting[index].content.push({
+        type: 1,
+        contentType: '1',
+         // 默认5分钟
+        setting: [{
+          intervalTime: 5,
+          contentType: '1',
+          value: ""
+        }]
+      });
+    },
+
+    delContent(index, contentIndex) {
+      this.setting[index].content.splice(contentIndex, 1);
+    },
+
+    addSetting() {
+      this.setting.push({
+        content: [{
+          type: 1,
+          contentType: '1',
+
+          setting: [{
+			intervalTime: 5, // 默认5分钟
+            contentType: '1',
+            value: ""
+          }]
+        }]
+      });
+    },
+
+    delSetting(index) {
+      this.setting.splice(index, 1);
+    },
+
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          // 验证内容
+          for(let day of this.setting) {
+            for(let content of day.content) {
+              if(content.time === undefined && this.setting.indexOf(day) > 0) {
+                return this.$message.error("时间不能为空");
+              }
+              for(let set of content.setting) {
+				if(!set.intervalTime) {
+				  return this.$message.error("间隔时间不能为空");
+				}
+                if(!set.value) {
+                  return this.$message.error("内容不能为空");
+                }
+              }
+            }
+          }
+          this.form.setting = JSON.stringify(this.setting);
+
+		  if (this.formType == 1) {
+		    if (this.form.id != null) {
+		      updateSopTemp(this.form).then(response => {
+		        this.msgSuccess("修改成功");
+		        this.$store.dispatch("tagsView/delView", this.$route);
+		        window.location.replace('/qw/conversion/sopTemp')
+		        this.reset();
+		      });
+		    }
+		  } else {
+		    //id制空,防止键值重复-报错
+		    this.form.id = null
+		    //更新时间制空
+		    this.form.updateTime = null
+		    addSopTemp(this.form).then(response => {
+		     this.msgSuccess("复制成功");
+		     this.$store.dispatch("tagsView/delView", this.$route);
+		     window.location.replace('/qw/conversion/sopTemp')
+		     this.reset();
+		    });
+		  }
+
+
+
+        }
+      });
+    },
+
+    cancel() {
+      this.$router.push('/qw/conversion/sopTemp');
+    }
+  }
+};
+</script>
+
+<style scoped>
+.custom-input /deep/ .el-input__inner {
+  height: 20px;
+  text-align: center;
+}
+
+.custom-input /deep/ .el-input__icon {
+  line-height: 10px;
+}
+
+.tip {
+  color: #909399;
+  font-size: 12px;
+  margin-left: 10px;
+}
+</style>

+ 1702 - 0
src/views/qw/sopTemp/updateSopTemp.vue

@@ -0,0 +1,1702 @@
+<template>
+  <div class="app-container">
+
+    <div style="margin: 30px;" v-if="this.form.sendType == 1 && formType==1 "> sop规则【修改企微接口】模板</div>
+    <div style="margin: 30px;" v-if="this.form.sendType == 1 && formType==2 "> sop规则【复制企微接口】模板</div>
+    <div style="margin: 30px;" v-if="this.form.sendType == 1 && formType==3 "> sop规则【查看企微接口】模板</div>
+    <div style="margin: 30px;" v-if="this.form.sendType == 2 && formType==1 "> sop规则【修改群发助手】模板</div>
+    <div style="margin: 30px;" v-if="this.form.sendType == 2 && formType==2 "> sop规则【复制群发助手】模板</div>
+    <div style="margin: 30px;" v-if="this.form.sendType == 2 && formType==3 "> sop规则【查看群发助手】模板</div>
+    <div style="margin: 30px;">模板编号:【{{ this.form.id }}】</div>
+
+    <div style="margin-top: 10px;margin-left: 50px;margin-right: 100px;margin-bottom: 60px;">
+      <el-form ref="form" :model="form" :rules="rules" label-width="100px">
+        <el-form-item label="名称" prop="name">
+          {{ form.name }}
+        </el-form-item>
+
+        <el-form-item label="状态">
+          <el-tag v-for="dict in statusOptions" v-if="dict.dictValue == form.status">{{ dict.dictLabel }}</el-tag>
+        </el-form-item>
+        <el-form-item label="间隔天数" prop="gap">
+          {{ form.gap }}天
+        </el-form-item>
+        <el-form-item label="规则" prop="setting">
+          <el-link type="primary" class="el-icon-plus" :underline="false" @click='addSetting()'
+                   v-if="form.sendType != 4 && formType != 3">添加天数
+          </el-link>
+          <el-link type="primary" class="el-icon-sort" :underline="false" @click='openUpdateDaySorts()'
+                   style="margin-left: 20px"
+                   v-if="form.sendType != 4 && formType != 3">修改天数排序
+          </el-link>
+          <el-link type="primary" class="el-icon-sort" :underline="false" @click='openUpdateSorts()'
+                   style="margin-left: 20px"
+                   v-if="form.sendType != 4 && formType != 3">修改规则排序
+          </el-link>
+          <el-link type="primary" class="el-icon-plus" :underline="false" @click='addSetting()'
+                   v-if="form.sendType == 4 && (formType != 3 && setting.length < 1)">添加天数
+          </el-link>
+          <el-tabs v-model="tabIndex" type="card" @tab-remove="delSetting" v-if="setting && setting.length > 0"
+                   :before-leave="leave" @tab-click="tabClick">
+            <el-tab-pane v-for="(item, index) in setting" :closable="formType != 3" :key="index" :name="index + ''">
+              <el-badge slot="label" :is-dot="!item.id" class="item" style="display: inline-block">
+                <span>{{ '第' + item.dayNum + '天' }}</span>
+              </el-badge>
+              <el-row v-loading="loading">
+                <el-col :span="22">
+                  <div
+                    style="background-color: #fbfbfb;padding: 15px;  border: 1px solid #e6e6e6; margin-bottom: 20px;">
+                    <el-form :model="item" label-width="80px">
+                      <el-form-item v-if="form.sendType != 4" label="内容名称" style="height: 50px;">
+                        <el-input :disabled="formType == 3" v-model="item.name"
+                                  placeholder="内容名称,仅内部可见"/>
+                      </el-form-item>
+                      <el-form-item label="课程" v-if="form.sendType == 5 && item.content && item.content.length > 0" required>
+                        <el-select :disabled="formType == 3 || form.sendType == 5" v-model="item.content[0].courseId"
+                                   placeholder="请选择课程" style=" margin-right: 10px;" size="mini" remote
+                                   filterable
+                                   @change="courseChangeUpdate(item.content[0], index, 0)">
+                          <el-option
+                            v-for="dict in courseList"
+                            :key="dict.dictValue"
+                            :label="dict.dictLabel"
+                            :value="parseInt(dict.dictValue)"
+                          />
+                        </el-select>
+                        <el-select :disabled="formType == 3 || form.sendType == 5" v-model="item.content[0].videoId"
+                                   placeholder="请选择小节" size="mini" style=" margin-right: 10px;" remote
+                                   filterable
+                                   @change="videoIdChange(item.content[0],index,0)">
+                          <el-option
+                            v-for="dict in videoList[0]"
+                            :key="dict.dictValue"
+                            :label="dict.dictLabel"
+                            :value="parseInt(dict.dictValue)"
+                          />
+                        </el-select>
+                        <el-select :disabled="formType == 3" v-model="item.content[0].courseType"
+                                   placeholder="请选择消息类型" size="mini"
+                                   style=" margin-right: 10px;" v-if="item.content[0].type != 4 ">
+                          <el-option
+                            v-for="dict in sysFsSopWatchStatus"
+                            :key="dict.dictValue"
+                            :label="dict.dictLabel"
+                            :value="Number(dict.dictValue)"
+                          />
+                        </el-select>
+
+                      </el-form-item>
+                      <el-form-item label="规则">
+                        <div v-for="(content, contentIndex) in item.content" :key="contentIndex"
+                             style="background-color: #fdfdfd;padding: 15px;  border: 1px solid #e6e6e6; margin-bottom: 20px;">
+                          <el-row>
+                            <el-col el-col :span="22">
+                              <el-form :model="content" label-width="70px">
+                                <div v-if="item.dayNum==1">
+                                  <el-form-item label="时间" v-if="form.sendType != 4">
+                                    <el-time-picker
+                                      :disabled="formType == 3"
+                                      class="custom-input"
+                                      v-model="content.time"
+                                      value-format="HH:mm"
+                                      format="HH:mm"
+                                      :picker-options="{ selectableRange: '01:01:00 - 23:59:59' }"
+                                      placeholder="时间"
+                                      style="width: 100px;height: 20px;">
+                                    </el-time-picker>
+                                  </el-form-item>
+                                </div>
+                                <div v-else>
+                                  <el-form-item label="时间" v-if="form.sendType != 4">
+                                    <el-time-picker
+                                      :disabled="formType == 3"
+                                      class="custom-input"
+                                      v-model="content.time"
+                                      value-format="HH:mm"
+                                      format="HH:mm"
+                                      placeholder="时间"
+                                      style="width: 100px;height: 20px;">
+                                    </el-time-picker>
+                                  </el-form-item>
+                                </div>
+                                <el-form-item label="官方群发" v-if="contentIndex==0 && content.type==2">
+                                  <el-switch
+                                    v-model="content.isOfficial"
+                                    active-color="#13ce66"
+                                    inactive-color="#DCDFE6"
+                                    active-value="1"
+                                    inactive-value="0">
+                                  </el-switch>
+                                </el-form-item>
+
+                                <el-form-item label="消息类别" v-if="form.sendType != 4 && form.sendType != 5">
+                                  <el-radio-group v-model="content.type"
+                                                  :disabled="formType == 3 || content.isOfficial === '1'"
+                                                  @change="updateHtml(() => content.contentType = '1')">
+                                    <el-radio :label="1">普通</el-radio>
+                                    <el-radio :label="2">课程</el-radio>
+                                    <el-radio :label="3">订单</el-radio>
+                                    <el-radio :label="4">AI触达</el-radio>
+                                    <el-radio :label="5">打标签</el-radio>
+                                  </el-radio-group>
+                                </el-form-item>
+                                <el-form-item label="课程" v-if="content.type == 2 && form.sendType != 5" required>
+                                  <el-select :disabled="formType == 3 || form.sendType == 5" v-model="content.courseId"
+                                             placeholder="请选择课程" style=" margin-right: 10px;" size="mini" remote
+                                             filterable
+                                             @change="courseChangeUpdate(content,index,contentIndex)">
+                                    <el-option
+                                      v-for="dict in courseList"
+                                      :key="dict.dictValue"
+                                      :label="dict.dictLabel"
+                                      :value="parseInt(dict.dictValue)"
+                                    />
+                                  </el-select>
+                                  <el-select :disabled="formType == 3 || form.sendType == 5" v-model="content.videoId"
+                                             placeholder="请选择小节" size="mini" style=" margin-right: 10px;" remote
+                                             filterable
+                                             @change="videoIdChange(content,index,contentIndex)">
+                                    <el-option
+                                      v-for="dict in videoList[contentIndex]"
+                                      :key="dict.dictValue"
+                                      :label="dict.dictLabel"
+                                      :value="parseInt(dict.dictValue)"
+                                    />
+                                  </el-select>
+                                  <el-select :disabled="formType == 3" v-model="content.courseType"
+                                             placeholder="请选择消息类型" size="mini"
+                                             style=" margin-right: 10px;" v-if="content.type != 4 ">
+                                    <el-option
+                                      v-for="dict in sysFsSopWatchStatus"
+                                      :key="dict.dictValue"
+                                      :label="dict.dictLabel"
+                                      :value="Number(dict.dictValue)"
+                                    />
+                                  </el-select>
+
+                                </el-form-item>
+                                <el-form-item label="Ai触达" v-if="content.type == 4 ">
+                                  <el-select :disabled="formType == 3" v-model="content.aiTouch"
+                                             placeholder="请选择Ai触达类型" size="mini"
+                                             style=" margin-right: 10px;" v-if="content.type == 4 ">
+                                    <el-option label="非首次交流" value="非首次交流"></el-option>
+                                    <el-option label="首次交流1" value="首次交流1"></el-option>
+                                    <el-option label="首次交流2" value="首次交流2"></el-option>
+                                    <el-option label="交流状态1" value="交流状态1"></el-option>
+                                    <el-option label="交流状态2" value="交流状态2"></el-option>
+                                    <el-option label="交流状态3" value="交流状态3"></el-option>
+                                  </el-select>
+                                </el-form-item>
+                                <el-form-item label="添加标签" prop="addTag" v-if="content.type == 5 "
+                                              v-model="content.addTag">
+
+                                  <el-tag
+                                    :key="tag"
+                                    v-for="tag in content.addTag"
+                                    closable
+                                    :disable-transitions="false"
+                                    @close="handleClose(contentIndex,tag,content)">
+                                    {{ tag }}
+                                  </el-tag>
+                                  <el-input
+                                    style="width:110px"
+                                    class="input-new-tag"
+                                    v-if="addTag[contentIndex].inputVisible"
+                                    v-model="addTag[contentIndex].inputValue"
+                                    ref="saveTagInput"
+                                    size="small"
+                                    @keyup.enter.native="handleInputConfirm(contentIndex,content)"
+                                    @blur="handleInputConfirm(contentIndex,content)"
+                                  >
+                                  </el-input>
+                                  <el-button v-else class="button-new-tag" size="small" style="width: 110px"
+                                             @click="showInput(contentIndex)">新增标签
+                                  </el-button>
+                                </el-form-item>
+
+                                <el-form-item label="移除标签" prop="remarkMobiles" v-if="content.type == 5 ">
+
+                                  <el-tag
+                                    :key="tag"
+                                    v-for="tag in content.delTag"
+                                    closable
+                                    :disable-transitions="false"
+                                    @close="handleCloseDel(contentIndex,tag,content)">
+                                    {{ tag }}
+                                  </el-tag>
+                                  <el-input
+                                    style="width:110px"
+                                    class="input-new-tag"
+                                    v-if="addTag[contentIndex].delTagVisible"
+                                    v-model="addTag[contentIndex].delTagValue"
+                                    ref="saveTagInputDel"
+                                    size="small"
+                                    @keyup.enter.native="handleInputConfirmDel(contentIndex,content)"
+                                    @blur="handleInputConfirmDel(contentIndex,content)"
+                                  >
+                                  </el-input>
+                                  <el-button v-else class="button-new-tag" size="small" style="width: 110px"
+                                             @click="showInputDel(contentIndex)">新增标签
+                                  </el-button>
+
+                                </el-form-item>
+
+                                <div v-for="(setList, setIndex) in content.setting" :key="setIndex"
+                                     style="background-color: #fdfdfd; border: 1px solid #e6e6e6; margin-bottom: 20px;"
+                                     v-if="content.type != 4 && content.type != 5">
+                                  <el-row style="padding-bottom: 20px">
+                                    <el-col :span="22">
+                                      <el-form v-if="form.sendType != 4" :model="setList" label-width="70px">
+                                        <el-form-item label="内容类别" style="margin: 2%">
+                                          <div v-if="form.sendType == 1 ">
+                                            <el-radio-group v-model="setList.contentType" :disabled="formType == 3">
+                                              <!--                                              <el-radio  :label="item.dictValue" v-for="item in sysQwSopContentType">{{item.dictLabel}}</el-radio>-->
+                                              <el-radio
+                                                v-for="item in sysQwSopContentType"
+                                                :key="item.dictValue"
+                                                :label="item.dictValue"
+                                                :disabled="item.dictValue === '1' && content.setting.some(s => s.contentType == '1') ">
+                                                {{ item.dictLabel }}
+                                              </el-radio>
+                                            </el-radio-group>
+                                          </div>
+                                          <div v-if="form.sendType == 2 || form.sendType == 5">
+                                            <el-radio-group v-model="setList.contentType"
+                                                            :disabled="formType == 3"
+                                                            @change="handleContentTypeChange(content,index,contentIndex,setIndex)">
+                                              <el-radio
+                                                :key="item.dictValue"
+                                                :label="item.dictValue"
+                                                :disabled="(content.type!=2 && item.dictValue === '9') || (content.isOfficial==1 && ['5','6','7','8','9'].includes(item.dictValue))"
+                                                v-for="item in sysQwSopAiContentType">{{ item.dictLabel }}
+                                              </el-radio>
+                                            </el-radio-group>
+                                          </div>
+                                        </el-form-item>
+                                        <el-form-item label="内容">
+                                          <el-input :disabled="formType == 3" v-if="setList.contentType == 1 "
+                                                    v-model="setList.value"
+                                                    type="textarea" :rows="3" placeholder="内容"
+                                                    style="width: 90%;margin-top: 10px;"
+                                                    @keydown.native="handleKeydown($event, index, contentIndex, setIndex)"
+                                                    :ref="`textarea-${index}-${contentIndex}-${setIndex}`"
+                                          />
+
+                                          <!-- 修改按钮部分 -->
+                                          <el-link
+                                            v-if="setList.contentType == 1"
+                                            type="primary"
+                                            @click="toggleSalesCall(index, contentIndex, setIndex)"
+                                            style="margin-top: 10px;"
+                                          >
+                                            {{ setList.isSalesCallAdded ? '移除#销售称呼#' : '添加#销售称呼#' }}
+                                          </el-link>
+
+                                          <ImageUpload :disabled="formType == 3" v-if="setList.contentType == 2 "
+                                                       v-model="setList.imgUrl"
+                                                       type="image" :num="1" :width="150" :height="150"/>
+
+                                          <div
+                                            v-if="setList.contentType == 3  || (setList.contentType == 9 && content.type==2 )">
+                                            <el-card class="box-card">
+                                              <el-form-item label="链接标题:" label-width="100px" required>
+                                                <el-input :disabled="formType == 3" v-model="setList.linkTitle"
+                                                          placeholder="请输入链接标题"
+                                                          style="width: 90%;"/>
+                                              </el-form-item>
+                                              <el-form-item label="链接描述:" label-width="100px" required>
+                                                <el-input :disabled="formType == 3" type="textarea" :rows="3"
+                                                          v-model="setList.linkDescribe"
+                                                          placeholder="请输入链接描述"
+                                                          style="width: 90%;margin-top: 1%;"/>
+                                              </el-form-item>
+                                              <el-form-item label="链接封面:" label-width="100px" required>
+                                                <ImageUpload :disabled="formType == 3" v-model="setList.linkImageUrl"
+                                                             type="image" :num="1"
+                                                             :file-size="2" :width="150" :height="150"
+                                                             style="margin-top: 1%;"/>
+                                              </el-form-item>
+                                              <div v-if="content.type != 2 " style="margin-top: 1%">
+                                                <el-form-item label="链接地址:" label-width="100px" required>
+                                                  <el-input :disabled="formType == 3" v-model="setList.linkUrl"
+                                                            placeholder="请输入链接地址"
+                                                            style="width: 90%;"/>
+                                                </el-form-item>
+                                              </div>
+                                              <div v-if="content.type == 2 ">
+                                                <el-form-item label="链接地址:" label-width="100px">
+                                                  <el-tag type="warning" v-model="setList.isBindUrl = 1 ">选择的课程小节
+                                                    即为卡片链接地址
+                                                  </el-tag>
+                                                </el-form-item>
+                                              </div>
+                                            </el-card>
+                                          </div>
+
+                                          <div v-if="setList.contentType == 4">
+                                            <el-card class="box-card">
+                                              <el-form-item label="标题" prop="miniprogramTitle">
+                                                <el-input v-model="setList.miniprogramTitle"
+                                                          placeholder="请输入小程序消息标题,最长为64字节" :rows="2"
+                                                          maxlength="64" type="textarea"
+                                                          @input="checkByteLength(content,setList.contentType,content.isOfficial)"/>
+                                              </el-form-item>
+                                              <el-form-item label="封面" prop="miniprogramPicUrl">
+                                                <ImageUpload v-if="content.isOfficial !== '1'"
+                                                             v-model="setList.miniprogramPicUrl" type="image" :num="10"
+                                                             :width="150" :height="150"/>
+                                              </el-form-item>
+                                              <el-form-item label="appid" prop="miniprogramAppid" v-show="false">
+                                                <el-input v-model="setList.miniprogramAppid='wx73f85f8d62769119' "
+                                                          disabled/>
+                                              </el-form-item>
+                                              <el-form-item label="page路径" prop="miniprogramPage" v-show="false"
+                                                            label-width="100px" style="margin-left: -30px">
+                                                <el-input v-model="setList.miniprogramPage"
+                                                          placeholder="小程序消息打开后的路径" disabled/>
+                                              </el-form-item>
+                                            </el-card>
+                                          </div>
+
+
+                                          <div v-if="setList.contentType == 5 ">
+
+                                            <el-form-item label="上传文件:" prop="fileUrl" label-width="100px">
+                                              <el-upload
+                                                :disabled="formType == 3"
+                                                v-model="setList.fileUrl"
+                                                class="avatar-uploader"
+                                                :action="uploadUrl"
+                                                :show-file-list="false"
+                                                :on-success="(res, file) => handleAvatarSuccessFile(res, file, setList)"
+                                                :before-upload="beforeAvatarUploadFile">
+                                                <i class="el-icon-plus avatar-uploader-icon"></i>
+                                              </el-upload>
+                                              <el-link v-if="setList.fileUrl" type="primary"
+                                                       :href="downloadUrl(setList.fileUrl)" download>
+                                                {{ setList.fileUrl }}
+                                              </el-link>
+                                            </el-form-item>
+
+                                          </div>
+
+                                          <div v-if="setList.contentType == 6 ">
+                                            <el-form-item label="上传视频:" prop="videoUrl" label-width="100px">
+                                              <el-upload
+                                                :disabled="formType == 3"
+                                                v-model="setList.videoUrl"
+                                                class="avatar-uploader"
+                                                :action="uploadUrl"
+                                                :show-file-list="false"
+                                                :on-success="(res, file) => handleAvatarSuccessVideo(res, file, setList)"
+                                                :before-upload="beforeAvatarUploadVideo">
+                                                <i class="el-icon-plus avatar-uploader-icon"></i>
+                                              </el-upload>
+                                              <video v-if="setList.videoUrl"
+                                                     :src="setList.videoUrl"
+                                                     controls style="width: 200px;height: 100px">
+                                              </video>
+                                            </el-form-item>
+                                          </div>
+                                          <div v-if="setList.contentType == 7 ">
+                                            <el-input
+                                              :disabled="formType == 3"
+                                              v-model="setList.value"
+                                              type="textarea" :rows="3" maxlength="66" show-word-limit
+                                              placeholder="输入要转为语音的内容"
+                                              style="width: 90%;margin-top: 10px;"
+                                              @input="handleInputVideoText(setList.value,setList)"/>
+
+
+                                          </div>
+                                          <div v-if="setList.contentType == 8 ">
+                                            <el-button :disabled="formType == 3" type="primary"
+                                                       style="margin-bottom: 1%"
+                                                       @click="hanldeSelectVideoNum(content,index,contentIndex,setIndex)">
+                                              选择视频号
+                                            </el-button>
+                                            <el-card class="box-card" v-if="setList.coverUrl">
+                                              <el-form-item label="封面标题:" label-width="100px">
+                                                <el-input :disabled="formType == 3" v-model="setList.nickname"
+                                                          style="width: 90%;margin-bottom: 1%" disabled/>
+                                              </el-form-item>
+                                              <el-form-item label="头像:" label-width="100px">
+                                                <el-image
+                                                  v-if="setList.avatar != null"
+                                                  :src="setList.avatar"
+                                                  :preview-src-list="[setList.avatar]"
+                                                  :style="{ width: '50px', height: '50px' }"
+                                                ></el-image>
+                                              </el-form-item>
+                                              <el-form-item label="封面:" label-width="100px">
+                                                <el-image
+                                                  v-if="setList.coverUrl != null"
+                                                  :src="setList.coverUrl"
+                                                  :preview-src-list="[setList.coverUrl]"
+                                                  :style="{ width: '200px', height: '200px' }"
+                                                ></el-image>
+
+                                              </el-form-item>
+                                              <el-form-item label="简介:" label-width="100px">
+                                                <el-input :disabled="formType == 3" type="textarea" :rows="3"
+                                                          v-model="setList.desc"
+                                                          style="width: 90%;margin-top: 1%;" disabled/>
+                                              </el-form-item>
+                                              <el-form-item label="视频地址:" label-width="100px"
+                                                            style="margin-top: 1%">
+                                                <el-input :disabled="formType == 3" v-model="setList.url"
+                                                          style="width: 90%;" disabled/>
+                                              </el-form-item>
+
+                                            </el-card>
+                                          </div>
+                                          <div v-if="setList.contentType == 10 ">
+                                            <el-card class="box-card">
+                                              <el-form-item label="链接标题:" label-width="100px" required>
+                                                <el-input :disabled="formType == 3" v-model="setList.linkTitle"
+                                                          placeholder="请输入链接标题"
+                                                          style="width: 90%;"/>
+                                              </el-form-item>
+                                              <el-form-item label="链接描述:" label-width="100px" required>
+                                                <el-input :disabled="formType == 3" type="textarea" :rows="3"
+                                                          v-model="setList.linkDescribe"
+                                                          placeholder="请输入链接描述"
+                                                          style="width: 90%;margin-top: 1%;"/>
+                                              </el-form-item>
+                                              <el-form-item label="链接封面:" label-width="100px" required>
+                                                <ImageUpload :disabled="formType == 3" v-model="setList.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-tag type="warning"> 链接地址自动生成
+                                                </el-tag>
+                                              </el-form-item>
+                                            </el-card>
+                                          </div>
+                                        </el-form-item>
+                                        <el-form-item label="添加短链"
+                                                      v-if="content.type == 2 && setList.contentType == 1  ">
+                                          <el-tooltip content="请先根据课程选定课程小节之后再添加" effect="dark"
+                                                      :disabled="!!content.videoId">
+                                            <el-switch
+                                              @change="updateHtml"
+                                              v-model="setList.isBindUrl"
+                                              :disabled="!content.videoId && formType == 3"
+                                              active-color="#13ce66"
+                                              inactive-color="#DCDFE6"
+                                              active-value="1"
+                                              inactive-value="2">
+                                            </el-switch>
+                                          </el-tooltip>
+
+                                          <span v-if="setList.isBindUrl == '1'"
+                                                style="margin-left: 10px; color: #13ce66">添加URL</span>
+                                          <span v-if="setList.isBindUrl == '2'"
+                                                style="margin-left: 10px; color: #b1b4ba">不加URL</span>
+                                        </el-form-item>
+                                        <el-form-item label="课节过期时间"
+                                                      v-if="content.type == 2 && setList.isBindUrl == '1' && setList.contentType != 2  && setList.contentType != 5  && setList.contentType != 6 && setList.contentType != 8 && setList.contentType != 9 && setList.contentType != 10  ">
+                                          <el-row>
+                                            <el-input type="number" v-model="setList.expiresDays"
+                                                      style="width: 200px">
+                                              <template slot="append">天</template>
+                                            </el-input>
+                                          </el-row>
+                                          <el-row>
+                                            <span class="tip">填写0或不填时,默认为系统配置的默认时间</span>
+                                          </el-row>
+                                        </el-form-item>
+
+                                      </el-form>
+                                      <el-form v-if="form.sendType == 4" :model="setList" label-width="70px">
+                                        <el-form-item label="添加客服" prop="intervalTime" style="margin: 2%">
+                                          <el-input-number
+                                            v-model="setList.intervalTime"
+                                            :min="1"
+                                            :max="1440"
+                                            style="width:100px;margin-top: 10px;"
+                                          >
+                                          </el-input-number>
+                                          <span class="tip">单位:分钟,最大1440分钟(24小时)</span>
+                                        </el-form-item>
+                                        <el-form-item label="内容" style="margin: 2%">
+                                          <el-input
+                                            v-model="setList.value"
+                                            type="textarea"
+                                            :rows="3"
+                                            placeholder="内容"
+                                            style="width: 90%;margin-top: 10px;"/>
+                                        </el-form-item>
+                                        <el-form-item label="交流状态" style="margin: 2%">
+                                          <el-select v-model="setList.talkType" placeholder="更改交流状态" size="mini"
+                                                     style=" margin-right: 10px;" clearable>
+                                            <el-option label="非首次交流" value="非首次交流"></el-option>
+                                            <el-option label="首次交流1" value="首次交流1"></el-option>
+                                            <el-option label="首次交流2" value="首次交流2"></el-option>
+                                            <el-option label="交流状态1" value="交流状态1"></el-option>
+                                            <el-option label="交流状态2" value="交流状态2"></el-option>
+                                            <el-option label="交流状态3" value="交流状态3"></el-option>
+                                          </el-select>
+                                        </el-form-item>
+                                      </el-form>
+                                    </el-col>
+                                    <el-col :span="1" :offset="1">
+                                      <i class="el-icon-delete" @click="delSetList(index,contentIndex,setIndex)"
+                                         style="margin-top: 20px;"
+                                         v-if="content.setting.length>1 && (formType != 3)"></i>
+                                    </el-col>
+                                  </el-row>
+                                </div>
+                                <el-link type="primary" class="el-icon-plus" :underline="false"
+                                         @click='addSetList(contentIndex,item.content)'
+                                         v-if="content.type != 4 && formType != 3">添加内容
+                                </el-link>
+                              </el-form>
+                            </el-col>
+                            <el-col :span="1" :offset="1">
+                              <i class="el-icon-delete" @click="delContent(index,contentIndex)"
+                                 style="margin-top: 20px;"
+                                 v-if="item.content.length>1 && formType != 3"></i>
+                            </el-col>
+                          </el-row>
+                        </div>
+                        <el-link type="primary" class="el-icon-plus" :underline="false" @click='addContent(index)'
+                                 v-if="formType != 3 && form.sendType != 4">添加规则
+                        </el-link>
+                      </el-form-item>
+                    </el-form>
+
+                  </div>
+                  <div style="float: right;" v-if="formType != 3">
+                    <el-button type="primary" @click="save" v-if="!item.voice || item.voice == 0">
+                      保存({{ '第' + (1 + (form.gap * index)) + '天' }})
+                    </el-button>
+                    <el-button type="primary" disabled v-if="item.voice == 1">语言生成中</el-button>
+                    <el-button type="primary" @click="leave(tabIndex)" v-if="item.voice == 1">刷新状态</el-button>
+                  </div>
+                </el-col>
+              </el-row>
+            </el-tab-pane>
+          </el-tabs>
+        </el-form-item>
+      </el-form>
+    </div>
+
+    <el-dialog :title="videoNumOptions.title" :visible.sync="videoNumOptions.open" style="width: 1500px;height: 100%"
+               append-to-body>
+      <userVideo ref="QwUserVideo" @videoResult="qwUserVideoResult"></userVideo>
+    </el-dialog>
+    <el-dialog title="修改天数排序" :visible.sync="openSort" style="width: 1500px" append-to-body>
+      <draggable v-model="dayList" @end="onDragEndDay" style="padding: 20px">
+        <el-button v-for="(item, index) in dayList" :class="item.newDay != item.dayNum ? 'red':''">第{{
+            item.newDay
+          }}天(第{{ item.dayNum }}天)
+        </el-button>
+      </draggable>
+
+      <div style="float: right;">
+        <el-button type="primary" @click="saveSorts">保存</el-button>
+      </div>
+    </el-dialog>
+    <el-dialog title="修改规则排序" :visible.sync="openSort2" style="width: 1500px" append-to-body>
+      <draggable v-model="ruleList" @end="onDragEnd" style="padding: 20px">
+        <el-button v-for="(item, index) in ruleList">{{ item.time }}</el-button>
+      </draggable>
+
+      <div style="float: right;">
+        <el-button @click="autoSortsRules">自动排序</el-button>
+        <el-button type="primary" @click="saveSortsRules">保存</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+
+<style scoped>
+.el-button {
+  margin-left: 10px;
+}
+
+.red:hover {
+  color: #dbdbdb !important;
+}
+
+.red {
+  background-color: #F56C6C !important;
+  color: #fff !important;
+}
+
+.custom-input /deep/ .el-input__inner {
+  height: 20px;
+  text-align: center;
+}
+
+.custom-input /deep/ .el-input__icon {
+  line-height: 10px;
+}
+
+.el-icon-delete {
+  cursor: pointer;
+}
+
+/deep/ .el-badge__content.is-fixed {
+  top: 10px !important;
+  right: 0 !important;
+  width: 8px;
+}
+
+.tip {
+  color: #909399;
+  font-size: 12px;
+  margin-left: 10px;
+}
+
+.sortable-ghost {
+  background: rgb(26, 164, 255) !important;
+  color: #FFF !important;
+}
+</style>
+<script>
+import draggable from 'vuedraggable';
+import {
+  listSopTemp,
+  getSopTemp,
+  delSopTemp,
+  addSopTemp,
+  sortDay,
+  dayListFun,
+  updateSopTemp,
+  exportSopTemp,
+  addOrUpdateSetting,
+  selectRulesInfo,
+  delRules
+} from "@/api/qw/sopTemp";
+import {courseList, videoList} from "@/api/qw/sop";
+import ImageUpload from "@/views/qw/sop/ImageUpload";
+import userVideo from "@/views/qw/userVideo/userVideo.vue";
+
+export default {
+  name: "updateSopTemp",
+  components: {ImageUpload, userVideo, draggable},
+  data() {
+    return {
+      addTag: [{
+        addTag: [],
+        inputVisible: false,
+        inputValue: '',
+        delTag: [],
+        delTagVisible: false,
+        delTagValue: ''
+      }],
+      uploadUrl: process.env.VUE_APP_BASE_API + "/common/uploadOSS2",
+      uploadUrlByVoice: process.env.VUE_APP_BASE_API + "/common/uploadOSSByHOOKVoice",
+      //上传语音的遮罩层
+      voiceLoading: false,
+      openSort: false,
+      openSort2: false,
+      // 遮罩层
+      loading: false,
+      loading2: false,
+      loading3: false,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      dayList: [],
+      ruleList: [],
+      ids: [],
+      sysFsSopWatchStatus: [],
+      //消息内容类型 企微版
+      sysQwSopContentType: [],
+      //插件版
+      sysQwSopAiContentType: [],
+
+      //类别
+      sysQwSopSettingType: [],
+
+      courseList: [],
+      videoList: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      defaultContentType: 1,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      id: "",
+      total: 0,
+      tabIndex: null,
+      // sop模板表格数据
+      setting: [],
+      data: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 状态字典
+      statusOptions: [],
+      videoNumOptions: {
+        title: '选择视频号',
+        open: false,
+        content: null,
+        contentIndex: null,
+        setIndex: null,
+      },
+      //1 修改 2 复制 3 查看
+      formType: null,
+      // 查询参数
+      form: {
+        name: null,
+        setting: null,
+        status: "1",
+        sort: 1,
+        companyId: null,
+        gap: 1,
+      },
+      // 表单校验
+      rules: {
+        name: [
+          {required: true, message: '名称不能为空', trigger: 'blur'}
+        ],
+        status: [
+          {required: true, message: '状态不能为空', trigger: 'blur'}
+        ],
+        sort: [
+          {required: true, message: '排序不能为空', trigger: 'blur'}
+        ],
+        gap: [
+          {required: true, message: '间隔天数不能为空', trigger: 'blur'}
+        ],
+      }
+    };
+  },
+  created() {
+    this.getDicts("sys_qwSopAi_contentType").then(response => {
+      this.sysQwSopAiContentType = response.data;
+    });
+    this.getDicts("sys_fs_sop_watch_status").then(response => {
+      this.sysFsSopWatchStatus = response.data;
+    });
+
+    this.getDicts("sys_qwSop_contentType").then(response => {
+      this.sysQwSopContentType = response.data;
+    });
+
+    this.getDicts("sys_company_status").then(response => {
+      this.statusOptions = response.data;
+    });
+
+    this.getDicts("sys_qwSop_settingType").then(response => {
+      this.sysQwSopSettingType = response.data;
+    });
+
+    courseList().then(response => {
+      this.courseList = response.list;
+    });
+
+    const id = this.$route.params && this.$route.params.id;
+    this.id = id;
+    this.formType = this.$route.params && this.$route.params.type;
+    this.handleUpdate(id);
+  },
+  methods: {
+    handleClose(index, tag, content) {
+      content.addTag.splice(content.addTag.indexOf(tag), 1);
+    },
+    showInput(index) {
+      this.addTag[index].inputVisible = true;
+    },
+    handleInputConfirm(index, content) {
+      let inputValue = this.addTag[index].inputValue;
+      if (inputValue) {
+        if (!content.hasOwnProperty('addTag')) {
+          this.$set(content, 'addTag', []);
+        }
+        content.addTag.push(inputValue);
+      }
+      this.addTag[index].inputVisible = false;
+      this.addTag[index].inputValue = '';
+    },
+
+    // 检查字节长度
+    checkByteLength(content, type, isOfficial) {
+
+      if (type == 4 && isOfficial == '1')
+        for (let i = 0; i < content.setting.length; i++) {
+          const text = content.setting[i].miniprogramTitle;
+          const byteLength = this.getByteLength(text); // 获取当前字节数
+          // 如果字节数超过64,截断输入内容
+          if (byteLength > 64) {
+            this.$set(content.setting[i], 'miniprogramTitle', this.truncateTextByByteLength(text, 60));
+          }
+
+        }
+
+    },
+
+    // 计算字符串的字节数
+    getByteLength(text) {
+      return new Blob([text]).size; // 使用 Blob 计算字节数
+    },
+
+    // 根据字节数截断字符串
+    truncateTextByByteLength(text, maxByteLength) {
+      let byteLength = 0;
+      let result = "";
+
+      for (let i = 0; i < text.length; i++) {
+        const char = text[i];
+        const charByteLength = this.getByteLength(char); // 获取当前字符的字节数
+
+        // 如果加上当前字符的字节数后不超过限制,则添加到结果中
+        if (byteLength + charByteLength <= maxByteLength) {
+          result += char;
+          byteLength += charByteLength;
+        } else {
+          break; // 超过限制时停止
+        }
+      }
+
+      return result;
+    },
+
+    handleCloseDel(index, tag, content) {
+      content.delTag.splice(content.delTag.indexOf(tag), 1);
+    },
+
+    showInputDel(index) {
+      this.addTag[index].delTagVisible = true;
+    },
+    handleInputConfirmDel(index, content) {
+
+      let delTagValue = this.addTag[index].delTagValue;
+
+      if (delTagValue) {
+        if (!content.hasOwnProperty('delTag')) {
+          this.$set(content, 'delTag', []);
+        }
+        content.delTag.push(delTagValue);
+
+      }
+      this.addTag[index].delTagVisible = false;
+      this.addTag[index].delTagValue = '';
+    },
+    saveSorts() {
+      let list = this.dayList.filter(e => e.dayNum != e.newDay).map(e => {
+        return {dayNum: e.newDay, id: e.id}
+      })
+      this.loading3 = true;
+      sortDay(list).then(e => {
+        window.location.reload();
+      })
+    },
+    saveSortsRules() {
+      this.$set(this.setting[this.tabIndex], 'content', this.ruleList);
+      this.openSort2 = false;
+    },
+    autoSortsRules() {
+      this.ruleList = this.ruleList.sort((a, b) => {
+        // 将 time 字段转换为 Date 对象进行比较
+        const timeA = new Date(`1970-01-01T${a.time}Z`);
+        const timeB = new Date(`1970-01-01T${b.time}Z`);
+
+        // 比较时间顺序
+        return timeA - timeB; // 升序排序
+      });
+    },
+    openUpdateDaySorts() {
+      dayListFun(this.id).then(response => {
+        response.data.forEach((item) => item.newDay = item.dayNum);
+        this.dayList = response.data;
+        if (this.dayList == null || this.dayList.length == 0) {
+          this.$message.error("暂无天数")
+        } else {
+          this.openSort = true;
+        }
+      })
+    },
+    openUpdateSorts() {
+      this.ruleList = JSON.parse(JSON.stringify(this.setting[this.tabIndex].content));
+      if (this.ruleList == null || this.ruleList.length == 0) {
+        this.$message.error("暂无规则")
+      } else {
+        this.openSort2 = true;
+      }
+    },
+    onDragEndDay() {
+      this.dayList.forEach((item, index) => {
+        item.newDay = (this.form.gap * index) + 1;
+      })
+      this.$forceUpdate()
+    },
+    onDragEnd() {
+      // 更新排序值
+      // this.setting[this.tabIndex].content.forEach((item, index) => {
+      //   item.sorts = index;
+      // });
+    },
+    // 切换标签保存数据
+    leave(index, oldIndex) {
+      const newData = this.setting[index]
+      if (newData.dayNum && newData.id) {
+        this.loading = true;
+        selectRulesInfo(newData.id).then(res => {
+          this.$nextTick(() => {
+            this.loading = false;
+            this.videoList = [];
+            this.addTag = [];
+            this.$set(this.setting, index, {
+              ...newData,
+              voice: res.data.voice,
+              content: res.data.list || [{type: this.defaultContentType, contentType: '1', setting: [{contentType: '1', value: ""}]}]
+            });
+
+            for (let j = 0; j < this.setting[index].content.length; j++) {
+
+              if (this.setting[index].content[j].addTag != null) {
+                this.setting[index].content[j].addTag = JSON.parse(this.setting[index].content[j].addTag)
+              }
+              if (this.setting[index].content[j].delTag != null) {
+                this.setting[index].content[j].delTag = JSON.parse(this.setting[index].content[j].delTag)
+              }
+              this.videoList.push([])
+              this.addTag.push({
+                addTag: [],
+                inputVisible: false,
+                inputValue: '',
+                delTag: [],
+                delTagVisible: false,
+                delTagValue: ''
+              })
+              if (this.setting[index].content[j].type == 2) {
+                if (this.setting[index].content[j].hasOwnProperty('courseId')) {
+                  this.courseChange(this.setting[index].content[j], index, j);
+                }
+              }
+            }
+          })
+        });
+      }
+
+    },
+    save() {
+      let data = this.setting[this.tabIndex];
+      let check = this.checkData(data);
+      if (!check) {
+        return;
+      }
+      for (let j = 0; j < data.content.length; j++) {
+        if (data.content[j].type != 4 && data.content[j].type != 5) {
+          for (let k = 0; k < data.content[j].setting.length; k++) {
+            if (data.content[j].setting[k].contentType == 4 && (data.content[j].setting[k].miniprogramTitle != null || data.content[j].setting[k].miniprogramTitle != "")) {
+              data.content[j].setting[k].miniprogramTitle = this.truncateTextByByteLength(data.content[j].setting[k].miniprogramTitle, 60)
+            }
+          }
+        }
+      }
+
+      let index = 0;
+      const dataList = data.content.map(e => {
+        e.name = data.name;
+        e.tempId = this.id;
+        e.sorts = Number(this.tabIndex);
+        e.dayNum = (this.form.gap * e.sorts) + 1;
+        e.sorts = index++;
+        data.dayNum = e.dayNum;
+        e.contentType = e.type;
+        e.settingList = e.setting;
+        e.settingList.forEach(e => e.content = JSON.stringify(e));
+        if (e.addTag != null) {
+          e.addTag = JSON.stringify(e.addTag);
+        }
+        if (e.delTag != null) {
+          e.delTag = JSON.stringify(e.delTag);
+        }
+        return e;
+      })
+      data.tempId = this.id;
+      data.list = dataList;
+      this.loading = true;
+      addOrUpdateSetting(data).then(e => {
+        let content = this.setting[this.tabIndex].content;
+        for (let j = 0; j < content.length; j++) {
+          if (content[j].addTag != null) {
+            content[j].addTag = JSON.parse(content[j].addTag);
+          }
+          if (content[j].delTag != null) {
+            content[j].delTag = JSON.parse(content[j].delTag);
+          }
+        }
+        this.loading = false;
+        // 使用Vue.set确保响应式更新
+        this.$set(this.setting, this.tabIndex, {
+          ...this.setting[this.tabIndex],
+          id: e.data.id,
+        });
+      });
+    },
+    checkData(data) {
+      if (this.form.sendType != 4) {
+
+        for (let j = 0; j < data.content.length; j++) {
+          if (data.name == null || data.name == "") {
+            this.$message.error("内容名称不能为空")
+            return false;
+          }
+          if (data.content[j].time == null || data.content[j].time == "") {
+            this.$message.error("时间不能为空")
+            return false;
+          }
+          if (data.content[j].type == 2) {
+            if (data.content[j].courseId == null || data.content[j].courseId == "") {
+              this.$message.error("请选择课程")
+              return false;
+            }
+            if (data.content[j].videoId == null || data.content[j].videoId == "") {
+              this.$message.error("请选择课节")
+              return false;
+            }
+            if (data.content[j].courseType == null || data.content[j].courseType === "") {
+              this.$message.error("请选择消息类型")
+              return false;
+            }
+          }
+
+          if (data.content[j].type != 4 && data.content[j].type != 5) {
+            for (let k = 0; k < data.content[j].setting.length; k++) {
+              if (data.content[j].setting[k].contentType == 1 && (data.content[j].setting[k].value == null || data.content[j].setting[k].value == "")) {
+                this.$message.error("内容不能为空")
+                return false;
+              }
+              if (data.content[j].setting[k].contentType == 2 && (data.content[j].setting[k].imgUrl == null || data.content[j].setting[k].imgUrl == "")) {
+                this.$message.error("图片不能为空")
+                return false;
+              }
+              if ((data.content[j].setting[k].contentType == 3 || data.content[j].setting[k].contentType == 9 || data.content[j].setting[k].contentType == 10) && (data.content[j].setting[k].linkTitle == null || data.content[j].setting[k].linkTitle == "")) {
+                this.$message.error("链接标题不能为空")
+                return false;
+              }
+              if ((data.content[j].setting[k].contentType == 3 || data.content[j].setting[k].contentType == 9 || data.content[j].setting[k].contentType == 10) && (data.content[j].setting[k].linkDescribe == null || data.content[j].setting[k].linkDescribe == "")) {
+                this.$message.error("链接描述不能为空")
+                return false;
+              }
+              if ((data.content[j].setting[k].contentType == 3 || data.content[j].setting[k].contentType == 9 || data.content[j].setting[k].contentType == 10) && (data.content[j].setting[k].linkImageUrl == null || data.content[j].setting[k].linkImageUrl == "")) {
+                this.$message.error("链接图片不能为空")
+                return false;
+              }
+              if (data.content[j].setting[k].contentType == 3 && data.content[j].setting[k].type == 1 && (data.content[j].setting[k].linkUrl == null || data.content[j].setting[k].linkUrl == "")) {
+                this.$message.error("链接地址不能为空")
+                return false;
+              }
+
+              if (data.content[j].setting[k].contentType == 4 && (data.content[j].setting[k].miniprogramTitle == null || data.content[j].setting[k].miniprogramTitle == "")) {
+                this.$message.error("小程序消息标题不能为空")
+                return false;
+              }
+              if (data.content[j].setting[k].contentType == 4 && data.content[j].isOfficial !== '1' && (data.content[j].setting[k].miniprogramPicUrl == null || data.content[j].setting[k].miniprogramPicUrl == "")) {
+                this.$message.error("小程序封面地址不能为空")
+                return false;
+              }
+
+              if (data.content[j].setting[k].contentType == 5 && (data.content[j].setting[k].fileUrl == null || data.content[j].setting[k].fileUrl == "")) {
+                this.$message.error("文件不能为空")
+                return false;
+              }
+              if (data.content[j].setting[k].contentType == 6 && (data.content[j].setting[k].videoUrl == null || data.content[j].setting[k].videoUrl == "")) {
+                this.$message.error("视频不能为空")
+                return false;
+              }
+              if (data.content[j].setting[k].contentType == 7 && (data.content[j].setting[k].value == null || data.content[j].setting[k].value == "")) {
+                this.$message.error("语音文本不能为空")
+                return false;
+              }
+              if (data.content[j].setting[k].contentType == 8 && (data.content[j].setting[k].url == null || data.content[j].setting[k].url == "")) {
+                this.$message.error("视频号信息不能为空")
+                return false;
+              }
+            }
+          } else if (data.content[j].type == 4) {
+            if (data.content[j].aiTouch == null || data.content[j].aiTouch == '') {
+              this.$message.error("AI触达不能为空")
+              return false;
+            }
+          }
+        }
+      } else {
+        for (let day of this.setting) {
+          for (let content of day.content) {
+            for (let set of content.setting) {
+              if (!set.intervalTime) {
+                this.$message.error("客服数量不能为空");
+                return false;
+              }
+              if (!set.value) {
+                this.$message.error("内容不能为空");
+                return false;
+              }
+            }
+          }
+        }
+      }
+
+      return true;
+    },
+    tabClick(obj) {
+
+    },
+    addSetList(contentIndex, content) {
+
+      if (this.form.sendType == 1) {
+        for (let i = 0; i < content.length; i++) {
+
+          if (content[i].setting.length > 9) {
+            return this.$message.error("因为企微接口限制,企微模板一条消息只能设置最多~9个内容!!")
+          } else {
+
+            // 动态生成新的设置项
+            const newSetting = content[i].setting.some(item => item.contentType === '1')
+              ? {contentType: '2', imgUrl: ''}  // 如果存在 contentType = '1',添加 contentType = '2'
+              : {contentType: '1', value: ''};  // 如果不存在 contentType = '1',添加 contentType = '1'
+
+            // 将新设置项添加到 content[i].setting 数组中
+            content[i].setting.push(newSetting);
+          }
+
+        }
+      } else {
+
+        const newSetting = {
+          contentType: '1',
+          value: '',
+        };
+        if (this.form.sendType == 4) {
+          newSetting.intervalTime = 5;
+        }
+        // 将新设置项添加到 content.setting 数组中
+        content[contentIndex].setting.push(newSetting);
+      }
+
+
+    },
+    delSetList(index, contentIndex, setIndex) {
+      this.setting[index].content[contentIndex].setting.splice(setIndex, 1)
+
+    },
+
+    addContent(index) {
+      if (this.setting[index].content.length > 0 && this.form.sendType == 1) {
+        return this.$message.error("因为企微接口限制,企微模板一天只能设置一条消息~")
+      } else {
+        this.setting[index].content.push({type: this.defaultContentType, contentType: 1, setting: [{contentType: '1', value: "",}]})
+        this.videoList.push([])
+        this.addTag.push({
+          addTag: [],
+          inputVisible: false,
+          inputValue: '',
+          delTag: [],
+          delTagVisible: false,
+          delTagValue: ''
+        })
+      }
+
+    },
+    delContent(index, contentIndex) {
+      this.setting[index].content.splice(contentIndex, 1)
+      this.videoList.splice(contentIndex, 1)
+      this.addTag.splice(contentIndex, 1)
+    },
+    addSetting() {
+      let content = [{type: this.defaultContentType, contentType: '1', setting: [{contentType: '1', value: "",}]}];
+      if (this.form.sendType == 4) {
+        content[0].setting[0].intervalTime = 5;
+      }
+      this.setting.push({
+        name: null,
+        dayNum: (this.form.gap * this.setting.length) + 1,
+        content
+      })
+
+    },
+    delSetting(index) {
+      const newData = this.setting[index]
+      if (newData.id && newData.dayNum) {
+        this.$confirm('此操作将永久删除, 是否继续?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(() => {
+          delRules(newData.id, newData.name, newData.dayNum).then(res => {
+            this.del(index);
+          });
+        });
+      } else {
+        this.del(index);
+      }
+
+    },
+    del(index) {
+      if (index == 0) {
+        this.tabIndex = "0";
+      } else {
+        this.tabIndex = (index - 1) + "";
+      }
+      this.setting.splice(index, 1)
+
+    },
+    handleClosegroupUser(list) {
+      const index = this.userSelectList.findIndex(t => t === list);
+      if (index !== -1) {
+        this.userSelectList.splice(index, 1);
+      }
+
+    },
+    //选择视频号
+    hanldeSelectVideoNum(content, index, contentIndex, setIndex) {
+      this.videoNumOptions.content = content;
+      this.videoNumOptions.contentIndex = contentIndex;
+      this.videoNumOptions.setIndex = setIndex;
+      this.videoNumOptions.open = true;
+
+    },
+
+    qwUserVideoResult(val) {
+
+      // 根据选中的内容,将返回的数据更新到相应的表单项
+      const content = this.videoNumOptions.content;
+      const setList = content.setting[this.videoNumOptions.setIndex];
+
+      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;
+
+      this.videoNumOptions.open = false;
+
+    },
+    //首次刷新
+    courseChange(content, index, countIndex) {
+
+      this.courseUpdate(content, index, countIndex);
+    },
+
+    //修改
+    courseChangeUpdate(content, index, countIndex) {
+
+      this.$set(content, 'videoId', null);
+      // 查找选中的课程对应的 label
+      const selectedCourse = this.courseList.find(course => parseInt(course.dictValue) === content.courseId);
+      for (let i = 0; i < content.setting.length; i++) {
+
+
+        this.$set(content.setting[i], 'linkTitle', null);
+        this.$set(content.setting[i], 'linkDescribe', null);
+        this.$set(content.setting[i], 'linkImageUrl', null);
+
+
+        //如果是链接的才上
+        if (selectedCourse && content.type == 2 && content.courseId != null) {
+          //响应式直接给链接的标题/封面上值
+
+          if (content.setting[i].contentType == 3 || content.setting[i].contentType == 9) {
+            this.$set(content.setting[i], 'linkTitle', selectedCourse.dictLabel);
+            this.$set(content.setting[i], 'linkImageUrl', selectedCourse.dictImgUrl);
+          }
+          if (content.setting[i].contentType == 4) {
+            this.$set(content.setting[i], 'miniprogramPicUrl', selectedCourse.dictImgUrl);
+          }
+
+
+        }
+      }
+
+
+      this.courseUpdate(content, index, countIndex);
+    },
+    handleInputVideoText(value, content) {
+      // 允许的字符:中文、英文(大小写)、数字和指定标点符号(,。!?)
+      const regex = /^[\u4e00-\u9fa5,。!?,!?]+$/;
+
+      // 删除不符合条件的字符
+      const filteredValue = value.split('').filter(char => regex.test(char)).join('');
+
+      this.$set(content, 'value', filteredValue);
+
+    },
+    courseUpdate(content, index, countIndex) {
+      videoList(content.courseId).then(response => {
+        // this.$set(this.videoList, countIndex, response.list); // 响应式设置videoList
+        this.videoList.splice(countIndex, 1, response.list);
+
+      });
+
+    },
+    toggleSalesCall(itemIndex, contentIndex, setIndex) {
+      // 获取目标对象
+      const setItem = this.setting[itemIndex].content[contentIndex].setting[setIndex];
+      const salesCall = '#销售称呼#';
+      const refKey = `textarea-${itemIndex}-${contentIndex}-${setIndex}`;
+      const textarea = this.$refs[refKey][0]?.$refs?.textarea;
+
+      if (!textarea) return;
+
+      // 获取光标位置
+      const cursorPosition = textarea.selectionStart;
+
+      if (setItem.isSalesCallAdded) {
+        // 移除所有标签
+        setItem.value = setItem.value.replace(new RegExp(salesCall, 'g'), '');
+      } else {
+        // 插入到光标位置
+        setItem.value =
+          setItem.value.slice(0, cursorPosition) +
+          salesCall +
+          setItem.value.slice(cursorPosition);
+      }
+
+      // 切换状态
+      setItem.isSalesCallAdded = !setItem.isSalesCallAdded;
+
+      // 保持光标位置
+      this.$nextTick(() => {
+        const newCursorPos = cursorPosition + (setItem.isSalesCallAdded ? salesCall.length : 0);
+        textarea.setSelectionRange(newCursorPos, newCursorPos);
+      });
+    },
+    handleKeydown(event, itemIndex, contentIndex, setIndex) {
+
+      const setItem = this.setting[itemIndex].content[contentIndex].setting[setIndex];
+      const refKey = `textarea-${itemIndex}-${contentIndex}-${setIndex}`;
+      const textarea = this.$refs[refKey][0]?.$refs?.textarea;
+
+      if (!textarea) return;
+
+      const tags = ['#销售称呼#', '#客户称呼#'];
+      const key = event.key;
+      const value = setItem.value;
+      const cursorPosition = textarea.selectionStart;
+
+      if (key === 'Backspace' || key === 'Delete') {
+        tags.forEach(tag => {
+          let start, end;
+
+          // Backspace 处理
+          if (key === 'Backspace' && cursorPosition >= tag.length) {
+            start = cursorPosition - tag.length;
+            if (value.slice(start, cursorPosition) === tag) {
+              event.preventDefault();
+              setItem.value = value.slice(0, start) + value.slice(cursorPosition);
+              if (tag === '#销售称呼#') setItem.isSalesCallAdded = false;
+              this.$nextTick(() => textarea.setSelectionRange(start, start));
+            }
+          }
+
+          // Delete 处理
+          if (key === 'Delete') {
+            end = cursorPosition + tag.length;
+            if (value.slice(cursorPosition, end) === tag) {
+              event.preventDefault();
+              setItem.value = value.slice(0, cursorPosition) + value.slice(end);
+              if (tag === '#销售称呼#') setItem.isSalesCallAdded = false;
+            }
+          }
+        });
+      }
+    },
+    handleContentTypeChange(content, index, countIndex) {
+      //如果是链接的才上
+      if (content.courseId != null && content.type == 2) {
+        const selectedCourse = this.courseList.find(course => parseInt(course.dictValue) === content.courseId);
+        for (let i = 0; i < content.setting.length; i++) {
+          //响应式直接给链接的标题/封面上值
+          if (selectedCourse && content.type == 2 && content.courseId != null) {
+            if (content.setting[i].contentType == 3 || content.setting[i].contentType == 9) {
+              this.$set(content.setting[i], 'linkTitle', selectedCourse.dictLabel);
+              this.$set(content.setting[i], 'linkImageUrl', selectedCourse.dictImgUrl);
+            }
+            if (content.setting[i].contentType == 4 && (content.isOfficial == '0' || content.isOfficial == null)) {
+              console.log(content.isOfficial);
+              this.$set(content.setting[i], 'miniprogramPicUrl', selectedCourse.dictImgUrl);
+            }
+
+          }
+
+        }
+
+      }
+      if (content.videoId != null && content.type == 2) {
+        // 查找选中的课节对应的 label
+        const selectedVideo = this.videoList[countIndex].find(course => parseInt(course.dictValue) === content.videoId);
+
+        for (let i = 0; i < content.setting.length; i++) {
+          //响应式直接给链接的描述上值
+          if (selectedVideo && content.type == 2 && content.videoId != null) {
+
+            if (content.setting[i].contentType == 3 || content.setting[i].contentType == 9) {
+              this.$set(content.setting[i], 'linkDescribe', selectedVideo.dictLabel);
+            }
+            if (content.setting[i].contentType == 4) {
+              this.$set(content.setting[i], 'miniprogramTitle', this.truncateTextByByteLength(selectedVideo.dictLabel, 60));
+            }
+          }
+        }
+      }
+
+    },
+    videoIdChange(content, index, countIndex) {
+
+      //选择了课程小节则 默认绑上
+      let selectedVideo;
+      // 查找选中的课程对应的 label
+      if (content.videoId != null) {
+        selectedVideo = this.videoList[countIndex].find(course => parseInt(course.dictValue) === content.videoId);
+      }
+      for (let i = 0; i < content.setting.length; i++) {
+        //课程消息-文本内容
+        if (content.setting[i].contentType == 1 && content.type == 2) {
+          this.$set(content.setting[i], 'isBindUrl', '1');
+        }
+        //如果是链接的才上
+        if (selectedVideo && content.type == 2 && content.videoId != null) {
+
+          if (content.setting[i].contentType == 3 || content.setting[i].contentType == 9) {
+            this.$set(content.setting[i], 'linkDescribe', selectedVideo.dictLabel);
+          }
+          if (content.setting[i].contentType == 4) {
+            this.$set(content.setting[i], 'miniprogramTitle', this.truncateTextByByteLength(selectedVideo.dictLabel, 60));
+          }
+
+
+        }
+      }
+
+    },
+    //上传文件
+    handleAvatarSuccessFile(res, file, content) {
+      if (res.code === 200) {
+        // 使用 $set 确保响应式更新
+        this.$set(content, '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;
+    },
+
+    handleAvatarSuccessVideo(res, file, content) {
+      if (res.code == 200) {
+        // 使用 $set 确保响应式更新
+        this.$set(content, '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;
+    },
+
+    handleAvatarSuccessVoice(res, file, content) {
+      if (res.code == 200) {
+        // 创建 Audio 对象加载音频
+        const audio = new Audio(res.mp3Url);
+        audio.addEventListener('loadedmetadata', () => {
+          // 获取音频时长
+          this.$set(content, 'voiceDuration', Math.ceil(audio.duration));
+        });
+        // 使用 $set 确保响应式更新
+        this.$set(content, 'voiceUrl', res.silkUrl);
+        this.$set(content, 'mp3Url', res.mp3Url);
+      } else {
+        this.msgError(res.msg);
+      }
+      this.voiceLoading = false;
+
+    },
+    beforeAvatarUploadVoice(file) {
+      return new Promise((resolve, reject) => {
+        const isLt10M = file.size / 1024 / 1024 < 10; // 假设语音文件大小限制为10MB
+        const isVoiceType = ['audio/mp3', 'audio/mpeg', 'audio/wav', 'audio/x-wav'].includes(file.type);
+
+        if (!isVoiceType) {
+          this.$message.error('仅支持上传 MP3, WAV, X-WAV 格式的语音文件!');
+          return reject(false); // 不允许继续上传
+        }
+
+        if (!isLt10M) {
+          this.$message.error('上传大小不能超过 10MB!');
+          return reject(false); // 不允许继续上传
+        }
+
+        // 使用 FileReader 读取文件
+        const reader = new FileReader();
+        reader.onload = (event) => {
+          const audio = new Audio(event.target.result);
+          audio.addEventListener('loadedmetadata', () => {
+            // 获取时长并保存
+            if (Math.ceil(audio.duration) > 30) {
+              this.$message.error('音频时长不能超过30秒!');
+              this.voiceLoading = false;
+              return reject(false); // 不允许继续上传
+            }
+            resolve(true); // 允许上传
+          });
+        };
+
+        reader.onerror = () => {
+          this.$message.error('无法读取音频文件!请上传正确的音频文件');
+          reject(false); // 不允许继续上传
+        };
+
+        this.voiceLoading = true;
+        reader.readAsDataURL(file); // 开始读取文件
+      });
+
+    },
+
+    /** 查询sop模板列表 */
+    getList() {
+      this.loading = true;
+      listSopTemp(this.queryParams).then(response => {
+        this.sopTempList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+
+    },
+    // 取消按钮
+    cancel() {
+      this.$store.dispatch("tagsView/delView", this.$route);
+      this.$router.replace('/qw/conversion/sopTemp')
+      // this.reset();
+
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        name: null,
+        setting: null,
+        status: "0",
+        sort: null,
+        createTime: null,
+        createBy: null,
+        companyId: null
+      };
+      this.resetForm("form");
+
+    },
+
+
+    /** 修改按钮操作 */
+    handleUpdate(id) {
+      getSopTemp(id).then(response => {
+        this.videoList = []
+        this.form = response.data;
+        this.setting = this.form.list || [];
+
+        if (Array.isArray(this.setting)) {
+          this.setting.forEach(item => {
+            if (item && Array.isArray(item.content)) {
+              item.content.forEach(content => {
+                if (content && Array.isArray(content.setting)) {
+                  content.setting.forEach(setList => {
+                    if (setList && !Object.hasOwn(setList, 'isSalesCallAdded')) {
+                      this.$set(setList, 'isSalesCallAdded', false);
+                    }
+                  });
+                }
+              });
+            }
+          });
+        }
+        this.form.list.forEach(e => e.newDay = e.dayNum)
+        this.dayList = JSON.parse(JSON.stringify(this.form.list));
+        this.videoList.push([]);
+        this.addTag.push({
+          addTag: [],
+          inputVisible: false,
+          inputValue: '',
+          delTag: [],
+          delTagVisible: false,
+          delTagValue: ''
+        })
+      });
+
+    },
+    /** 提交按钮 */
+    submitForm() {
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除sop模板编号为"' + ids + '"的数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function () {
+        return delSopTemp(ids);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("删除成功");
+      }).catch(() => {
+      });
+
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有sop模板数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        this.exportLoading = true;
+        return exportSopTemp(queryParams);
+      }).then(response => {
+        this.download(response.msg);
+        this.exportLoading = false;
+      }).catch(() => {
+      });
+
+    },
+    updateHtml(val) {
+      val || val();
+
+    },
+    // updateChange(val) {
+    //   console.log(val)
+    // }
+  }
+};
+</script>

+ 906 - 0
src/views/qw/sopTemp/updateSopTemp2.vue

@@ -0,0 +1,906 @@
+<template>
+  <div class="app-container">
+
+    <div style="margin: 30px;" v-if="this.form.sendType == 1 && formType==1 "> sop规则【修改企微接口】模板</div>
+    <div style="margin: 30px;" v-if="this.form.sendType == 1 && formType==2 "> sop规则【复制企微接口】模板</div>
+    <div style="margin: 30px;" v-if="this.form.sendType == 1 && formType==3 "> sop规则【查看企微接口】模板</div>
+    <div style="margin: 30px;" v-if="this.form.sendType == 2 && formType==1 "> sop规则【修改AI插件】模板</div>
+    <div style="margin: 30px;" v-if="this.form.sendType == 2 && formType==2 "> sop规则【复制AI插件】模板</div>
+    <div style="margin: 30px;" v-if="this.form.sendType == 2 && formType==3 "> sop规则【查看AI插件】模板</div>
+
+    <div style="margin-top: 10px;margin-left: 50px;margin-right: 100px;margin-bottom: 60px;">
+      <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="dict.dictValue"
+            >{{dict.dictLabel}}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="间隔天数" prop="gap">
+          <el-input-number v-model="form.gap"  :min="1" label="间隔天数"></el-input-number>
+        </el-form-item>
+        <el-form-item label="排序" prop="sort">
+          <el-input-number v-model="form.sort"  :min="0" label="排序"></el-input-number>
+        </el-form-item>
+        <el-form-item label="规则" prop="setting">
+          <el-timeline >
+            <el-timeline-item :timestamp="'第'+(1+(form.gap*index))+'天'" :color="'#0bbd87'" placement="top" v-for="(item, index) in setting"  style="margin-top: 10px;">
+              <el-row>
+                <el-col :span="22">
+                  <div style="background-color: #fbfbfb;padding: 15px;  border: 1px solid #e6e6e6; margin-bottom: 20px;">
+                    <el-form :model="item"  label-width="80px">
+
+                      <el-form-item label="内容名称"  style="height: 50px;">
+                        <el-input v-model="item.name" placeholder="内容名称,仅内部可见" />
+                      </el-form-item>
+
+                      <el-form-item label="规则"  >
+                        <div v-for="(content, contentIndex) in item.content" style="background-color: #fdfdfd;padding: 15px;  border: 1px solid #e6e6e6; margin-bottom: 20px;">
+
+                          <el-row>
+
+                            <el-col el-col :span="22" >
+                              <el-form :model="content"  label-width="70px">
+                                <el-form-item label="时间" >
+                                  <el-time-picker
+                                    class="custom-input"
+                                    v-model="content.time"
+                                    value-format="HH:mm"
+                                    format="HH:mm"
+                                    :picker-options="{ selectableRange: '00:21:00 - 23:59:59' }"
+                                    placeholder="时间"
+                                    style="width: 100px;height: 20px;" >
+                                  </el-time-picker>
+                                </el-form-item>
+                                <el-form-item label="消息类别"  >
+                                  <el-radio-group v-model="content.type" @change="() => content.contentType = '1'" >
+                                    <el-radio
+                                      :label="1"
+                                    >普通</el-radio>
+                                    <el-radio
+                                      :label="2"
+                                    >课程</el-radio>
+                                    <el-radio
+                                      :label="3"
+                                    >订单</el-radio>
+                                    <el-radio
+                                      :label="4"
+                                    >AI触达</el-radio>
+                                  </el-radio-group>
+                                </el-form-item>
+
+                                <el-form-item label="课程"  v-if="content.type == 2 " required>
+                                  <el-select v-model="content.courseId" placeholder="请选择课程" style=" margin-right: 10px;" size="mini"  @change="courseChangeUpdate(content,index,contentIndex)">
+                                    <el-option
+                                      v-for="dict in courseList"
+                                      :key="dict.dictValue"
+                                      :label="dict.dictLabel"
+                                      :value="parseInt(dict.dictValue)"
+                                    />
+                                  </el-select>
+                                  <el-select v-model="content.videoId" placeholder="请选择小节" size="mini" style=" margin-right: 10px;"  @change="videoIdChange(content,index,contentIndex)">
+                                    <el-option
+                                      v-for="dict in videoList[index][contentIndex]"
+                                      :key="dict.dictValue"
+                                      :label="dict.dictLabel"
+                                      :value="parseInt(dict.dictValue)"
+                                    />
+                                  </el-select>
+                                  <el-select v-model="content.courseType" placeholder="请选择消息类型" size="mini" style=" margin-right: 10px;" v-if="content.type != 4 ">
+                                    <el-option
+                                      v-for="dict in sysFsSopWatchStatus"
+                                      :key="dict.dictValue"
+                                      :label="dict.dictLabel"
+                                      :value="parseInt(dict.dictValue)"
+                                    />
+                                  </el-select>
+
+                                </el-form-item>
+                                <el-form-item label="Ai触达"  v-if="content.type == 4 ">
+                                  <el-select  v-model="content.aiTouch" placeholder="请选择Ai触达类型" size="mini" style=" margin-right: 10px;" v-if="content.type == 4 ">
+                                    <el-option label="非首次交流" value="非首次交流"></el-option>
+                                    <el-option label="首次交流1" value="首次交流1"></el-option>
+                                    <el-option label="首次交流2" value="首次交流2"></el-option>
+                                    <el-option label="交流状态1" value="交流状态1"></el-option>
+                                    <el-option label="交流状态2" value="交流状态2"></el-option>
+                                    <el-option label="交流状态3" value="交流状态3"></el-option>
+                                  </el-select>
+                                </el-form-item>
+
+                                <div v-for="(setList, setIndex) in content.setting" :key="setIndex" style="background-color: #fdfdfd; border: 1px solid #e6e6e6; margin-bottom: 20px;" v-if="content.type != 4 ">
+                                  <el-row>
+                                    <el-col :span="22">
+                                      <el-form :model="setList" label-width="70px">
+                                        <el-form-item label="内容类别" style="margin: 2%">
+                                          <div v-if="form.sendType == 1 ">
+                                            <el-radio-group  v-model="setList.contentType" >
+                                              <!--                                              <el-radio  :label="item.dictValue" v-for="item in sysQwSopContentType">{{item.dictLabel}}</el-radio>-->
+                                              <el-radio
+                                                v-for="item in sysQwSopContentType"
+                                                :label="item.dictValue"
+                                                :disabled="item.dictValue === '1' && content.setting.some(s => s.contentType == '1')">
+                                                {{ item.dictLabel }}
+                                              </el-radio>
+                                            </el-radio-group>
+                                          </div>
+                                          <div v-if="form.sendType == 2 ">
+                                            <el-radio-group  v-model="setList.contentType" @change="handleContentTypeChange(content,index,contentIndex,setIndex)">
+                                              <el-radio   :label="item.dictValue" v-for="item in sysQwSopAiContentType">{{item.dictLabel}}</el-radio>
+                                            </el-radio-group>
+                                          </div>
+                                        </el-form-item>
+                                        <el-form-item label="内容"  >
+                                          <el-input v-if="setList.contentType == 1 " v-model="setList.value" type="textarea" :rows="3" placeholder="内容" style="width: 90%;margin-top: 10px;"/>
+                                            <ImageUpload v-if="setList.contentType == 2 " v-model="setList.imgUrl" type="image" :num="1" :width="150" :height="150" />
+                                          <div v-if="setList.contentType == 3 ">
+                                            <el-card class="box-card">
+                                              <el-form-item label="链接标题:"  label-width="100px" required >
+                                                <el-input v-model="setList.linkTitle" placeholder="请输入链接标题" style="width: 90%;"/>
+                                              </el-form-item>
+                                              <el-form-item label="链接描述:"   label-width="100px" required >
+                                                <el-input type="textarea" :rows="3" v-model="setList.linkDescribe" placeholder="请输入链接描述" style="width: 90%;margin-top: 1%;"/>
+                                              </el-form-item>
+                                              <el-form-item label="链接封面:"   label-width="100px" required>
+                                                <ImageUpload  v-model="setList.linkImageUrl" type="image" :num="1" :file-size="2" :width="150" :height="150" style="margin-top: 1%;" />
+                                              </el-form-item>
+                                              <div v-if="content.type != 2 " style="margin-top: 1%" >
+                                                <el-form-item label="链接地址:"  label-width="100px" required>
+                                                  <el-input v-model="setList.linkUrl" placeholder="请输入链接地址" style="width: 90%;"/>
+                                                </el-form-item>
+                                              </div>
+                                              <div v-if="content.type == 2 ">
+                                                <el-form-item label="链接地址:"  label-width="100px" >
+                                                  <el-tag type="warning" v-model="setList.isBindUrl = 1 ">选择的课程小节 即为卡片链接地址</el-tag>
+                                                </el-form-item>
+                                              </div>
+                                            </el-card>
+                                          </div>
+                                          <div v-if="setList.contentType == 5 ">
+
+                                            <el-form-item label="上传文件:" prop="fileUrl" label-width="100px">
+                                              <el-upload
+                                                v-model="setList.fileUrl"
+                                                class="avatar-uploader"
+                                                :action="uploadUrl"
+                                                :show-file-list="false"
+                                                :on-success="(res, file) => handleAvatarSuccessFile(res, file, setList)"
+                                                :before-upload="beforeAvatarUploadFile">
+                                                <i class="el-icon-plus avatar-uploader-icon"></i>
+                                              </el-upload>
+                                              <el-link v-if="setList.fileUrl" type="primary" :href="downloadUrl(setList.fileUrl)" download>
+                                                {{setList.fileUrl}}
+                                              </el-link>
+                                            </el-form-item>
+
+                                          </div>
+
+                                          <div v-if="setList.contentType == 6 ">
+                                            <el-form-item label="上传视频:" prop="videoUrl" label-width="100px">
+                                              <el-upload
+                                                v-model="setList.videoUrl"
+                                                class="avatar-uploader"
+                                                :action="uploadUrl"
+                                                :show-file-list="false"
+                                                :on-success="(res, file) => handleAvatarSuccessVideo(res, file, setList)"
+                                                :before-upload="beforeAvatarUploadVideo">
+                                                <i class="el-icon-plus avatar-uploader-icon"></i>
+                                              </el-upload>
+                                              <video v-if="setList.videoUrl"
+                                                     :src="setList.videoUrl"
+                                                     controls style="width: 200px;height: 100px">
+                                              </video>
+                                            </el-form-item>
+                                          </div>
+                                          <div v-if="setList.contentType == 7 ">
+                                              <el-input
+                                                v-model="setList.value"
+                                                type="textarea" :rows="3" maxlength="66" show-word-limit
+                                                placeholder="输入要转为语音的内容" style="width: 90%;margin-top: 10px;"
+                                                @input="handleInputVideoText(setList.value,setList)"/>
+
+
+                                          </div>
+                                          <div v-if="setList.contentType == 8 ">
+                                            <el-button type="primary" style="margin-bottom: 1%" @click="hanldeSelectVideoNum(content,index,contentIndex,setIndex)">选择视频号</el-button>
+                                            <el-card class="box-card" v-if="setList.coverUrl">
+                                              <el-form-item label="封面标题:"  label-width="100px">
+                                                <el-input v-model="setList.nickname" style="width: 90%;margin-bottom: 1%" disabled/>
+                                              </el-form-item>
+                                              <el-form-item label="头像:"   label-width="100px" >
+                                                <el-image
+                                                  v-if="setList.avatar != null"
+                                                  :src="setList.avatar"
+                                                  :preview-src-list="[setList.avatar]"
+                                                  :style="{ width: '50px', height: '50px' }"
+                                                ></el-image>
+                                              </el-form-item>
+                                              <el-form-item label="封面:"   label-width="100px" >
+                                                <el-image
+                                                  v-if="setList.coverUrl != null"
+                                                  :src="setList.coverUrl"
+                                                  :preview-src-list="[setList.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="setList.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="setList.url" style="width: 90%;" disabled />
+                                              </el-form-item >
+
+                                            </el-card>
+                                          </div>
+                                        </el-form-item>
+                                        <el-form-item label="添加短链" v-if="content.type == 2 && setList.contentType == 1  "  >
+                                          <el-tooltip content="请先根据课程选定课程小节之后再添加" effect="dark" :disabled="!!content.videoId">
+                                            <el-switch
+                                              v-model="setList.isBindUrl"
+                                              :disabled="!content.videoId"
+                                              active-color="#13ce66"
+                                              inactive-color="#DCDFE6"
+                                              active-value="1"
+                                              inactive-value="2">
+                                            </el-switch>
+                                          </el-tooltip>
+
+                                          <span v-if="setList.isBindUrl == '1'" style="margin-left: 10px; color: #13ce66">添加URL</span>
+                                          <span v-if="setList.isBindUrl == '2'" style="margin-left: 10px; color: #b1b4ba">不加URL</span>
+                                        </el-form-item>
+                                        <el-form-item label="课节过期时间" v-if="content.type == 2 && setList.isBindUrl == '1' && setList.contentType != 2  && setList.contentType != 5  && setList.contentType != 6 && setList.contentType != 8 ">
+                                          <el-row>
+                                            <el-input-number  v-model="setList.expiresDays"  :min="0" :max="100" ></el-input-number>
+                                            (天)
+                                          </el-row>
+                                          <el-row>
+                                            <span class="tip">填写0或不填时,默认为系统配置的默认时间</span>
+                                          </el-row>
+                                        </el-form-item>
+
+                                      </el-form>
+                                    </el-col>
+                                    <el-col :span="1" :offset="1">
+                                      <i class="el-icon-delete" @click="delSetList(index,contentIndex,setIndex)" style="margin-top: 20px;" v-if="content.setting.length>1 && (formType==1 || formType==2)"></i>
+                                    </el-col>
+                                  </el-row>
+                                </div>
+                                <el-link type="primary" class="el-icon-plus" :underline="false" @click='addSetList(contentIndex,item.content)' v-if="content.type != 4 && (formType==1 || formType==2)">添加内容</el-link>
+                              </el-form>
+
+
+                            </el-col>
+                            <el-col :span="1" :offset="1">
+                              <i class="el-icon-delete" @click="delContent(index,contentIndex)" style="margin-top: 20px;" v-if="item.content.length>1 && (formType==1 || formType==2)"></i>
+                            </el-col>
+                          </el-row>
+                        </div>
+                        <el-link type="primary" class="el-icon-plus" :underline="false" @click='addContent(index)' v-if="formType==1 || formType==2 ">添加规则</el-link>
+                      </el-form-item>
+                    </el-form>
+                  </div>
+                </el-col>
+                <el-col :span="1" :offset="1">
+                  <i class="el-icon-delete-solid" @click="delSetting(index)"  v-if="setting.length>1 && (formType==1 || formType==2)"></i>
+                </el-col>
+              </el-row>
+            </el-timeline-item>
+
+
+          </el-timeline>
+          <el-link type="primary" class="el-icon-plus" :underline="false" @click='addSetting()' v-if="formType==1 || formType==2">添加天数</el-link>
+        </el-form-item>
+
+      </el-form>
+      <div slot="footer" class="dialog-footer" style="float: right;" v-if="formType==1 || formType==2 ">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </div>
+
+    <el-dialog :title="videoNumOptions.title" :visible.sync="videoNumOptions.open" style="width: 1500px;height: 100%" append-to-body>
+      <userVideo ref="QwUserVideo" @videoResult="qwUserVideoResult" ></userVideo>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listSopTemp, getSopTemp, delSopTemp, addSopTemp, updateSopTemp, exportSopTemp } from "@/api/qw/sopTemp";
+import { courseList,videoList } from "@/api/qw/sop";
+import ImageUpload from "@/views/qw/sop/ImageUpload";
+import userVideo from "@/views/qw/userVideo/userVideo.vue";
+export default {
+  name: "updateSopTemp",
+  components: { ImageUpload,userVideo},
+  data() {
+    return {
+      uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS2",
+      uploadUrlByVoice:process.env.VUE_APP_BASE_API+"/common/uploadOSSByHOOKVoice",
+      //上传语音的遮罩层
+      voiceLoading :false,
+
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      sysFsSopWatchStatus: [],
+      //消息内容类型 企微版
+      sysQwSopContentType:[],
+      //插件版
+      sysQwSopAiContentType:[],
+
+      //类别
+      sysQwSopSettingType:[],
+
+      courseList:[],
+      videoList:[],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // sop模板表格数据
+      setting: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 状态字典
+      statusOptions: [],
+      videoNumOptions:{
+        title:'选择视频号',
+        open:false,
+        content:null,
+        contentIndex:null,
+        setIndex:null,
+      },
+      //1 修改 2 复制 3 查看
+      formType:null,
+      // 查询参数
+      form: {
+        name: null,
+        setting: null,
+        status: "1",
+        sort: 1,
+        companyId: null,
+        gap:1,
+      },
+      // 表单校验
+      rules: {
+        name: [
+          { required: true, message: '名称不能为空', trigger: 'blur' }
+        ],
+        status: [
+          { required: true, message: '状态不能为空', trigger: 'blur' }
+        ],
+        sort: [
+          { required: true, message: '排序不能为空', trigger: 'blur' }
+        ],
+        gap: [
+          { required: true, message: '间隔天数不能为空', trigger: 'blur' }
+        ],
+      }
+    };
+  },
+  created() {
+    this.getDicts("sys_qwSopAi_contentType").then(response => {
+      this.sysQwSopAiContentType = response.data;
+    });
+    this.getDicts("sys_fs_sop_watch_status").then(response => {
+      this.sysFsSopWatchStatus = response.data;
+    });
+
+    this.getDicts("sys_qwSop_contentType").then(response => {
+      this.sysQwSopContentType = response.data;
+    });
+
+    this.getDicts("sys_company_status").then(response => {
+      this.statusOptions = response.data;
+    });
+
+    this.getDicts("sys_qwSop_settingType").then(response => {
+      this.sysQwSopSettingType = response.data;
+    });
+
+    courseList().then(response => {
+      this.courseList = response.list;
+    });
+
+    const id = this.$route.params && this.$route.params.id;
+    this.formType = this.$route.params && this.$route.params.type;
+    this.handleUpdate(id);
+  },
+  methods: {
+    addSetList(contentIndex,content){
+
+      if (this.form.sendType==1){
+        for (let i = 0; i < content.length; i++) {
+
+          if (content[i].setting.length > 9) {
+            return this.$message.error("因为企微接口限制,企微模板一条消息只能设置最多~9个内容!!")
+          } else {
+
+            // 动态生成新的设置项
+            const newSetting = content[i].setting.some(item => item.contentType === '1')
+              ? {contentType: '2', imgUrl: ''}  // 如果存在 contentType = '1',添加 contentType = '2'
+              : {contentType: '1', value: ''};  // 如果不存在 contentType = '1',添加 contentType = '1'
+
+            // 将新设置项添加到 content[i].setting 数组中
+            content[i].setting.push(newSetting);
+          }
+
+        }
+      } else {
+
+        const newSetting = {
+          contentType: '1',
+          value: '',
+        };
+
+        // 将新设置项添加到 content.setting 数组中
+        content[contentIndex].setting.push(newSetting);
+
+      }
+
+    },
+    delSetList(index, contentIndex, setIndex) {
+      this.setting[index].content[contentIndex].setting.splice(setIndex, 1)
+    },
+
+    addContent(index) {
+
+      if (this.setting[index].content.length > 0 && this.form.sendType == 1) {
+        return this.$message.error("因为企微接口限制,企微模板一天只能设置一条消息~")
+      } else {
+        this.setting[index].content.push({type: 1, contentType: 1, setting: [{contentType: '1', value: "",}]})
+        this.videoList[index].push([])
+      }
+
+    },
+    delContent(index, contentIndex) {
+      this.setting[index].content.splice(contentIndex, 1)
+      this.videoList[index].splice(contentIndex, 1)
+    },
+    addSetting() {
+      this.setting.push({name: null, content: [{type: 1, contentType: '1', setting: [{contentType: '1', value: "",}]}]})
+      this.videoList.push([])
+
+    },
+    delSetting(index) {
+      this.setting.splice(index, 1)
+      this.videoList.splice(index, 1)
+    },
+    handleClosegroupUser(list) {
+      const index = this.userSelectList.findIndex(t => t === list);
+      if (index !== -1) {
+        this.userSelectList.splice(index, 1);
+      }
+    },
+    //选择视频号
+    hanldeSelectVideoNum(content,index,contentIndex,setIndex){
+      this.videoNumOptions.content = content;
+      this.videoNumOptions.contentIndex = contentIndex;
+      this.videoNumOptions.setIndex = setIndex;
+      this.videoNumOptions.open=true;
+    },
+
+    qwUserVideoResult(val){
+
+      // 根据选中的内容,将返回的数据更新到相应的表单项
+      const content = this.videoNumOptions.content;
+      const setList = content.setting[this.videoNumOptions.setIndex];
+
+      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;
+
+      this.videoNumOptions.open=false;
+    },
+    //首次刷新
+    courseChange(content, index, countIndex) {
+
+      this.courseUpdate(content, index, countIndex);
+    },
+
+    //修改
+    courseChangeUpdate(content, index, countIndex) {
+      this.$set(content, 'videoId', null);
+      // 查找选中的课程对应的 label
+      const selectedCourse = this.courseList.find(course => parseInt(course.dictValue) === content.courseId);
+
+      for (let i = 0; i < content.setting; i++) {
+        this.$set(content.setting[i], 'linkTitle', null);
+        this.$set(content.setting[i], 'linkDescribe', null);
+        this.$set(content.setting[i], 'linkImageUrl', null);
+
+        //如果是链接的才上
+        if (content.setting[i].contentType == 3 && content.type == 2 && content.courseId != null) {
+
+          //响应式直接给链接的标题/封面上值
+          if (selectedCourse) {
+            this.$set(content.setting[i], 'linkTitle', selectedCourse.dictLabel);
+            this.$set(content.setting[i], 'linkImageUrl', selectedCourse.dictImgUrl);
+          }
+        }
+      }
+
+
+      this.courseUpdate(content, index, countIndex);
+    },
+    handleInputVideoText(value,content){
+      // 允许的字符:中文、英文(大小写)、数字和指定标点符号(,。!?)
+      const regex = /^[\u4e00-\u9fa5,。!?,!?]+$/;
+
+      // 删除不符合条件的字符
+      const filteredValue = value.split('').filter(char => regex.test(char)).join('');
+
+      this.$set(content, 'value', filteredValue);
+
+    },
+    courseUpdate(content, index, countIndex) {
+      videoList(content.courseId).then(response => {
+
+        this.videoList[index].splice(countIndex, 1, response.list);
+
+      });
+    },
+    handleContentTypeChange(content, index, countIndex) {
+
+      //如果是链接的才上
+      if (content.courseId != null && content.type == 2) {
+        const selectedCourse = this.courseList.find(course => parseInt(course.dictValue) === content.courseId);
+        for (let i = 0; i < content.setting.length; i++) {
+          //响应式直接给链接的标题/封面上值
+          if (selectedCourse && content.setting[i].contentType == 3 && content.type == 2 && content.courseId != null) {
+            this.$set(content.setting[i], 'linkTitle', selectedCourse.dictLabel);
+            this.$set(content.setting[i], 'linkImageUrl', selectedCourse.dictImgUrl);
+          }
+
+        }
+
+      }
+      if (content.videoId != null && content.type == 2) {
+        // 查找选中的课节对应的 label
+        const selectedVideo = this.videoList[index][countIndex].find(course => parseInt(course.dictValue) === content.videoId);
+
+        for (let i = 0; i < content.setting.length; i++) {
+          //响应式直接给链接的描述上值
+          if (selectedVideo && content.setting[i].contentType == 3 && content.type == 2 && content.videoId != null) {
+            this.$set(content.setting[i], 'linkDescribe', selectedVideo.dictLabel);
+          }
+        }
+      }
+
+    },
+    videoIdChange(content, index, countIndex) {
+      //选择了课程小节则 默认绑上
+      let selectedVideo;
+      // 查找选中的课程对应的 label
+      if (content.videoId != null) {
+        selectedVideo = this.videoList[index][countIndex].find(course => parseInt(course.dictValue) === content.videoId);
+      }
+      for (let i = 0; i < content.setting.length; i++) {
+        //课程消息-文本内容
+        if (content.setting[i].contentType == 1 && content.type == 2) {
+          this.$set(content.setting[i], 'isBindUrl', '1');
+        }
+        //如果是链接的才上
+        if (content.setting[i].contentType == 3 && content.type == 2 && content.videoId != null) {
+
+          //响应式直接给链接的描述上值
+          if (selectedVideo) {
+            this.$set(content.setting[i], 'linkDescribe', selectedVideo.dictLabel);
+          }
+        }
+      }
+
+    },
+    //上传文件
+    handleAvatarSuccessFile(res, file, content) {
+      if (res.code === 200) {
+        // 使用 $set 确保响应式更新
+        this.$set(content, '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;
+    },
+
+    handleAvatarSuccessVideo(res, file, content) {
+      if (res.code == 200) {
+        // 使用 $set 确保响应式更新
+        this.$set(content, '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;
+    },
+
+    handleAvatarSuccessVoice(res, file, content) {
+      if (res.code == 200) {
+        // 创建 Audio 对象加载音频
+        const audio = new Audio(res.mp3Url);
+        audio.addEventListener('loadedmetadata', () => {
+          // 获取音频时长
+          this.$set(content, 'voiceDuration', Math.ceil(audio.duration));
+        });
+        // 使用 $set 确保响应式更新
+        this.$set(content, 'voiceUrl', res.silkUrl);
+        this.$set(content, 'mp3Url', res.mp3Url);
+      } else {
+        this.msgError(res.msg);
+      }
+      this.voiceLoading = false;
+    },
+    beforeAvatarUploadVoice(file) {
+      return new Promise((resolve, reject) => {
+        const isLt10M = file.size / 1024 / 1024 < 10; // 假设语音文件大小限制为10MB
+        const isVoiceType = ['audio/mp3', 'audio/mpeg', 'audio/wav', 'audio/x-wav'].includes(file.type);
+
+        if (!isVoiceType) {
+          this.$message.error('仅支持上传 MP3, WAV, X-WAV 格式的语音文件!');
+          return reject(false); // 不允许继续上传
+        }
+
+        if (!isLt10M) {
+          this.$message.error('上传大小不能超过 10MB!');
+          return reject(false); // 不允许继续上传
+        }
+
+        // 使用 FileReader 读取文件
+        const reader = new FileReader();
+        reader.onload = (event) => {
+          const audio = new Audio(event.target.result);
+          audio.addEventListener('loadedmetadata', () => {
+            // 获取时长并保存
+            if (Math.ceil(audio.duration) > 30) {
+              this.$message.error('音频时长不能超过30秒!');
+              this.voiceLoading = false;
+              return reject(false); // 不允许继续上传
+            }
+            resolve(true); // 允许上传
+          });
+        };
+
+        reader.onerror = () => {
+          this.$message.error('无法读取音频文件!请上传正确的音频文件');
+          reject(false); // 不允许继续上传
+        };
+
+        this.voiceLoading = true;
+        reader.readAsDataURL(file); // 开始读取文件
+      });
+    },
+
+    /** 查询sop模板列表 */
+    getList() {
+      this.loading = true;
+      listSopTemp(this.queryParams).then(response => {
+        this.sopTempList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.$store.dispatch("tagsView/delView", this.$route);
+      this.$router.replace('/qw/conversion/sopTemp')
+      // this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        name: null,
+        setting: null,
+        status: "0",
+        sort: null,
+        createTime: null,
+        createBy: null,
+        companyId: null
+      };
+      this.resetForm("form");
+    },
+
+
+    /** 修改按钮操作 */
+    handleUpdate(id) {
+
+      getSopTemp(id).then(response => {
+        this.videoList = []
+        this.form = response.data;
+        this.setting = JSON.parse(response.data.setting);
+        for (let i = 0; i < this.setting.length; i++) {
+          this.videoList.push([])
+          for (let j = 0; j < this.setting[i].content.length; j++) {
+            this.videoList[i].push([])
+            if (this.setting[i].content[j].type == 2) {
+              if (this.setting[i].content[j].hasOwnProperty('courseId')) {
+                this.courseChange(this.setting[i].content[j], i, j);
+              }
+            }
+          }
+        }
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+
+        this.form.setting = JSON.stringify(this.setting)
+
+        if (this.setting.length <= 0) {
+          return this.$message("请添加规则")
+        }
+
+        for (let i = 0; i < this.setting.length; i++) {
+
+
+          for (let j = 0; j < this.setting[i].content.length; j++) {
+
+            if (this.setting[i].name == null || this.setting[i].name == "") {
+              return this.$message.error("内容名称不能为空")
+            }
+            if (this.setting[i].content[j].time == null || this.setting[i].content[j].time == "") {
+              return this.$message.error("时间不能为空")
+            }
+            if (this.setting[i].content[j].type != 4) {
+              for (let k = 0; k < this.setting[i].content[j].setting.length; k++) {
+                if (this.setting[i].content[j].setting[k].contentType == 1 && (this.setting[i].content[j].setting[k].value == null || this.setting[i].content[j].setting[k].value == "")) {
+                  return this.$message.error("内容不能为空")
+                }
+                if (this.setting[i].content[j].setting[k].contentType == 2 && (this.setting[i].content[j].setting[k].imgUrl == null || this.setting[i].content[j].setting[k].imgUrl == "")) {
+                  return this.$message.error("图片不能为空")
+                }
+                if (this.setting[i].content[j].setting[k].contentType == 3 && (this.setting[i].content[j].setting[k].linkTitle == null || this.setting[i].content[j].setting[k].linkTitle == "")) {
+                  return this.$message.error("链接标题不能为空")
+                }
+                if (this.setting[i].content[j].setting[k].contentType == 3 && (this.setting[i].content[j].setting[k].linkDescribe == null || this.setting[i].content[j].setting[k].linkDescribe == "")) {
+                  return this.$message.error("链接描述不能为空")
+                }
+                if (this.setting[i].content[j].setting[k].contentType == 3 && (this.setting[i].content[j].setting[k].linkImageUrl == null || this.setting[i].content[j].setting[k].linkImageUrl == "")) {
+                  return this.$message.error("链接图片不能为空")
+                }
+                if (this.setting[i].content[j].setting[k].contentType == 3 && this.setting[i].content[j].setting[k].type == 1 && (this.setting[i].content[j].setting[k].linkUrl == null || this.setting[i].content[j].setting[k].linkUrl == "")) {
+                  return this.$message.error("链接地址不能为空")
+                }
+                if (this.setting[i].content[j].setting[k].contentType == 5 && (this.setting[i].content[j].setting[k].fileUrl == null || this.setting[i].content[j].setting[k].fileUrl == "")) {
+                  return this.$message.error("文件不能为空")
+                }
+                if (this.setting[i].content[j].setting[k].contentType == 6 && (this.setting[i].content[j].setting[k].videoUrl == null || this.setting[i].content[j].setting[k].videoUrl == "")) {
+                  return this.$message.error("视频不能为空")
+                }
+                if (this.setting[i].content[j].setting[k].contentType == 7 && (this.setting[i].content[j].setting[k].value == null || this.setting[i].content[j].setting[k].value == "")) {
+                  return this.$message.error("语音文本不能为空")
+                }
+                if (this.setting[i].content[j].setting[k].contentType == 8 && (this.setting[i].content[j].setting[k].url == null || this.setting[i].content[j].setting[k].url == "")) {
+                  return this.$message.error("视频号信息不能为空")
+                }
+              }
+            } else {
+              if (this.setting[i].content[j].aiTouch == null || this.setting[i].content[j].aiTouch == '') {
+                return this.$message.error("AI触达不能为空")
+              }
+            }
+          }
+
+
+        }
+        if (valid) {
+          if (this.formType == 1) {
+            if (this.form.id != null) {
+              updateSopTemp(this.form).then(response => {
+                this.msgSuccess("修改成功");
+                this.$store.dispatch("tagsView/delView", this.$route);
+                // this.$router.replace('/qw/conversion/sopTemp')
+                window.location.replace('/qw/conversion/sopTemp')
+                this.reset();
+
+              });
+            }
+          } else {
+            //id制空,防止键值重复-报错
+            this.form.id = null
+            //更新时间制空
+            this.form.updateTime = null
+            addSopTemp(this.form).then(response => {
+              this.msgSuccess("复制成功");
+              this.$store.dispatch("tagsView/delView", this.$route);
+              // this.$router.replace('/qw/conversion/sopTemp')
+              window.location.replace('/qw/conversion/sopTemp')
+              this.reset();
+
+            });
+          }
+
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除sop模板编号为"' + ids + '"的数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function () {
+        return delSopTemp(ids);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("删除成功");
+      }).catch(() => {
+      });
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有sop模板数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        this.exportLoading = true;
+        return exportSopTemp(queryParams);
+      }).then(response => {
+        this.download(response.msg);
+        this.exportLoading = false;
+      }).catch(() => {
+      });
+    }
+  }
+};
+</script>
+
+<style scoped>
+.custom-input /deep/ .el-input__inner {
+  height: 20px;
+  text-align: center;
+}
+
+.custom-input /deep/ .el-input__icon {
+  line-height: 10px;
+}
+</style>

+ 664 - 0
src/views/qw/sopTemp/updateSopTempOld.vue

@@ -0,0 +1,664 @@
+<template>
+  <div class="app-container">
+
+    <div style="margin: 30px;" v-if="this.form.sendType == 1 "> sop规则【企微接口】模板</div>
+    <div style="margin: 30px;" v-if="this.form.sendType == 2 "> sop规则【AI插件】模板</div>
+
+    <div style="margin-top: 10px;margin-left: 50px;margin-right: 100px;margin-bottom: 60px;">
+      <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="dict.dictValue"
+            >{{dict.dictLabel}}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="间隔天数" prop="gap">
+          <el-input-number v-model="form.gap"  :min="1" label="间隔天数"></el-input-number>
+        </el-form-item>
+        <el-form-item label="排序" prop="sort">
+          <el-input-number v-model="form.sort"  :min="0" label="排序"></el-input-number>
+        </el-form-item>
+        <el-form-item label="规则" prop="setting">
+          <el-timeline >
+            <el-timeline-item :timestamp="'第'+(1+(form.gap*index))+'天'" :color="'#0bbd87'" placement="top" v-for="(item, index) in setting"  style="margin-top: 10px;">
+              <el-row>
+                <el-col :span="22">
+                  <div style="background-color: #fbfbfb;padding: 15px;  border: 1px solid #e6e6e6; margin-bottom: 20px;">
+                    <el-form :model="item"  label-width="80px">
+
+                      <el-form-item label="内容名称"  style="height: 50px;">
+                        <el-input v-model="item.name" placeholder="内容名称,仅内部可见" />
+                      </el-form-item>
+
+                      <el-form-item label="规则"  >
+                        <div v-for="(content, contentIndex) in item.content" style="background-color: #fdfdfd;padding: 15px;  border: 1px solid #e6e6e6; margin-bottom: 20px;">
+
+                          <el-row>
+
+                            <el-col el-col :span="22" >
+                              <el-form :model="content"  label-width="70px">
+                                <el-form-item label="时间" >
+                                  <el-time-picker
+                                    class="custom-input"
+                                    v-model="content.time"
+                                    value-format="HH:mm"
+                                    format="HH:mm"
+                                    :picker-options="{ selectableRange: '00:00:00 - 23:59:59' }"
+                                    placeholder="时间"
+                                    style="width: 100px;height: 20px;" >
+                                  </el-time-picker>
+                                </el-form-item>
+                                <el-form-item label="消息类别"  >
+                                  <el-radio-group v-model="content.type" @change="() => content.contentType = '1'" >
+
+                                    <el-radio
+                                      :label="1"
+                                    >普通</el-radio>
+                                    <el-radio
+                                      :label="2"
+                                    >课程</el-radio>
+                                    <el-radio
+                                      :label="3"
+                                    >订单</el-radio>
+
+                                  </el-radio-group>
+                                </el-form-item>
+
+                                <el-form-item label="课程"  v-if="content.type == 2 " required>
+                                  <el-select v-model="content.courseId" placeholder="请选择课程" style=" margin-right: 10px;" size="mini"  @change="courseChangeUpdate(content,index,contentIndex)">
+                                    <el-option
+                                      v-for="dict in courseList"
+                                      :key="dict.dictValue"
+                                      :label="dict.dictLabel"
+                                      :value="parseInt(dict.dictValue)"
+                                    />
+                                  </el-select>
+                                  <el-select v-model="content.videoId" placeholder="请选择小节" size="mini" style=" margin-right: 10px;"  @change="videoIdChange(content,index,contentIndex)">
+                                    <el-option
+                                      v-for="dict in videoList[index][contentIndex]"
+                                      :key="dict.dictValue"
+                                      :label="dict.dictLabel"
+                                      :value="parseInt(dict.dictValue)"
+                                    />
+                                  </el-select>
+                                  <el-select v-model="content.courseType" placeholder="请选择消息类型" size="mini" style=" margin-right: 10px;">
+                                    <el-option
+                                      v-for="dict in sysFsSopWatchStatus"
+                                      :key="dict.dictValue"
+                                      :label="dict.dictLabel"
+                                      :value="parseInt(dict.dictValue)"
+                                    />
+                                  </el-select>
+
+                                </el-form-item>
+                                <el-form-item label="内容类别"  >
+                                  <div v-if="form.sendType == 1 ">
+                                    <el-radio-group v-model="content.contentType">
+                                      <el-radio :label="item.dictValue" v-for="item in sysQwSopContentType">{{item.dictLabel}}</el-radio>
+                                    </el-radio-group>
+                                  </div>
+                                  <div v-if="form.sendType == 2 ">
+                                    <div v-if="content.type == 2 ">
+                                      <el-radio-group  v-model="content.contentType" @change="handleContentTypeChange(content,index,contentIndex)">
+                                        <el-radio  :label="item.dictValue" v-for="item in sysQwSopSettingType">{{item.dictLabel}}</el-radio>
+                                      </el-radio-group>
+                                    </div>
+                                    <div v-else>
+                                      <el-radio-group  v-model="content.contentType" @change="handleContentTypeChange(content,index,contentIndex)">
+                                        <el-radio   :label="item.dictValue" v-for="item in sysQwSopAiContentType">{{item.dictLabel}}</el-radio>
+                                      </el-radio-group>
+                                    </div>
+                                  </div>
+                                  <!--                                   <el-radio-group v-model="content.contentType">-->
+                                  <!--                                       <el-radio-->
+                                  <!--                                         :label="1"-->
+                                  <!--                                       >文字</el-radio>-->
+                                  <!--                                       <el-radio-->
+                                  <!--                                         :label="2"-->
+                                  <!--                                       >图片</el-radio>-->
+                                  <!--                                   </el-radio-group>-->
+                                </el-form-item>
+
+                                <el-form-item label="内容"  >
+                                  <el-input v-if="content.contentType == 1 " v-model="content.value" type="textarea" :rows="3" placeholder="内容" style="width: 90%;margin-top: 10px;"/>
+                                  <ImageUpload v-if="content.contentType == 2 " v-model="content.imgUrl" type="image" :num="1" :width="150" :height="150" />
+                                  <div v-if="content.contentType == 3 ">
+                                    <el-card class="box-card">
+                                      <el-form-item label="链接标题:"  label-width="100px" required >
+                                        <el-input v-model="content.linkTitle" placeholder="请输入链接标题" style="width: 90%;"/>
+                                      </el-form-item>
+                                      <el-form-item label="链接描述:"   label-width="100px" required >
+                                        <el-input type="textarea" :rows="3" v-model="content.linkDescribe" placeholder="请输入链接描述" style="width: 90%;margin-top: 1%;"/>
+                                      </el-form-item>
+                                      <el-form-item label="链接封面:"   label-width="100px" required>
+                                        <ImageUpload  v-model="content.linkImageUrl" type="image" :num="1" :width="150" :height="150" style="margin-top: 1%;" />
+                                      </el-form-item>
+                                      <div v-if="content.type != 2 " style="margin-top: 1%" >
+                                        <el-form-item label="链接地址:"  label-width="100px" required>
+                                          <el-input v-model="content.linkUrl" placeholder="请输入链接地址" style="width: 90%;"/>
+                                        </el-form-item>
+                                      </div>
+                                      <div v-if="content.type == 2 ">
+                                        <el-form-item label="链接地址:"  label-width="100px" >
+                                          <el-tag type="warning" v-model="content.isBindUrl = 1 ">选择的课程小节 即为卡片链接地址</el-tag>
+                                        </el-form-item>
+                                      </div>
+                                    </el-card>
+                                  </div>
+                                  <div v-if="content.contentType == 5 ">
+
+                                    <el-form-item label="上传文件:" prop="fileUrl" label-width="100px">
+                                      <el-upload
+                                        v-model="content.fileUrl"
+                                        class="avatar-uploader"
+                                        :action="uploadUrl"
+                                        :show-file-list="false"
+                                        :on-success="(res, file) => handleAvatarSuccessFile(res, file, content)"
+                                        :before-upload="beforeAvatarUploadFile">
+                                        <i class="el-icon-plus avatar-uploader-icon"></i>
+                                      </el-upload>
+                                      <el-link v-if="content.fileUrl" type="primary" :href="downloadUrl(content.fileUrl)" download>
+                                        {{content.fileUrl}}
+                                      </el-link>
+                                    </el-form-item>
+
+                                  </div>
+
+                                  <div v-if="content.contentType == 6 ">
+                                    <el-form-item label="上传视频:" prop="videoUrl" label-width="100px">
+                                      <el-upload
+                                        v-model="content.videoUrl"
+                                        class="avatar-uploader"
+                                        :action="uploadUrl"
+                                        :show-file-list="false"
+                                        :on-success="(res, file) => handleAvatarSuccessVideo(res, file, content)"
+                                        :before-upload="beforeAvatarUploadVideo">
+                                        <i class="el-icon-plus avatar-uploader-icon"></i>
+                                      </el-upload>
+                                      <video v-if="content.videoUrl"
+                                             :src="content.videoUrl"
+                                             controls style="width: 200px;height: 100px">
+                                      </video>
+                                    </el-form-item>
+                                  </div>
+                                  <div v-if="content.contentType == 7 ">
+                                    <el-form-item label="上传语音:" prop="videoUrl" label-width="100px">
+                                      <el-upload
+                                        v-model="content.voiceUrl"
+                                        class="avatar-uploader"
+                                        :action="uploadUrl"
+                                        :show-file-list="false"
+                                        :on-success="(res, file) => handleAvatarSuccessVoice(res, file, content)"
+                                        :before-upload="beforeAvatarUploadVoice">
+                                        <i class="el-icon-plus avatar-uploader-icon"></i>
+                                      </el-upload>
+                                      <audio  v-if="content.voiceUrl"
+                                              :src="content.voiceUrl"
+                                              controls style="width: 300px;height: 50px">
+                                      </audio >
+                                    </el-form-item>
+                                  </div>
+                                </el-form-item>
+                                <el-form-item label="添加短链" v-if="content.type == 2 && content.contentType == 1  "  >
+                                  <el-tooltip content="请先根据课程选定课程小节之后再添加" effect="dark" :disabled="!!content.videoId">
+                                    <el-switch
+                                      v-model="content.isBindUrl"
+                                      :disabled="!content.videoId"
+                                      active-color="#13ce66"
+                                      inactive-color="#DCDFE6"
+                                      active-value="1"
+                                      inactive-value="2">
+                                    </el-switch>
+                                  </el-tooltip>
+
+                                  <span v-if="content.isBindUrl == '1'" style="margin-left: 10px; color: #13ce66">添加URL</span>
+                                  <span v-if="content.isBindUrl == '2'" style="margin-left: 10px; color: #b1b4ba">不加URL</span>
+                                </el-form-item>
+                                <el-form-item label="课节过期时间" v-if="content.type == 2 && content.isBindUrl == '1' && content.contentType != 2  && content.contentType != 5  && content.contentType != 6 ">
+                                  <el-row>
+                                    <el-input-number  v-model="content.expiresDays"  :min="0" :max="100" ></el-input-number>
+                                    (天)
+                                  </el-row>
+                                  <el-row>
+                                    <span class="tip">填写0或不填时,默认为系统配置的默认时间</span>
+                                  </el-row>
+                                </el-form-item>
+                              </el-form>
+
+
+
+
+                            </el-col>
+                            <el-col :span="1" :offset="1">
+                              <i class="el-icon-delete" @click="delContent(index,contentIndex)" style="margin-top: 20px;" v-if="item.content.length>1"></i>
+                            </el-col>
+                          </el-row>
+                        </div>
+                        <el-link type="primary" class="el-icon-plus" :underline="false" @click='addContent(index)'>添加规则</el-link>
+                      </el-form-item>
+                    </el-form>
+                  </div>
+                </el-col>
+                <el-col :span="1" :offset="1">
+                  <i class="el-icon-delete-solid" @click="delSetting(index)"  v-if="setting.length>1"></i>
+                </el-col>
+              </el-row>
+            </el-timeline-item>
+
+
+          </el-timeline>
+          <el-link type="primary" class="el-icon-plus" :underline="false" @click='addSetting()'>添加天数</el-link>
+        </el-form-item>
+
+      </el-form>
+      <div slot="footer" class="dialog-footer" style="float: right;">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { listSopTemp, getSopTemp, delSopTemp, addSopTemp, updateSopTemp, exportSopTemp } from "@/api/qw/sopTemp";
+import { courseList,videoList } from "@/api/qw/sop";
+import ImageUpload from "@/views/qw/sop/ImageUpload";
+export default {
+  name: "SopTemp",
+  components: { ImageUpload},
+  data() {
+    return {
+      uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      sysFsSopWatchStatus: [],
+      //消息内容类型 企微版
+      sysQwSopContentType:[],
+      //插件版
+      sysQwSopAiContentType:[],
+
+      //类别
+      sysQwSopSettingType:[],
+
+      courseList:[],
+      videoList:[],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // sop模板表格数据
+      setting: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 状态字典
+      statusOptions: [],
+      // 查询参数
+      form: {
+        name: null,
+        setting: null,
+        status: "1",
+        sort: 1,
+        companyId: null,
+        gap:1,
+      },
+      // 表单校验
+      rules: {
+        name: [
+          { required: true, message: '名称不能为空', trigger: 'blur' }
+        ],
+        status: [
+          { required: true, message: '状态不能为空', trigger: 'blur' }
+        ],
+        sort: [
+          { required: true, message: '排序不能为空', trigger: 'blur' }
+        ],
+        gap: [
+          { required: true, message: '间隔天数不能为空', trigger: 'blur' }
+        ],
+      }
+    };
+  },
+  created() {
+    this.getDicts("sys_qwSopAi_contentType").then(response => {
+      this.sysQwSopAiContentType = response.data;
+    });
+    this.getDicts("sys_fs_sop_watch_status").then(response => {
+      this.sysFsSopWatchStatus = response.data;
+    });
+
+    this.getDicts("sys_qwSop_contentType").then(response => {
+      this.sysQwSopContentType = response.data;
+    });
+
+    this.getDicts("sys_company_status").then(response => {
+      this.statusOptions = response.data;
+    });
+
+    this.getDicts("sys_qwSop_settingType").then(response => {
+      this.sysQwSopSettingType = response.data;
+    });
+
+    courseList().then(response => {
+      this.courseList = response.list;
+    });
+
+    const id = this.$route.params && this.$route.params.id;
+    this.handleUpdate(id);
+
+
+
+  },
+  methods: {
+    addContent(index){
+
+      if (this.setting[index].content.length>0 && this.form.sendType==1){
+        return this.$message.error("因为企微接口限制,企微模板一天只能设置一条消息~")
+      }else {
+        this.setting[index].content.push({type:1,contentType:1})
+        this.videoList[index].push([])
+      }
+
+    },
+    delContent(index,contentIndex){
+      this.setting[index].content.splice(contentIndex,1)
+      this.videoList[index].splice(contentIndex,1)
+    },
+    addSetting(){
+      this.setting.push({name:null,type:1,content:[],day:"0",hour:"0",minute:"0",time:""})
+      this.videoList.push([])
+
+    },
+    delSetting(index){
+      this.setting.splice(index,1)
+      this.videoList.splice(index,1)
+    },
+    handleClosegroupUser(list){
+      const index = this.userSelectList.findIndex(t => t === list);
+      if (index !== -1) {
+        this.userSelectList.splice(index, 1);
+      }
+    },
+
+    //首次刷新
+    courseChange(content,index,countIndex){
+
+      this.courseUpdate(content,index,countIndex);
+    },
+
+    //修改
+    courseChangeUpdate(content,index,countIndex){
+      this.$set(content, 'videoId', null);
+      this.$set(content, 'linkTitle', null);
+      this.$set(content, 'linkDescribe', null);
+      this.$set(content, 'linkImageUrl', null);
+      //如果是链接的才上
+      if (content.contentType==3 && content.type==2 && content.courseId!=null){
+
+        // 查找选中的课程对应的 label
+        const selectedCourse = this.courseList.find(course => parseInt(course.dictValue) === content.courseId);
+
+        //响应式直接给链接的标题/封面上值
+        if (selectedCourse) {
+          this.$set(content, 'linkTitle', selectedCourse.dictLabel);
+          this.$set(content, 'linkImageUrl', selectedCourse.dictImgUrl);
+        }
+      }
+
+      this.courseUpdate(content,index,countIndex);
+    },
+
+    courseUpdate(content,index,countIndex){
+      videoList(content.courseId).then(response => {
+
+        this.videoList[index].splice(countIndex, 1, response.list);
+
+      });
+    },
+    handleContentTypeChange(content,index,countIndex){
+
+      //如果是链接的才上
+      if (content.contentType==3 && content.type==2 ) {
+
+        if (content.courseId!=null) {
+          // 查找选中的课程对应的 label
+          const selectedCourse = this.courseList.find(course => parseInt(course.dictValue) === content.courseId);
+
+          //响应式直接给链接的标题/封面上值
+          if (selectedCourse) {
+            this.$set(content, 'linkTitle', selectedCourse.dictLabel);
+            this.$set(content, 'linkImageUrl', selectedCourse.dictImgUrl);
+          }
+        }
+        if (content.videoId!=null){
+          // 查找选中的课程对应的 label
+          const selectedVideo = this.videoList[index][countIndex].find(course => parseInt(course.dictValue) === content.videoId);
+
+          //响应式直接给链接的描述上值
+          if (selectedVideo) {
+            this.$set(content, 'linkDescribe', selectedVideo.dictLabel);
+          }
+        }
+
+      }
+    },
+    videoIdChange(content,index,countIndex){
+
+      //选择了课程小节则 默认绑上
+      this.$set(content,'isBindUrl','1');
+
+      //如果是链接的才上
+      if (content.contentType==3 && content.type==2 && content.videoId!=null) {
+
+        // 查找选中的课程对应的 label
+        const selectedVideo = this.videoList[index][countIndex].find(course => parseInt(course.dictValue) === content.videoId);
+
+        //响应式直接给链接的描述上值
+        if (selectedVideo) {
+          this.$set(content, 'linkDescribe', selectedVideo.dictLabel);
+        }
+      }
+
+    },
+    //上传文件
+    handleAvatarSuccessFile(res, file, content) {
+      if (res.code === 200) {
+        // 使用 $set 确保响应式更新
+        this.$set(content, 'fileUrl', res.url);
+      } else {
+        this.msgError(res.msg);
+      }
+    },
+    beforeAvatarUploadFile(file){
+      const isLt1M = file.size / 1024 / 1024 < 30;
+      if (!isLt1M) {
+        this.$message.error('上传大小不能超过 30MB!');
+      }
+      return isLt1M;
+    },
+
+    //下载文件
+    downloadUrl(materialUrl) {
+      // 直接返回文件 URL
+      return materialUrl;
+    },
+
+    handleAvatarSuccessVideo(res, file, content) {
+      if(res.code==200){
+        // 使用 $set 确保响应式更新
+        this.$set(content, 'videoUrl', res.url);
+      }
+      else{
+        this.msgError(res.msg);
+      }
+    },
+    beforeAvatarUploadVideo(file){
+      const isLt30M = file.size / 1024 / 1024 < 50;
+      const isMP4 = file.type === 'video/mp4';
+
+      if (!isMP4) {
+        this.$message.error('仅支持上传 MP4 格式的视频文件!');
+        return false;
+      }
+
+      if (!isLt30M) {
+        this.$message.error('上传大小不能超过 50MB!');
+        return false;
+      }
+
+      return true;
+    },
+
+    handleAvatarSuccessVoice(res, file, content){
+      if(res.code==200){
+        // 使用 $set 确保响应式更新
+        this.$set(content, 'voiceUrl', res.url);
+      }
+      else{
+        this.msgError(res.msg);
+      }
+    },
+    beforeAvatarUploadVoice(file){
+      const isLt10M = file.size / 1024 / 1024 < 10; //语音文件大小限制为10MB
+      const isVoiceType = ['audio/mp3'].includes(file.type); // 支持的语音文件类型
+
+      if (!isVoiceType) {
+        this.$message.error('仅支持上传 MP3 格式的语音文件!');
+        return false;
+      }
+
+      if (!isLt10M) {
+        this.$message.error('上传大小不能超过 10MB!');
+        return false;
+      }
+
+      return true;
+    },
+    /** 查询sop模板列表 */
+    getList() {
+      this.loading = true;
+      listSopTemp(this.queryParams).then(response => {
+        this.sopTempList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.$store.dispatch("tagsView/delView", this.$route);
+      this.$router.replace('/qw/conversion/sopTemp')
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        name: null,
+        setting: null,
+        status: "0",
+        sort: null,
+        createTime: null,
+        createBy: null,
+        companyId: null
+      };
+      this.resetForm("form");
+    },
+
+
+
+    /** 修改按钮操作 */
+    handleUpdate(id) {
+
+      getSopTemp(id).then(response => {
+        this.videoList=[]
+        this.form=response.data;
+        this.setting = JSON.parse(response.data.setting);
+        for (let i = 0; i < this.setting.length; i++) {
+          this.videoList.push([])
+          for (let j = 0; j < this.setting[i].content.length; j++) {
+            this.videoList[i].push([])
+            if(this.setting[i].content[j].type==2){
+              if(this.setting[i].content[j].hasOwnProperty('courseId')){
+                this.courseChange(this.setting[i].content[j],i,j);
+              }
+            }
+          }
+        }
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        this.form.setting=JSON.stringify(this.setting)
+        if (valid) {
+          if (this.form.id != null) {
+            updateSopTemp(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.$store.dispatch("tagsView/delView", this.$route);
+              // this.$router.replace('/qw/conversion/sopTemp')
+              window.location.replace('/qw/conversion/sopTemp')
+              this.reset();
+
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除sop模板编号为"' + ids + '"的数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function() {
+        return delSopTemp(ids);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("删除成功");
+      }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有sop模板数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        this.exportLoading = true;
+        return exportSopTemp(queryParams);
+      }).then(response => {
+        this.download(response.msg);
+        this.exportLoading = false;
+      }).catch(() => {});
+    }
+  }
+};
+</script>
+
+<style scoped>
+.custom-input /deep/ .el-input__inner {
+  height: 20px;
+  text-align:center;
+}
+.custom-input /deep/ .el-input__icon {
+  line-height: 10px;
+}
+</style>

+ 201 - 0
src/views/qw/sopTemp/updateTemp.vue

@@ -0,0 +1,201 @@
+<template>
+  <div class="app-container">
+
+    <div style="margin: 30px;" v-if="this.form.sendType == 1 && formType==1 "> sop规则【修改企微接口】模板</div>
+    <div style="margin: 30px;" v-if="this.form.sendType == 1 && formType==2 "> sop规则【复制企微接口】模板</div>
+    <div style="margin: 30px;" v-if="this.form.sendType == 1 && formType==3 "> sop规则【查看企微接口】模板</div>
+    <div style="margin: 30px;" v-if="this.form.sendType == 2 && formType==1 "> sop规则【修改AI插件】模板</div>
+    <div style="margin: 30px;" v-if="this.form.sendType == 2 && formType==2 "> sop规则【复制AI插件】模板</div>
+    <div style="margin: 30px;" v-if="this.form.sendType == 2 && formType==3 "> sop规则【查看AI插件】模板</div>
+
+    <div style="margin-top: 10px;margin-left: 50px;margin-right: 100px;margin-bottom: 60px;">
+      <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="dict.dictValue"
+            >{{dict.dictLabel}}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="间隔天数" prop="gap">
+          <el-input-number v-model="form.gap"  :min="1" label="间隔天数"></el-input-number>
+        </el-form-item>
+        <el-form-item label="排序" prop="sort">
+          <el-input-number v-model="form.sort"  :min="0" label="排序"></el-input-number>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer" style="float: right;" v-if="formType==1 || formType==2 ">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { listSopTemp, getSopTemp, delSopTemp, updateTemp, exportSopTemp } from "@/api/qw/sopTemp";
+export default {
+  name: "updateSopTemp",
+  data() {
+    return {
+      uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS2",
+      uploadUrlByVoice:process.env.VUE_APP_BASE_API+"/common/uploadOSSByHOOKVoice",
+      //上传语音的遮罩层
+      voiceLoading :false,
+
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      sysFsSopWatchStatus: [],
+      //消息内容类型 企微版
+      sysQwSopContentType:[],
+      //插件版
+      sysQwSopAiContentType:[],
+
+      //类别
+      sysQwSopSettingType:[],
+
+      courseList:[],
+      videoList:[],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // sop模板表格数据
+      setting: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 状态字典
+      statusOptions: [],
+      videoNumOptions:{
+        title:'选择视频号',
+        open:false,
+        content:null,
+        contentIndex:null,
+        setIndex:null,
+      },
+      //1 修改 2 复制 3 查看
+      formType:null,
+      // 查询参数
+      form: {
+        name: null,
+        setting: null,
+        status: "1",
+        sort: 1,
+        companyId: null,
+        gap:1,
+      },
+      // 表单校验
+      rules: {
+        name: [
+          { required: true, message: '名称不能为空', trigger: 'blur' }
+        ],
+        status: [
+          { required: true, message: '状态不能为空', trigger: 'blur' }
+        ],
+        sort: [
+          { required: true, message: '排序不能为空', trigger: 'blur' }
+        ],
+        gap: [
+          { required: true, message: '间隔天数不能为空', trigger: 'blur' }
+        ],
+      }
+    };
+  },
+  created() {
+    this.getDicts("sys_company_status").then(response => {
+      this.statusOptions = response.data;
+    });
+    const id = this.$route.params && this.$route.params.id;
+    this.formType = this.$route.params && this.$route.params.type;
+    this.handleUpdate(id);
+  },
+  methods: {
+    // 取消按钮
+    cancel() {
+      this.$store.dispatch("tagsView/delView", this.$route);
+      this.$router.replace('/qw/conversion/sopTemp')
+      // this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        name: null,
+        setting: null,
+        status: "0",
+        sort: null,
+        createTime: null,
+        createBy: null,
+        companyId: null
+      };
+      this.resetForm("form");
+    },
+
+
+    /** 修改按钮操作 */
+    handleUpdate(id) {
+      getSopTemp(id).then(response => {
+        this.form = response.data;
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.formType == 1) {
+            if (this.form.id != null) {
+              updateTemp(this.form).then(response => {
+                this.msgSuccess("修改成功");
+                this.$store.dispatch("tagsView/delView", this.$route);
+                // this.$router.replace('/qw/conversion/sopTemp')
+                window.location.replace('/qw/conversion/sopTemp')
+                this.reset();
+              });
+            }
+          } else {
+            //id制空,防止键值重复-报错
+            this.form.id = null
+            //更新时间制空
+            this.form.updateTime = null
+            addTemp(this.form).then(response => {
+              this.msgSuccess("复制成功");
+              this.$store.dispatch("tagsView/delView", this.$route);
+              // this.$router.replace('/qw/conversion/sopTemp')
+              window.location.replace('/qw/conversion/sopTemp')
+              this.reset();
+            });
+          }
+
+        }
+      });
+    },
+  }
+};
+</script>
+
+<style scoped>
+.custom-input /deep/ .el-input__inner {
+  height: 20px;
+  text-align: center;
+}
+
+.custom-input /deep/ .el-input__icon {
+  line-height: 10px;
+}
+</style>

+ 274 - 0
src/views/qw/userVideo/userVideo.vue

@@ -0,0 +1,274 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
+        <el-alert
+          title="注意事项"
+          type="warning"
+          description="请先搜索相关【企业微信账号名称】,获取视频号信息"
+          :closable="false"
+          show-icon>
+        </el-alert>
+      <el-form-item label="企微账号名称" prop="qwUserName" >
+        <el-input
+          v-model="queryParams.qwUserName"
+          placeholder="请输入企微账号名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="封面标题" prop="nickname">
+        <el-input
+          v-model="queryParams.nickname"
+          placeholder="请输入封面标题"
+          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="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+        >删除</el-button>
+      </el-col>
+    </el-row>
+
+    <el-table border v-loading="loading" :data="QwUserVideoList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="封面图片" align="center" prop="coverUrl">
+        <template slot-scope="scope">
+          <el-popover
+            placement="right"
+            title=""
+            trigger="hover"
+          >
+            <img slot="reference" :src="scope.row.coverUrl" width="100" >
+            <img :src="scope.row.coverUrl" style="max-width: 300px;">
+          </el-popover>
+        </template>
+      </el-table-column>
+      <el-table-column label="头像" align="center" prop="avatar">
+        <template slot-scope="scope">
+          <el-popover
+            placement="right"
+            title=""
+            trigger="hover">
+            <img slot="reference" :src="scope.row.coverUrl" style="width: 50px;height: 50px">
+            <img :src="scope.row.coverUrl" style="max-width: 200px;max-height: 200px">
+          </el-popover>
+        </template>
+      </el-table-column>
+      <el-table-column label="封面标题" align="center" prop="nickname" />
+      <el-table-column label="简介" align="center" prop="desc" >
+        <template slot-scope="scope">
+          <el-tooltip class="item" effect="dark" :content="scope.row.desc" placement="top">
+            <div style="display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; text-overflow: ellipsis;">
+              <span>{{ scope.row.desc }}</span>
+            </div>
+          </el-tooltip>
+        </template>
+      </el-table-column>
+      <el-table-column label="视频地址" align="center" prop="url">
+        <template slot-scope="scope">
+          <el-tooltip class="item" effect="dark" :content="scope.row.url" placement="top">
+            <div style="display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; text-overflow: ellipsis;">
+              <span>{{ scope.row.url }}</span>
+            </div>
+          </el-tooltip>
+        </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-check"
+            @click="handleChangeVideoNum(scope.row)"
+          >选择此视频号</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"
+    />
+
+  </div>
+</template>
+
+<script>
+import { listQwUserVideo, getQwUserVideo, delQwUserVideo, addQwUserVideo, updateQwUserVideo, exportQwUserVideo } from "@/api/qw/userVideo";
+
+export default {
+  name: "userVideo",
+  data() {
+    return {
+      // 遮罩层
+      loading: false,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 企业微信的视频号表格数据
+      QwUserVideoList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        appKey: null,
+        senderName: null,
+        qwUserName:null,
+        objectId: null,
+        coverUrl: null,
+        thumbUrl: null,
+        avatar: null,
+        nickname: null,
+        desc: null,
+        url: null,
+        extras: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      }
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+
+
+    /** 查询企业微信的视频号列表 */
+    getList() {
+      this.loading = true;
+      listQwUserVideo(this.queryParams).then(response => {
+        this.QwUserVideoList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        appKey: null,
+        senderName: null,
+        objectId: null,
+        coverUrl: null,
+        thumbUrl: null,
+        avatar: null,
+        nickname: null,
+        desc: null,
+        url: null,
+        extras: 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
+    },
+
+    handleChangeVideoNum(val){
+        this.$emit("videoResult",val)
+    },
+
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != null) {
+            updateQwUserVideo(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addQwUserVideo(this.form).then(response => {
+              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 delQwUserVideo(ids);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有企业微信的视频号数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportQwUserVideo(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    }
+  }
+};
+</script>