yfh 17 stundas atpakaļ
vecāks
revīzija
5beee3e531
94 mainītis faili ar 7596 papildinājumiem un 1208 dzēšanām
  1. 6 7
      src/api/company/company.js
  2. 9 0
      src/api/company/companyUser.js
  3. 0 53
      src/api/company/userTag.js
  4. 8 0
      src/api/course/courseFinishTemp.js
  5. 37 1
      src/api/course/courseFinishTempParent.js
  6. 7 0
      src/api/course/qw/courseWatchLog.js
  7. 0 9
      src/api/crm/customer.js
  8. 19 0
      src/api/hisStore/answer.js
  9. 70 0
      src/api/hisStore/collection.js
  10. 18 0
      src/api/qw/externalContact.js
  11. 3 2
      src/api/qw/externalContactTransferAudit.js
  12. 8 0
      src/api/qw/qwDept.js
  13. 9 0
      src/api/qw/qwUser.js
  14. 3 1
      src/api/qw/sop.js
  15. 7 0
      src/api/qw/sopUserLogs.js
  16. 19 0
      src/api/qw/user.js
  17. 15 0
      src/api/statistics/statistics.js
  18. BIN
      src/assets/logo/cdhyt.jpg
  19. BIN
      src/assets/logo/cqtyt.jpg
  20. BIN
      src/assets/logo/czt.png
  21. BIN
      src/assets/logo/ddgy.jpg
  22. BIN
      src/assets/logo/hat.png
  23. BIN
      src/assets/logo/hst_logo.png
  24. BIN
      src/assets/logo/img.png
  25. BIN
      src/assets/logo/knt.jpg
  26. 140 44
      src/views/company/companyConfig/index.vue
  27. 272 156
      src/views/company/companyUser/index.vue
  28. 125 56
      src/views/company/components/selectQwUser.vue
  29. 184 0
      src/views/company/components/selectQwUserOlder.vue
  30. 10 6
      src/views/company/components/userSelect.vue
  31. 0 313
      src/views/company/userTag/index.vue
  32. 1 0
      src/views/components/course/userCourseCatalogDetails.vue
  33. 377 17
      src/views/course/courseFinishTemp/index.vue
  34. 996 0
      src/views/course/courseFinishTempParent/deptIndex.vue
  35. 297 5
      src/views/course/courseFinishTempParent/index.vue
  36. 993 0
      src/views/course/courseFinishTempParent/myIndex.vue
  37. 3 3
      src/views/course/courseRedPacketLog/index.vue
  38. 3 3
      src/views/course/courseRedPacketLog/myCourseRedPacketLog.vue
  39. 10 31
      src/views/course/courseUserStatistics/qw/index.vue
  40. 282 21
      src/views/course/courseWatchLog/deptWatchLog.vue
  41. 485 93
      src/views/course/courseWatchLog/index.vue
  42. 6 15
      src/views/course/courseWatchLog/myCourseWatchLog.vue
  43. 1 1
      src/views/course/courseWatchLog/qw/myCourseWatchLog.vue
  44. 29 5
      src/views/course/courseWatchLog/qw/statistics.vue
  45. 2 2
      src/views/course/courseWatchLog/qw/watchLog.vue
  46. 384 108
      src/views/course/courseWatchLog/watchLog.vue
  47. 2 0
      src/views/course/courseWatchLog/watchLogStatistics.vue
  48. 3 1
      src/views/course/userCourse/index.vue
  49. 10 10
      src/views/crm/components/customerAssignList.vue
  50. 16 11
      src/views/crm/customer/full.vue
  51. 5 0
      src/views/crm/customer/index.vue
  52. 7 39
      src/views/crm/customer/line.vue
  53. 21 15
      src/views/crm/customer/my.vue
  54. 9 7
      src/views/crm/customerVisit/index.vue
  55. 115 8
      src/views/index.vue
  56. 7 2
      src/views/member/list.vue
  57. 8 3
      src/views/member/mylist.vue
  58. 22 5
      src/views/qw/autoTags/dayPartingIndex.vue
  59. 4 1
      src/views/qw/autoTags/dayPartingIndexDetails.vue
  60. 1 0
      src/views/qw/autoTags/groupIndex.vue
  61. 1 0
      src/views/qw/contactWay/index.vue
  62. 3 1
      src/views/qw/externalContact/deptIndex.vue
  63. 250 6
      src/views/qw/externalContact/index.vue
  64. 274 4
      src/views/qw/externalContact/myExternalContact.vue
  65. 12 0
      src/views/qw/externalContact/selectUser.vue
  66. 20 4
      src/views/qw/externalContactTransfer/companyTransfer.vue
  67. 13 21
      src/views/qw/externalContactTransfer/companyTransferQwUserSelect.vue
  68. 16 2
      src/views/qw/externalContactTransfer/deptTransferIndex.vue
  69. 15 2
      src/views/qw/externalContactTransfer/index.vue
  70. 47 20
      src/views/qw/externalContactTransferLog/companyTransferDetail.vue
  71. 11 0
      src/views/qw/externalContactTransferLog/deptTransferLogIndex.vue
  72. 12 0
      src/views/qw/externalContactTransferLog/index.vue
  73. 12 0
      src/views/qw/externalContactTransferLog/my.vue
  74. 20 4
      src/views/qw/externalContactUnassigned/companyUnassigned.vue
  75. 14 1
      src/views/qw/externalContactUnassigned/deptUnassignedIndex.vue
  76. 14 1
      src/views/qw/externalContactUnassigned/index.vue
  77. 3 2
      src/views/qw/friendWelcome/indexNew.vue
  78. 4 4
      src/views/qw/friendWelcome/myIndexNew.vue
  79. 81 17
      src/views/qw/friendWelcome/myWelcome.vue
  80. 1 0
      src/views/qw/groupMsg/index.vue
  81. 11 7
      src/views/qw/qwUserVoiceLog/index.vue
  82. 10 2
      src/views/qw/qwUserVoiceLogTotal/index.vue
  83. 33 1
      src/views/qw/sopLogs/sopLogsList.vue
  84. 1 1
      src/views/qw/sopTemp/index.vue
  85. 24 1
      src/views/qw/sopTemp/updateSopTemp.vue
  86. 55 12
      src/views/qw/sopUserLogs/sopUserLogsSchedule.vue
  87. 93 1
      src/views/qw/sopUserLogsInfo/sendMsgOpenTool.vue
  88. 95 1
      src/views/qw/sopUserLogsInfo/sendMsgSopOpenTool.vue
  89. 29 1
      src/views/qw/sopUserLogsInfo/sopUserLogsInfoDetails.vue
  90. 2 18
      src/views/qw/user/cuDeptIdIndex.vue
  91. 56 19
      src/views/qw/user/index.vue
  92. 1272 0
      src/views/qw/user/myIndex.vue
  93. 9 1
      src/views/qw/user/qwUserSelectOne.vue
  94. 15 0
      src/views/users/user/transfer.vue

+ 6 - 7
src/api/company/company.js

@@ -34,13 +34,6 @@ export function getCompanyList() {
     method: 'get'
   })
 }
-// 销售端获取自己的公司列表
-export function getCompanyListByUserId() {
-  return request({
-    url: '/company/company/getCompanyListByUserId',
-    method: 'get'
-  })
-}
 
 export function allList(query) {
   return request({
@@ -50,3 +43,9 @@ export function allList(query) {
   })
 }
 
+export function getCompanyListByCorId(corId) {
+  return request({
+    url: '/company/company/getCompanyListByCorId/' + corId,
+    method: 'get'
+  })
+}

+ 9 - 0
src/api/company/companyUser.js

@@ -298,3 +298,12 @@ export function unBindDoctorId(userId) {
     method: 'get'
   })
 }
+
+//批量修改用户的角色
+export function updateBatchUserRoles(data) {
+  return request({
+    url: '/company/user/updateBatchUserRoles',
+    method: 'post',
+    data: data
+  })
+}

+ 0 - 53
src/api/company/userTag.js

@@ -1,53 +0,0 @@
-import request from '@/utils/request'
-
-// 查询小程序会员标签列表
-export function listTag(query) {
-  return request({
-    url: '/his/userTag/list',
-    method: 'get',
-    params: query
-  })
-}
-
-// 查询小程序会员标签详细
-export function getTag(tagId) {
-  return request({
-    url: '/his/userTag/' + tagId,
-    method: 'get'
-  })
-}
-
-// 新增小程序会员标签
-export function addTag(data) {
-  return request({
-    url: '/his/userTag',
-    method: 'post',
-    data: data
-  })
-}
-
-// 修改小程序会员标签
-export function updateTag(data) {
-  return request({
-    url: '/his/userTag',
-    method: 'put',
-    data: data
-  })
-}
-
-// 删除小程序会员标签
-export function delTag(tagId) {
-  return request({
-    url: '/his/userTag/' + tagId,
-    method: 'delete'
-  })
-}
-
-// 导出小程序会员标签
-export function exportTag(query) {
-  return request({
-    url: '/his/userTag/export',
-    method: 'get',
-    params: query
-  })
-}

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

@@ -51,3 +51,11 @@ export function exportCourseFinishTemp(query) {
     params: query
   })
 }
+
+export function updateStatusBatch(data) {
+  return request({
+    url: '/course/courseFinishTemp/updateStatusBatch',
+    method: 'post',
+    data: data
+  })
+}

+ 37 - 1
src/api/course/courseFinishTempParent.js

@@ -9,6 +9,24 @@ export function listCourseFinishTempParent(query) {
   })
 }
 
+export function myListCourseFinishTempParent(query) {
+  return request({
+    url: '/course/courseFinishTempParent/myList',
+    method: 'get',
+    params: query
+  })
+}
+
+
+export function deptListCourseFinishTempParent(query) {
+  return request({
+    url: '/course/courseFinishTempParent/deptList',
+    method: 'get',
+    params: query
+  })
+}
+
+
 // 查询完课模板详细
 export function getCourseFinishTempParent(id) {
   return request({
@@ -50,4 +68,22 @@ export function exportCourseFinishTempParent(query) {
     method: 'get',
     params: query
   })
-}
+}
+
+export function myExportCourseFinishTempParent(query) {
+  return request({
+    url: '/course/courseFinishTempParent/myExport',
+    method: 'get',
+    params: query
+  })
+}
+
+
+
+export function deptExportCourseFinishTempParent(query) {
+  return request({
+    url: '/course/courseFinishTempParent/deptExport',
+    method: 'get',
+    params: query
+  })
+}

+ 7 - 0
src/api/course/qw/courseWatchLog.js

@@ -23,6 +23,13 @@ export function statisticsList(query) {
     params: query
   })
 }
+export function statisticsExport(query) {
+  return request({
+    url: '/qw/course/courseWatchLog/statisticsExport',
+    method: 'get',
+    params: query
+  })
+}
 export function qwWatchLogStatisticsList(query) {
   return request({
     url: '/qw/course/courseWatchLog/qwWatchLogStatisticsList',

+ 0 - 9
src/api/crm/customer.js

@@ -196,12 +196,3 @@ export function getMyAssistList(query) {
     params: query
   })
 }
-
-// 模糊查询客户姓名
-export function getCustName(query) {
-  return request({
-    url: '/crm/customer/getCustName',
-    method: 'get',
-    params: query
-  })
-}

+ 19 - 0
src/api/hisStore/answer.js

@@ -0,0 +1,19 @@
+import request from '@/utils/request'
+
+
+
+// 查询问答详细
+export function getAnswer(id) {
+  return request({
+    url: '/store/answer/' + id,
+    method: 'get'
+  })
+}
+
+// 问答列表选项
+export function questionOptions() {
+  return request({
+    url: '/store/answer/allList',
+    method: 'get',
+  })
+}

+ 70 - 0
src/api/hisStore/collection.js

@@ -0,0 +1,70 @@
+import request from '@/utils/request'
+
+// 查询用户信息采集列表
+export function listCollection(query) {
+  return request({
+    url: '/hisStore/collection/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询用户信息采集详细
+export function getCollection(id) {
+  return request({
+    url: '/hisStore/collection/' + id,
+    method: 'get'
+  })
+}
+
+// 查询用户信息采集详细
+export function getInfo(query) {
+  return request({
+    url: '/hisStore/collection/getInfo',
+    method: 'get',
+    params: query
+  })
+}
+
+// 新增用户信息采集
+export function addCollection(data) {
+  return request({
+    url: '/hisStore/collection',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改用户信息采集
+export function updateCollection(data) {
+  return request({
+    url: '/hisStore/collection',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除用户信息采集
+export function delCollection(id) {
+  return request({
+    url: '/hisStore/collection/' + id,
+    method: 'delete'
+  })
+}
+
+// 导出用户信息采集
+export function exportCollection(query) {
+  return request({
+    url: '/hisStore/collection/export',
+    method: 'get',
+    params: query
+  })
+}
+
+
+export function getWxaCodeCollectionUnLimit(collectionId) {
+  return request({
+    url: '/hisStore/collection/getWxaCodeCollectionUnLimit/'+collectionId,
+    method: 'get',
+  })
+}

+ 18 - 0
src/api/qw/externalContact.js

@@ -9,6 +9,15 @@ export function listExternalContact(query) {
   })
 }
 
+// 查询重粉用户看课记录
+export function getWatchLogList(query) {
+  return request({
+    url: '/qw/externalContact/getWatchLogList',
+    method: 'get',
+    params: query
+  })
+}
+
 // 查询企业微信客户列表
 export function getRepeat(query) {
   return request({
@@ -158,6 +167,15 @@ export function updateExternalContactCall(data) {
     data: data
   })
 }
+
+//修改用户的状态
+export function updateExternalContactStatus(query) {
+  return request({
+    url: '/qw/externalContact/status',
+    method: 'get',
+    params: query
+  })
+}
 // 修改企业微信客户
 export function editStatus(data) {
   return request({

+ 3 - 2
src/api/qw/externalContactTransferAudit.js

@@ -8,10 +8,11 @@ export function listExternalContactTransferAudit(query) {
   })
 }
 
-export function detail(id) {
+export function detail(id, query) {
   return request({
     url: '/qw/externalContactTransferCompanyAudit/detail/' + id,
-    method: 'get'
+    method: 'get',
+    params: query
   })
 }
 

+ 8 - 0
src/api/qw/qwDept.js

@@ -59,3 +59,11 @@ export function exportQwDept(query) {
     params: query
   })
 }
+
+export function treeselect(query) {
+  return request({
+    url: '/qw/qwDept/treeselect',
+    method: 'get',
+    params: query
+  })
+}

+ 9 - 0
src/api/qw/qwUser.js

@@ -7,3 +7,12 @@ export function getQwUserAll() {
     method: 'get'
   })
 }
+
+// 根据条件查询企微用户列表
+export function getQwList(params) {
+  return request({
+    url: '/qw/user/qwList',
+    method: 'get',
+    params: params
+  })
+}

+ 3 - 1
src/api/qw/sop.js

@@ -49,9 +49,11 @@ export function courseList() {
     method: 'get',
   })
 }
-export function videoList(id) {
+export function videoList(id,query) {
+
   return request({
     url: '/qw/sop/videoList/' + id,
+    params: query,
     method: 'get'
   })
 }

+ 7 - 0
src/api/qw/sopUserLogs.js

@@ -69,6 +69,13 @@ export function updateLogDate(data) {
     data: data
   })
 }
+export function replaceUser(data) {
+  return request({
+    url: '/qwSop/sopUserLogs/replaceUser',
+    method: 'post',
+    data: data
+  })
+}
 // 修改sopUserLogs
 export function addGroupChat(data) {
   return request({

+ 19 - 0
src/api/qw/user.js

@@ -9,6 +9,15 @@ export function staffListUser(query) {
   })
 }
 
+export function myStaffListUser(query) {
+  return request({
+    url: '/qw/user/myStaffList',
+    method: 'get',
+    params: query
+  })
+}
+
+
 export function myDepartListUser(query) {
   return request({
     url: '/qw/user/myDepartList',
@@ -165,6 +174,16 @@ export function exportUser(query) {
     params: query
   })
 }
+
+// 导出企微员工
+export function exportStaff(query) {
+  return request({
+    url: '/qw/user/exportStaff',
+    method: 'get',
+    params: query
+  })
+}
+
 /**
  * 登录企业微信(发起登录)
  */

+ 15 - 0
src/api/statistics/statistics.js

@@ -309,3 +309,18 @@ export function thisMonthRecvCount(){
     params: {}
   })
 }
+
+
+/**
+ * 课程观看统计 按公司
+ * @param param
+ * @returns {AxiosPromise}
+ */
+export function getWatchCourseStatisticsData(param){
+  const safeParam = JSON.parse(JSON.stringify(param));
+  return request({
+    url: '/index/statistics/getWatchCourseStatisticsData',
+    method: 'post',
+    data: safeParam
+  })
+}

BIN
src/assets/logo/cdhyt.jpg


BIN
src/assets/logo/cqtyt.jpg


BIN
src/assets/logo/czt.png


BIN
src/assets/logo/ddgy.jpg


BIN
src/assets/logo/hat.png


BIN
src/assets/logo/hst_logo.png


BIN
src/assets/logo/img.png


BIN
src/assets/logo/knt.jpg


+ 140 - 44
src/views/company/companyConfig/index.vue

@@ -82,56 +82,103 @@
               <el-button type="primary" @click="onSubmit2">提交</el-button>
             </div>
         </el-tab-pane>
-        <el-tab-pane label="企微配置" name="qwConfig">
-          <el-form ref="qwConfig" :model="qwConfig" label-width="200px">
-            <el-form-item label="企业CoripID">
-              <el-input v-model="qwConfig.corpId" style="width:400px"   ></el-input>
+<!--        <el-tab-pane label="企微配置" name="qwConfig">-->
+<!--          <el-form ref="qwConfig" :model="qwConfig" label-width="200px">-->
+<!--            <el-form-item label="企业CoripID">-->
+<!--              <el-input v-model="qwConfig.corpId" style="width:400px"   ></el-input>-->
+<!--            </el-form-item>-->
+<!--            <el-form-item label="通讯录Secret">-->
+<!--              <el-input v-model="qwConfig.bookSecret" style="width:400px"   ></el-input>-->
+<!--            </el-form-item>-->
+
+<!--            <el-form-item label="应用Secret">-->
+<!--              <el-input v-model="qwConfig.appSecret" style="width:400px"   ></el-input>-->
+<!--            </el-form-item>-->
+<!--            <el-form-item label="应用AgentId">-->
+<!--              <el-input v-model="qwConfig.AgentId" style="width:400px"   ></el-input>-->
+<!--            </el-form-item>-->
+<!--            <el-form-item label="Token">-->
+<!--              <el-input v-model="qwConfig.token" style="width:400px"   :readonly="true"></el-input>-->
+<!--            </el-form-item>-->
+<!--            <el-form-item label="EncodingAESKey">-->
+<!--              <el-input v-model="qwConfig.encodingAESKey" style="width:400px"   :readonly="true"></el-input>-->
+<!--            </el-form-item>-->
+<!--            <el-form-item label="域名地址">-->
+<!--              <el-input v-model="qwConfig.realmNameURL" style="width:600px"></el-input>-->
+<!--            </el-form-item>-->
+<!--            <el-form-item label="回调地接">-->
+<!--              <el-input v-model="qwConfig.notifyUrl" style="width:600px"   :readonly="true"></el-input>-->
+<!--            </el-form-item>-->
+<!--            <el-form-item label="聊天工具栏跳转地址">-->
+<!--              <el-input v-model="qwConfig.chatToolbar" style="width:600px"   :readonly="true"></el-input>-->
+<!--            </el-form-item>-->
+<!--            <el-form-item label="聊天工具栏实际运用地址">-->
+<!--              <el-input v-model="qwConfig.chatToolbarOAuth2" :rows="2"  type="textarea" style="width:1000px;"   :readonly="true"></el-input>-->
+<!--            </el-form-item>-->
+<!--            <div class="line"></div>-->
+<!--            <div style="float:right;margin-right:20px">-->
+<!--              <el-button type="primary" @click="onSubmit4">提交</el-button>-->
+<!--            </div>-->
+<!--          </el-form>-->
+<!--        </el-tab-pane>-->
+        <el-tab-pane label="AI客服配置" name="AiKfConfig">
+          <el-form ref="AiKfConfig" :model="AiKfConfig" label-width="120px">
+            <el-form-item label="通用Key">
+              <el-input  v-model="AiKfConfig.Key" style="width:600px"   ></el-input>
             </el-form-item>
-            <el-form-item label="通讯录Secret">
-              <el-input v-model="qwConfig.bookSecret" style="width:400px"   ></el-input>
+            <el-form-item label="调用地址">
+              <el-input  v-model="AiKfConfig.url" style="width:600px"   ></el-input>
             </el-form-item>
-
-            <el-form-item label="应用Secret">
-              <el-input v-model="qwConfig.appSecret" style="width:400px"   ></el-input>
+            <div class="line"></div>
+            <div style="float:right;margin-right:20px">
+              <el-button type="primary" @click="onSubmit5">提交</el-button>
+            </div>
+          </el-form>
+        </el-tab-pane>
+        <el-tab-pane label="红包商户配置" name="redPacketConfig" >
+          <el-form ref="redPacketConfig" :model="redPacketConfig"  label-width="150px">
+            <el-form-item   label="红包接口类型" prop="isNew">
+              <el-radio-group v-model="redPacketConfig.isNew">
+                <el-radio label="0">商家转账到零钱(旧)</el-radio>
+                <el-radio label="1">商家转账(新)</el-radio>
+              </el-radio-group>
             </el-form-item>
-            <el-form-item label="应用AgentId">
-              <el-input v-model="qwConfig.AgentId" style="width:400px"   ></el-input>
+            <el-form-item   label="公众号appid" prop="appId">
+              <el-input   v-model="redPacketConfig.appId"  label="请输入appId"></el-input>
             </el-form-item>
-            <el-form-item label="Token">
-              <el-input v-model="qwConfig.token" style="width:400px"   :readonly="true"></el-input>
+            <el-form-item   label="小程序appid" prop="appId">
+              <el-input   v-model="redPacketConfig.miniappId"  label="请输入appId"></el-input>
             </el-form-item>
-            <el-form-item label="EncodingAESKey">
-              <el-input v-model="qwConfig.encodingAESKey" style="width:400px"   :readonly="true"></el-input>
+            <el-form-item   label="商户号" prop="mchId">
+              <el-input   v-model="redPacketConfig.mchId"  label="请输入mchId"></el-input>
             </el-form-item>
-            <el-form-item label="域名地址">
-              <el-input v-model="qwConfig.realmNameURL" style="width:600px"></el-input>
+            <el-form-item   label="商户密钥" prop="mchKey">
+              <el-input   v-model="redPacketConfig.mchKey"  label="mchKey"></el-input>
             </el-form-item>
-            <el-form-item label="回调地接">
-              <el-input v-model="qwConfig.notifyUrl" style="width:600px"   :readonly="true"></el-input>
+            <el-form-item   label="p12证书路径" prop="keyPath">
+              <el-input   v-model="redPacketConfig.keyPath"  label="请输入keyPath"></el-input>
             </el-form-item>
-            <el-form-item label="聊天工具栏跳转地址">
-              <el-input v-model="qwConfig.chatToolbar" style="width:600px"   :readonly="true"></el-input>
+            <el-form-item   label="apiV3密钥" prop="apiV3Key">
+              <el-input   v-model="redPacketConfig.apiV3Key"  label="请输入apiV3Key"></el-input>
             </el-form-item>
-            <el-form-item label="聊天工具栏实际运用地址">
-              <el-input v-model="qwConfig.chatToolbarOAuth2" :rows="2"  type="textarea" style="width:1000px;"   :readonly="true"></el-input>
+            <el-form-item   label="公钥ID" prop="publicKeyId">
+              <el-input   v-model="redPacketConfig.publicKeyId"  label="请输入公钥ID"></el-input>
             </el-form-item>
-            <div class="line"></div>
-            <div style="float:right;margin-right:20px">
-              <el-button type="primary" @click="onSubmit4">提交</el-button>
-            </div>
-          </el-form>
-        </el-tab-pane>
-        <el-tab-pane label="AI客服配置" name="AiKfConfig">
-          <el-form ref="AiKfConfig" :model="AiKfConfig" label-width="120px">
-            <el-form-item label="通用Key">
-              <el-input  v-model="AiKfConfig.Key" style="width:600px"   ></el-input>
+            <el-form-item   label="公钥证书" prop="publicKeyPath">
+              <el-input   v-model="redPacketConfig.publicKeyPath"  label="请输入publicKeyPath"></el-input>
             </el-form-item>
-            <el-form-item label="调用地址">
-              <el-input  v-model="AiKfConfig.url" style="width:600px"   ></el-input>
+            <el-form-item   label="key路径" prop="privateKeyPath">
+              <el-input   v-model="redPacketConfig.privateKeyPath"  label="请输入"></el-input>
             </el-form-item>
-            <div class="line"></div>
+            <el-form-item   label="cert路径" prop="privateCertPath">
+              <el-input   v-model="redPacketConfig.privateCertPath"  label="请输入"></el-input>
+            </el-form-item>
+            <el-form-item   label="回调地址" prop="notifyUrl">
+              <el-input   v-model="redPacketConfig.notifyUrl"  label="请输入"></el-input>
+            </el-form-item>
+
             <div style="float:right;margin-right:20px">
-              <el-button type="primary" @click="onSubmit5">提交</el-button>
+              <el-button type="primary" @click="onSubmit4">提交</el-button>
             </div>
           </el-form>
         </el-tab-pane>
@@ -147,6 +194,23 @@
             </div>
           </el-form>
         </el-tab-pane>
+        <el-tab-pane label="配置销售端隐藏总账号" name="adminIsShowForm">
+          <el-form ref="adminIsShowForm" label-width="140px">
+            <el-form-item label="账号是否显示">
+              <el-row>
+                <el-switch v-model="adminIsShow"></el-switch>
+                <span style="margin-left: 10px;">
+                  <el-tag :type="adminIsShow ? 'success' : 'info'" size="mini">
+                    {{ adminIsShow ? '当前显示' : '当前隐藏' }}
+                  </el-tag>
+                </span>
+              </el-row>
+            </el-form-item>
+            <div style="float:right;margin-right:20px">
+              <el-button type="primary" @click="onSubmit7">提交</el-button>
+            </div>
+          </el-form>
+        </el-tab-pane>
       </el-tabs>
 
       <el-dialog :title="customerExt.title" :visible.sync="customerExt.open" width="500px" append-to-body>
@@ -198,6 +262,7 @@ export default {
   },
   data() {
     return {
+      adminIsShow: false,
       company:null,
       statusOptions:[],
       customerExt:{
@@ -238,7 +303,11 @@ export default {
       qwConfigForm:{},
       qwkfConfigForm:{},
       companyUserConfig: {},
+      adminIsShowForm:{},
       userIsDefaultBlack: null,
+      redPacketConfig:{},
+
+      redPacketConfigForm:{}
     };
   },
   created() {
@@ -248,6 +317,8 @@ export default {
     this.getConfigKey("sys:qw:config");
     this.getConfigKey("customer:config");
     this.getConfigKey("sys:AiKf:config");
+    this.getConfigKey("company:admin:show");
+    this.getConfigKey("redPacket:config");
     this.getDicts("sys_company_status").then((response) => {
       this.statusOptions = response.data;
     });
@@ -379,13 +450,21 @@ export default {
                 this.AiKfConfig=JSON.parse(response.data.configValue);
               }
             }else if (key=="companyUser:config"){
-              console.log(response.data)
               this.companyUserConfig=response.data;
               if(response.data.configValue != null){
                 this.userIsDefaultBlack = JSON.parse(response.data.configValue);
               }
+            }else if(key == "company:admin:show"){
+              this.adminIsShowForm = response.data;
+              if(response.data.configValue != null){
+                this.adminIsShow = JSON.parse(response.data.configValue);
+              }
+            }else if(key=="redPacket:config"){
+              this.redPacketConfigForm=response.data;
+              if(response.data.configValue!=null){
+                this.redPacketConfig=JSON.parse(response.data.configValue);
+              }
             }
-
         });
     },
     onSubmit1() {
@@ -415,12 +494,21 @@ export default {
         }
       });
     },
+    // onSubmit4() {
+    //   this.qwConfigForm.configValue=JSON.stringify(this.qwConfig);
+    //   updateConfig(this.qwConfigForm).then(response => {
+    //     if (response.code === 200) {
+    //       this.msgSuccess("修改成功");
+    //       this.getConfigKey("sys:qw:config");
+    //     }
+    //   });
+    // },
     onSubmit4() {
-      this.qwConfigForm.configValue=JSON.stringify(this.qwConfig);
-      updateConfig(this.qwConfigForm).then(response => {
+      this.redPacketConfigForm.configValue=JSON.stringify(this.redPacketConfig);
+      updateConfig(this.redPacketConfigForm).then(response => {
         if (response.code === 200) {
           this.msgSuccess("修改成功");
-          this.getConfigKey("sys:qw:config");
+          this.getConfigKey("redPacket:config");
         }
       });
     },
@@ -435,8 +523,6 @@ export default {
     },
     onSubmit6() {
       this.companyUserConfig.configValue=JSON.stringify(this.userIsDefaultBlack);
-      console.log(this.companyUserConfig)
-      console.log(this.userIsDefaultBlack)
       configUserCheck({userIsDefaultBlack: this.userIsDefaultBlack}).then(response => {
         if (response.code === 200) {
           this.msgSuccess("修改成功");
@@ -444,6 +530,16 @@ export default {
         }
       });
     },
+    onSubmit7() {
+      // 实现提交逻辑
+      this.adminIsShowForm.configValue=JSON.stringify(this.adminIsShow);
+      updateConfig(this.adminIsShowForm).then(response => {
+        if (response.code === 200) {
+          this.msgSuccess("修改成功");
+          this.getConfigKey("company:admin:show");
+        }
+      })
+    },
   }
 };
 </script>

+ 272 - 156
src/views/company/companyUser/index.vue

@@ -2,7 +2,7 @@
   <div class="app-container">
     <el-row :gutter="20">
       <!--部门数据-->
-      <el-col :span="4" :xs="24">
+      <el-col :span="2" :xs="24">
         <div class="head-container">
           <el-input v-model="deptName" placeholder="请输入部门名称" clearable size="small" prefix-icon="el-icon-search" style="margin-bottom: 20px" />
         </div>
@@ -11,7 +11,7 @@
         </div>
       </el-col>
       <!--用户数据-->
-      <el-col :span="20" :xs="24">
+      <el-col :span="22" :xs="24">
         <el-form  :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
           <el-form-item label="员工后台昵称" prop="nickName">
             <el-input v-model="queryParams.nickName" placeholder="请输入员工后台昵称" clearable size="small" style="width: 240px" @keyup.enter.native="handleQuery" />
@@ -46,6 +46,9 @@
           <el-col :span="1.5">
             <el-button  plain type="success" icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate" v-hasPermi="['company:user:edit']">修改</el-button>
           </el-col>
+          <el-col :span="1.5">
+            <el-button  plain type="success" icon="el-icon-edit" size="mini" :disabled="multiple" @click="batchEditRole"v-hasPermi="['company:user:edit']">批量修改角色</el-button>
+          </el-col>
           <el-col :span="1.5">
             <el-button  plain type="danger" icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete" v-hasPermi="['company:user:remove']">删除</el-button>
           </el-col>
@@ -118,6 +121,14 @@
           <el-table-column label="员工后台昵称" align="center" prop="nickName" :show-overflow-tooltip="true" 员工后台  width="100"/>
           <el-table-column label="部门" align="center" prop="deptName" :show-overflow-tooltip="true" />
           <el-table-column label="手机号码" align="center" prop="phonenumber" width="120" />
+          <el-table-column label="账户角色" align="center" :show-overflow-tooltip="true" width="150">
+            <template slot-scope="scope">
+              <span v-if="scope.row.roleNames && scope.row.roleNames.length > 0">
+                {{ scope.row.roleNames.join('、') }}
+              </span>
+              <span v-else>-</span>
+            </template>
+          </el-table-column>
           <el-table-column label="二维码" align="center" prop="qrCodeWeixin">
             <template slot-scope="scope">
               <!-- 显示已上传的二维码 -->
@@ -159,6 +170,18 @@
               <dict-tag :options="qwStatusOptions" :value="scope.row.qwStatus"/>
             </template>
           </el-table-column>
+          <el-table-column label="绑定的企微号" align="center">
+            <template slot-scope="scope">
+              <div v-if="scope.row.qwUsers && scope.row.qwUsers.length > 0">
+                <div v-for="user in scope.row.qwUsers" :key="user.id">
+                  <el-tag size="mini">{{ user.qwUserName }}</el-tag>
+                </div>
+              </div>
+              <div v-else>
+                <dict-tag :options="qwStatusOptions" :value="scope.row.qwStatus"/>
+              </div>
+            </template>
+          </el-table-column>
           <el-table-column label="区域" align="center" prop="addressId">
           </el-table-column>
           <el-table-column label="创建时间"  sortable align="center" prop="createTime" width="160">
@@ -172,11 +195,6 @@
                 :type="scope.row.isNeedRegisterMember === 1 ? 'success' : 'info'">{{scope.row.isNeedRegisterMember === 1 ? '是' : '否' }}</el-tag>
             </template>
           </el-table-column>
-          <el-table-column label="创建时间"  sortable align="center" prop="createTime" width="160">
-            <template slot-scope="scope">
-              <span>{{ parseTime(scope.row.createTime) }}</span>
-            </template>
-          </el-table-column>
           <el-table-column label="允许注册会员" align="center" prop="isNeedRegisterMember" width="80px">
             <template slot-scope="scope">
               <el-tag
@@ -237,14 +255,34 @@
         <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
       </el-col>
     </el-row>
-    <el-dialog title="绑定企微账号"  :visible.sync="qwOpen" width="800px" append-to-body >
 
+    <!-- 批量修改角色对话框 -->
+    <el-dialog title="批量修改角色" :visible.sync="batchRoleDialogVisible" width="500px" append-to-body>
+      <el-form :model="batchRoleForm" label-width="80px">
+        <el-form-item label="选择角色">
+          <el-select v-model="selectedRoleIds" multiple placeholder="请选择角色" style="width: 100%;">
+            <el-option
+              v-for="item in roleOptions"
+              :key="item.roleId"
+              :label="item.roleName"
+              :value="item.roleId">
+            </el-option>
+          </el-select>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="batchRoleDialogVisible = false">取 消</el-button>
+        <el-button type="primary" @click="submitBatchRoles">确 定</el-button>
+      </div>
+    </el-dialog>
+
+    <!--当点击查或换绑微信时候弹出此框-->
+    <el-dialog title="绑定企微账号"  :visible.sync="qwOpen" width="800px" append-to-body >
       <el-form ref="form" :model="form"  label-width="80px" >
         <el-form-item label="查询"  prop="companyUserId">
           <el-button type="primary" icon="el-icon-search"  @click="selectQwUser()"  size="mini">搜索账号</el-button>
         </el-form-item>
         <el-form-item label="企微账号"  prop="companyUserId">
-
           <el-tag
             style="margin-left: 5px"
             size="medium"
@@ -257,15 +295,14 @@
             </span>
           </el-tag>
         </el-form-item>
-
       </el-form>
       <div slot="footer" class="dialog-footer">
         <el-button type="primary" @click="qwSubmitForm">绑 定</el-button>
         <el-button @click="qwCancel">取 消</el-button>
       </div>
     </el-dialog>
-    <el-dialog title="选择企微主体" :visible.sync="synOpen" width="800px" append-to-body>
 
+    <el-dialog title="选择企微主体" :visible.sync="synOpen" width="800px" append-to-body>
       <el-form   label-width="80px">
         <el-form-item label="企微公司" prop="corpId">
           <el-select v-model="synform.corpId" placeholder="企微公司"  >
@@ -277,8 +314,6 @@
             />
           </el-select>
         </el-form-item>
-
-
       </el-form>
       <div slot="footer" class="dialog-footer">
         <el-button type="primary" @click="synSubmitForm">确 定</el-button>
@@ -287,7 +322,6 @@
     </el-dialog>
 
     <el-dialog title="选择企微主体" :visible.sync="synNameOpen" width="800px" append-to-body>
-
       <el-form   label-width="80px">
         <el-form-item label="企微公司" prop="corpId">
           <el-select v-model="synNameform.corpId" placeholder="企微公司"  >
@@ -492,11 +526,11 @@
       </div>
     </el-dialog>
 
+    <!-- 修改 selectUser 组件的引用 -->
     <el-dialog :title="user.title" :visible.sync="user.open" width="1000px" append-to-body>
-      <selectUser ref="selectUser" @bindQwUser="bindQwUser"></selectUser>
+      <selectUser ref="selectUser" @bindQwUser="bindQwUser" @close="handleSelectUserClose"/>
     </el-dialog>
 
-
     <el-dialog :title="companyUserArea.title" :visible.sync="companyUserArea.open" width="300px" append-to-body>
       <el-select v-model="addressId"  filterable placeholder="请选择所属销售的区域" style="width: 200px;">
         <el-option
@@ -513,29 +547,34 @@
     </el-dialog>
 
     <!-- 设置单独注册会员弹窗 -->
+    <!-- 设置单独注册会员 -->
     <el-dialog title="设置单独注册会员" :visible.sync="registerOpen" width="400px" append-to-body>
-      <el-form ref="registerForm" :model="registerForm" label-width="180px">
-        <el-form-item label="是否需要开启单独注册">
-          <el-switch v-model="registerForm.status" active-value="true" inactive-value="false"></el-switch>
+      <el-form ref="registerForm" :model="registerForm" label-width="160px">
+        <el-form-item label="是否开启单独注册">
+          <el-switch v-model="registerForm.status" :active-value="true" :inactive-value="false"></el-switch>
         </el-form-item>
       </el-form>
-      <div slot="footer" class="dialog-footer">
-        <el-button type="primary" @click="submitRegisterForm">确 定</el-button>
-        <el-button @click="registerOpen = false">取 消</el-button>
-      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitRegisterForm">确 定</el-button>
+          <el-button @click="registerOpen = false">取 消</el-button>
+        </div>
+      </template>
     </el-dialog>
 
-    <!-- 是否允许所有方式注册会员弹窗 -->
+    <!-- 允许注册会员 -->
     <el-dialog title="允许注册会员" :visible.sync="allowedAllRegisterOpen" width="400px" append-to-body>
-      <el-form ref="registerForm" :model="allowedAllRegisterForm" label-width="180px">
+      <el-form ref="allowedAllRegisterForm" :model="allowedAllRegisterForm" label-width="160px">
         <el-form-item label="是否允许注册会员">
-          <el-switch v-model="allowedAllRegisterForm.status" active-value="true" inactive-value="false"></el-switch>
+          <el-switch v-model="allowedAllRegisterForm.status" :active-value="true" :inactive-value="false"></el-switch>
         </el-form-item>
       </el-form>
-      <div slot="footer" class="dialog-footer">
-        <el-button type="primary" @click="submitAllowedAllRegisterForm">确 定</el-button>
-        <el-button @click="allowedAllRegisterOpen = false">取 消</el-button>
-      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitAllowedAllRegisterForm">确 定</el-button>
+          <el-button @click="allowedAllRegisterOpen = false">取 消</el-button>
+        </div>
+      </template>
     </el-dialog>
 
     <el-dialog :title="bindCompanyUrl.title" v-if="bindCompanyUrl.open"  :visible.sync="bindCompanyUrl.open" width="450px"  append-to-body>
@@ -597,7 +636,7 @@ import {
   generateSubDomain,
   setIsRegisterMember,
   updateCompanyUserAreaList,
-  isAllowedAllRegister, unBindDoctorId, bindDoctorId
+  isAllowedAllRegister, unBindDoctorId, bindDoctorId,updateBatchUserRoles
 } from "@/api/company/companyUser";
 import { getToken } from "@/utils/auth";
 import { treeselect } from "@/api/company/companyDept";
@@ -637,6 +676,12 @@ export default {
       },
       // 选中数组
       ids: [],
+
+      //允许注册会员
+      isAllowedAllRegister: [],
+
+      //是否单独注册
+      isNeedRegisterMember: [],
       synform:{corpId:null},
       synOpen:false,
       synNameform:{corpId:null},
@@ -731,7 +776,7 @@ export default {
       qwStatusOptions:[],
       registerOpen: false,
       registerForm: {
-        status: false
+        status: false,
       },
       // 表单校验
       rules: {
@@ -795,6 +840,13 @@ export default {
       allowedAllRegisterForm: {
         status: true
       },
+      // 在 data() 中添加
+      batchRoleDialogVisible: false,
+      selectedRoleIds: [],
+      batchRoleForm: {
+        userIds: [],
+        roleIds: []
+      },
     };
   },
   watch: {
@@ -827,7 +879,7 @@ export default {
   },
   methods: {
     onDomainBlur() {
-      if (this.form.domain!=null){
+      if (this.form.domain != null) {
         let value = this.form.domain.trim();
 
         // 强制只保留第一个 http://
@@ -844,13 +896,100 @@ export default {
         this.form.domain = value; // 重新赋值,保证输入正确
 
         // 正则校验最终格式,提醒用户
-        // const domainPattern = /^http:\/\/([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/;
-        // if (!domainPattern.test(this.form.domain)) {
-        //   return  this.$message.error('请输入正确格式的域名,如:http://xxx.xxx.com');
-        // }
+        const domainPattern = /^http:\/\/([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/;
+        if (!domainPattern.test(this.form.domain)) {
+          return this.$message.error('请输入正确格式的域名,如:http://xxx.xxx.com');
+        }
+      }
+    },
+
+    // 添加处理 selectUser 关闭的方法
+    handleSelectUserClose() {
+      this.user.open = false
+      // 如果 selectUser 组件有 clearSelection 方法,也可以调用
+      if (this.$refs.selectUser && typeof this.$refs.selectUser.clearSelection === 'function') {
+        this.$refs.selectUser.clearSelection()
+      }
+    },
+
+    // edit G start
+    // 修改 selectQwUser 方法
+    selectQwUser() {
+      this.user.open = true;
+      // 在下次DOM更新后设置已选中的项
+      this.$nextTick(() => {
+        if (this.$refs.selectUser && this.qwUserList.length > 0) {
+          // 将已选中的用户传递给子组件
+          this.$refs.selectUser.setSelectedUsers(this.qwUserList);
+        }
+      });
+    },
+
+    // 修改 bindQwUser 方法
+    bindQwUser(row) {
+      // this.user.open = false; // 暂时注释掉这行,改为在 handleBatchBind 中处理
+      if (!this.qwUserList.some(item => item.id == row.id)) {
+        this.qwUserList.push(row)
       }
+      if (!this.qwUser.some(item => item == row.id)) {
+        this.qwUser.push(row.id)
+      }
+    },
 
+    // 修改 qwBind 方法
+    qwBind(row) {
+      this.qwUser = [];
+      this.qwUserList = [];
+      this.qwForm.companyUserId = row.userId;
+      getUser(row.userId).then((response) => {
+        if (response.data.qwUserId != null) {
+          // 保存已绑定的企微用户ID列表
+          this.qwUser = ((response.data.qwUserId).split(",").map(Number));
+          getQwUserByIds(this.qwUser).then(res => {
+            this.qwUserList = res.data;
+          });
+        }
+        // 先打开"绑定企微账号"对话框
+        this.qwOpen = true;
+      });
     },
+
+    // 在 methods 中添加批量修改角色的方法
+    batchEditRole() {
+      if (this.ids.length === 0) {
+        this.$message.warning("请至少选择一个用户");
+        return;
+      }
+      // 获取角色列表数据
+      getUser().then((response) => {
+        this.roleOptions = response.roles;
+        this.batchRoleDialogVisible = true;
+        this.selectedRoleIds = []; // 清空之前的选择
+      });
+    },
+
+    // 添加批量角色更新提交方法
+    submitBatchRoles() {
+      // 验证选择的角色
+      if (!this.selectedRoleIds || this.selectedRoleIds.length === 0) {
+        this.$message.warning("请至少选择一个角色");
+        return;
+      }
+      console.log(this.ids)
+      console.log(this.selectedRoleIds)
+
+      // 调用API批量更新用户角色
+      updateBatchUserRoles({ userIds: this.ids, roleIds: this.selectedRoleIds })
+        .then(response => {
+          if (response.code === 200) {
+            this.$message.success("批量修改角色成功");
+            this.batchRoleDialogVisible = false;
+            this.getList(); // 刷新列表
+          }
+        });
+    },
+    // edit G end
+
     /** 查询用户列表 */
     getList() {
       this.loading = true;
@@ -859,14 +998,12 @@ export default {
           this.userList = response.rows;
           this.total = response.total;
           this.loading = false;
-          console.log(" this.userList ", this.userList )
+          console.log(" this.userList ", this.userList)
         }
       );
     },
-    selectQwUser(){
-      this.user.open=true;
-    },
-    handleClosegroupUser(list){
+
+    handleClosegroupUser(list) {
       const index = this.qwUser.findIndex(t => t === list);
       if (index !== -1) {
         this.qwUser.splice(index, 1);
@@ -900,13 +1037,13 @@ export default {
           type: "warning",
         }
       )
-        .then(function () {
+        .then(function() {
           return changeUserStatus(row.userId, row.status);
         })
         .then(() => {
           this.msgSuccess(text + "成功");
         })
-        .catch(function () {
+        .catch(function() {
           row.status = row.status === "0" ? "1" : "0";
         });
     },
@@ -915,37 +1052,37 @@ export default {
       this.open = false;
       this.reset();
     },
-    cancelBind(){
-      this.bindCompanyOpen=false;
+    cancelBind() {
+      this.bindCompanyOpen = false;
       this.resetBindCompany();
     },
 
-    submitFormArea(address){
+    submitFormArea(address) {
       const uIds = this.ids;
 
-      if (address == null){
+      if (address == null) {
         this.$message.error("请选择地区");
         return;
       }
-      updateCompanyUserAreaList({userIds:uIds,addressId:address}).then(res=>{
-        this.companyUserArea.open=false;
+      updateCompanyUserAreaList({ userIds: uIds, addressId: address }).then(res => {
+        this.companyUserArea.open = false;
         this.getList();
         this.msgSuccess("操作成功");
 
       })
 
     },
-    cancelArea(){
-      this.companyUserArea.open=false;
-      this.addressId=null;
+    cancelArea() {
+      this.companyUserArea.open = false;
+      this.addressId = null;
     },
     qwCancel() {
       this.qwOpen = false;
-      this.qwUserId=null;
+      this.qwUserId = null;
     },
 
-    handerCompanyUserAreaList(){
-      this.companyUserArea.open=true;
+    handerCompanyUserAreaList() {
+      this.companyUserArea.open = true;
     },
     // 表单重置
     reset() {
@@ -991,6 +1128,8 @@ export default {
     // 多选框选中数据
     handleSelectionChange(selection) {
       this.ids = selection.map((item) => item.userId);
+      this.isNeedRegisterMember = selection.map((item) => item.isNeedRegisterMember);
+      this.isAllowedAllRegister = selection.map((item) => item.isAllowedAllRegister);
       this.single = selection.length != 1;
       this.multiple = !selection.length;
     },
@@ -1008,7 +1147,7 @@ export default {
       });
     },
 
-    handleBindCompanyUserCode(){
+    handleBindCompanyUserCode() {
       this.reset();
       this.getTreeselect();
       getUser().then((response) => {
@@ -1018,40 +1157,7 @@ export default {
         this.title = "创建 新增/绑定销售 的二维码";
       });
     },
-    qwBind(row){
-      this.qwUser=[];
-      this.qwUserList=[];
-      this.qwForm.companyUserId=row.userId
-      getUser(row.userId).then((response) => {
-        if(response.data.qwUserId!=null){
-           this.qwUser=((response.data.qwUserId).split(",").map(Number))
-          getQwUserByIds(this.qwUser).then(res => {
-            this.qwUserList=res.data;
-          })
-           // this.qwUser.forEach(item => {
-           //     getQwUser(item).then(response => {
-           //         this.qwUserList.push(response.data);
-           //     });
-           // });
-        }
-        this.qwOpen = true;
-      });
-
-    },
-
-    bindQwUser(row){
-      this.user.open=false;
-
 
-        if (!this.qwUserList.some(item => item.id == row.id)) {
-          this.qwUserList.push(row)
-        }
-
-        if (!this.qwUser.some(item => item == row.id)) {
-          this.qwUser.push(row.id);
-        }
-
-    },
     /** 修改按钮操作 */
     handleUpdate(row) {
       this.reset();
@@ -1093,12 +1199,13 @@ export default {
             }
           });
         })
-        .catch(() => {});
+        .catch(() => {
+        });
     },
     /** 提交按钮 */
-    submitForm: function () {
+    submitForm: function() {
 
-      this.onDomainBlur();
+      // this.onDomainBlur();
 
       // const domainPattern = /^http:\/\/([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/;
       // if (this.form.domain!=null && !domainPattern.test(this.form.domain)) {
@@ -1138,7 +1245,7 @@ export default {
       document.body.removeChild(link);
     },
 
-    submitBingCompanyForm: function () {
+    submitBingCompanyForm: function() {
 
       this.$refs["formBindCompany"].validate((valid) => {
         if (valid) {
@@ -1150,23 +1257,23 @@ export default {
             background: 'rgba(0, 0, 0, 0.7)'
           });
 
-            addCodeUrl(this.formBindCompany).then((response) => {
-              this.bindCompanyOpen=false;
-              this.bindCompanyUrl.url=response.data.url
-              this.bindCompanyUrl.open=true;
-              this.bindCompanyUrl.name="绑定或新增 销售二维码";
-            }).finally(res=>{
-              loadingRock.close();
-            })
+          addCodeUrl(this.formBindCompany).then((response) => {
+            this.bindCompanyOpen = false;
+            this.bindCompanyUrl.url = response.data.url
+            this.bindCompanyUrl.open = true;
+            this.bindCompanyUrl.name = "绑定或新增 销售二维码";
+          }).finally(res => {
+            loadingRock.close();
+          })
         }
       });
     },
     /**
-    * 同步企业微信员工
-    */
+     * 同步企业微信员工
+     */
     synSubmitForm() {
-      this.synOpen=false;
-      this.loading=true;
+      this.synOpen = false;
+      this.loading = true;
       /*this.msgSuccess("");
 
       let loadingRock = this.$loading({
@@ -1176,63 +1283,60 @@ export default {
         background: 'rgba(0, 0, 0, 0.7)'
       });*/
 
-
       addQwUser(this.synform.corpId).then(response => {
         //this.msgSuccess("同步成功");
         this.msgSuccess("正在同步中...");
         this.getList();
-        this.synOpen=false;
-      }).finally(()=>{
-        this.loading=false;
-        this.synOpen=false;
+        this.synOpen = false;
+      }).finally(() => {
+        this.loading = false;
+        this.synOpen = false;
         //loadingRock.close();
       });
     },
 
     synNameSubmitForm() {
-      this.synNameOpen=false;
-      this.loading=true;
+      this.synNameOpen = false;
+      this.loading = true;
 
       addQwUserName(this.synNameform.corpId).then(response => {
         // this.msgSuccess("同步成功");
         this.msgSuccess("正在同步中...");
         this.getList();
-        this.synNameOpen=false;
-      }).finally(()=>{
-        this.loading=false;
-        this.synNameOpen=false;
+        this.synNameOpen = false;
+      }).finally(() => {
+        this.loading = false;
+        this.synNameOpen = false;
         //loadingRock.close();
       });
     },
     /**
-    * 同步企业微信部门
-    */
-    qwSyncDept(){
+     * 同步企业微信部门
+     */
+    qwSyncDept() {
       syncDept().then(response => {
         this.msgSuccess("同步成功");
         this.getList();
-      }).catch(()=>{
-        this.msgError("同步失败:"+response.msg);
+      }).catch(() => {
+        this.msgError("同步失败:" + response.msg);
       })
     },
-    qwSubmitForm(){
 
+    qwSubmitForm() {
       let loadingRock = this.$loading({
         lock: true,
         text: '绑定中.....同步客户信息中.....',
         spinner: 'el-icon-loading',
         background: 'rgba(0, 0, 0, 0.7)'
       });
-
       this.qwForm.id = (this.qwUser).join(',');
-
       bindQwUser(this.qwForm).then(response => {
         this.msgSuccess("绑定成功");
         this.qwOpen = false;
         this.getList();
-        this.qwUserId=null;
-        this.qwUser=[];
-      }).finally(res=>{
+        this.qwUserId = null;
+        this.qwUser = [];
+      }).finally(res => {
         loadingRock.close()
       });
     },
@@ -1248,14 +1352,15 @@ export default {
           type: "warning",
         }
       )
-        .then(function () {
+        .then(function() {
           return delUser(userIds);
         })
         .then(() => {
           this.getList();
           this.msgSuccess("删除成功");
         })
-        .catch(function () {});
+        .catch(function() {
+        });
     },
     /** 导出按钮操作 */
     handleExport() {
@@ -1265,13 +1370,14 @@ export default {
         cancelButtonText: "取消",
         type: "warning",
       })
-        .then(function () {
+        .then(function() {
           return exportUser(queryParams);
         })
         .then((response) => {
           this.download(response.msg);
         })
-        .catch(function () {});
+        .catch(function() {
+        });
     },
     /** 导入按钮操作 */
     handleImport() {
@@ -1300,25 +1406,30 @@ export default {
     submitFileForm() {
       this.$refs.upload.submit();
     },
-    generateDomain(){
+    generateDomain() {
       let queryParams;
-      if(this.form.userId){
-        queryParams= {
+      if (this.form.userId) {
+        queryParams = {
           'userId': this.form.userId,
         }
       }
       generateSubDomain(queryParams).then(response => {
-        this.form.domain=response.data
+        this.form.domain = response.data
       });
     },
     /** 设置单独注册会员按钮操作 */
     handleSetRegister() {
       this.registerOpen = true;
-      this.registerForm.status = false;
+
+      if (this.isNeedRegisterMember && this.isNeedRegisterMember.some(item => item === 0)) {
+        this.registerForm.status = false;
+      } else {
+        this.registerForm.status = true;
+      }
     },
     /** 提交设置单独注册会员 */
     submitRegisterForm() {
-      setIsRegisterMember({status: this.registerForm.status}, this.ids).then(response => {
+      setIsRegisterMember({ status: this.registerForm.status }, this.ids).then(response => {
         if (response.code === 200) {
           this.msgSuccess("设置成功");
           this.registerOpen = false;
@@ -1330,11 +1441,16 @@ export default {
     /** 开关是否允许所有方式注册会员 */
     handleAllowedAllRegister() {
       this.allowedAllRegisterOpen = true;
+      if (this.isAllowedAllRegister && this.isAllowedAllRegister.some(item => item === 0)) {
+        this.allowedAllRegisterForm.status = false;
+      } else {
+        this.allowedAllRegisterForm.status = true;
+      }
       // this.allowedAllRegisterForm.status = true;
     },
     // 提交
-    submitAllowedAllRegisterForm(){
-      isAllowedAllRegister({status: this.allowedAllRegisterForm.status}, this.ids).then(response => {
+    submitAllowedAllRegisterForm() {
+      isAllowedAllRegister({ status: this.allowedAllRegisterForm.status }, this.ids).then(response => {
         if (response.code === 200) {
           this.msgSuccess("操作成功");
           this.allowedAllRegisterOpen = false;
@@ -1380,7 +1496,7 @@ export default {
       const formData = new FormData();
       formData.append('file', file);
 
-      formData.append('userId',row.userId)
+      formData.append('userId', row.userId)
 
       this.$set(row, 'uploading', true);
       this.$set(row, 'uploadError', '');
@@ -1422,7 +1538,7 @@ export default {
     },
     requestUpload() {
     },
-    beforeUpload(){
+    beforeUpload() {
       console.log(file.type)
       const isPic =
         file.type === 'image/jpeg' ||
@@ -1440,25 +1556,25 @@ export default {
       }
       return isPic && isLt2M
     },
-    handleUpdateDoctor(row){
-      this.doctor.title="绑定医生"
-      this.doctor.open=true;
-      this.doctorForm.userId=row.userId;
+    handleUpdateDoctor(row) {
+      this.doctor.title = "绑定医生"
+      this.doctor.open = true;
+      this.doctorForm.userId = row.userId;
     },
-    bindCompanyUserDoctorId(row){
+    bindCompanyUserDoctorId(row) {
       console.log(row)
-      this.doctorForm.doctorId=row;
-      bindDoctorId(this.doctorForm).then(res=>{
-        if (res.code==200){
+      this.doctorForm.doctorId = row;
+      bindDoctorId(this.doctorForm).then(res => {
+        if (res.code == 200) {
           this.$message.success('绑定成功')
-        }else {
-          this.$message.error('绑定失败:',res.msg)
+        } else {
+          this.$message.error('绑定失败:', res.msg)
         }
         this.getList()
-        this.doctor.open=false;
+        this.doctor.open = false;
       })
     },
-    handleUnBindUserId(val){
+    handleUnBindUserId(val) {
       this.$confirm(
         '确认解绑医生:<span style="color: green;">' + val.nickName + '' +
         '</span> 的医生?',
@@ -1473,10 +1589,10 @@ export default {
       }).then(response => {
         this.getList();
         this.msgSuccess("解绑成功");
-      }).finally(res=>{
+      }).finally(res => {
         this.getList();
       })
     },
   },
-};
+}
 </script>

+ 125 - 56
src/views/company/components/selectQwUser.vue

@@ -1,6 +1,7 @@
 <template>
   <div class="app-container">
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <!-- 现有表单内容保持不变 -->
       <el-form-item label="企微主体" prop="corpId">
         <el-select v-model="queryParams.corpId" placeholder="企微主体" size="small" @change="updateCorpId()">
           <el-option
@@ -36,27 +37,32 @@
       </el-form-item>
     </el-form>
 
-    <el-table  height="500" border v-loading="loading" :data="customerList" ref="customerList" >
-       <el-table-column label="企微昵称" align="center" prop="qwUserName" />
+    <el-table
+      height="500"
+      border
+      v-loading="loading"
+      :data="customerList"
+      ref="customerList"
+      @selection-change="handleSelectionChange"
+    >
+      <!-- 添加多选列 -->
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="企微昵称" align="center" prop="qwUserName" />
       <el-table-column label="企微账号" align="center" prop="qwUserId" />
       <el-table-column label="企微所属部门" align="center" prop="departmentName" />
-	  <el-table-column label="状态" align="center" prop="status" >
-	    <template slot-scope="scope">
-	      <dict-tag :options="qwStatusOptions" :value="scope.row.status"/>
-	    </template>
-	   </el-table-column>
-      <el-table-column label="企微主体" align="center" prop="corpName" />
-      <el-table-column label="操作"   align="center" fixed="right" width="100px" class-name="small-padding fixed-width">
+      <el-table-column label="状态" align="center" prop="status" >
         <template slot-scope="scope">
-          <el-button
-            size="medium"
-            type="primary"
-            plain
-            @click="handleBind(scope.row)"
-          >选择</el-button>
+          <dict-tag :options="qwStatusOptions" :value="scope.row.status"/>
         </template>
       </el-table-column>
+      <el-table-column label="企微主体" align="center" prop="corpName" />
     </el-table>
+
+    <div style="margin-top: 10px; text-align: right;">
+      <el-button type="primary" @click="handleBatchBind">确定</el-button>
+      <el-button @click="cancelSelect">取消选择</el-button>
+    </div>
+
     <pagination
       v-show="total>0"
       :total="total"
@@ -64,8 +70,6 @@
       :limit.sync="queryParams.pageSize"
       @pagination="getList"
     />
-
-
   </div>
 </template>
 
@@ -80,7 +84,7 @@ export default {
       // 遮罩层
       loading: true,
       myQwCompanyList:[],
-	  qwStatusOptions:[],
+      qwStatusOptions:[],
       // 显示搜索条件
       showSearch: true,
       // 总条数
@@ -100,29 +104,31 @@ export default {
         qwUserName: null,
       },
       // 表单参数
-      form: {
-      },
+      form: {},
       // 表单校验
-      rules: {
-      },
+      rules: {},
+      // 多选数据
+      selectedUsers: [],
+      // 已选中的用户ID列表(用于初始化选中状态)
+      preSelectedUserIds: []
     };
   },
   created() {
-	  this.getDicts("sys_qw_user_status").then(response => {
-	        this.qwStatusOptions = response.data;
-	  });
+    this.getDicts("sys_qw_user_status").then(response => {
+      this.qwStatusOptions = response.data;
+    });
     getMyQwCompanyList().then(response => {
-           this.myQwCompanyList = response.data;
-           if(this.myQwCompanyList!=null){
-             this.queryParams.corpId=this.myQwCompanyList[0].dictValue
-             this.getList();
-           }
+      this.myQwCompanyList = response.data;
+      if(this.myQwCompanyList!=null){
+        this.queryParams.corpId=this.myQwCompanyList[0].dictValue
+        this.getList();
+      }
     });
   },
   methods: {
     updateCorpId(){
-           this.getList();
-     },
+      this.getList();
+    },
     /** 查询客户列表 */
     getList() {
       this.loading = true;
@@ -131,25 +137,88 @@ export default {
         this.customerList = response.rows;
         this.total = response.total;
         this.loading = false;
+
+        // 在数据加载完成后设置已选中项
+        this.$nextTick(() => {
+          this.setPreSelectedItems();
+        });
+      });
+    },
+
+    // 设置预选中的项
+    setPreSelectedItems() {
+      if (this.preSelectedUserIds.length > 0 && this.customerList.length > 0) {
+        // 找到需要预选中的行
+        const selectedRows = this.customerList.filter(row =>
+          this.preSelectedUserIds.includes(row.id)
+        );
+
+        // 设置选中状态
+        this.$nextTick(() => {
+          selectedRows.forEach(row => {
+            this.$refs.customerList.toggleRowSelection(row, true);
+          });
+        });
+      }
+    },
+
+    // 多选处理
+    handleSelectionChange(selection) {
+      this.selectedUsers = selection;
+    },
+
+    // 批量绑定选择
+    handleBatchBind() {
+      if (this.selectedUsers.length === 0) {
+        this.$message.warning("请至少选择一个用户");
+        return;
+      }
+
+      // 发送所有选中的用户数据
+      this.selectedUsers.forEach(user => {
+        this.$emit("bindQwUser", user);
       });
+
+      // 清空选择
+      this.clearSelection();
+      this.$emit('close'); // 通知父组件关闭对话框
     },
 
-    //绑定选择
-    handleBind(row){
+    // 添加一个新的方法用于清除选择
+    clearSelection() {
+      this.selectedUsers = [];
+      this.preSelectedUserIds = [];
+      if (this.$refs.customerList) {
+        this.$refs.customerList.clearSelection();
+      }
+    },
+
+    // 设置已选中的用户(由父组件调用)
+    setSelectedUsers(users) {
+      this.preSelectedUserIds = users.map(user => user.id);
+      this.selectedUsers = [...users];
+
+      // 如果数据已经加载,直接设置选中状态
+      if (this.customerList.length > 0) {
+        this.setPreSelectedItems();
+      }
+    },
 
-      this.$emit("bindQwUser",row)
-      this.resetQuery();
+    // 取消选择
+    cancelSelect() {
       this.$refs.customerList.clearSelection();
+      this.selectedUsers = [];
     },
+
     // 取消按钮
     cancel() {
       this.open = false;
       this.reset();
+      this.clearSelection(); // 清除选择
     },
     /** 搜索按钮操作 */
     handleQuery() {
       this.queryParams.pageNum = 1;
-
       this.getList();
     },
     /** 重置按钮操作 */
@@ -158,27 +227,27 @@ export default {
       this.queryParams.corpId= this.myQwCompanyList[0].dictValue;
       this.handleQuery();
     },
-
   }
 };
 </script>
+
 <style>
-  .el-tag + .el-tag {
-    margin-left: 10px;
-  }
-  .button-new-tag {
-    margin-left: 10px;
-    height: 32px;
-    line-height: 30px;
-    padding-top: 0;
-    padding-bottom: 0;
-  }
-  .input-new-tag {
-    width: 90px;
-    margin-left: 10px;
-    vertical-align: bottom;
-  }
-  .el-dialog__wrapper{
-    z-index: 100000;
-  }
+.el-tag + .el-tag {
+  margin-left: 10px;
+}
+.button-new-tag {
+  margin-left: 10px;
+  height: 32px;
+  line-height: 30px;
+  padding-top: 0;
+  padding-bottom: 0;
+}
+.input-new-tag {
+  width: 90px;
+  margin-left: 10px;
+  vertical-align: bottom;
+}
+.el-dialog__wrapper{
+  z-index: 100000;
+}
 </style>

+ 184 - 0
src/views/company/components/selectQwUserOlder.vue

@@ -0,0 +1,184 @@
+<!--<template>-->
+<!--  <div class="app-container">-->
+<!--    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">-->
+<!--      <el-form-item label="企微主体" prop="corpId">-->
+<!--        <el-select v-model="queryParams.corpId" placeholder="企微主体" size="small" @change="updateCorpId()">-->
+<!--          <el-option-->
+<!--            v-for="dict in myQwCompanyList"-->
+<!--            :key="dict.dictValue"-->
+<!--            :label="dict.dictLabel"-->
+<!--            :value="dict.dictValue"-->
+<!--          />-->
+<!--        </el-select>-->
+<!--      </el-form-item>-->
+<!--      <el-form-item label="企微账号" prop="qwUserId">-->
+<!--        <el-input-->
+<!--          v-model="queryParams.qwUserId"-->
+<!--          placeholder="请输入企微账号"-->
+<!--          clearable-->
+<!--          size="small"-->
+<!--          @keyup.enter.native="handleQuery"-->
+<!--        />-->
+<!--      </el-form-item>-->
+
+<!--      <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>-->
+<!--        <el-button type="cyan" 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  height="500" border v-loading="loading" :data="customerList" ref="customerList" >-->
+<!--       <el-table-column label="企微昵称" align="center" prop="qwUserName" />-->
+<!--      <el-table-column label="企微账号" align="center" prop="qwUserId" />-->
+<!--      <el-table-column label="企微所属部门" align="center" prop="departmentName" />-->
+<!--	  <el-table-column label="状态" align="center" prop="status" >-->
+<!--	    <template slot-scope="scope">-->
+<!--	      <dict-tag :options="qwStatusOptions" :value="scope.row.status"/>-->
+<!--	    </template>-->
+<!--	   </el-table-column>-->
+<!--      <el-table-column label="企微主体" align="center" prop="corpName" />-->
+<!--      <el-table-column label="操作"   align="center" fixed="right" width="100px" class-name="small-padding fixed-width">-->
+<!--        <template slot-scope="scope">-->
+<!--          <el-button-->
+<!--            size="medium"-->
+<!--            type="primary"-->
+<!--            plain-->
+<!--            @click="handleBind(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 { userList,listUser, getUser, delUser, addUser, updateUser, exportUser, updateUserWeclome,getMyQwCompanyList,getMyQwUserList,relieveFastGptRoleById,staffListUser } from '@/api/qw/user'-->
+
+<!--export default {-->
+<!--  name: "miniCustomer",-->
+<!--  components: {},-->
+<!--  data() {-->
+<!--    return {-->
+<!--      // 遮罩层-->
+<!--      loading: true,-->
+<!--      myQwCompanyList:[],-->
+<!--	  qwStatusOptions:[],-->
+<!--      // 显示搜索条件-->
+<!--      showSearch: true,-->
+<!--      // 总条数-->
+<!--      total: 0,-->
+<!--      // 员工表格数据-->
+<!--      customerList: [],-->
+<!--      // 弹出层标题-->
+<!--      title: "",-->
+<!--      // 是否显示弹出层-->
+<!--      open: false,-->
+<!--      // 查询参数-->
+<!--      queryParams: {-->
+<!--        pageNum: 1,-->
+<!--        pageSize: 10,-->
+<!--        qwUserId: null,-->
+<!--        corpId: null,-->
+<!--        qwUserName: null,-->
+<!--      },-->
+<!--      // 表单参数-->
+<!--      form: {-->
+<!--      },-->
+<!--      // 表单校验-->
+<!--      rules: {-->
+<!--      },-->
+<!--    };-->
+<!--  },-->
+<!--  created() {-->
+<!--	  this.getDicts("sys_qw_user_status").then(response => {-->
+<!--	        this.qwStatusOptions = response.data;-->
+<!--	  });-->
+<!--    getMyQwCompanyList().then(response => {-->
+<!--           this.myQwCompanyList = response.data;-->
+<!--           if(this.myQwCompanyList!=null){-->
+<!--             this.queryParams.corpId=this.myQwCompanyList[0].dictValue-->
+<!--             this.getList();-->
+<!--           }-->
+<!--    });-->
+<!--  },-->
+<!--  methods: {-->
+<!--    updateCorpId(){-->
+<!--           this.getList();-->
+<!--     },-->
+<!--    /** 查询客户列表 */-->
+<!--    getList() {-->
+<!--      this.loading = true;-->
+
+<!--      userList(this.queryParams).then(response => {-->
+<!--        this.customerList = response.rows;-->
+<!--        this.total = response.total;-->
+<!--        this.loading = false;-->
+<!--      });-->
+<!--    },-->
+
+<!--    //绑定选择-->
+<!--    handleBind(row){-->
+
+<!--      this.$emit("bindQwUser",row)-->
+<!--      this.resetQuery();-->
+<!--      this.$refs.customerList.clearSelection();-->
+<!--    },-->
+<!--    // 取消按钮-->
+<!--    cancel() {-->
+<!--      this.open = false;-->
+<!--      this.reset();-->
+<!--    },-->
+<!--    /** 搜索按钮操作 */-->
+<!--    handleQuery() {-->
+<!--      this.queryParams.pageNum = 1;-->
+
+<!--      this.getList();-->
+<!--    },-->
+<!--    /** 重置按钮操作 */-->
+<!--    resetQuery() {-->
+<!--      this.resetForm("queryForm");-->
+<!--      this.queryParams.corpId= this.myQwCompanyList[0].dictValue;-->
+<!--      this.handleQuery();-->
+<!--    },-->
+
+<!--  }-->
+<!--};-->
+<!--</script>-->
+<!--<style>-->
+<!--  .el-tag + .el-tag {-->
+<!--    margin-left: 10px;-->
+<!--  }-->
+<!--  .button-new-tag {-->
+<!--    margin-left: 10px;-->
+<!--    height: 32px;-->
+<!--    line-height: 30px;-->
+<!--    padding-top: 0;-->
+<!--    padding-bottom: 0;-->
+<!--  }-->
+<!--  .input-new-tag {-->
+<!--    width: 90px;-->
+<!--    margin-left: 10px;-->
+<!--    vertical-align: bottom;-->
+<!--  }-->
+<!--  .el-dialog__wrapper{-->
+<!--    z-index: 100000;-->
+<!--  }-->
+<!--</style>-->

+ 10 - 6
src/views/company/components/userSelect.vue

@@ -51,7 +51,7 @@
           </el-table-column>
         </el-table>
         <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
-        
+
         <div class="btns">
           <el-button type="primary" @click="submit"  >确 定</el-button>
         </div>
@@ -75,7 +75,7 @@ import {
 import { treeselect } from "@/api/company/companyDept";
 export default {
   name: "User",
-  
+
   data() {
     return {
       selectUser:[],
@@ -180,8 +180,8 @@ export default {
       this.queryParams.deptId = data.id;
       this.getList();
     },
-     
-     
+
+
     /** 搜索按钮操作 */
     handleQuery() {
       this.queryParams.page = 1;
@@ -199,6 +199,10 @@ export default {
       this.ids = selection.map((item) => item.userId);
     },
     addUser(){
+      if(this.selectUser.length < 1) {
+        this.$message.warning("请选择员工")
+        return
+      }
       var that=this;
       this.selectUser.forEach(element => {
         var flag= that.users.some(item => item.userId === element.userId)
@@ -215,11 +219,11 @@ export default {
 };
 </script>
 <style >
- 
+
 .btns{
   padding-top: 15px;
   display: flex;
   align-content: center;
   justify-content: flex-end;
 }
-</style>
+</style>

+ 0 - 313
src/views/company/userTag/index.vue

@@ -1,313 +0,0 @@
-<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  v-model="queryParams.companyId" placeholder="请选择销售公司" size="small" @change="handleCompanyChange" style="width: 180px" >
-          <el-option
-            v-for="company in companyOptions"
-            :key="company.companyId"
-            :label="company.companyName"
-            :value="company.companyId"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="标签" prop="tag">
-        <el-input
-          v-model="queryParams.tag"
-          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="['tag:tag:add']"
-        >新增</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="success"
-          plain
-          icon="el-icon-edit"
-          size="mini"
-          :disabled="single"
-          @click="handleUpdate"
-          v-hasPermi="['tag:tag:edit']"
-        >修改</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="danger"
-          plain
-          icon="el-icon-delete"
-          size="mini"
-          :disabled="multiple"
-          @click="handleDelete"
-          v-hasPermi="['tag:tag: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="['tag:tag:export']"
-        >导出</el-button>
-      </el-col> -->
-      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
-    </el-row>
-
-    <el-table border v-loading="loading" :data="tagList" @selection-change="handleSelectionChange">
-      <el-table-column type="selection" width="55" align="center" />
-      <el-table-column label="id" align="center" prop="tagId"  />
-      <el-table-column label="企业名称" align="center" prop="companyName" >
-        <template slot-scope="scope">
-          <el-tag prop="companyId" v-for="item in companyOptions"
-                  :key="item.companyId"
-                  v-show="scope.row.companyId === item.companyId">
-            {{item.companyName}}
-          </el-tag>
-        </template>
-      </el-table-column>
-      <el-table-column label="标签" align="center" prop="tag" />
-      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
-        <template slot-scope="scope">
-          <el-button
-            size="mini"
-            type="text"
-            icon="el-icon-edit"
-            @click="handleUpdate(scope.row)"
-            v-hasPermi="['tag:tag:edit']"
-          >修改</el-button>
-          <el-button
-            size="mini"
-            type="text"
-            icon="el-icon-delete"
-            @click="handleDelete(scope.row)"
-            v-hasPermi="['tag:tag: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="500px" append-to-body>
-      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
-        <el-form-item label="企业名称" prop="companyId" >
-          <el-select  v-model="form.companyId" placeholder="请选择销售公司" size="small" clearable style="width: 180px" >
-            <el-option
-              v-for="company in companyOptions"
-              :key="company.companyId"
-              :label="company.companyName"
-              :value="company.companyId"
-            />
-        </el-select>
-        </el-form-item>
-        <el-form-item label="标签" prop="tag" >
-          <el-input v-model="form.tag" placeholder="请输入标签" />
-        </el-form-item>
-      </el-form>
-      <div slot="footer" class="dialog-footer">
-        <el-button type="primary" @click="submitForm">确 定</el-button>
-        <el-button @click="cancel">取 消</el-button>
-      </div>
-    </el-dialog>
-  </div>
-</template>
-
-<script>
-import { listTag, getTag, delTag, addTag, updateTag, exportTag } from "@/api/company/userTag";
-import { getCompanyListByUserId } from "@/api/company/company";
-
-export default {
-  name: "Tag",
-  data() {
-    return {
-      companyOptions: [],
-      // 遮罩层
-      loading: true,
-      // 导出遮罩层
-      exportLoading: false,
-      // 选中数组
-      ids: [],
-      // 非单个禁用
-      single: true,
-      // 非多个禁用
-      multiple: true,
-      // 显示搜索条件
-      showSearch: true,
-      // 总条数
-      total: 0,
-      // 小程序会员标签表格数据
-      tagList: [],
-      // 弹出层标题
-      title: "",
-      // 是否显示弹出层
-      open: false,
-      // 查询参数
-      queryParams: {
-        pageNum: 1,
-        pageSize: 10,
-        companyId: null,
-        tag: null,
-      },
-      // 表单参数
-      form: {},
-      // 表单校验
-      rules: {
-        companyId: [
-          { required: true, message: '请选择企业名称', trigger: 'change' }
-        ],
-        tag: [
-          { required: true, message: '请输入标签', trigger: 'blur' }
-        ]
-      },
-    };
-  },
-  created() {
-    getCompanyListByUserId().then(response => {
-      this.companyOptions = response.data;
-      // 设置默认选中的公司
-      if (this.companyOptions && this.companyOptions.length > 0) {
-        this.queryParams.companyId = this.companyOptions[0].companyId;
-      }
-      // 在获取到公司列表后再执行查询
-      this.getList();
-    })
-  },
-  methods: {
-    handleCompanyChange(){
-      this.getList();
-    },
-    /** 查询小程序会员标签列表 */
-    getList() {
-      this.loading = true;
-      listTag(this.queryParams).then(response => {
-        this.tagList = response.rows;
-        this.total = response.total;
-        this.loading = false;
-      });
-    },
-    // 取消按钮
-    cancel() {
-      this.open = false;
-      this.reset();
-    },
-    // 表单重置
-    reset() {
-      this.form = {
-        tagId: null,
-        companyId: null,
-        tag: null,
-        createTime: 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.tagId)
-      this.single = selection.length!==1
-      this.multiple = !selection.length
-    },
-    /** 新增按钮操作 */
-    handleAdd() {
-      this.reset();
-      this.open = true;
-      this.title = "添加小程序会员标签";
-    },
-    /** 修改按钮操作 */
-    handleUpdate(row) {
-      this.reset();
-      const tagId = row.tagId || this.ids
-      getTag(tagId).then(response => {
-        this.form = response.data;
-        this.open = true;
-        this.title = "修改小程序会员标签";
-      });
-    },
-    /** 提交按钮 */
-    submitForm() {
-      this.$refs["form"].validate(valid => {
-        if (valid) {
-          if (this.form.tagId != null) {
-            updateTag(this.form).then(response => {
-              this.msgSuccess("修改成功");
-              this.open = false;
-              this.getList();
-            });
-          } else {
-            addTag(this.form).then(response => {
-              this.msgSuccess("新增成功");
-              this.open = false;
-              this.getList();
-            });
-          }
-        }
-      });
-    },
-    /** 删除按钮操作 */
-    handleDelete(row) {
-      const tagIds = row.tagId || this.ids;
-      this.$confirm('是否确认删除小程序会员标签编号为"' + tagIds + '"的数据项?', "警告", {
-          confirmButtonText: "确定",
-          cancelButtonText: "取消",
-          type: "warning"
-        }).then(function() {
-          return delTag(tagIds);
-        }).then(() => {
-          this.getList();
-          this.msgSuccess("删除成功");
-        }).catch(() => {});
-    },
-    /** 导出按钮操作 */
-    handleExport() {
-      const queryParams = this.queryParams;
-      this.$confirm('是否确认导出所有小程序会员标签数据项?', "警告", {
-          confirmButtonText: "确定",
-          cancelButtonText: "取消",
-          type: "warning"
-        }).then(() => {
-          this.exportLoading = true;
-          return exportTag(queryParams);
-        }).then(response => {
-          this.download(response.msg);
-          this.exportLoading = false;
-        }).catch(() => {});
-    }
-  }
-};
-</script>

+ 1 - 0
src/views/components/course/userCourseCatalogDetails.vue

@@ -385,6 +385,7 @@ import request from '@/utils/request'
       /** 重置按钮操作 */
       resetQuery() {
         this.resetForm("queryForm");
+        this.queryParams.title = null;
         this.handleQuery();
       },
       // 多选框选中数据

+ 377 - 17
src/views/course/courseFinishTemp/index.vue

@@ -56,18 +56,18 @@
         >新增
         </el-button>
       </el-col>
-<!--      <el-col :span="1.5">-->
-<!--        <el-button-->
-<!--          type="success"-->
-<!--          plain-->
-<!--          icon="el-icon-edit"-->
-<!--          size="mini"-->
-<!--          :disabled="multiple"-->
-<!--          @click="handleUpdate"-->
-<!--          v-hasPermi="['courseFinishTemp:course:edit']"-->
-<!--        >修改状态-->
-<!--        </el-button>-->
-<!--      </el-col>-->
+     <el-col :span="1.5">
+       <el-button
+         type="success"
+         plain
+         icon="el-icon-edit"
+         size="mini"
+         :disabled="multiple"
+         @click="handleUpdateStatusBatch"
+         v-hasPermi="['courseFinishTemp:course:edit']"
+       >批量修改状态
+       </el-button>
+     </el-col>
       <el-col :span="1.5">
         <el-button
           type="danger"
@@ -224,7 +224,52 @@
                       </el-card>
                     </div>
                     <div v-if="item.contentType == 4">
+                      <el-card class="box-card">
+
+                        <el-form-item label="选择课程">
+                          <el-select
+                            v-model="item.courseId"
+                            placeholder="请选择课程"
+                            style="margin-right: 10px"
+                            size="mini"
+                            @change="handleRuleCourseChange(item)"
+                          >
+                            <el-option
+                              v-for="dict in courseList"
+                              :key="dict.dictValue"
+                              :label="dict.dictLabel"
+                              :value="parseInt(dict.dictValue)"
+                            />
+                          </el-select>
+                          <el-select
+                            v-model="item.videoId"
+                            placeholder="请选择小节"
+                            size="mini"
+                            style="margin-right: 10px"
+                            filterable
+                            remote
+                            :remote-method="(query) => remoteMethodRuleVideo(query, item)"
+                            :loading="videoOptionsLoading"
+                            @change="handleRuleVideoChange(item)"
+                          >
+                            <el-option
+                              v-for="dict in videoOptions || []"
+                              :key="dict.dictValue"
+                              :label="dict.dictLabel"
+                              :value="parseInt(dict.dictValue)"
+                            />
+                          </el-select>
+                        </el-form-item>
 
+                        <el-card class="box-card" style="margin-top: 10px">
+                          <el-form-item label="标题" prop="miniprogramTitle">
+                            <el-input v-model="item.miniprogramTitle" placeholder="请输入小程序消息标题,最长为64字" />
+                          </el-form-item>
+                          <el-form-item label="封面" prop="miniprogramPicUrl">
+                            <ImageUpload v-model="item.miniprogramPicUrl" type="image" :num="10" :width="150" :height="150" />
+                          </el-form-item>
+                        </el-card>
+                      </el-card>
                     </div>
                     <div v-if="item.contentType == 5 ">
 
@@ -270,7 +315,44 @@
                         @input="handleInputVideoText(item.value,item)"/>
                     </div>
                     <div v-if="item.contentType == 8">
+                      <el-button type="primary"
+                                 style="margin-bottom: 1%"
+                                 @click="hanldeSelectVideoNum(setting,index)">
+                        选择视频号
+                      </el-button>
+                      <el-card class="box-card" v-if="item.coverUrl">
+                        <el-form-item label="封面标题:" label-width="100px">
+                          <el-input v-model="item.nickname"
+                                    style="width: 90%;margin-bottom: 1%" disabled/>
+                        </el-form-item>
+                        <el-form-item label="头像:" label-width="100px">
+                          <el-image
+                            v-if="item.avatar != null"
+                            :src="item.avatar"
+                            :preview-src-list="[item.avatar]"
+                            :style="{ width: '50px', height: '50px' }"
+                          ></el-image>
+                        </el-form-item>
+                        <el-form-item label="封面:" label-width="100px">
+                          <el-image
+                            v-if="item.coverUrl != null"
+                            :src="item.coverUrl"
+                            :preview-src-list="[item.coverUrl]"
+                            :style="{ width: '200px', height: '200px' }"
+                          ></el-image>
 
+                        </el-form-item>
+                        <el-form-item label="简介:" label-width="100px">
+                          <el-input type="textarea" :rows="3"
+                                    v-model="item.desc"
+                                    style="width: 90%;margin-top: 1%;" disabled/>
+                        </el-form-item>
+                        <el-form-item label="视频地址:" label-width="100px"
+                                      style="margin-top: 1%">
+                          <el-input v-model="item.url"
+                                    style="width: 90%;" disabled/>
+                        </el-form-item>
+                      </el-card>
                     </div>
 
                   </el-form-item>
@@ -324,7 +406,52 @@
                       </el-card>
                     </div>
                     <div v-if="item.contentType == 4">
+                      <el-card class="box-card">
 
+                        <el-form-item label="选择课程">
+                          <el-select
+                            v-model="item.courseId"
+                            placeholder="请选择课程"
+                            style="margin-right: 10px"
+                            size="mini"
+                            @change="handleRuleCourseChange(item)"
+                          >
+                            <el-option
+                              v-for="dict in courseList"
+                              :key="dict.dictValue"
+                              :label="dict.dictLabel"
+                              :value="parseInt(dict.dictValue)"
+                            />
+                          </el-select>
+                          <el-select
+                            v-model="item.videoId"
+                            placeholder="请选择小节"
+                            size="mini"
+                            style="margin-right: 10px"
+                            filterable
+                            remote
+                            :remote-method="(query) => remoteMethodRuleVideo(query, item)"
+                            :loading="videoOptionsLoading"
+                            @change="handleRuleVideoChange(item)"
+                          >
+                            <el-option
+                              v-for="dict in videoOptions || []"
+                              :key="dict.dictValue"
+                              :label="dict.dictLabel"
+                              :value="parseInt(dict.dictValue)"
+                            />
+                          </el-select>
+                        </el-form-item>
+
+                        <el-card class="box-card" style="margin-top: 10px">
+                          <el-form-item label="标题" prop="miniprogramTitle">
+                            <el-input v-model="item.miniprogramTitle" placeholder="请输入小程序消息标题,最长为64字" />
+                          </el-form-item>
+                          <el-form-item label="封面" prop="miniprogramPicUrl">
+                            <ImageUpload v-model="item.miniprogramPicUrl" type="image" :num="10" :width="150" :height="150" />
+                          </el-form-item>
+                        </el-card>
+                      </el-card>
                     </div>
                     <div v-if="item.contentType == 5 ">
 
@@ -370,7 +497,44 @@
                         @input="handleInputVideoText(item.value,item)"/>
                     </div>
                     <div v-if="item.contentType == 8">
+                      <el-button type="primary"
+                                 style="margin-bottom: 1%"
+                                 @click="hanldeSelectVideoNum(setting,index)">
+                        选择视频号
+                      </el-button>
+                      <el-card class="box-card" v-if="item.coverUrl">
+                        <el-form-item label="封面标题:" label-width="100px">
+                          <el-input v-model="item.nickname"
+                                    style="width: 90%;margin-bottom: 1%" disabled/>
+                        </el-form-item>
+                        <el-form-item label="头像:" label-width="100px">
+                          <el-image
+                            v-if="item.avatar != null"
+                            :src="item.avatar"
+                            :preview-src-list="[item.avatar]"
+                            :style="{ width: '50px', height: '50px' }"
+                          ></el-image>
+                        </el-form-item>
+                        <el-form-item label="封面:" label-width="100px">
+                          <el-image
+                            v-if="item.coverUrl != null"
+                            :src="item.coverUrl"
+                            :preview-src-list="[item.coverUrl]"
+                            :style="{ width: '200px', height: '200px' }"
+                          ></el-image>
 
+                        </el-form-item>
+                        <el-form-item label="简介:" label-width="100px">
+                          <el-input type="textarea" :rows="3"
+                                    v-model="item.desc"
+                                    style="width: 90%;margin-top: 1%;" disabled/>
+                        </el-form-item>
+                        <el-form-item label="视频地址:" label-width="100px"
+                                      style="margin-top: 1%">
+                          <el-input v-model="item.url"
+                                    style="width: 90%;" disabled/>
+                        </el-form-item>
+                      </el-card>
                     </div>
 
                   </el-form-item>
@@ -414,6 +578,28 @@
         <el-button @click="cancel">取 消</el-button>
       </div>
     </el-dialog>
+
+    <el-dialog title="批量修改状态" :visible.sync="batchOpen" width="400px" append-to-body>
+      <el-form ref="form" :model="statusForm">
+         <el-form-item label="状态">
+          <el-radio-group v-model="statusForm.status">
+            <el-radio
+              v-for="dict in statusOptions"
+              :key="dict.dictValue"
+              :label="parseInt(dict.dictValue)"
+            >{{ dict.dictLabel }}
+            </el-radio>
+          </el-radio-group>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer" v-if="formType==1">
+        <el-button type="primary" @click="submitFormBatchStatus" :loading="submitFormBatchStatusLoading">确 定</el-button>
+        <el-button @click="batchOpen = false">取 消</el-button>
+      </div>
+    </el-dialog>
+    <el-dialog :title="videoNumOptions.title" :visible.sync="videoNumOptions.open" width="1500px" append-to-body>
+      <userVideo ref="QwUserVideo" @videoResult="qwUserVideoResult"></userVideo>
+    </el-dialog>
   </div>
 </template>
 
@@ -424,18 +610,28 @@ import {
   exportCourseFinishTemp,
   getCourseFinishTemp,
   listCourseFinishTemp,
-  updateCourseFinishTemp
+  updateCourseFinishTemp,
+  updateStatusBatch
 } from '@/api/course/courseFinishTemp'
 import { getUserList } from '@/api/company/companyUser'
 import { courseList, videoList } from '@/api/qw/sop'
 import ImageUpload from '@/views/qw/sop/ImageUpload.vue'
 import Tip from "../../../components/Tip/index.vue";
-
+import userVideo from "@/views/qw/userVideo/userVideo.vue";
 export default {
   name: "CourseFinishTemp",
-  components: {Tip, ImageUpload},
+  components: {Tip, ImageUpload,userVideo},
   data() {
     return {
+      videoOptionsLoading: false,
+      videoOptions: [],
+      videoLoading: false,
+      videoNumOptions: {
+        title: '选择视频号',
+        open: false,
+        content: null,
+        contentIndex: null,
+      },
       //上传语音的遮罩层
       voiceLoading: false,
       uploadUrl: process.env.VUE_APP_BASE_API + "/common/uploadOSS2",
@@ -473,6 +669,8 @@ export default {
       title: "",
       // 是否显示弹出层
       open: false,
+      batchOpen:false,
+      submitFormBatchStatusLoading:false,
       // 查询参数
       queryParams: {
         pageNum: 1,
@@ -493,11 +691,15 @@ export default {
       chatSetting: [],
       // 表单参数
       form: {
+        companyUserIds: [],
         setting: null,
         chatSetting: null,
         videoIdSet: null,
         courseIdSet: null,
       },
+      statusForm:{
+        status:null
+      },
       // 表单校验
       rules: {}
     };
@@ -531,6 +733,101 @@ export default {
     this.getList();
   },
   methods: {
+    // 处理规则中课程变化
+    handleRuleCourseChange(item) {
+      // 为当前规则项单独加载视频列表
+      videoList(item.courseId).then((response) => {
+        // 只保存视频列表,不保存整个响应对象
+        this.videoOptions = response.list;
+
+        this.$set(item, 'videoId', null); // Reset video selection when course changes
+
+        // 自动设置封面为课程封面
+        const selectedCourse = this.courseList.find(
+          course => parseInt(course.dictValue) === item.courseId
+        );
+        if (selectedCourse) {
+          this.$set(item, 'miniprogramPicUrl', selectedCourse.dictImgUrl);
+        }
+      });
+    },
+
+
+    // 处理规则中视频变化
+    handleRuleVideoChange(item) {
+      if (!item.videoId) return;
+
+      // 自动设置标题为视频标题
+      const selectedVideo = (this.videoOptions || []).find(
+        video => parseInt(video.dictValue) === item.videoId
+      );
+      if (selectedVideo) {
+        this.$set(item, 'miniprogramTitle', selectedVideo.dictLabel);
+      }
+    },
+
+    // 远程搜索规则中的视频
+    remoteMethodRuleVideo(query, item) {
+      if (!item.courseId) {
+        this.$message.warning('请先选择课程');
+        this.videoOptions = [];
+        reject();
+        return;
+      }
+
+      this.videoOptionsLoading = true;
+      const data = query ? { title: query } : {};
+
+      videoList(item.courseId, data).then((response) => {
+        this.videoOptions =  response.list;
+        this.videoOptionsLoading =  false;
+        resolve(response);
+      }).catch((error) => {
+        this.videoOptionsLoading = false;
+        reject(error);
+      });
+    },
+    remoteMethodVideo(query) {
+      if (!this.form.courseId) {
+        this.$message.warning('请先选择课程');
+        this.videoList = []; // 清空小节列表
+        return;
+      }
+      if (query !== '') {
+        this.videoLoading = true;
+        // 这里调用接口搜索小节,假设 videoList 方法支持搜索参数
+        var data = {
+          title:query
+        }
+        videoList(this.form.courseId, data).then((response) => {
+          this.videoList = response.list;
+          this.videoLoading = false;
+        });
+      } else {
+        // 如果查询为空,则加载全部
+        videoList(this.form.courseId).then((response) => {
+          this.videoList = response.list;
+        });
+      }
+    },
+    qwUserVideoResult(val) {
+
+      // 根据选中的内容,将返回的数据更新到相应的表单项
+      const content = this.videoNumOptions.content;
+      const setList = content[this.videoNumOptions.contentIndex];
+      setList.nickname = val.nickname;
+      setList.avatar = val.avatar;
+      setList.coverUrl = val.coverUrl;
+      setList.thumbUrl = val.thumbUrl;
+      setList.desc = val.desc;
+      setList.url = val.url;
+      setList.extras = val.extras;
+      setList.videoId = val.id;
+      console.info(setList)
+
+      this.videoNumOptions.open = false;
+
+    },
     courseChange() {
 
       videoList(this.form.courseId).then(response => {
@@ -650,6 +947,7 @@ export default {
       this.setting = []
       this.chatSetting = []
       this.resetForm("form");
+      this.formType = 1
     },
     /** 搜索按钮操作 */
     handleQuery() {
@@ -673,6 +971,12 @@ export default {
       this.open = true;
       this.title = "添加完课模板";
     },
+    //选择视频号
+    hanldeSelectVideoNum(content, index) {
+      this.videoNumOptions.content = content;
+      this.videoNumOptions.contentIndex = index;
+      this.videoNumOptions.open = true;
+    },
 
     /**
      * 查看完课模板
@@ -701,6 +1005,15 @@ export default {
 
       });
     },
+    // 批量修改状态
+    handleUpdateStatusBatch(){
+      console.log(this.ids);
+      if(this.ids == null || this.ids == undefined || this.ids.length == 0 ){
+        return this.$message.error("请选择数据")
+      }
+      this.statusForm.status = null;
+      this.batchOpen = true;
+    },
     /** 修改按钮操作 */
     handleUpdate(row) {
       this.reset();
@@ -718,13 +1031,48 @@ export default {
         videoList(this.form.courseId).then(response => {
           this.videoList = response.list;
         });
+        this.setting.forEach(item => {
+          if (item.contentType == 4 && item.courseId) {
+            // 初始化 videoOptions
+            this.videoOptions = [];
+            this.videoOptionsLoading = false;
 
+            // 获取课程对应的小节列表
+            videoList(item.courseId).then((videoResponse) => {
+              this.videoOptions = videoResponse.list;
+            });
+          }
+        });
 
         this.open = true;
         this.title = "修改完课模板";
-
+        this.formType = 1;
       });
     },
+    submitFormBatchStatus(){
+      if(this.statusForm.status==null){
+        return this.$message.error("状态不能为空,请选择状态")
+      }
+      if(this.ids == null || this.ids == undefined || this.ids.length == 0 ){
+        return this.$message.error("请选择数据")
+      }
+      let _this = this;
+      _this.submitFormBatchStatusLoading = true;
+      let param ={
+        ids:_this.ids,
+        status:_this.statusForm.status
+      }
+      updateStatusBatch(param).then(res=>{
+        _this.submitFormBatchStatusLoading = false;
+        _this.msgSuccess("修改成功");
+        _this.batchOpen = false;
+        _this.getList();
+      }).catch(res=>{
+        console.log(res);
+        _this.submitFormBatchStatusLoading = false;
+      })
+
+    },
     /** 提交按钮 */
     submitForm() {
       this.$refs["form"].validate(valid => {
@@ -736,7 +1084,19 @@ export default {
           this.form.companyUserIds = this.companyUserIds.toString()
           this.form.parentId = this.parentId
 
-          this.form.setting = JSON.stringify(this.setting)
+
+          const processedSetting = this.setting.map(item => {
+            const newItem = {...item};
+            if (newItem.videoOptions) {
+              delete newItem.videoOptions;
+            }
+            if (newItem.videoLoading !== undefined) {
+              delete newItem.videoLoading;
+            }
+            return newItem;
+          });
+
+          this.form.setting = JSON.stringify(processedSetting);
           this.form.chatSetting = JSON.stringify(this.chatSetting)
 
           if (this.setting.length <= 0) {

+ 996 - 0
src/views/course/courseFinishTempParent/deptIndex.vue

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

+ 297 - 5
src/views/course/courseFinishTempParent/index.vue

@@ -178,7 +178,52 @@
                       </el-card>
                     </div>
                     <div v-if="item.contentType == 4">
+                      <el-card class="box-card">
+
+                        <el-form-item label="选择课程">
+                          <el-select
+                            v-model="item.courseId"
+                            placeholder="请选择课程"
+                            style="margin-right: 10px"
+                            size="mini"
+                            @change="handleRuleCourseChange(item)"
+                          >
+                            <el-option
+                              v-for="dict in courseList"
+                              :key="dict.dictValue"
+                              :label="dict.dictLabel"
+                              :value="parseInt(dict.dictValue)"
+                            />
+                          </el-select>
+                          <el-select
+                            v-model="item.videoId"
+                            placeholder="请选择小节"
+                            size="mini"
+                            style="margin-right: 10px"
+                            filterable
+                            remote
+                            :remote-method="(query) => remoteMethodRuleVideo(query, item)"
+                            :loading="videoOptionsLoading"
+                            @change="handleRuleVideoChange(item)"
+                          >
+                            <el-option
+                              v-for="dict in videoOptions || []"
+                              :key="dict.dictValue"
+                              :label="dict.dictLabel"
+                              :value="parseInt(dict.dictValue)"
+                            />
+                          </el-select>
+                        </el-form-item>
 
+                        <el-card class="box-card" style="margin-top: 10px">
+                          <el-form-item label="标题" prop="miniprogramTitle">
+                            <el-input v-model="item.miniprogramTitle" placeholder="请输入小程序消息标题,最长为64字" />
+                          </el-form-item>
+                          <el-form-item label="封面" prop="miniprogramPicUrl">
+                            <ImageUpload v-model="item.miniprogramPicUrl" type="image" :num="10" :width="150" :height="150" />
+                          </el-form-item>
+                        </el-card>
+                      </el-card>
                     </div>
                     <div v-if="item.contentType == 5 ">
 
@@ -224,7 +269,44 @@
                         @input="handleInputVideoText(item.value,item)"/>
                     </div>
                     <div v-if="item.contentType == 8">
+                      <el-button type="primary"
+                                 style="margin-bottom: 1%"
+                                 @click="hanldeSelectVideoNum(setting,index)">
+                        选择视频号
+                      </el-button>
+                      <el-card class="box-card" v-if="item.coverUrl">
+                        <el-form-item label="封面标题:" label-width="100px">
+                          <el-input v-model="item.nickname"
+                                    style="width: 90%;margin-bottom: 1%" disabled/>
+                        </el-form-item>
+                        <el-form-item label="头像:" label-width="100px">
+                          <el-image
+                            v-if="item.avatar != null"
+                            :src="item.avatar"
+                            :preview-src-list="[item.avatar]"
+                            :style="{ width: '50px', height: '50px' }"
+                          ></el-image>
+                        </el-form-item>
+                        <el-form-item label="封面:" label-width="100px">
+                          <el-image
+                            v-if="item.coverUrl != null"
+                            :src="item.coverUrl"
+                            :preview-src-list="[item.coverUrl]"
+                            :style="{ width: '200px', height: '200px' }"
+                          ></el-image>
 
+                        </el-form-item>
+                        <el-form-item label="简介:" label-width="100px">
+                          <el-input type="textarea" :rows="3"
+                                    v-model="item.desc"
+                                    style="width: 90%;margin-top: 1%;" disabled/>
+                        </el-form-item>
+                        <el-form-item label="视频地址:" label-width="100px"
+                                      style="margin-top: 1%">
+                          <el-input v-model="item.url"
+                                    style="width: 90%;" disabled/>
+                        </el-form-item>
+                      </el-card>
                     </div>
 
                   </el-form-item>
@@ -278,7 +360,52 @@
                       </el-card>
                     </div>
                     <div v-if="item.contentType == 4">
+                      <el-card class="box-card">
+
+                        <el-form-item label="选择课程">
+                          <el-select
+                            v-model="item.courseId"
+                            placeholder="请选择课程"
+                            style="margin-right: 10px"
+                            size="mini"
+                            @change="handleRuleCourseChange(item)"
+                          >
+                            <el-option
+                              v-for="dict in courseList"
+                              :key="dict.dictValue"
+                              :label="dict.dictLabel"
+                              :value="parseInt(dict.dictValue)"
+                            />
+                          </el-select>
+                          <el-select
+                            v-model="item.videoId"
+                            placeholder="请选择小节"
+                            size="mini"
+                            style="margin-right: 10px"
+                            filterable
+                            remote
+                            :remote-method="(query) => remoteMethodRuleVideo(query, item)"
+                            :loading="videoOptionsLoading"
+                            @change="handleRuleVideoChange(item)"
+                          >
+                            <el-option
+                              v-for="dict in videoOptions || []"
+                              :key="dict.dictValue"
+                              :label="dict.dictLabel"
+                              :value="parseInt(dict.dictValue)"
+                            />
+                          </el-select>
+                        </el-form-item>
 
+                        <el-card class="box-card" style="margin-top: 10px">
+                          <el-form-item label="标题" prop="miniprogramTitle">
+                            <el-input v-model="item.miniprogramTitle" placeholder="请输入小程序消息标题,最长为64字" />
+                          </el-form-item>
+                          <el-form-item label="封面" prop="miniprogramPicUrl">
+                            <ImageUpload v-model="item.miniprogramPicUrl" type="image" :num="10" :width="150" :height="150" />
+                          </el-form-item>
+                        </el-card>
+                      </el-card>
                     </div>
                     <div v-if="item.contentType == 5 ">
 
@@ -324,7 +451,44 @@
                         @input="handleInputVideoText(item.value,item)"/>
                     </div>
                     <div v-if="item.contentType == 8">
+                      <el-button type="primary"
+                                 style="margin-bottom: 1%"
+                                 @click="hanldeSelectVideoNum(setting,index)">
+                        选择视频号
+                      </el-button>
+                      <el-card class="box-card" v-if="item.coverUrl">
+                        <el-form-item label="封面标题:" label-width="100px">
+                          <el-input v-model="item.nickname"
+                                    style="width: 90%;margin-bottom: 1%" disabled/>
+                        </el-form-item>
+                        <el-form-item label="头像:" label-width="100px">
+                          <el-image
+                            v-if="item.avatar != null"
+                            :src="item.avatar"
+                            :preview-src-list="[item.avatar]"
+                            :style="{ width: '50px', height: '50px' }"
+                          ></el-image>
+                        </el-form-item>
+                        <el-form-item label="封面:" label-width="100px">
+                          <el-image
+                            v-if="item.coverUrl != null"
+                            :src="item.coverUrl"
+                            :preview-src-list="[item.coverUrl]"
+                            :style="{ width: '200px', height: '200px' }"
+                          ></el-image>
 
+                        </el-form-item>
+                        <el-form-item label="简介:" label-width="100px">
+                          <el-input type="textarea" :rows="3"
+                                    v-model="item.desc"
+                                    style="width: 90%;margin-top: 1%;" disabled/>
+                        </el-form-item>
+                        <el-form-item label="视频地址:" label-width="100px"
+                                      style="margin-top: 1%">
+                          <el-input v-model="item.url"
+                                    style="width: 90%;" disabled/>
+                        </el-form-item>
+                      </el-card>
                     </div>
 
                   </el-form-item>
@@ -367,21 +531,34 @@
         <el-button @click="cancel">取 消</el-button>
       </div>
     </el-dialog>
+    <el-dialog :title="videoNumOptions.title" :visible.sync="videoNumOptions.open" width="1500px" append-to-body>
+      <userVideo ref="QwUserVideo" @videoResult="qwUserVideoResult"></userVideo>
+    </el-dialog>
   </div>
 </template>
 
 <script>
 import { listCourseFinishTempParent, getCourseFinishTempParent, delCourseFinishTempParent, addCourseFinishTempParent, updateCourseFinishTempParent, exportCourseFinishTempParent } from "@/api/course/courseFinishTempParent";
-import {courseList} from '@/api/qw/sop'
+import {courseList, videoList} from '@/api/qw/sop'
 import ImageUpload from '@/views/qw/sop/ImageUpload.vue'
 import { getUserList } from '@/api/company/companyUser'
+import userVideo from "@/views/qw/userVideo/userVideo.vue";
 
 
 export default {
   name: "CourseFinishTempParent",
-  components: { ImageUpload },
+  components: { ImageUpload ,userVideo},
   data() {
     return {
+      videoOptionsLoading: false,
+      videoOptions: [],
+      videoLoading: false,
+      videoNumOptions: {
+        title: '选择视频号',
+        open: false,
+        content: null,
+        contentIndex: null,
+      },
       voiceLoading: false,
       uploadUrl: process.env.VUE_APP_BASE_API + "/common/uploadOSS2",
       uploadUrlByVoice: process.env.VUE_APP_BASE_API + "/common/uploadOSSByHOOKVoice",
@@ -424,7 +601,7 @@ export default {
         courseId: null,
       },
       // 表单参数
-      form: {},
+      form: {companyUserIds: [],},
       // 表单校验
       rules: {
         name:[
@@ -460,6 +637,83 @@ export default {
     this.getList();
   },
   methods: {
+    // 处理规则中课程变化
+    handleRuleCourseChange(item) {
+      // 为当前规则项单独加载视频列表
+      videoList(item.courseId).then((response) => {
+        // 只保存视频列表,不保存整个响应对象
+        this.videoOptions = response.list;
+
+        this.$set(item, 'videoId', null); // Reset video selection when course changes
+
+        // 自动设置封面为课程封面
+        const selectedCourse = this.courseList.find(
+          course => parseInt(course.dictValue) === item.courseId
+        );
+        if (selectedCourse) {
+          this.$set(item, 'miniprogramPicUrl', selectedCourse.dictImgUrl);
+        }
+      });
+    },
+
+
+    // 处理规则中视频变化
+    handleRuleVideoChange(item) {
+      if (!item.videoId) return;
+
+      // 自动设置标题为视频标题
+      const selectedVideo = (this.videoOptions || []).find(
+        video => parseInt(video.dictValue) === item.videoId
+      );
+      if (selectedVideo) {
+        this.$set(item, 'miniprogramTitle', selectedVideo.dictLabel);
+      }
+    },
+
+    // 远程搜索规则中的视频
+    remoteMethodRuleVideo(query, item) {
+      if (!item.courseId) {
+        this.$message.warning('请先选择课程');
+        this.videoOptions = [];
+        reject();
+        return;
+      }
+
+      this.videoOptionsLoading = true;
+      const data = query ? { title: query } : {};
+
+      videoList(item.courseId, data).then((response) => {
+        this.videoOptions =  response.list;
+        this.videoOptionsLoading =  false;
+        resolve(response);
+      }).catch((error) => {
+        this.videoOptionsLoading = false;
+        reject(error);
+      });
+    },
+    remoteMethodVideo(query) {
+      if (!this.form.courseId) {
+        this.$message.warning('请先选择课程');
+        this.videoList = []; // 清空小节列表
+        return;
+      }
+      if (query !== '') {
+        this.videoLoading = true;
+        // 这里调用接口搜索小节,假设 videoList 方法支持搜索参数
+        var data = {
+          title:query
+        }
+        videoList(this.form.courseId, data).then((response) => {
+          this.videoList = response.list;
+          this.videoLoading = false;
+        });
+      } else {
+        // 如果查询为空,则加载全部
+        videoList(this.form.courseId).then((response) => {
+          this.videoList = response.list;
+        });
+      }
+    },
     /** 查询完课模板列表 */
     getList() {
       this.loading = true;
@@ -488,7 +742,8 @@ export default {
         updateBy: null,
         updateTime: null,
         companyUserIds: null,
-        remark: null
+        remark: null,
+        isAllCompanyUser: 2
       };
       this.resetForm("form");
     },
@@ -514,6 +769,31 @@ export default {
       this.open = true;
       this.title = "添加完课模板";
     },
+    //选择视频号
+    hanldeSelectVideoNum(content, index) {
+      this.videoNumOptions.content = content;
+      this.videoNumOptions.contentIndex = index;
+      this.videoNumOptions.open = true;
+    },
+
+    qwUserVideoResult(val) {
+
+      // 根据选中的内容,将返回的数据更新到相应的表单项
+      const content = this.videoNumOptions.content;
+      const setList = content[this.videoNumOptions.contentIndex];
+      setList.nickname = val.nickname;
+      setList.avatar = val.avatar;
+      setList.coverUrl = val.coverUrl;
+      setList.thumbUrl = val.thumbUrl;
+      setList.desc = val.desc;
+      setList.url = val.url;
+      setList.extras = val.extras;
+      setList.videoId = val.id;
+      console.info(setList)
+
+      this.videoNumOptions.open = false;
+
+    },
     /** 修改按钮操作 */
     handleUpdate(row) {
       this.reset();
@@ -540,7 +820,19 @@ export default {
             }
             this.form.companyUserIds = this.companyUserIds.toString()
 
-            this.form.setting = JSON.stringify(this.setting)
+
+            const processedSetting = this.setting.map(item => {
+              const newItem = {...item};
+              if (newItem.videoOptions) {
+                delete newItem.videoOptions;
+              }
+              if (newItem.videoLoading !== undefined) {
+                delete newItem.videoLoading;
+              }
+              return newItem;
+            });
+
+            this.form.setting = JSON.stringify(processedSetting);
             this.form.chatSetting = JSON.stringify(this.chatSetting)
 
             if (this.setting.length <= 0) {

+ 993 - 0
src/views/course/courseFinishTempParent/myIndex.vue

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

+ 3 - 3
src/views/course/courseRedPacketLog/index.vue

@@ -136,7 +136,7 @@
       <el-table-column label="所属销售" align="center" prop="companyUserName" />
       <el-table-column label="所属公司" align="center" prop="companyName" />
       <el-table-column label="企微员工名称" align="center" prop="qwUserName" />
-      <el-table-column label="转金额" align="center" prop="amount" />
+      <el-table-column label="转金额" align="center" prop="amount" />
       <el-table-column label="状态" align="center" prop="status" >
         <template slot-scope="scope">
           <el-tag>{{ scope.row.status === 0 ? "发送中" : scope.row.status === 1 ? "已完成" : "待补发" }}</el-tag>
@@ -181,8 +181,8 @@
         <el-form-item label="公司id" prop="companyId">
           <el-input v-model="form.companyId" placeholder="请输入公司id" />
         </el-form-item>
-        <el-form-item label="转金额" prop="amount">
-          <el-input v-model="form.amount" placeholder="请输入转金额" />
+        <el-form-item label="转金额" prop="amount">
+          <el-input v-model="form.amount" placeholder="请输入转金额" />
         </el-form-item>
         <el-form-item label="企微userid" prop="qwUserId">
           <el-input v-model="form.qwUserId" placeholder="请输入分享企微userid" />

+ 3 - 3
src/views/course/courseRedPacketLog/myCourseRedPacketLog.vue

@@ -126,7 +126,7 @@
       <el-table-column label="会员电话" align="center" prop="phone" />
       <el-table-column label="所属销售" align="center" prop="companyUserName" />
       <el-table-column label="所属公司" align="center" prop="companyName" />
-      <el-table-column label="转金额" align="center" prop="amount" />
+      <el-table-column label="转金额" align="center" prop="amount" />
       <el-table-column label="状态" align="center" prop="status" >
         <template slot-scope="scope">
           <el-tag>{{ scope.row.status === 0 ? "发送中" : scope.row.status === 1 ? "已完成" : "待补发" }}</el-tag>
@@ -171,8 +171,8 @@
         <el-form-item label="公司id" prop="companyId">
           <el-input v-model="form.companyId" placeholder="请输入公司id" />
         </el-form-item>
-        <el-form-item label="转金额" prop="amount">
-          <el-input v-model="form.amount" placeholder="请输入转金额" />
+        <el-form-item label="转金额" prop="amount">
+          <el-input v-model="form.amount" placeholder="请输入转金额" />
         </el-form-item>
         <el-form-item label="企微userid" prop="qwUserId">
           <el-input v-model="form.qwUserId" placeholder="请输入分享企微userid" />

+ 10 - 31
src/views/course/courseUserStatistics/qw/index.vue

@@ -367,7 +367,7 @@ import {
   exportCourseWatchLog,
   statisticsList,
   qwWatchLogStatisticsList,
-  exportWatchLogStatistics
+  // exportWatchLogStatistics
 } from "@/api/course/qw/courseWatchLog";
 import { courseList, videoList } from "@/api/course/courseRedPacketLog";
 import { treeselect } from "@/api/company/companyDept";
@@ -698,39 +698,18 @@ export default {
     },
     /** 导出按钮操作 */
     handleExport() {
-      let that = this;
       const queryParams = this.queryParams;
-      this.$confirm("是否确认导出所有进线客户统计记录数据项?", "警告", {
+      this.$confirm('是否确认导出所有短链课程看课记录数据项?', "警告", {
         confirmButtonText: "确定",
         cancelButtonText: "取消",
-        type: "warning",
-      })
-        .then(() => {
-          this.exportLoading = true;
-          // return exportCourseWatchLog(queryParams);
-         return  exportWatchLogStatistics(queryParams);
-        })
-        .then((response) => {
-          console.log(response);
-          if (response.code == 200) {
-            that.msgSuccess(response.msg);
-            that.taskId = response.data;
-            that.time = setInterval(function () {
-              //查订单
-              getTask(that.taskId).then((res) => {
-                if (res.data.status == 1) {
-                  that.exportLoading = false;
-                  clearTimeout(that.time);
-                  that.time = null;
-                  that.download(res.data.fileUrl);
-                }
-              });
-            }, 10000);
-          }
-        })
-        .catch(() => {
-          this.exportLoading = false;
-        });
+        type: "warning"
+      }).then(() => {
+        this.exportLoading = true;
+        return exportCourseWatchLog(queryParams);
+      }).then(response => {
+        this.download(response.msg);
+        this.exportLoading = false;
+      }).catch(() => {});
     },
     handleScheduleTimeChange(val) {
       if (val) {

+ 282 - 21
src/views/course/courseWatchLog/deptWatchLog.vue

@@ -1,6 +1,16 @@
 <template>
   <div class="app-container">
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
+      <el-form-item label="看课方式" prop="sendType">
+        <el-select v-model="queryParams.sendType" placeholder="选择看课方式"  clearable size="small" @change="handleSendTypeChange">
+          <el-option
+            v-for="dict in sendTypeOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
       <el-form-item label="会员ID" prop="userId">
         <el-input
           v-model="queryParams.userId"
@@ -74,6 +84,26 @@
           />
         </el-select>
       </el-form-item>
+      <!-- sop名称 -->
+      <el-form-item label="SOP名称" prop="sopId" v-if="queryParams.sendType == 2">
+        <el-autocomplete
+          v-model="sopSearchText"
+          :fetch-suggestions="querySopAsync"
+          placeholder="请输入SOP名称"
+          clearable
+          size="small"
+          style="width: 200px"
+          @select="handleSopSelect"
+          @clear="handleSopClear"
+          :trigger-on-focus="false"
+        >
+          <template slot-scope="{ item }">
+            <div class="sop-item">
+              <span class="sop-name">{{ item.name }}</span>
+            </div>
+          </template>
+        </el-autocomplete>
+      </el-form-item>
       <el-form-item label="企微ID" prop="qwUserUserId">
         <el-input
           v-model="queryParams.qwUserUserId"
@@ -102,7 +132,7 @@
 <!--        />-->
 <!--      </el-form-item>-->
       <!-- 营期时间 -->
-      <el-form-item label="营期时间" prop="scheduleTime">
+      <!-- <el-form-item label="营期时间" prop="scheduleTime">
         <el-input
           v-model="scheduleTimeText"
           placeholder="请选择营期时间"
@@ -116,9 +146,21 @@
           @change="handleScheduleTimeChange"
           :key="scheduleCalendarKey"
         />
+      </el-form-item> -->
+       <el-form-item label="营期时间" prop="scheduleTime">
+        <el-date-picker
+          v-model="scheduleTimeText"
+          type="daterange"
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          value-format="yyyy-MM-dd"
+          style="width: 240px"
+           @change="handleScheduleTimeChange"
+        />
       </el-form-item>
       <!-- 创建时间 -->
-      <el-form-item label="创建时间" prop="createTime">
+      <!-- <el-form-item label="创建时间" prop="createTime">
         <el-input
           v-model="createTimeText"
           placeholder="请选择创建时间"
@@ -132,9 +174,21 @@
           @change="createChange"
           :key="createCalendarKey"
         />
+      </el-form-item> -->
+      <el-form-item label="创建时间" prop="createTime">
+        <el-date-picker
+          v-model="createTimeText"
+          type="datetimerange"
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          value-format="yyyy-MM-dd HH:mm:ss"
+          @change="createChange"
+          :default-time="['00:00:00', '23:59:59']"
+        />
       </el-form-item>
       <!-- 最新更新时间 -->
-      <el-form-item label="最新更新时间" prop="updateTime">
+      <!-- <el-form-item label="最新更新时间" prop="updateTime">
         <el-input
           v-model="updateTimeText"
           placeholder="请选择更新时间"
@@ -148,9 +202,21 @@
           @change="updateChange"
           :key="updateCalendarKey"
         />
+      </el-form-item> -->
+       <el-form-item label="最新更新时间" prop="updateTime">
+        <el-date-picker
+          v-model="updateTimeText"
+          type="daterange"
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          value-format="yyyy-MM-dd"
+          style="width: 240px"
+           @change="updateChange"
+        />
       </el-form-item>
       <!-- 进线时间 -->
-      <el-form-item label="进线时间" prop="qecCreateTime">
+      <!-- <el-form-item label="进线时间" prop="qecCreateTime">
         <el-input
           v-model="qecCreateTimeText"
           placeholder="请选择进线时间"
@@ -164,6 +230,35 @@
           @change="qecCreateTimeChange"
           :key="qecCalendarKey"
         />
+      </el-form-item> -->
+      <el-form-item label="进线时间" prop="qecCreateTime">
+        <el-date-picker
+          v-model="qecCreateTimeText"
+          type="daterange"
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          value-format="yyyy-MM-dd"
+          style="width: 240px"
+          @change="qecCreateTimeChange"
+        />
+      </el-form-item>
+
+
+      <el-form-item label="营期课程时间" prop="periodTime" v-if="queryParams.sendType==1">
+        <el-date-picker
+          v-model="periodTimeText"
+          type="datetimerange"
+          align="right"
+          unlink-panels
+          value-format="yyyy-MM-dd HH:mm:ss"
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          :picker-options="pickerOptions"
+          @change="qecPeriodTimeChange"
+          :default-time="['00:00:00', '23:59:59']">
+        </el-date-picker>
       </el-form-item>
 
       <el-form-item>
@@ -239,6 +334,16 @@
       <el-table-column label="完课时间" align="center" prop="finishTime" />
       <el-table-column label="营期时间" align="center" prop="campPeriodTime" />
       <el-table-column label="进线时间" align="center" prop="qecCreateTime" />
+      <el-table-column label="是否领奖" align="center" prop="rewardType" >
+        <template slot-scope="scope">
+          <el-tag
+            :type="scope.row.rewardType ? 'success' : 'info'"
+            effect="plain"
+          >
+            {{ scope.row.rewardType ? '已领取' : '未领取' }}
+          </el-tag>
+        </template>
+      </el-table-column>
       <el-table-column
         fixed="right"
         label="操作"
@@ -329,7 +434,7 @@
 <!--        <el-table-column label="会员电话" align="center" prop="phone" />-->
 <!--        <el-table-column label="所属销售" align="center" prop="companyUserName" />-->
 <!--        <el-table-column label="所属公司" align="center" prop="companyName" />-->
-        <el-table-column label="转金额" align="center" prop="amount" />
+        <el-table-column label="转金额" align="center" prop="amount" />
         <el-table-column label="状态" align="center" prop="status" >
           <template slot-scope="scope">
             <el-tag>
@@ -365,6 +470,8 @@ import { getCompanyUserListLikeName } from "@/api/company/companyUser";
 import {getTask} from "@/api/common";
 import Vue from 'vue'
 import Calendar from 'vue-mobile-calendar'
+import {infoSop} from "@/api/qw/sop";
+import {getMyQwUserList} from "@/api/qw/user";
 Vue.use(Calendar)
 
 
@@ -373,21 +480,33 @@ export default {
   data() {
     return {
       companyName:process.env.VUE_APP_COURSE_COMPANY_NAME,
+      sopSearchText: '', // SOP搜索框显示的文本
+      selectedSopId: null, // 选中的SOP ID
+      sendTypeOptions:[{
+        dictLabel:"会员",dictValue:'1'
+      },
+        {
+          dictLabel:"企微",dictValue:'2'
+        }
+      ],
       // 日历 key 控制刷新
       scheduleCalendarKey: 0,
       createCalendarKey: 0,
       updateCalendarKey: 0,
       qecCalendarKey: 0,
+      periodTimeKey: 0,
 
       createTimeText: '',
       scheduleTimeText: '',  // 新增
       updateTimeText: '',    // 新增
       qecCreateTimeText: '', // 新增
+      periodTimeText: '', // 营期课程时间
 
       scheduleTime: [],  // 改为数组
       createTime: [],    // 改为数组
       updateTime: [],    // 改为数组
       qecCreateTime: [], // 改为数组
+      periodTime: [], // 改为数组
 
       // 控制日历显隐
       showScheduleCalendar: false,
@@ -443,9 +562,9 @@ export default {
 
       pickerOptions: {
         disabledDate(time) {
-          // 获取6天前的日期(加上今天就是7天)
+          // 获取13天前的日期(加上今天就是14天)
           const sixDaysAgo = new Date();
-          sixDaysAgo.setDate(sixDaysAgo.getDate() - 6);
+          sixDaysAgo.setDate(sixDaysAgo.getDate() - 13);
           sixDaysAgo.setHours(0, 0, 0, 0);
 
           // 获取明天的日期(不包括今天)
@@ -480,8 +599,12 @@ export default {
         upETime:null,
         qecSTime:null,
         qecETime:null,
+        periodSTime:null,
+        periodETime:null,
         scheduleStartTime: null,
         scheduleEndTime: null,
+        sendType:process.env.VUE_APP_COURSE_DEFAULT,
+        sopId: null, // sopId
       },
       // 表单参数
       form: {},
@@ -511,23 +634,30 @@ export default {
     this.getCompanyUserListLikeName(true);
   },
   methods: {
+    handleSendTypeChange() {
+      this.handleQuery(); // 重新查询列表
+    },
+
     // 重置日历组件
     resetCalendars() {
       this.scheduleTime = [];
       this.createTime = [];
       this.updateTime = [];
       this.qecCreateTime = [];
+      this.periodTime = [];
 
       this.scheduleTimeText = '';
       this.createTimeText = '';
       this.updateTimeText = '';
       this.qecCreateTimeText = '';
+      this.periodTimeText = [];
 
       // 强制刷新日历组件
       this.scheduleCalendarKey++;
       this.createCalendarKey++;
       this.updateCalendarKey++;
       this.qecCalendarKey++;
+      this.periodTimeKey++;
     },
     formatDateRange(dates) {
       if (!dates || dates.length < 2) return '';
@@ -609,10 +739,20 @@ export default {
       this.queryParams.upETime = null;
       this.queryParams.qecSTime = null;
       this.queryParams.qecETime = null;
+      this.queryParams.periodSTime = null;
+      this.queryParams.periodDTime = null;
       this.queryParams.scheduleStartTime = null;
       this.queryParams.scheduleEndTime = null;
+      this.queryParams.sopId = null; // 重置SOP ID
       this.scheduleTime=null;
       this.updateTime=null;
+      this.queryParams.externalUserName=null;
+
+      // 重置SOP搜索
+      this.handleSopClear();
+      // 统一重置日历组件
+      this.resetCalendars();
+
       this.handleQuery();
     },
     // 多选框选中数据
@@ -725,7 +865,7 @@ export default {
     },
     redLogList() {
       this.loadingRedLog = true;
-      console.info(this.redLogQueryParams)
+      console.info("------------dept",this.redLogQueryParams)
       myListCourseRedPacketLog(this.redLogQueryParams).then(e => {
         this.redLogsList = e.rows;
         this.redLogTotal = e.total;
@@ -736,11 +876,11 @@ export default {
     // 营期时间
     handleScheduleTimeChange(scheduleTime) {
       if (scheduleTime && scheduleTime.length >= 2) {
-        this.scheduleTimeText = this.formatDateRange(scheduleTime);
-        this.queryParams.scheduleStartTime = scheduleTime.map(date => date.format('YYYY-MM-DD'))[0] || null;
-        this.queryParams.scheduleEndTime = scheduleTime.map(date => date.format('YYYY-MM-DD'))[1] || null;
+        // this.scheduleTimeText = this.formatDateRange(scheduleTime);
+        this.queryParams.scheduleStartTime = scheduleTime[0] || null;
+        this.queryParams.scheduleEndTime = scheduleTime[1] || null;
       } else {
-        this.scheduleTimeText = '';
+        this.scheduleTimeText = [];
         this.queryParams.scheduleStartTime = null;
         this.queryParams.scheduleEndTime = null;
       }
@@ -748,9 +888,9 @@ export default {
     // 创建时间
     createChange(createTime) {
       if (createTime && createTime.length >= 2) {
-        this.createTimeText = this.formatDateRange(createTime);
-        this.queryParams.sTime = createTime.map(date => date.format('YYYY-MM-DD'))[0] || null;
-        this.queryParams.eTime = createTime.map(date => date.format('YYYY-MM-DD'))[1] || null;
+        // this.createTimeText = this.formatDateRange(createTime);
+        this.queryParams.sTime = this.formatDate(createTime[0]) || null;
+        this.queryParams.eTime = this.formatDate(createTime[1]) || null;
       } else {
         this.createTimeText = '';
         this.queryParams.sTime = null;
@@ -762,8 +902,8 @@ export default {
     updateChange(updateTime) {
       if (updateTime && updateTime.length >= 2) {
         this.updateTimeText = this.formatDateRange(updateTime);
-        this.queryParams.upSTime = updateTime.map(date => date.format('YYYY-MM-DD'))[0] || null;
-        this.queryParams.upETime = updateTime.map(date => date.format('YYYY-MM-DD'))[1] || null;
+        this.queryParams.upSTime = updateTime[0] || null;
+        this.queryParams.upETime = updateTime[1] || null;
       } else {
         this.updateTimeText = '';
         this.queryParams.upSTime = null;
@@ -790,22 +930,87 @@ export default {
           this.$message.error('进线时间选择范围不能超过7天');
           // 清空选择
           this.qecCreateTime = [];
-          this.qecCreateTimeText = '';
+          this.qecCreateTimeText = [];
           this.queryParams.qecSTime = null;
           this.queryParams.qecETime = null;
           this.qecCalendarKey++;
           return;
         }
 
-        this.qecCreateTimeText = this.formatDateRange(qecCreateTime);
-        this.queryParams.qecSTime = qecCreateTime.map(date => date.format('YYYY-MM-DD'))[0] || null;
-        this.queryParams.qecETime = qecCreateTime.map(date => date.format('YYYY-MM-DD'))[1] || null;
+        // this.qecCreateTimeText = this.formatDateRange(qecCreateTime);
+        this.queryParams.qecSTime = qecCreateTime[0] || null;
+        this.queryParams.qecETime = qecCreateTime[1] || null;
       } else {
         this.qecCreateTimeText = '';
         this.queryParams.qecSTime = null;
         this.queryParams.qecETime = null;
       }
     },
+
+    //营期课程时间
+    qecPeriodTimeChange(periodTime){
+
+      if (periodTime && periodTime.length >= 2) {
+        // 检查选择的日期范围是否超过7天(包括起始和结束日期)
+        const startDate = new Date(periodTime[0]);
+        const endDate = new Date(periodTime[1]);
+
+        // 设置时间为当天开始,避免时间部分影响计算
+        startDate.setHours(0, 0, 0, 0);
+        endDate.setHours(0, 0, 0, 0);
+
+        const timeDiff = Math.abs(endDate - startDate);
+        const diffDays = Math.ceil(timeDiff / (1000 * 60 * 60 * 24));
+
+        // 如果超过6天的范围(总共7天,包括起始日)
+        if (diffDays > 13) {
+          this.$message.error('时间选择范围不能超过14天');
+          // 清空选择
+          this.periodTime = [];
+          this.periodTimeText = [];
+          this.queryParams.periodSTime = null;
+          this.queryParams.periodETime = null;
+          this.periodTimeKey++;
+          return;
+        }
+
+        this.queryParams.periodSTime = this.formatDate(periodTime[0]) || null;
+        this.queryParams.periodETime = this.formatDate(periodTime[1]) || null;
+
+      } else {
+
+        this.periodTimeText = '';
+        this.queryParams.periodSTime = null;
+        this.queryParams.periodETime = null;
+      }
+
+    },
+
+    formatDate(date) {
+      if (!date) return ''
+
+      // 确保 date 是 Date 对象
+      let dateObj = date
+      if (typeof date === 'string') {
+        dateObj = new Date(date)
+      }
+
+      // 如果转换失败,返回空字符串
+      if (!(dateObj instanceof Date) || isNaN(dateObj.getTime())) {
+        return ''
+      }
+
+      // 使用更安全的格式化方法
+      const year = dateObj.getFullYear()
+      const month = String(dateObj.getMonth() + 1).padStart(2, '0')
+      const day = String(dateObj.getDate()).padStart(2, '0')
+      const hours = String(dateObj.getHours()).padStart(2, '0')
+      const minutes = String(dateObj.getMinutes()).padStart(2, '0')
+      const seconds = String(dateObj.getSeconds()).padStart(2, '0')
+
+      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
+    },
+
     /**
      * 根据名称模糊查询用户列表
      * @param query 参数
@@ -834,6 +1039,7 @@ export default {
           this.companyUserOptionsLoading = false;
         });
       }else {
+        this.companyUserOptionsParams.pageSize=10;
         getCompanyUserListLikeName(this.companyUserOptionsParams).then(response => {
           this.companyUserOptions = [...this.companyUserOptions, ...response.data.list]
           this.companyUserOptionsParams.hasNextPage = response.data.hasNextPage
@@ -853,6 +1059,61 @@ export default {
       this.companyUserOptionsParams.pageNum += 1
       this.getCompanyUserListLikeName()
     },
+    /**
+     * 选择SOP
+     * @param {object} item - 选中的SOP项
+     */
+    handleSopSelect(item) {
+      this.selectedSopId = item.id;
+      this.queryParams.sopId = item.id;
+      this.sopSearchText = item.name;
+    },
+
+    /**
+     * 清空SOP选择
+     */
+    handleSopClear() {
+      this.selectedSopId = null;
+      this.queryParams.sopId = null;
+      this.sopSearchText = '';
+    },
+
+    /**
+     * 异步查询SOP列表
+     * @param {string} queryString - 查询字符串
+     * @param {function} callback - 回调函数
+     */
+    querySopAsync(queryString, callback) {
+      if (!queryString) {
+        callback([]);
+        return;
+      }
+
+      infoSop({ name: queryString }).then(response => {
+        if (response && response.rows) {
+          const suggestions = response.rows.map(item => ({
+            value: item.name,
+            id: item.id,
+            name: item.name
+          }));
+          callback(suggestions);
+        } else {
+          callback([]);
+        }
+      }).catch(error => {
+        console.error('通过sop查询失败:', error);
+        callback([]);
+      });
+    },
+    updateQwuser() {
+      for (const user of this.myQwUserList) {
+        if (user.dictValue == this.queryParams.qwUserId) {
+          this.queryParams.corpId = user.corpId;
+          break;
+        }
+      }
+      this.getList();
+    },
   }
 };
 </script>

+ 485 - 93
src/views/course/courseWatchLog/index.vue

@@ -2,7 +2,7 @@
   <div class="app-container">
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
       <el-form-item label="看课方式" prop="sendType">
-        <el-select v-model="queryParams.sendType" placeholder="选择看课方式"  clearable size="small" @change="handleSendTypeChange">
+        <el-select v-model="queryParams.sendType" placeholder="选择看课方式" size="small" @change="handleSendTypeChange">
           <el-option
             v-for="dict in sendTypeOptions"
             :key="dict.dictValue"
@@ -11,6 +11,16 @@
           />
         </el-select>
       </el-form-item>
+<!--        <el-form-item label="项目" prop="project">-->
+<!--          <el-select  v-model="queryParams.project" placeholder="请选择项目" clearable size="small" >-->
+<!--            <el-option-->
+<!--              v-for="item in projectOptions"-->
+<!--              :key="item.dictValue"-->
+<!--              :label="item.dictLabel"-->
+<!--              :value="item.dictValue"-->
+<!--            />-->
+<!--          </el-select>-->
+<!--        </el-form-item>-->
       <el-form-item label="会员昵称" prop="nickName" v-if="queryParams.sendType == 1">
         <el-input
           v-model="queryParams.nickName"
@@ -58,11 +68,14 @@
           />
         </el-select>
       </el-form-item>
-      <el-form-item label="所属销售" prop="companyUserId">
+      <el-form-item v-if="companyName === undefined || companyName === 1" label="所属销售" prop="companyUserId">
         <el-select v-model="queryParams.companyUserId" clearable filterable remote
                    placeholder="请输入关键词" :remote-method="loadCompanyUserOptions"
                    v-select-load-more="loadMoreCompanyUserOptions"
-                   :loading="companyUserOptionsLoading">
+                   @change="handleCompanyUserChange"
+                   :loading="companyUserOptionsLoading"
+                   @visible-change="handleCompanyUserDropdownVisible"
+        >
           <el-option
             v-for="item in companyUserOptions"
             :key="item.dictValue"
@@ -71,6 +84,35 @@
           </el-option>
         </el-select>
       </el-form-item>
+      <el-form-item v-if="companyName==2" label="所属销售" prop="companyUserId">
+        <el-select v-model="queryParams.companyUserId" clearable filterable remote
+                   placeholder="请输入关键词"
+                   v-select-load-more="loadMoreCompanyUserOptions"
+                   @change="handleCompanyUserChange"
+                   @visible-change="handleQwUserDropdownVisible"
+                   :loading="companyUserOptionsLoading">
+          <el-option
+            v-for="item in companyUserOptionsByAll"
+            :key="item.dictValue"
+            :label="item.dictLabel"
+            :value="item.dictValue">
+          </el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="所属企微" prop="qwUserName" v-if="queryParams.companyUserId && (queryParams.sendType == 2)">
+        <el-select v-model="queryParams.qwUserName"
+                   clearable filterable
+                   placeholder="请输入关键词"
+                   @visible-change="handleQwUserDropdownVisible"
+                   :loading="qwUserOptionsLoading">
+          <el-option
+            v-for="item in qwUserOptions"
+            :key="item.qwUserId"
+            :label="item.qwUserName"
+            :value="item.qwUserName">
+          </el-option>
+        </el-select>
+      </el-form-item>
     <!-- sop名称 -->
     <el-form-item label="SOP名称" prop="sopId" v-if="queryParams.sendType == 2">
       <el-autocomplete
@@ -93,76 +135,151 @@
     </el-form-item>
 
       <!-- 营期时间 -->
+      <!-- <el-form-item label="营期时间" prop="scheduleTime">
+        <el-input
+          v-model="scheduleTimeText"
+          placeholder="请选择营期时间"
+          readonly
+          @click.native="showScheduleCalendar = true"
+        />
+        <calendar
+          v-model="scheduleTime"
+          mode="during"
+          :show.sync="showScheduleCalendar"
+          @change="handleScheduleTimeChange"
+          :key="scheduleCalendarKey"
+        />
+      </el-form-item>-->
       <el-form-item label="营期时间" prop="scheduleTime">
         <el-date-picker
-          v-model="scheduleTime"
-          size="small"
-          style="width: 220px"
-          value-format="yyyy-MM-dd"
+          v-model="scheduleTimeText"
           type="daterange"
-          range-separator="-"
+          range-separator="至"
           start-placeholder="开始日期"
           end-placeholder="结束日期"
-          @change="handleScheduleTimeChange">
-        </el-date-picker>
+          value-format="yyyy-MM-dd"
+          style="width: 240px"
+           @change="handleScheduleTimeChange"
+        />
       </el-form-item>
       <!-- 创建时间 -->
+      <!-- <el-form-item label="创建时间" prop="createTime">
+        <el-input
+          v-model="createTimeText"
+          placeholder="请选择创建时间"
+          readonly
+          @click.native="showCreateCalendar = true"
+        />
+        <calendar
+          v-model="createTime"
+          mode="during"
+          :show.sync="showCreateCalendar"
+          @change="createChange"
+          :key="createCalendarKey"
+        />
+      </el-form-item> -->
+
       <el-form-item label="创建时间" prop="createTime">
         <el-date-picker
-          v-model="createTime"
-          size="small"
-          style="width: 220px"
-          value-format="yyyy-MM-dd"
-          type="daterange"
-          range-separator="-"
+          v-model="createTimeText"
+          type="datetimerange"
+          range-separator="至"
           start-placeholder="开始日期"
           end-placeholder="结束日期"
-          @change="createChange">
-        </el-date-picker>
+          value-format="yyyy-MM-dd HH:mm:ss"
+           @change="createChange"
+          :default-time="['00:00:00', '23:59:59']"
+        />
       </el-form-item>
       <!-- 最新更新时间 -->
-      <el-form-item label="最新更新时间" prop="updateTime">
-        <el-date-picker
+      <!-- <el-form-item label="最新更新时间" prop="updateTime">
+        <el-input
+          v-model="updateTimeText"
+          placeholder="请选择更新时间"
+          readonly
+          @click.native="showUpdateCalendar = true"
+        />
+        <calendar
           v-model="updateTime"
-          size="small"
-          style="width: 220px"
-          value-format="yyyy-MM-dd"
+          mode="during"
+          :show.sync="showUpdateCalendar"
+          @change="updateChange"
+          :key="updateCalendarKey"
+        />
+      </el-form-item> -->
+       <el-form-item label="最新更新时间" prop="updateTime">
+        <el-date-picker
+          v-model="updateTimeText"
           type="daterange"
-          range-separator="-"
+          range-separator=""
           start-placeholder="开始日期"
           end-placeholder="结束日期"
-          @change="updateChange">
-        </el-date-picker>
+          value-format="yyyy-MM-dd"
+          style="width: 240px"
+           @change="updateChange"
+        />
       </el-form-item>
       <!-- 进线时间 -->
-      <el-form-item label="进线时间" prop="qecCreateTime">
-        <el-date-picker
+      <!-- <el-form-item label="进线时间" prop="qecCreateTime">
+        <el-input
+          v-model="qecCreateTimeText"
+          placeholder="请选择进线时间"
+          readonly
+          @click.native="showQecCalendar = true"
+        />
+        <calendar
           v-model="qecCreateTime"
-          size="small"
-          style="width: 220px"
-          value-format="yyyy-MM-dd"
+          mode="during"
+          :show.sync="showQecCalendar"
+          @change="qecCreateTimeChange"
+          :key="qecCalendarKey"
+        />
+      </el-form-item> -->
+       <el-form-item label="进线时间" prop="qecCreateTime">
+        <el-date-picker
+          v-model="qecCreateTimeText"
           type="daterange"
-          range-separator="-"
+          range-separator=""
           start-placeholder="开始日期"
           end-placeholder="结束日期"
-          @change="qecCreateTimeChange">
-        </el-date-picker>
+          value-format="yyyy-MM-dd"
+          style="width: 240px"
+           @change="qecCreateTimeChange"
+        />
       </el-form-item>
-      <el-form-item label="是否为会员" prop="isVip">
-        <el-select
-          filterable
-          v-model="queryParams.isVip"
-          placeholder="请选择是否为会员"
-          clearable size="small">
-          <el-option
-            v-for="dict in isVipList"
-            :key="dict.dictValue"
-            :label="dict.dictLabel"
-            :value="dict.dictValue"
-          />
-        </el-select>
+
+
+      <el-form-item label="营期课程时间" prop="periodTime" v-if="queryParams.sendType==1">
+        <el-date-picker
+          v-model="periodTimeText"
+          type="datetimerange"
+          align="right"
+          unlink-panels
+          value-format="yyyy-MM-dd HH:mm:ss"
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          :picker-options="pickerOptions"
+          @change="qecPeriodTimeChange"
+          :default-time="['00:00:00', '23:59:59']">
+        </el-date-picker>
       </el-form-item>
 
+<!--      <el-form-item label="是否注册" prop="isVip">-->
+<!--        <el-select-->
+<!--          filterable-->
+<!--          v-model="queryParams.isVip"-->
+<!--          placeholder="请选择是否注册"-->
+<!--          clearable size="small">-->
+<!--          <el-option-->
+<!--            v-for="dict in isVipList"-->
+<!--            :key="dict.dictValue"-->
+<!--            :label="dict.dictLabel"-->
+<!--            :value="dict.dictValue"-->
+<!--          />-->
+<!--        </el-select>-->
+<!--      </el-form-item>-->
+
       <el-form-item>
         <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
@@ -180,7 +297,7 @@
           @click="handleExport"
           v-hasPermi="['course:courseWatchLog:export']"
         >导出</el-button>
-        <el-col :span="1.5" v-if="queryParams.sendType==2">
+        <el-col :span="1.5" v-if="queryParams.sendType == 2">
           <el-button
             type="primary"
             plain
@@ -189,7 +306,7 @@
             v-hasPermi="['qw:externalContact:addTag']"
           >批量添加标签</el-button>
         </el-col>
-        <el-col :span="1.5" v-if="queryParams.sendType==2">
+        <el-col :span="1.5" v-if="queryParams.sendType == 2" >
           <el-button
             type="primary"
             plain
@@ -237,6 +354,7 @@
           </div>
         </template>
       </el-table-column>
+      <el-table-column label="营期名称" align="center" prop="periodIdName" v-if="this.queryParams.sendType==1" />
       <el-table-column label="课程名称" align="center" prop="courseName" />
       <el-table-column label="小节名称" align="center" prop="videoName" />
       <el-table-column label="记录类型" align="center" prop="logType">
@@ -254,6 +372,16 @@
       <el-table-column label="完课时间" align="center" prop="finishTime" />
       <el-table-column label="营期时间" align="center" prop="campPeriodTime" />
       <el-table-column label="进线时间" align="center" prop="qecCreateTime" />
+      <el-table-column label="是否领奖" align="center" prop="rewardType" >
+        <template slot-scope="scope">
+          <el-tag
+            :type="scope.row.rewardType ? 'success' : 'info'"
+            effect="plain"
+          >
+            {{ scope.row.rewardType ? '已领取' : '未领取' }}
+          </el-tag>
+        </template>
+      </el-table-column>
       <el-table-column
         fixed="right"
         label="操作"
@@ -344,7 +472,7 @@
 <!--        <el-table-column label="会员电话" align="center" prop="phone" />-->
 <!--        <el-table-column label="所属销售" align="center" prop="companyUserName" />-->
 <!--        <el-table-column label="所属公司" align="center" prop="companyName" />-->
-        <el-table-column label="转金额" align="center" prop="amount" />
+        <el-table-column label="转金额" align="center" prop="amount" />
         <el-table-column label="状态" align="center" prop="status" >
           <template slot-scope="scope">
             <el-tag>
@@ -480,13 +608,47 @@ import {listLogs} from "@/api/course/courseAnswerlogs";
 import {allListTagGroup} from "../../../api/qw/tagGroup";
 import {searchTags} from "../../../api/qw/tag";
 import {addTagByWatch, delTagByWatch} from "../../../api/qw/externalContact";
+import Vue from 'vue'
+import Calendar from 'vue-mobile-calendar'
 import {infoSop} from "@/api/qw/sop";
 import {getCompanyUserListLikeName} from "../../../api/company/companyUser";
+import {getQwList} from "@/api/qw/qwUser";
+Vue.use(Calendar)
 
 export default {
   name: "CourseWatchLog",
   data() {
     return {
+      companyUserFirstLoad: true, // 首次加载标志
+      qwUserFirstLoad: true,     // 首次加载标志
+      companyUserDropdownVisible: false, // 下拉框显示状态
+      qwUserDropdownVisible: false,     // 下拉框显示状态
+      companyName:process.env.VUE_APP_COURSE_COMPANY_NAME,
+      // 日历 key 控制刷新
+      scheduleCalendarKey: 0,
+      createCalendarKey: 0,
+      updateCalendarKey: 0,
+      qecCalendarKey: 0,
+      periodTimeKey: 0,
+
+      createTimeText: '',
+      scheduleTimeText: '',  // 新增
+      updateTimeText: '',    // 新增
+      qecCreateTimeText: '', // 新增
+      periodTimeText: '', // 营期课程时间
+
+      scheduleTime: [],  // 改为数组
+      createTime: [],    // 改为数组
+      updateTime: [],    // 改为数组
+      qecCreateTime: [], // 改为数组
+      periodTime: [], // 改为数组
+
+      // 控制日历显隐
+      showScheduleCalendar: false,
+      showCreateCalendar: false,
+      showUpdateCalendar: false,
+      showQecCalendar: false,
+
       resultDialogVisible: false,
       resultMessage: '',
       resultTitle:'',
@@ -494,9 +656,9 @@ export default {
       activeName:"2",
       pickerOptions: {
         disabledDate(time) {
-          // 获取6天前的日期(加上今天就是7天)
+          // 获取13天前的日期(加上今天就是14天)
           const sixDaysAgo = new Date();
-          sixDaysAgo.setDate(sixDaysAgo.getDate() - 6);
+          sixDaysAgo.setDate(sixDaysAgo.getDate() - 13);
           sixDaysAgo.setHours(0, 0, 0, 0);
 
           // 获取明天的日期(不包括今天)
@@ -510,6 +672,7 @@ export default {
       courseLists:[],
       videoList:[],
       logTypeOptions:[],
+      projectOptions:[],
       sendTypeOptions:[{
         dictLabel:"会员",dictValue:'1'
       },
@@ -594,6 +757,7 @@ export default {
       queryParams: {
         pageNum: 1,
         pageSize: 10,
+        project: null,
         userId: null,
         nickName: null,
         videoId: null,
@@ -602,6 +766,7 @@ export default {
         externalUserName:null,
         duration: null,
         qwUserId: null,
+        qwUserName: null,
         companyUserId: null,
         companyId: null,
         courseId: null,
@@ -611,6 +776,8 @@ export default {
         upETime:null,
         qecSTime:null,
         qecETime:null,
+        periodSTime:null,
+        periodETime:null,
         scheduleStartTime: null,
         scheduleEndTime: null,
         sendType:process.env.VUE_APP_COURSE_DEFAULT,
@@ -630,13 +797,18 @@ export default {
         pageSize: 10
       },
       companyUserOptions: [],
+      companyUserOptionsByAll: [],
       companyUserOptionsLoading: false,
-
-      // 时间选择器数据
-      scheduleTime: [],
-      createTime: [],
-      updateTime: [],
-      qecCreateTime: [],
+      // 企微信息
+      qwUserOptions: [],
+      // 员工选项列表
+      qwUserOptionsParams: {
+        name: undefined,
+        hasNextPage: false,
+        pageNum: 1,
+        pageSize: 10
+      },
+      qwUserOptionsLoading: false,
     };
   },
   created() {
@@ -644,20 +816,63 @@ export default {
       this.courseLists = response.list;
     });
     this.getList();
-    this.getDicts("sys_course_watch_log_type").then(response => {
+    this.getDicts("sys_course_watch_log_type_new").then(response => {
       this.logTypeOptions = response.data;
     });
+    this.getDicts("sys_course_project").then(response => {
+      this.projectOptions = response.data;
+    });
+    this.getCompanyUserListLikeName(true);
   },
   methods: {
-    courseChange(row){
-      this.queryParams.videoId=null;
-      if(row === ''){
-        this.videoList=[];
-        return
+    /**
+     * 处理所属销售下拉框显示状态变化
+     */
+    handleCompanyUserDropdownVisible(visible) {
+      this.companyUserDropdownVisible = visible;
+      if (visible && this.companyUserFirstLoad) {
+        // 首次展开下拉框时加载数据
+        this.companyUserFirstLoad = false;
+        this.loadCompanyUserOptions('');
+      } else if (visible && this.companyUserOptions.length === 0) {
+        // 下拉框显示但无数据时重新加载
+        this.loadCompanyUserOptions('');
+      }
+    },
+    /**
+     * 处理所属企微下拉框显示状态变化
+     */
+    handleQwUserDropdownVisible(visible) {
+      this.qwUserDropdownVisible = visible;
+      if (visible && this.qwUserFirstLoad) {
+        // 首次展开下拉框时加载数据
+        this.qwUserFirstLoad = false;
+        this.loadQwUserOptions('');
+      } else if (visible && this.qwUserOptions.length === 0) {
+        // 下拉框显示但无数据时重新加载
+        this.loadQwUserOptions('');
+      }
+    },
+
+    /**
+     * 当销售被选择
+     * 级联更新企微列表
+     */
+    handleCompanyUserChange(companyUserId){
+      if (companyUserId) {
+        // 清空当前企微选择
+        this.queryParams.qwUserName = "";
+        this.qwUserOptions = [];
+        this.qwUserOptionsParams.pageNum = 1;
+
+        // 重新加载企微列表,传入销售ID作为过滤条件
+        this.getQwList();
+      } else {
+        // 清空销售时也清空企微
+        this.queryParams.qwUserName = "";
+        this.qwUserOptions = [];
+        this.qwUserOptionsParams.pageNum = 1;
       }
-      videoList(row).then(response => {
-        this.videoList=response.list
-      });
     },
     /**
      * 根据名称模糊查询用户列表
@@ -665,15 +880,24 @@ export default {
      */
     loadCompanyUserOptions(query) {
       this.companyUserOptions = [];
-      if (query === '') {
-        return;
-      }
 
       this.companyUserOptionsParams.pageNum = 1
       this.companyUserOptionsParams.name = query
       this.companyUserOptionsLoading = true;
       this.getCompanyUserListLikeName()
     },
+    /**
+     * 根据条件查询企微列表
+     * @param query 参数
+     */
+    loadQwUserOptions(query) {
+      this.qwUserOptions = [];
+      this.qwUserOptionsParams.pageNum = 1
+      // 将搜索关键词设置到queryParams中
+      this.queryParams.qwUserName = query
+      this.qwUserOptionsLoading = true;
+      this.getQwList()
+    },
     /**
      * 加载更多员工选项
      */
@@ -689,30 +913,117 @@ export default {
     /**
      * 获取员工列表
      */
-    getCompanyUserListLikeName() {
-      getCompanyUserListLikeName(this.companyUserOptionsParams).then(response => {
-        this.companyUserOptions = [...this.companyUserOptions, ...response.data.list]
-        this.companyUserOptionsParams.hasNextPage = response.data.hasNextPage
-        this.companyUserOptionsLoading = false;
+    getCompanyUserListLikeName(isAll) {
+
+      if (isAll){
+        this.companyUserOptionsParams.pageSize = 200;
+        getCompanyUserListLikeName(this.companyUserOptionsParams).then(response => {
+          this.companyUserOptionsByAll = [...this.companyUserOptions, ...response.data.list]
+          this.companyUserOptionsParams.hasNextPage = response.data.hasNextPage
+          this.companyUserOptionsLoading = false;
+        });
+      }else {
+        this.companyUserOptionsParams.pageSize = 10;
+        getCompanyUserListLikeName(this.companyUserOptionsParams).then(response => {
+          this.companyUserOptions = [...this.companyUserOptions, ...response.data.list]
+          this.companyUserOptionsParams.hasNextPage = response.data.hasNextPage
+          this.companyUserOptionsLoading = false;
+        });
+      }
+
+    },
+    getQwList() {
+      const params = {
+        name: this.qwUserOptionsParams.name,
+        pageNum: this.qwUserOptionsParams.pageNum,
+        pageSize: this.qwUserOptionsParams.pageSize,
+        // 添加销售ID作为过滤条件
+        companyUserId: this.queryParams.companyUserId || null,
+        disableCompanyId: 1
+      };
+      console.log("企微参数", this.queryParams);
+      getQwList(params).then(response => {
+        if (this.qwUserOptionsParams.pageNum === 1) {
+          this.qwUserOptions = response.rows || [];
+        } else {
+          this.qwUserOptions = [...this.qwUserOptions, ...(response.rows || [])];
+        }
+        this.qwUserOptionsParams.hasNextPage = response.rows && response.rows.length >= this.qwUserOptionsParams.pageSize;
+      }).finally(()=>{
+        this.qwUserOptionsLoading = false;
+      })
+    },
+
+    /**
+     * 加载更多员工选项
+     */
+    loadMoreQwUserOptions() {
+      if (!this.qwUserOptionsParams.hasNextPage) {
+        return;
+      }
+
+      this.qwUserOptionsParams.pageNum += 1
+      this.getQwList()
+    },
+
+    // 重置日历组件
+    resetCalendars() {
+      this.scheduleTime = [];
+      this.createTime = [];
+      this.updateTime = [];
+      this.qecCreateTime = [];
+      this.periodTime = [];
+
+      this.scheduleTimeText = [];
+      this.createTimeText = [];
+      this.updateTimeText = [];
+      this.qecCreateTimeText = [];
+      this.periodTimeText = [];
+
+      // 强制刷新日历组件
+      this.scheduleCalendarKey++;
+      this.createCalendarKey++;
+      this.updateCalendarKey++;
+      this.qecCalendarKey++;
+      this.periodTimeKey++;
+    },
+    formatDateRange(dates) {
+      if (!dates || dates.length < 2) return '';
+      return dates.map(date => date.format('YYYY-MM-DD')).join(' ~ ');
+    },
+    courseChange(row){
+      this.queryParams.videoId=null;
+      if(row === ''){
+        this.videoList=[];
+        return
+      }
+      videoList(row).then(response => {
+        this.videoList=response.list
       });
     },
 
     // 营期时间
     handleScheduleTimeChange(scheduleTime) {
-      if (scheduleTime && scheduleTime.length === 2) {
-        this.queryParams.scheduleStartTime = scheduleTime[0];
-        this.queryParams.scheduleEndTime = scheduleTime[1];
+      if (scheduleTime && scheduleTime.length >= 2) {
+        // this.scheduleTimeText = this.formatDateRange(scheduleTime);
+        this.queryParams.scheduleStartTime = scheduleTime[0] || null;
+        this.queryParams.scheduleEndTime = scheduleTime[1] || null;
+        console.log(this.queryParams.scheduleStartTime)
+        console.log(this.queryParams.scheduleEndTime)
       } else {
+        this.scheduleTimeText = '';
         this.queryParams.scheduleStartTime = null;
         this.queryParams.scheduleEndTime = null;
       }
     },
     // 创建时间
     createChange(createTime) {
-      if (createTime && createTime.length === 2) {
-        this.queryParams.sTime = createTime[0];
-        this.queryParams.eTime = createTime[1];
+      if (createTime && createTime.length >= 2) {
+        // this.createTimeText = this.formatDateRange(createTime);
+        this.queryParams.sTime = this.formatDate(createTime[0]) || null;
+        this.queryParams.eTime = this.formatDate(createTime[1]) || null;
       } else {
+        this.createTimeText = '';
         this.queryParams.sTime = null;
         this.queryParams.eTime = null;
       }
@@ -720,10 +1031,12 @@ export default {
 
     // 更新时间
     updateChange(updateTime) {
-      if (updateTime && updateTime.length === 2) {
-        this.queryParams.upSTime = updateTime[0];
-        this.queryParams.upETime = updateTime[1];
+      if (updateTime && updateTime.length >= 2) {
+        // this.updateTimeText = this.formatDateRange(updateTime);
+        this.queryParams.upSTime = updateTime[0] || null;
+        this.queryParams.upETime = updateTime[1] || null;
       } else {
+        this.updateTimeText = '';
         this.queryParams.upSTime = null;
         this.queryParams.upETime = null;
       }
@@ -731,7 +1044,7 @@ export default {
 
     // 进线时间
     qecCreateTimeChange(qecCreateTime) {
-      if (qecCreateTime && qecCreateTime.length === 2) {
+      if (qecCreateTime && qecCreateTime.length >= 2) {
         // 检查选择的日期范围是否超过7天(包括起始和结束日期)
         const startDate = new Date(qecCreateTime[0]);
         const endDate = new Date(qecCreateTime[1]);
@@ -748,19 +1061,88 @@ export default {
           this.$message.error('进线时间选择范围不能超过7天');
           // 清空选择
           this.qecCreateTime = [];
+          this.qecCreateTimeText = [];
           this.queryParams.qecSTime = null;
           this.queryParams.qecETime = null;
+          this.qecCalendarKey++;
           return;
         }
 
-        this.queryParams.qecSTime = qecCreateTime[0];
-        this.queryParams.qecETime = qecCreateTime[1];
+        // this.qecCreateTimeText = this.formatDateRange(qecCreateTime);
+        this.queryParams.qecSTime = qecCreateTime[0] || null;
+        this.queryParams.qecETime = qecCreateTime[1] || null;
+        console.log(this.queryParams.qecSTime);
+        console.log(this.queryParams.qecETime);
       } else {
+        this.qecCreateTimeText = '';
         this.queryParams.qecSTime = null;
         this.queryParams.qecETime = null;
       }
     },
 
+    //营期课程时间
+    qecPeriodTimeChange(periodTime){
+
+      if (periodTime && periodTime.length >= 2) {
+        // 检查选择的日期范围是否超过7天(包括起始和结束日期)
+        const startDate = new Date(periodTime[0]);
+        const endDate = new Date(periodTime[1]);
+
+        // 设置时间为当天开始,避免时间部分影响计算
+        startDate.setHours(0, 0, 0, 0);
+        endDate.setHours(0, 0, 0, 0);
+
+        const timeDiff = Math.abs(endDate - startDate);
+        const diffDays = Math.ceil(timeDiff / (1000 * 60 * 60 * 24));
+
+        // 如果超过6天的范围(总共7天,包括起始日)
+        if (diffDays > 13) {
+          this.$message.error('时间选择范围不能超过14天');
+          // 清空选择
+          this.periodTime = [];
+          this.periodTimeText = [];
+          this.queryParams.periodSTime = null;
+          this.queryParams.periodETime = null;
+          this.periodTimeKey++;
+          return;
+        }
+
+        this.queryParams.periodSTime = this.formatDate(periodTime[0]) || null;
+        this.queryParams.periodETime = this.formatDate(periodTime[1]) || null;
+
+      } else {
+
+        this.periodTimeText = '';
+        this.queryParams.periodSTime = null;
+        this.queryParams.periodETime = null;
+      }
+
+    },
+
+    formatDate(date) {
+      if (!date) return ''
+
+      // 确保 date 是 Date 对象
+      let dateObj = date
+      if (typeof date === 'string') {
+        dateObj = new Date(date)
+      }
+
+      // 如果转换失败,返回空字符串
+      if (!(dateObj instanceof Date) || isNaN(dateObj.getTime())) {
+        return ''
+      }
+
+      // 使用更安全的格式化方法
+      const year = dateObj.getFullYear()
+      const month = String(dateObj.getMonth() + 1).padStart(2, '0')
+      const day = String(dateObj.getDate()).padStart(2, '0')
+      const hours = String(dateObj.getHours()).padStart(2, '0')
+      const minutes = String(dateObj.getMinutes()).padStart(2, '0')
+      const seconds = String(dateObj.getSeconds()).padStart(2, '0')
+
+      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
+    },
 
 
     handleClickX(tab,event){
@@ -815,6 +1197,8 @@ export default {
         scheduleStartTime: null,
         scheduleEndTime: null,
       };
+      // 统一重置日历组件
+      this.resetCalendars();
       this.resetForm("form");
     },
     /** 搜索按钮操作 */
@@ -826,11 +1210,15 @@ export default {
     resetQuery() {
       this.resetForm("queryForm");
       this.queryParams.sTime = null;
+      this.queryParams.project = null;
       this.queryParams.eTime = null;
       this.queryParams.upSTime = null;
       this.queryParams.upETime = null;
       this.queryParams.qecSTime = null;
       this.queryParams.qecETime = null;
+      this.queryParams.periodSTime = null;
+      this.queryParams.periodDTime = null;
+      this.queryParams.externalUserName=null;
       this.queryParams.scheduleStartTime = null;
       this.queryParams.scheduleEndTime = null;
       this.queryParams.sopId = null; // 重置SOP ID
@@ -838,11 +1226,8 @@ export default {
       // 重置SOP搜索
       this.handleSopClear();
 
-      // 重置日期选择器
-      this.scheduleTime = [];
-      this.createTime = [];
-      this.updateTime = [];
-      this.qecCreateTime = [];
+      // 统一重置日历组件
+      this.resetCalendars();
 
       this.handleQuery();
     },
@@ -939,7 +1324,7 @@ export default {
     },
     redLogList() {
       this.loadingRedLog = true;
-      console.info(this.redLogQueryParams)
+      console.info("-----index",this.redLogQueryParams)
       listCourseRedPacketLog(this.redLogQueryParams).then(e => {
         this.redLogsList = e.rows;
         this.redLogTotal = e.total;
@@ -948,6 +1333,11 @@ export default {
     },
 
     handleSendTypeChange() {
+
+      // 如果是会员,清除一下选中的企微
+      if(this.queryParams.sendType == 1) {
+        this.queryParams.qwUserName = null;
+      }
       this.handleQuery(); // 重新查询列表
     },
 
@@ -1017,6 +1407,7 @@ export default {
 
       searchTags(this.queryTagParams).then(response => {
         this.tagGroupList = response.rows;
+        this.tagTotal = response.total;
       });
 
       // searchTags({name:name,corpId:this.queryParams.corpId}).then(response => {
@@ -1033,6 +1424,7 @@ export default {
     },
 
     resetSearchQueryTag(){
+      this.tagChange.tagName=null;
 
       this.queryTagParams= {
         pageNum: 1,

+ 6 - 15
src/views/course/courseWatchLog/myCourseWatchLog.vue

@@ -1,16 +1,6 @@
 <template>
   <div class="app-container">
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
-      <el-form-item label="看课方式" prop="sendType">
-        <el-select v-model="queryParams.sendType" placeholder="选择看课方式"  clearable size="small" @change="handleSendTypeChange">
-          <el-option
-            v-for="dict in sendTypeOptions"
-            :key="dict.dictValue"
-            :label="dict.dictLabel"
-            :value="dict.dictValue"
-          />
-        </el-select>
-      </el-form-item>
       <el-form-item label="企微账号" prop="qwUserId">
         <el-select v-model="queryParams.qwUserId" placeholder="企微账号" clearable size="small" @change="updateQwuser()">
           <el-option
@@ -102,7 +92,7 @@
     </el-form>
 
     <el-row :gutter="10" class="mb8">
-      <el-col :span="1.5">
+      <el-col :span="1.5" >
         <el-button
           type="warning"
           plain
@@ -113,7 +103,7 @@
           v-hasPermi="['course:courseWatchLog:myExport']"
         >导出</el-button>
       </el-col>
-      <el-col :span="1.5" v-if="queryParams.sendType==2">
+      <el-col :span="1.5" v-if="queryParams.sendType == 2">
         <el-button
           type="primary"
           plain
@@ -122,7 +112,7 @@
           v-hasPermi="['qw:externalContact:addTag']"
         >批量添加标签</el-button>
       </el-col>
-      <el-col :span="1.5" v-if="queryParams.sendType==2">
+      <el-col :span="1.5" v-if="queryParams.sendType == 2">
         <el-button
           type="primary"
           plain
@@ -379,7 +369,6 @@ export default {
         scheduleEndTime: null,
         qecSTime:null,
         qecETime:null,
-        sendType:process.env.VUE_APP_COURSE_DEFAULT,
       },
       // 表单参数
       form: {},
@@ -393,7 +382,7 @@ export default {
       this.courseLists = response.list;
     });
     this.getList();
-    this.getDicts("sys_course_watch_log_type").then(response => {
+    this.getDicts("sys_course_watch_log_type_new").then(response => {
       this.logTypeOptions = response.data;
     });
 
@@ -501,6 +490,7 @@ export default {
       this.queryParams.qecSTime = null;
       this.queryParams.qecETime = null;
       this.queryParams.scheduleStartTime = null;
+      this.queryParams.externalUserName=null;
       this.queryParams.scheduleEndTime = null;
       this.scheduleTime=null;
       this.handleQuery();
@@ -642,6 +632,7 @@ export default {
 
       searchTags(this.queryTagParams).then(response => {
         this.tagGroupList = response.rows;
+        this.tagTotal = response.total;
       });
 
       // searchTags({name:name,corpId:this.queryParams.corpId}).then(response => {

+ 1 - 1
src/views/course/courseWatchLog/qw/myCourseWatchLog.vue

@@ -222,7 +222,7 @@ export default {
       this.courseLists = response.list;
     });
     this.getList();
-    this.getDicts("sys_course_watch_log_type").then(response => {
+    this.getDicts("sys_course_watch_log_type_new").then(response => {
       this.logTypeOptions = response.data;
     });
 

+ 29 - 5
src/views/course/courseWatchLog/qw/statistics.vue

@@ -39,6 +39,21 @@
       </el-form-item>
     </el-form>
 
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          :loading="exportLoading"
+          @click="handleExport"
+          v-hasPermi="['qw:user:export']"
+        >导出</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
     <el-table border v-loading="loading" :data="courseWatchLogList" @selection-change="handleSelectionChange"  show-summary>
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="企微员工名称" align="center" prop="qwUserName" />
@@ -49,8 +64,12 @@
       <el-table-column label="看课中" align="center" prop="type1" />
       <el-table-column label="已完课" align="center" prop="type2" />
       <el-table-column label="看课中断" align="center" prop="type4" />
-
-
+      <el-table-column label="发课数" align="center" prop="sendNumber" />
+      <el-table-column label="注册用户待看课数" align="center" prop="isUserWaitNumber" />
+      <el-table-column label="未注册用户待看课数" align="center" prop="noUserWaitNumber" />
+      <el-table-column label="上线率" align="center" prop="onLineRate" />
+      <el-table-column label="完课率" align="center" prop="finishedRate" />
+      <el-table-column label="消耗红包金额" align="center" prop="redAmount" />
     </el-table>
 
     <pagination
@@ -65,7 +84,7 @@
 </template>
 
 <script>
-import { listCourseWatchLog, getCourseWatchLog, delCourseWatchLog, addCourseWatchLog, updateCourseWatchLog, exportCourseWatchLog,statisticsList } from "@/api/course/qw/courseWatchLog";
+import { listCourseWatchLog, getCourseWatchLog, delCourseWatchLog, addCourseWatchLog, updateCourseWatchLog, exportCourseWatchLog,statisticsList,statisticsExport } from "@/api/course/qw/courseWatchLog";
 import { courseList,videoList } from '@/api/course/courseRedPacketLog'
 import { getDateRange } from '@/utils/common'
 export default {
@@ -278,14 +297,19 @@ export default {
     },
     /** 导出按钮操作 */
     handleExport() {
+      // 检查是否选择了时间范围
+      if (!this.queryParams.sTime || !this.queryParams.eTime) {
+        this.$message.warning("请选择创建时间后才能导出");
+        return;
+      }
       const queryParams = this.queryParams;
-      this.$confirm('是否确认导出所有短链课程看课记录数据项?', "警告", {
+      this.$confirm('是否确认导出所有企微看课记录数据项?', "警告", {
           confirmButtonText: "确定",
           cancelButtonText: "取消",
           type: "warning"
         }).then(() => {
           this.exportLoading = true;
-          return exportCourseWatchLog(queryParams);
+          return statisticsExport(queryParams);
         }).then(response => {
           this.download(response.msg);
           this.exportLoading = false;

+ 2 - 2
src/views/course/courseWatchLog/qw/watchLog.vue

@@ -247,7 +247,7 @@
         <el-table-column label="会员电话" align="center" prop="phone" />
         <el-table-column label="所属销售" align="center" prop="companyUserName" />
         <el-table-column label="所属公司" align="center" prop="companyName" />
-        <el-table-column label="转金额" align="center" prop="amount" />
+        <el-table-column label="转金额" align="center" prop="amount" />
         <el-table-column label="状态" align="center" prop="status" >
           <template slot-scope="scope">
             <el-tag>{{ scope.row.status === 0 ? "发送中" : "已完成" }}</el-tag>
@@ -370,7 +370,7 @@ export default {
       this.courseLists = response.list;
     });
     this.getList();
-    this.getDicts("sys_course_watch_log_type").then(response => {
+    this.getDicts("sys_course_watch_log_type_new").then(response => {
       this.logTypeOptions = response.data;
     });
 

+ 384 - 108
src/views/course/courseWatchLog/watchLog.vue

@@ -11,6 +11,16 @@
           />
         </el-select>
       </el-form-item>
+<!--      <el-form-item label="项目" prop="project">-->
+<!--        <el-select  v-model="queryParams.project" placeholder="请选择项目" clearable size="small" >-->
+<!--          <el-option-->
+<!--            v-for="item in projectOptions"-->
+<!--            :key="item.dictValue"-->
+<!--            :label="item.dictLabel"-->
+<!--            :value="item.dictValue"-->
+<!--          />-->
+<!--        </el-select>-->
+<!--      </el-form-item>-->
       <el-form-item label="企微账号" prop="qwUserId" v-if="queryParams.sendType == 2">
         <el-select v-model="queryParams.qwUserId" placeholder="企微账号" clearable size="small"
                    @change="updateQwuser()">
@@ -101,76 +111,149 @@
         </el-autocomplete>
       </el-form-item>
       <!-- 营期时间 -->
+      <!-- <el-form-item label="营期时间" prop="scheduleTime">
+        <el-input
+          v-model="scheduleTimeText"
+          placeholder="请选择营期时间"
+          readonly
+          @click.native="showScheduleCalendar = true"
+        />
+        <calendar
+          v-model="scheduleTime"
+          mode="during"
+          :show.sync="showScheduleCalendar"
+          @change="handleScheduleTimeChange"
+          :key="scheduleCalendarKey"
+        />
+      </el-form-item> -->
       <el-form-item label="营期时间" prop="scheduleTime">
-        <el-date-picker 
-          v-model="scheduleTime" 
-          size="small" 
-          style="width: 220px" 
-          value-format="yyyy-MM-dd" 
+        <el-date-picker
+          v-model="scheduleTimeText"
           type="daterange"
-          range-separator="-" 
-          start-placeholder="开始日期" 
-          end-placeholder="结束日期" 
-          @change="handleScheduleTimeChange">
-        </el-date-picker>
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          value-format="yyyy-MM-dd"
+          style="width: 240px"
+           @change="handleScheduleTimeChange"
+        />
       </el-form-item>
       <!-- 创建时间 -->
+      <!-- <el-form-item label="创建时间" prop="createTime">
+        <el-input
+          v-model="createTimeText"
+          placeholder="请选择创建时间"
+          readonly
+          @click.native="showCreateCalendar = true"
+        />
+        <calendar
+          v-model="createTime"
+          mode="during"
+          :show.sync="showCreateCalendar"
+          @change="createChange"
+          :key="createCalendarKey"
+        />
+      </el-form-item> -->
       <el-form-item label="创建时间" prop="createTime">
-        <el-date-picker 
-          v-model="createTime" 
-          size="small" 
-          style="width: 220px" 
-          value-format="yyyy-MM-dd" 
-          type="daterange"
-          range-separator="-" 
-          start-placeholder="开始日期" 
-          end-placeholder="结束日期" 
-          @change="createChange">
-        </el-date-picker>
+        <el-date-picker
+          v-model="createTimeText"
+          type="datetimerange"
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          value-format="yyyy-MM-dd HH:mm:ss"
+          @change="createChange"
+          :default-time="['00:00:00', '23:59:59']"
+        />
       </el-form-item>
       <!-- 最新更新时间 -->
-      <el-form-item label="最新更新时间" prop="updateTime">
-        <el-date-picker 
-          v-model="updateTime" 
-          size="small" 
-          style="width: 220px" 
-          value-format="yyyy-MM-dd" 
+      <!-- <el-form-item label="最新更新时间" prop="updateTime">
+        <el-input
+          v-model="updateTimeText"
+          placeholder="请选择更新时间"
+          readonly
+          @click.native="showUpdateCalendar = true"
+        />
+        <calendar
+          v-model="updateTime"
+          mode="during"
+          :show.sync="showUpdateCalendar"
+          @change="updateChange"
+          :key="updateCalendarKey"
+        />
+      </el-form-item> -->
+        <el-form-item label="最新更新时间" prop="updateTime">
+        <el-date-picker
+          v-model="updateTimeText"
           type="daterange"
-          range-separator="-" 
-          start-placeholder="开始日期" 
-          end-placeholder="结束日期" 
-          @change="updateChange">
-        </el-date-picker>
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          value-format="yyyy-MM-dd"
+          style="width: 240px"
+           @change="updateChange"
+        />
       </el-form-item>
       <!-- 进线时间 -->
-      <el-form-item label="进线时间" prop="qecCreateTime">
-        <el-date-picker 
-          v-model="qecCreateTime" 
-          size="small" 
-          style="width: 220px" 
-          value-format="yyyy-MM-dd" 
+      <!-- <el-form-item label="进线时间" prop="qecCreateTime">
+        <el-input
+          v-model="qecCreateTimeText"
+          placeholder="请选择进线时间"
+          readonly
+          @click.native="showQecCalendar = true"
+        />
+        <calendar
+          v-model="qecCreateTime"
+          mode="during"
+          :show.sync="showQecCalendar"
+          @change="qecCreateTimeChange"
+          :key="qecCalendarKey"
+        />
+      </el-form-item> -->
+       <el-form-item label="进线时间" prop="qecCreateTime">
+        <el-date-picker
+          v-model="qecCreateTimeText"
           type="daterange"
-          range-separator="-" 
-          start-placeholder="开始日期" 
-          end-placeholder="结束日期" 
-          @change="qecCreateTimeChange">
-        </el-date-picker>
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          value-format="yyyy-MM-dd"
+          style="width: 240px"
+          @change="qecCreateTimeChange"
+        />
       </el-form-item>
-      <el-form-item label="是否为会员" prop="isVip">
-        <el-select
-          filterable
-          v-model="queryParams.isVip"
-          placeholder="请选择是否为会员"
-          clearable size="small">
-          <el-option
-            v-for="dict in isVipList"
-            :key="dict.dictValue"
-            :label="dict.dictLabel"
-            :value="dict.dictValue"
-          />
-        </el-select>
+
+      <el-form-item label="营期课程时间" prop="periodTime" v-if="queryParams.sendType==1">
+        <el-date-picker
+          v-model="periodTimeText"
+          type="datetimerange"
+          align="right"
+          unlink-panels
+          value-format="yyyy-MM-dd HH:mm:ss"
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          :picker-options="pickerOptions"
+          @change="qecPeriodTimeChange"
+          :default-time="['00:00:00', '23:59:59']">
+        </el-date-picker>
       </el-form-item>
 
+<!--      <el-form-item label="是否注册" prop="isVip">-->
+<!--        <el-select-->
+<!--          filterable-->
+<!--          v-model="queryParams.isVip"-->
+<!--          placeholder="请选择是否注册"-->
+<!--          clearable size="small">-->
+<!--          <el-option-->
+<!--            v-for="dict in isVipList"-->
+<!--            :key="dict.dictValue"-->
+<!--            :label="dict.dictLabel"-->
+<!--            :value="dict.dictValue"-->
+<!--          />-->
+<!--        </el-select>-->
+<!--      </el-form-item>-->
+
       <el-form-item>
         <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
@@ -189,7 +272,7 @@
           v-hasPermi="['course:courseWatchLog:myExport']"
         >导出
         </el-button>
-        <el-col :span="1.5"  v-if="queryParams.sendType=='2'">
+        <el-col :span="1.5" v-if="queryParams.sendType == 2">
           <el-button
             type="primary"
             plain
@@ -198,7 +281,7 @@
             v-hasPermi="['qw:externalContact:addTag']"
           >批量添加标签</el-button>
         </el-col>
-        <el-col :span="1.5"  v-if="queryParams.sendType=='2'">
+        <el-col :span="1.5" v-if="queryParams.sendType == 2">
           <el-button
             type="primary"
             plain
@@ -215,25 +298,45 @@
       <el-tab-pane label="全部" name="00"></el-tab-pane>
       <el-tab-pane v-for="(item,index) in logTypeOptions" :label="item.dictLabel" :name="item.dictValue"></el-tab-pane>
     </el-tabs>
-    <el-table border v-loading="loading" :data="courseWatchLogList" @selection-change="handleSelectionChange">
+<!--    <el-table border v-loading="loading" :data="courseWatchLogList" @selection-change="handleSelectionChange">-->
+    <el-table
+      border
+      v-loading="loading"
+      :data="courseWatchLogList"
+      @selection-change="handleSelectionChange"
+      :key="tableKey">
       <el-table-column type="selection" width="55" align="center"/>
       <el-table-column label="记录编号" align="center" prop="logId"/>
      <el-table-column label="客户昵称" align="center" prop="externalUserName" v-if="queryParams.sendType == 2"/>
 
-      <!--
+      &lt;!&ndash;
       <el-table-column label="会员ID" align="center" prop="userId" v-if="queryParams.sendType == 1"/>
-      -->
-     <el-table-column label="客户头像" align="center" prop="externalUserAvatar" v-if="queryParams.sendType == 2">
-       <template slot-scope="scope">
-         <el-popover
-           placement="right"
-           title=""
-           trigger="hover">
-           <img slot="reference" :src="scope.row.externalUserAvatar" style="width: 50px;height: 50px">
-           <img :src="scope.row.externalUserAvatar" style="max-width: 200px;max-height: 200px">
-         </el-popover>
-       </template>
-     </el-table-column>
+      &ndash;&gt;
+<!--     <el-table-column label="客户头像" align="center" prop="externalUserAvatar" v-if="queryParams.sendType == 2">-->
+<!--       <template slot-scope="scope">-->
+<!--         <el-popover-->
+<!--           placement="right"-->
+<!--           title=""-->
+<!--           trigger="hover">-->
+<!--           <img slot="reference" :src="scope.row.externalUserAvatar" style="width: 50px;height: 50px">-->
+<!--           <img :src="scope.row.externalUserAvatar" style="max-width: 200px;max-height: 200px">-->
+<!--         </el-popover>-->
+<!--       </template>-->
+<!--     </el-table-column>-->
+      <el-table-column label="头像" align="center">
+        <template slot-scope="scope">
+          <img
+            v-if="queryParams.sendType == 1"
+            :src="scope.row.fsAvatar"
+            style="width:50px;height:50px"
+          />
+          <img
+            v-else-if="queryParams.sendType == 2"
+            :src="scope.row.externalUserAvatar"
+            style="width:50px;height:50px"
+          />
+        </template>
+      </el-table-column>
       <el-table-column label="用户昵称" align="center" v-if="queryParams.sendType == 1">
         <template slot-scope="scope">
           {{ queryParams.sendType=='1' ? scope.row.fsNickName : scope.row.externalUserName }}
@@ -256,12 +359,28 @@
 <!--      <el-table-column label="所属销售" align="center" prop="companyUserName"/>-->
 <!--      <el-table-column label="所属公司" align="center" prop="companyName"/>-->
 <!--      <el-table-column label="企微员工名称" align="center" prop="qwUserName"/>-->
-      <el-table-column label="所属企微" align="center" prop="qwUserName" v-if="queryParams.sendType==2" />
+      <!-- 所属企微列 -->
+      <el-table-column
+        label="所属企微"
+        align="center"
+        prop="qwUserName"
+        v-if="queryParams.sendType == 2"
+      />
       <!--      <el-table-column label="所属发送方式" align="center" prop="sendType"/>-->
       <el-table-column label="创建时间" align="center" prop="createTime" width="100px"/>
       <el-table-column label="更新时间" align="center" prop="updateTime" width="100px" />
       <el-table-column label="完课时间" align="center" prop="finishTime" width="100px" />
       <el-table-column label="营期时间" align="center" prop="campPeriodTime" />
+      <el-table-column label="是否领奖" align="center" prop="rewardType" >
+        <template slot-scope="scope">
+          <el-tag
+            :type="scope.row.rewardType ? 'success' : 'info'"
+            effect="plain"
+          >
+            {{ scope.row.rewardType ? '已领取' : '未领取' }}
+          </el-tag>
+        </template>
+      </el-table-column>
       <el-table-column
         fixed="right"
         label="操作"
@@ -353,7 +472,7 @@
 <!--        <el-table-column label="会员电话" align="center" prop="phone" />-->
 <!--        <el-table-column label="所属销售" align="center" prop="companyUserName" />-->
 <!--        <el-table-column label="所属公司" align="center" prop="companyName" />-->
-        <el-table-column label="转金额" align="center" prop="amount" />
+        <el-table-column label="转金额" align="center" prop="amount" />
         <el-table-column label="状态" align="center" prop="status" >
           <template slot-scope="scope">
             <el-tag>
@@ -488,12 +607,41 @@ import {getMyQwUserList} from "@/api/qw/user";
 import {searchTags} from "../../../api/qw/tag";
 import {addTagByWatch, delTagByWatch} from "../../../api/qw/externalContact";
 import {allListTagGroup} from "../../../api/qw/tagGroup";
+import Vue from 'vue'
+import Calendar from 'vue-mobile-calendar'
 import {infoSop} from "@/api/qw/sop";
+Vue.use(Calendar)
 
 export default {
   name: "CourseWatchLog",
   data() {
     return {
+      tableKey: 0,
+      // 日历 key 控制刷新
+      scheduleCalendarKey: 0,
+      createCalendarKey: 0,
+      updateCalendarKey: 0,
+      qecCalendarKey: 0,
+      periodTimeKey: 0,
+
+      createTimeText: '',
+      scheduleTimeText: '',  // 新增
+      updateTimeText: '',    // 新增
+      qecCreateTimeText: '', // 新增
+      periodTimeText: '', // 营期课程时间
+
+      scheduleTime: [],  // 改为数组
+      createTime: [],    // 改为数组
+      updateTime: [],    // 改为数组
+      qecCreateTime: [], // 改为数组
+      periodTime: [], // 改为数组
+
+      // 控制日历显隐
+      showScheduleCalendar: false,
+      showCreateCalendar: false,
+      showUpdateCalendar: false,
+      showQecCalendar: false,
+
       resultDialogVisible: false,
       resultMessage: '',
       resultTitle:'',
@@ -501,9 +649,9 @@ export default {
       activeName:"2",
       pickerOptions: {
         disabledDate(time) {
-          // 获取6天前的日期(加上今天就是7天)
+          // 获取13天前的日期(加上今天就是14天)
           const sixDaysAgo = new Date();
-          sixDaysAgo.setDate(sixDaysAgo.getDate() - 6);
+          sixDaysAgo.setDate(sixDaysAgo.getDate() - 13);
           sixDaysAgo.setHours(0, 0, 0, 0);
 
           // 获取明天的日期(不包括今天)
@@ -545,11 +693,12 @@ export default {
         pageNum: 1,
         pageSize: 10,
       },
+      projectOptions:[],
       sendTypeOptions:[{
-        dictLabel:"会员",dictValue:'1'
+        dictLabel:'会员',dictValue:'1'
       },
         {
-          dictLabel:"企微",dictValue:'2'
+          dictLabel:'企微',dictValue:'2'
         }
       ],
 
@@ -602,6 +751,7 @@ export default {
       // 查询参数
       queryParams: {
         pageNum: 1,
+        project: null,
         pageSize: 10,
         userId: null,
         videoId: null,
@@ -620,6 +770,8 @@ export default {
         upETime:null,
         qecSTime:null,
         qecETime:null,
+        periodSTime:null,
+        periodETime:null,
         scheduleStartTime: null,
         scheduleEndTime: null,
         sendType:process.env.VUE_APP_COURSE_DEFAULT,
@@ -629,13 +781,7 @@ export default {
       // 表单参数
       form: {},
       // 表单校验
-      rules: {},
-      
-      // 时间选择器数据
-      scheduleTime: [],
-      createTime: [],
-      updateTime: [],
-      qecCreateTime: [],
+      rules: {}
     };
   },
   created() {
@@ -643,7 +789,7 @@ export default {
       this.courseLists = response.list;
     });
     this.getList();
-    this.getDicts("sys_course_watch_log_type").then(response => {
+    this.getDicts("sys_course_watch_log_type_new").then(response => {
       this.logTypeOptions = response.data;
     });
 
@@ -653,13 +799,62 @@ export default {
     getMyQwUserList().then(response => {
       this.myQwUserList = response.data;
     });
+    this.getDicts("sys_course_project").then(response => {
+      this.projectOptions = response.data;
+    });
   },
   methods: {
 
     handleSendTypeChange() {
-      this.handleQuery(); // 重新查询列表
+      // 重置相关参数
+      this.queryParams.qwUserId = null;
+      this.queryParams.qwExternalContactId = null;
+      this.queryParams.userId = null;
+      this.queryParams.nickName = null;
+      this.queryParams.externalUserName = null;
+      this.queryParams.corpId = null;
+      this.queryParams.project = null;
+      this.queryParams.courseId = null;
+      this.queryParams.videoId = null;
+
+      // 重置选择
+      this.ids = [];
+      this.courseWatchLogList = []; // 清空表格数据
+
+      // 重置分页
+      this.queryParams.pageNum = 1;
+
+      // 强制重新渲染表格
+      this.tableKey += 1;
+
+      this.getList();
     },
 
+    // 重置日历组件
+    resetCalendars() {
+      this.scheduleTime = [];
+      this.createTime = [];
+      this.updateTime = [];
+      this.qecCreateTime = [];
+      this.periodTime = [];
+
+      this.scheduleTimeText = '';
+      this.createTimeText = '';
+      this.updateTimeText = '';
+      this.qecCreateTimeText = '';
+      this.periodTimeText = [];
+
+      // 强制刷新日历组件
+      this.scheduleCalendarKey++;
+      this.createCalendarKey++;
+      this.updateCalendarKey++;
+      this.qecCalendarKey++;
+      this.periodTimeKey++;
+    },
+    formatDateRange(dates) {
+      if (!dates || dates.length < 2) return '';
+      return dates.map(date => date.format('YYYY-MM-DD')).join(' ~ ');
+    },
     courseChange(row) {
       this.queryParams.videoId = null;
       if (row === '') {
@@ -692,20 +887,26 @@ export default {
 
     // 营期时间
     handleScheduleTimeChange(scheduleTime) {
-      if (scheduleTime && scheduleTime.length === 2) {
-        this.queryParams.scheduleStartTime = scheduleTime[0];
-        this.queryParams.scheduleEndTime = scheduleTime[1];
+      if (scheduleTime && scheduleTime.length >= 2) {
+        // this.scheduleTimeText = this.formatDateRange(scheduleTime);
+        this.queryParams.scheduleStartTime = scheduleTime[0] || null;
+        this.queryParams.scheduleEndTime = scheduleTime[1] || null;
+        console.log(this.queryParams.scheduleStartTime)
+        console.log(this.queryParams.scheduleEndTime)
       } else {
+        this.scheduleTimeText = [];
         this.queryParams.scheduleStartTime = null;
         this.queryParams.scheduleEndTime = null;
       }
     },
     // 创建时间
     createChange(createTime) {
-      if (createTime && createTime.length === 2) {
-        this.queryParams.sTime = createTime[0];
-        this.queryParams.eTime = createTime[1];
+      if (createTime && createTime.length >= 2) {
+        // this.createTimeText = this.formatDateRange(createTime);
+        this.queryParams.sTime = this.formatDate(createTime[0]) || null;
+        this.queryParams.eTime = this.formatDate(createTime[1]) || null;
       } else {
+        this.createTimeText = [];
         this.queryParams.sTime = null;
         this.queryParams.eTime = null;
       }
@@ -713,10 +914,12 @@ export default {
 
     // 更新时间
     updateChange(updateTime) {
-      if (updateTime && updateTime.length === 2) {
-        this.queryParams.upSTime = updateTime[0];
-        this.queryParams.upETime = updateTime[1];
+      if (updateTime && updateTime.length >= 2) {
+        // this.updateTimeText = this.formatDateRange(updateTime);
+        this.queryParams.upSTime = updateTime[0] || null;
+        this.queryParams.upETime = updateTime[1] || null;
       } else {
+        this.updateTimeText = [];
         this.queryParams.upSTime = null;
         this.queryParams.upETime = null;
       }
@@ -724,7 +927,7 @@ export default {
 
     // 进线时间
     qecCreateTimeChange(qecCreateTime) {
-      if (qecCreateTime && qecCreateTime.length === 2) {
+      if (qecCreateTime && qecCreateTime.length >= 2) {
         // 检查选择的日期范围是否超过7天(包括起始和结束日期)
         const startDate = new Date(qecCreateTime[0]);
         const endDate = new Date(qecCreateTime[1]);
@@ -741,18 +944,87 @@ export default {
           this.$message.error('进线时间选择范围不能超过7天');
           // 清空选择
           this.qecCreateTime = [];
+          this.qecCreateTimeText = [];
           this.queryParams.qecSTime = null;
           this.queryParams.qecETime = null;
+          this.qecCalendarKey++;
           return;
         }
 
-        this.queryParams.qecSTime = qecCreateTime[0];
-        this.queryParams.qecETime = qecCreateTime[1];
+        this.qecCreateTimeText = this.formatDateRange(qecCreateTime);
+        this.queryParams.qecSTime = qecCreateTime[0] || null;
+        this.queryParams.qecETime = qecCreateTime[1] || null;
       } else {
+        this.qecCreateTimeText = [];
         this.queryParams.qecSTime = null;
         this.queryParams.qecETime = null;
       }
     },
+
+    //营期课程时间
+    qecPeriodTimeChange(periodTime){
+
+      if (periodTime && periodTime.length >= 2) {
+        // 检查选择的日期范围是否超过7天(包括起始和结束日期)
+        const startDate = new Date(periodTime[0]);
+        const endDate = new Date(periodTime[1]);
+
+        // 设置时间为当天开始,避免时间部分影响计算
+        startDate.setHours(0, 0, 0, 0);
+        endDate.setHours(0, 0, 0, 0);
+
+        const timeDiff = Math.abs(endDate - startDate);
+        const diffDays = Math.ceil(timeDiff / (1000 * 60 * 60 * 24));
+
+        // 如果超过6天的范围(总共7天,包括起始日)
+        if (diffDays > 13) {
+          this.$message.error('时间选择范围不能超过14天');
+          // 清空选择
+          this.periodTime = [];
+          this.periodTimeText = [];
+          this.queryParams.periodSTime = null;
+          this.queryParams.periodETime = null;
+          this.periodTimeKey++;
+          return;
+        }
+
+        this.queryParams.periodSTime = this.formatDate(periodTime[0]) || null;
+        this.queryParams.periodETime = this.formatDate(periodTime[1]) || null;
+
+      } else {
+
+        this.periodTimeText = '';
+        this.queryParams.periodSTime = null;
+        this.queryParams.periodETime = null;
+      }
+
+    },
+
+    formatDate(date) {
+      if (!date) return ''
+
+      // 确保 date 是 Date 对象
+      let dateObj = date
+      if (typeof date === 'string') {
+        dateObj = new Date(date)
+      }
+
+      // 如果转换失败,返回空字符串
+      if (!(dateObj instanceof Date) || isNaN(dateObj.getTime())) {
+        return ''
+      }
+
+      // 使用更安全的格式化方法
+      const year = dateObj.getFullYear()
+      const month = String(dateObj.getMonth() + 1).padStart(2, '0')
+      const day = String(dateObj.getDate()).padStart(2, '0')
+      const hours = String(dateObj.getHours()).padStart(2, '0')
+      const minutes = String(dateObj.getMinutes()).padStart(2, '0')
+      const seconds = String(dateObj.getSeconds()).padStart(2, '0')
+
+      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
+    },
+
     /** 查询短链课程看课记录列表 */
     getList() {
       this.loading = true;
@@ -796,6 +1068,8 @@ export default {
         scheduleStartTime: null,
         scheduleEndTime: null,
       };
+      // 统一重置日历组件
+      this.resetCalendars();
       this.resetForm("form");
     },
     /** 搜索按钮操作 */
@@ -808,22 +1082,23 @@ export default {
       this.resetForm("queryForm");
       this.queryParams.sTime = null;
       this.queryParams.eTime = null;
+      this.queryParams.project = null;
       this.queryParams.upSTime = null;
       this.queryParams.upETime = null;
       this.queryParams.qecSTime = null;
       this.queryParams.qecETime = null;
+      this.queryParams.periodSTime = null;
+      this.queryParams.periodDTime = null;
       this.queryParams.scheduleStartTime = null;
       this.queryParams.scheduleEndTime = null;
       this.queryParams.sopId = null; // 重置SOP ID
-
+      this.queryParams.isVip = null; // 重置 isVip 状态
+      this.queryParams.qwUserId = null; // 重置 qwUserId
+      this.queryParams.externalUserName=null;
       // 重置SOP搜索
       this.handleSopClear();
-      
-      // 重置日期选择器
-      this.scheduleTime = [];
-      this.createTime = [];
-      this.updateTime = [];
-      this.qecCreateTime = [];
+      // 统一重置日历组件
+      this.resetCalendars();
 
       this.handleQuery();
     },
@@ -920,7 +1195,7 @@ export default {
     },
     redLogList() {
       this.loadingRedLog = true;
-      console.info(this.redLogQueryParams)
+      console.info("-----watch",this.redLogQueryParams)
       myListCourseRedPacketLog(this.redLogQueryParams).then(e => {
         this.redLogsList = e.rows;
         this.redLogTotal = e.total;
@@ -993,6 +1268,7 @@ export default {
 
       searchTags(this.queryTagParams).then(response => {
         this.tagGroupList = response.rows;
+        this.tagTotal = response.total;
       });
 
       // searchTags({name:name,corpId:this.queryParams.corpId}).then(response => {
@@ -1255,4 +1531,4 @@ export default {
   font-size: 14px;
   color: #606266;
 }
-</style>
+</style>

+ 2 - 0
src/views/course/courseWatchLog/watchLogStatistics.vue

@@ -247,8 +247,10 @@ export default {
       this.queryParams.upETime = null;
       this.queryParams.scheduleStartTime = null;
       this.queryParams.scheduleEndTime = null;
+      this.queryParams.externalUserName=null;
       this.scheduleTime=null;
       this.updateTime=null;
+
       this.handleQuery();
     },
     // 多选框选中数据

+ 3 - 1
src/views/course/userCourse/index.vue

@@ -391,7 +391,8 @@ export default {
         likes:100000,
         shares:100000,
         favoriteNum:100000,
-        hotNum:100000
+        hotNum:100000,
+        companyIdsList:[]
       };
       this.tags=[];
       this.subCategoryOptions=[]
@@ -406,6 +407,7 @@ export default {
     /** 重置按钮操作 */
     resetQuery() {
       this.resetForm("queryForm");
+      this.queryParams.companyIdsList=null;
       this.queryParams.isShow=this.activeName
       this.handleQuery();
     },

+ 10 - 10
src/views/crm/components/customerAssignList.vue

@@ -19,7 +19,7 @@
            >
         </el-date-picker>
       </el-form-item>
-        
+
         <el-form-item>
           <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
           <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
@@ -41,7 +41,7 @@
         <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
         <template slot-scope="scope">
           <el-button
-           
+
             size="mini"
             type="text"
             @click="handleShow(scope.row)"
@@ -72,9 +72,9 @@
       </el-dialog>
     </div>
   </template>
-  
+
   <script>
-  
+
   import { listCustomerAssign,   cancelCustomerAssign } from "@/api/crm/customerAssign";
   import { getCustomerListByIds } from "@/api/crm/customer";
   export default {
@@ -115,7 +115,7 @@
           sysUserId: null,
           assignType: null,
         },
-        
+
       };
     },
     created() {
@@ -143,7 +143,7 @@
             this.loading = false;
             });
         },
-         
+
         /** 搜索按钮操作 */
         handleQuery() {
             this.queryParams.pageNum = 1;
@@ -151,8 +151,9 @@
         },
         /** 重置按钮操作 */
         resetQuery() {
-            this.resetForm("queryForm");
-            this.handleQuery();
+          this.dateRange = [];
+          this.resetForm("queryForm");
+          this.handleQuery();
         },
         // 多选框选中数据
         handleSelectionChange(selection) {
@@ -173,8 +174,7 @@
                 this.msgSuccess("操作成功");
             }).catch(function() {});
         },
-         
+
     }
   };
   </script>
-  

+ 16 - 11
src/views/crm/customer/full.vue

@@ -54,7 +54,7 @@
                   />
             </el-select>
           </el-form-item>
-          
+
           <el-form-item label="客户类型" prop="customerType">
             <el-select multiple filterable v-model="ctsTypeArr" placeholder="请选择客户类型" clearable size="small">
               <el-option
@@ -65,7 +65,7 @@
                   />
             </el-select>
           </el-form-item>
-          
+
           <el-form-item label="客户标签" prop="tags">
             <el-select multiple  filterable v-model="tagIds" placeholder="请选择客户标签" clearable size="small">
               <el-option
@@ -76,7 +76,7 @@
                   />
             </el-select>
           </el-form-item>
-          
+
           <!-- <el-form-item label="是否认领" prop="isReceive">
             <el-select v-model="queryParams.isReceive" placeholder="请选择是否认领" clearable size="small">
               <el-option
@@ -87,7 +87,7 @@
                   />
             </el-select>
           </el-form-item> -->
-          
+
           <el-form-item label="创建时间" prop="createTime">
             <el-date-picker
               style="width:205.4px"
@@ -215,7 +215,7 @@
             </template>
           </el-table-column>
         </el-table>
-        
+
         <pagination
           v-show="total>0"
           :total="total"
@@ -224,8 +224,8 @@
           @pagination="getList"
         />
 
-    
- 
+
+
     <el-drawer
       size="75%"
       :title="show.title" :visible.sync="show.open"
@@ -238,13 +238,13 @@
     <el-dialog :title="assign.title" :visible.sync="assign.open" width="800px" append-to-body>
         <assign-user  ref="assignUser" @close="closeAssign"   />
     </el-dialog>
-   
- 
+
+
   </div>
 </template>
 
 <script>
- 
+
 import { assignToUser,receive,getFullCustomerList,addCustomer,updateCustomer,getCustomerDetails,exportCustomer  } from "@/api/crm/customer";
 import customerDetails from '../components/customerDetails.vue';
 import {getCitys} from "@/api/store/city";
@@ -434,7 +434,7 @@ export default {
           else if(that.queryParams.isReceive=="1"){
             that.$refs.assignUser.init(ids,3);
           }
-          
+
       }, 200);
     },
     closeAssign(){
@@ -582,6 +582,11 @@ export default {
     /** 重置按钮操作 */
     resetQuery() {
       this.resetForm("queryForm");
+      this.visitStatusArr =  [];
+      this.tagIds =  [];
+      this.dateRange = [];
+      this.sourceArr  =  [];
+      this.ctsTypeArr =  [];
       this.handleQuery();
     },
     // 多选框选中数据

+ 5 - 0
src/views/crm/customer/index.vue

@@ -752,6 +752,11 @@ export default {
     /** 重置按钮操作 */
     resetQuery() {
       this.resetForm("queryForm");
+      this.sourceArr =  [];
+      this.visitStatusArr =  [];
+      this.tagIds =  [];
+      this.ctsTypeArr =  [];
+      this.dateRange = [];
       this.handleQuery();
     },
     // 多选框选中数据

+ 7 - 39
src/views/crm/customer/line.vue

@@ -12,25 +12,13 @@
           />
         </el-form-item>
         <el-form-item label="客户名称" prop="customerName">
-          <el-select
+          <el-input
             v-model="queryParams.customerName"
-            filterable
-            remote
+            placeholder="请输入客户名称"
             clearable
             size="small"
-            placeholder="请输入客户名称"
-            :remote-method="remoteCustomerNameQuery"
-            :loading="customerNameLoading"
-            @change="handleQuery"
-            @clear="handleClearCustomerName"
-          >
-            <el-option
-              v-for="item in customerNameOptions"
-              :key="item"
-              :label="item"
-              :value="item"
-            />
-          </el-select>
+            @keyup.enter.native="handleQuery"
+          />
         </el-form-item>
         <el-form-item label="手机" prop="mobile">
           <el-input
@@ -279,7 +267,7 @@
 
 <script>
  import store from "@/store";
-import { getCustName,importLineTemplate,getLineCustomerList ,assignToUser,getCustomerDetails,exportCustomer } from "@/api/crm/customer";
+import { importLineTemplate,getLineCustomerList ,assignToUser,getCustomerDetails,exportCustomer } from "@/api/crm/customer";
 import customerDetails from '../components/customerDetails.vue';
 import { treeselect } from "@/api/company/companyDept";
 import Treeselect from "@riophae/vue-treeselect";
@@ -294,8 +282,6 @@ export default {
   components: { assignUser,customerAssignList,addOrEditCustomer,customerDetails,Treeselect,editSource },
   data() {
     return {
-      customerNameOptions: [], // 客户名称
-      customerNameLoading: false,
       companyUser:store.getters.user,
       exportLoading:null,
       exportInteval:null,
@@ -597,6 +583,8 @@ export default {
     /** 重置按钮操作 */
     resetQuery() {
       this.resetForm("queryForm");
+      this.tagIds = [];
+      this.dateRange = null;
       this.handleQuery();
     },
     // 多选框选中数据
@@ -693,26 +681,6 @@ export default {
     submitFileForm() {
       this.$refs.upload.submit();
     },
-    // 远程搜索客户名称
-    remoteCustomerNameQuery(query) {
-      if (query !== '') {
-        this.customerNameLoading = true;
-        // 防抖处理,避免频繁请求
-        clearTimeout(this.customerNameTimer);
-        this.customerNameTimer = setTimeout(() => {
-          getCustName({ customerName: query })
-            .then(response => {
-              this.customerNameOptions = response
-              this.customerNameLoading = false;
-            })
-            .catch(() => {
-              this.customerNameLoading = false;
-            });
-        }, 300);
-      } else {
-        this.customerNameOptions = [];
-      }
-    },
 
     getCustomerName(customerName){
        if(customerName==null || customerName==""){

+ 21 - 15
src/views/crm/customer/my.vue

@@ -154,7 +154,7 @@
       </el-col>
 	  <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
-    
+
     <el-table  height="500" border v-loading="loading" :data="customerList" @selection-change="handleSelectionChange">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="客户编码" align="center" prop="customerCode" />
@@ -220,7 +220,7 @@
             v-hasPermi="['crm:customer:recover']"
           >回收公海</el-button>
           <!-- <el-button
-         
+
             size="mini"
             type="text"
             @click="handleAssign(scope.row)"
@@ -242,13 +242,13 @@
     <el-dialog :title="addSms.title" :visible.sync="addSms.open" width="1000px" append-to-body>
        <add-batch-sms ref="sms" @close="closeSms()"></add-batch-sms>
     </el-dialog>
- 
- 
+
+
     <el-dialog :title="source.title" :visible.sync="source.open" width="1000px" append-to-body>
        <edit-source ref="editSource" @close="closeSource()"></edit-source>
     </el-dialog>
- 
-    <el-dialog :title="visit.title" :visible.sync="visit.open" width="600px" append-to-body> 
+
+    <el-dialog :title="visit.title" :visible.sync="visit.open" width="600px" append-to-body>
       <add-visit @closeVisit="closeVisit"   ref="addVisit" />
     </el-dialog>
     <el-dialog :title="customer.title" :visible.sync="customer.open" width="1000px" append-to-body>
@@ -462,7 +462,7 @@ export default {
         setTimeout(() => {
             that.$refs.visitStatus.reset(row);
         }, 500);
-        
+
     },
     closeCustomerType(){
         this.addCustomerType.open=false;
@@ -474,7 +474,7 @@ export default {
         setTimeout(() => {
             that.$refs.customerType.reset(row);
         }, 500);
-        
+
     },
     closeRemark(){
         this.addRemark.open=false;
@@ -486,7 +486,7 @@ export default {
         setTimeout(() => {
             that.$refs.remark.reset(row);
         }, 500);
-        
+
     },
     closeTag(){
         this.addTag.open=false;
@@ -498,7 +498,7 @@ export default {
         setTimeout(() => {
             that.$refs.tag.reset(row);
         }, 500);
-        
+
     },
     handleShow(row){
       this.show.open=true;
@@ -549,7 +549,7 @@ export default {
       setTimeout(() => {
         that.$refs.editSource.handleEdit(that.ids);
       }, 200);
-      
+
     },
     closeSource(){
         this.source.open=false;
@@ -593,7 +593,7 @@ export default {
           this.citys=res.data;
         })
     },
-    
+
     /** 查询客户列表 */
     getList() {
       this.loading = true;
@@ -649,6 +649,12 @@ export default {
     /** 重置按钮操作 */
     resetQuery() {
       this.resetForm("queryForm");
+      this.statusArr=[];
+      this.ctsTypeArr=[];
+      this.tagIds=[];
+      this.sourceArr=[];
+      this.createTimeRange=null;
+      this.dateRange = null;
       this.handleQuery();
     },
     // 多选框选中数据
@@ -657,9 +663,9 @@ export default {
       this.single = selection.length!==1
       this.multiple = !selection.length
     },
-    
-    
-     
+
+
+
     /** 删除按钮操作 */
     handleDelete(row) {
       const customerIds = row.customerId || this.ids;

+ 9 - 7
src/views/crm/customerVisit/index.vue

@@ -1,6 +1,6 @@
 <template>
     <div class="app-container">
- 
+
         <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
           <el-form-item label="客户编码" prop="customerCode">
             <el-input
@@ -133,13 +133,13 @@
             <template slot-scope="scope">
               <el-image  v-for="(img) in parsePhoto(scope.row.photos)"
                 style="width: 30px; height: 30px;margin-right:5px"
-                :src="img" 
+                :src="img"
                 :preview-src-list="parsePhoto(scope.row.photos)">
               </el-image>
             </template>
           </el-table-column>
           <el-table-column label="跟进时间" align="center" prop="createTime" />
-          
+
           <el-table-column label="下次联系时间" align="center" prop="nextTime" width="180">
             <template slot-scope="scope">
               <span>{{ parseTime(scope.row.nextTime, '{y}-{m}-{d}') }}</span>
@@ -164,7 +164,7 @@
             </template>
           </el-table-column>
         </el-table>
-        
+
         <pagination
           v-show="total>0"
           :total="total"
@@ -172,7 +172,7 @@
           :limit.sync="queryParams.pageSize"
           @pagination="getList"
         />
-  
+
       <el-drawer
        size="75%"
         :title="show.title" :visible.sync="show.open"
@@ -181,7 +181,7 @@
       </el-drawer>
     </div>
   </template>
-  
+
   <script>
   import { listCustomerVisit, getCustomerVisit, delCustomerVisit, addCustomerVisit, updateCustomerVisit, exportCustomerVisit } from "@/api/crm/customerVisit";
   import customerDetails from '../components/customerDetails.vue';
@@ -358,6 +358,9 @@
       /** 重置按钮操作 */
       resetQuery() {
         this.resetForm("queryForm");
+        this.dateRange = [];
+        this.createTimeRange=[];
+        this.receiveTimeRange=[];
         this.handleQuery();
       },
       // 多选框选中数据
@@ -436,4 +439,3 @@
     }
   };
   </script>
-  

+ 115 - 8
src/views/index.vue

@@ -364,21 +364,40 @@
           </el-card>
         </el-col>
 
+
         <el-col :span="12">
           <el-card shadow="never">
             <div slot="header" class="chart-header">
-              <span>经销商会员观看TOP10</span>
+              <span>经销商看客统计</span>
               <div class="legend">
-                <el-radio-group v-model="viewerType" size="small" @change="handleDealerChartData">
-                  <el-radio-button label="0">按观看人数</el-radio-button>
-                  <el-radio-button label="1">按完播人数</el-radio-button>
-                </el-radio-group>
+                <div class="legend-item">
+                  <span class="dot viewer-dot"></span>
+                  <span>观看人数</span>
+                </div>
+                <div class="legend-item">
+                  <span class="dot complete-dot"></span>
+                  <span>完播人数</span>
+                </div>
               </div>
-              <!--              <el-button size="small" plain class="view-more">经销商统计 <i class="el-icon-arrow-right"></i></el-button>-->
             </div>
-            <div ref="dealerChart" class="chart-container"></div>
+            <div ref="dealerChartNew" class="chart-container"></div>
           </el-card>
         </el-col>
+<!--        <el-col :span="12">-->
+<!--          <el-card shadow="never">-->
+<!--            <div slot="header" class="chart-header">-->
+<!--              <span>经销商会员观看TOP10</span>-->
+<!--              <div class="legend">-->
+<!--                <el-radio-group v-model="viewerType" size="small" @change="handleDealerChartData">-->
+<!--                  <el-radio-button label="0">按观看人数</el-radio-button>-->
+<!--                  <el-radio-button label="1">按完播人数</el-radio-button>-->
+<!--                </el-radio-group>-->
+<!--              </div>-->
+<!--              &lt;!&ndash;              <el-button size="small" plain class="view-more">经销商统计 <i class="el-icon-arrow-right"></i></el-button>&ndash;&gt;-->
+<!--            </div>-->
+<!--            <div ref="dealerChart" class="chart-container"></div>-->
+<!--          </el-card>-->
+<!--        </el-col>-->
       </el-row>
     </transition>
     <transition name="fade">
@@ -509,7 +528,7 @@ import {
   authorizationInfo,
   dealerAggregated, deaMemberTopTen, rechargeComsumption, rewardMoneyTopTen, rewardMoneyTrend,
   smsBalance, thisMonthOrderCount, thisMonthRecvCount, trafficLog,
-  watchCourseTopTen, watchEndPlayTrend
+  watchCourseTopTen, watchEndPlayTrend,getWatchCourseStatisticsData
 } from "@/api/statistics/statistics";
 import dayjs from 'dayjs';
 
@@ -554,6 +573,56 @@ const viewCharOption = {
   ]
 }
 
+const dealerOptionNew = {
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'shadow'
+    }
+  },
+  grid: {
+    left: '3%',
+    right: '4%',
+    bottom: '3%',
+    containLabel: true
+  },
+  xAxis: {
+    type: 'category',
+    axisLabel: {
+      rotate: 30, // 设置标签倾斜45度
+      // fontSize: 12, // 减小字体大小
+      interval: 0, // 显示所有标签
+      // 可选:限制标签宽度并截断
+      width: 80,
+      overflow: 'truncate',
+      // 可选:设置标签的对齐方式
+      margin: 20,
+      fontWeight: 'bold' // 设置字体加粗
+    }
+  },
+  yAxis: {
+    type: 'value'
+  },
+  series: [
+    {
+      name: '观看人数',
+      type: 'bar',
+      data: [],
+      itemStyle: {
+        color: '#409EFF'
+      }
+    },
+    {
+      name: '完播人数',
+      type: 'bar',
+      data: [],
+      itemStyle: {
+        color: '#67C23A'
+      }
+    }
+  ]
+}
+
 const thisMonthOrderCountOption = {
   tooltip: {
     trigger: 'axis',
@@ -858,6 +927,7 @@ export default {
       smsRemainCount: 0,
       viewerType: '0',
       viewerChart: null,
+      dealerChartNew: null,
       userTypeText: process.env.VUE_APP_COURSE_DEFAULT==1?"会员":"企微",
       userType: process.env.VUE_APP_COURSE_DEFAULT,
       dealerChart: null,
@@ -925,6 +995,7 @@ export default {
   mounted() {
     this.$nextTick(() => {
       this.initViewerChart()
+      this.initDealerChartNew();
       this.initDealerChart()
       this.initCourseWatchChart();
       this.initAnswerRedPackViewerChart();
@@ -936,6 +1007,8 @@ export default {
       window.addEventListener('resize', () => {
         this.viewerChart && this.viewerChart.resize()
         this.dealerChart && this.dealerChart.resize()
+        this.dealerChartNew && this.dealerChartNew.resize()
+
       })
     })
   },
@@ -1130,6 +1203,7 @@ export default {
 
       this.handleCourseWatchChart()
       this.handleViewChartData()
+      this.handleDealerChartDataNew()
 
       // 经销商会员观看TOP10
       this.handleDealerChartData()
@@ -1192,6 +1266,7 @@ export default {
       if (this.selectedDiv === 0) {
         this.handleViewChartData()
         this.handleDealerChartData()
+        this.handleDealerChartDataNew()
       } else if (this.selectedDiv === 1) {
         this.handleCourseWatchChart()
       } else if (this.selectedDiv === 2) {
@@ -1267,6 +1342,7 @@ export default {
       if (this.selectedDiv === 0) {
         this.handleViewChartData()
         this.handleDealerChartData()
+        this.handleDealerChartDataNew()
       } else if (this.selectedDiv === 1) {
         this.handleCourseWatchChart()
       } else if (this.selectedDiv === 2) {
@@ -1344,6 +1420,35 @@ export default {
       })
 
     },
+    handleDealerChartDataNew() {
+      let param = this.getParam();
+
+      getWatchCourseStatisticsData({ ...param }).then(res => {
+        if (res.code === 200) {
+          console.log(res.data);
+          // 根据实际数据结构调整
+          let data = res.data;
+          let watchUserCountList = data.map(e => e.watchCount);     // 观看次数
+          let completedUserCountList = data.map(e => e.finishCount); // 完播次数
+          let xAxis = data.map(e => e.companyName);                 // X轴使用公司名称
+
+          // 更新图表配置
+          dealerOptionNew.series[0].data = watchUserCountList;
+          dealerOptionNew.series[1].data = completedUserCountList;
+          dealerOptionNew.xAxis.data = xAxis;
+
+          this.dealerChartNew.setOption(dealerOptionNew);
+
+        }
+      })
+
+    },
+    initDealerChartNew() {
+      console.log("初始化 initDealerChartNew");
+      this.dealerChartNew = echarts.init(this.$refs.dealerChartNew)
+      this.dealerChartNew.setOption(dealerOptionNew)
+      console.log("初始化结束 initDealerChartNew");
+    },
     handleThisMonthOrderCount(){
       thisMonthOrderCount().then(res=>{
         if(res.code === 200){
@@ -1434,6 +1539,8 @@ export default {
     // window.removeEventListener('resize', this.resizeHandler)
     this.viewerChart && this.viewerChart.dispose()
     this.dealerChart && this.dealerChart.dispose()
+    this.dealerChartNew && this.dealerChartNew.dispose()
+
   }
 }
 </script>

+ 7 - 2
src/views/member/list.vue

@@ -191,13 +191,18 @@
           </el-tag>
         </template>
       </el-table-column>
-      <el-table-column label="企微标签" align="center" prop="tag" show-overflow-tooltip />
-      <el-table-column label="小程序标签" align="center" prop="projectTagName" show-overflow-tooltip />
+      <el-table-column label="标签" align="center" prop="tag" show-overflow-tooltip />
       <el-table-column label="最后看课时间" align="center" prop="lastWatchDate" width="160" />
 <!--      <el-table-column label="停课天数" align="center" prop="stopWatchDays" />-->
       <el-table-column label="注册时间" align="center" prop="createTime" width="160" />
       <el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip />
       <el-table-column label="所属员工" align="center" prop="companyUserNickName" />
+      <el-table-column label="会员积分" align="center" prop="integral" />
+      <el-table-column label="是否购买" align="center" prop="isBuy">
+        <template slot-scope="scope">
+          <span>{{ scope.row.isBuy === 1 ? '是' : '否' }}</span>
+        </template>
+      </el-table-column>
       <el-table-column label="操作" align="center" width="120" class-name="small-padding fixed-width">
         <template slot-scope="scope">
           <el-button

+ 8 - 3
src/views/member/mylist.vue

@@ -191,13 +191,18 @@
           </el-tag>
         </template>
       </el-table-column>
-      <el-table-column label="企微标签" align="center" prop="tag" show-overflow-tooltip />
-      <el-table-column label="小程序标签" align="center" prop="projectTagName" show-overflow-tooltip />
+      <el-table-column label="标签" align="center" prop="tag" show-overflow-tooltip />
       <el-table-column label="最后看课时间" align="center" prop="lastWatchDate" width="160" />
       <el-table-column label="停课天数" align="center" prop="stopWatchDays" />
       <el-table-column label="注册时间" align="center" prop="createTime" width="160" />
       <el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip />
       <el-table-column label="所属员工" align="center" prop="companyUserNickName" />
+      <el-table-column label="会员积分" align="center" prop="integral" />
+      <el-table-column label="是否购买" align="center" prop="isBuy">
+        <template slot-scope="scope">
+          <span>{{ scope.row.isBuy === 1 ? '是' : '否' }}</span>
+        </template>
+      </el-table-column>
       <el-table-column label="操作" align="center" width="120" class-name="small-padding fixed-width">
         <template slot-scope="scope">
           <el-button
@@ -270,7 +275,7 @@
             />
           </el-select>
         </el-form-item> -->
-        <el-form-item label="企微标签" prop="tag">
+        <el-form-item label="标签" prop="tag">
           <el-input v-model="form.tag" disabled  clearable >
           </el-input>
         </el-form-item>

+ 22 - 5
src/views/qw/autoTags/dayPartingIndex.vue

@@ -174,13 +174,29 @@
             <div style="background-color: #fbfbfb;padding: 10px;  border: 1px solid #e6e6e6; margin-bottom: 10px;">
               <el-form ref="rulesTagsFrom" :rules="rulesTagsRules" :model="item" >
                 <div>
+                  <el-form-item label="执行规则" prop="dayOrWeek" style="width: 500px;margin:5px 0 0 8%">
+                    <el-radio-group v-model="item.dayOrWeek">
+                      <el-radio :label="0">星期</el-radio>
+                      <el-radio :label="1">日期</el-radio>
+                    </el-radio-group>
+                  </el-form-item>
                   <div style="display: flex; align-items: center; flex-wrap: nowrap;">
                     <span style="margin-right: 10px;">规则 {{ index + 1 }}:</span>
-                    <el-form-item label="为每:" prop="week" style="flex: 8;margin-bottom: 0">
+                    <el-form-item label="为每:" prop="week" style="flex: 8;margin-bottom: 0" v-if="item.dayOrWeek == 0">
                       <el-select v-model="item.week" remote multiple placeholder="请选择时间" filterable style="width: 350px;">
                         <el-option v-for="dict in weekOptions" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
                       </el-select>
                     </el-form-item>
+                    <el-form-item label="为每:" prop="days" style="flex: 8;margin-bottom: 0" v-if="item.dayOrWeek == 1">
+                      <el-date-picker
+                        clearable size="small"
+                        v-model="item.days"
+                        type="daterange"
+                        value-format="yyyy-MM-dd"
+                        start-placeholder="开始日期"
+                        end-placeholder="结束日期">
+                      </el-date-picker>
+                    </el-form-item>
                     <el-form-item prop="startTime" style="margin:0 5px 0 5px">
                       <el-time-select style="width: 120px;" placeholder="起始时间" v-model="item.startTime" :picker-options="{
                               start: '00:00',
@@ -523,7 +539,7 @@ export default {
         // 遍历 tagListFormIndex 并将 tagId 放入对应位置的 tags
         newList.forEach((tags, index) => {
           if (!this.form.rulesTags[index]) {
-            this.$set(this.form.rulesTags, index, {tags:[],week:[1,2,3,4,5,6,7],startTime:null,endTime:null,remarks:null});
+            this.$set(this.form.rulesTags, index, {tags:[],week:[1,2,3,4,5,6,7],startTime:null,endTime:null,remarks:null,dayOrWeek:0,days:null});
           }
 
           // 确保清空原来的 tags 数组
@@ -651,6 +667,7 @@ export default {
       this.queryTagParams.corpId=this.queryParams.corpId;
       searchTags(this.queryTagParams).then(response => {
         this.tagGroupList = response.rows;
+        this.tagTotal = response.total;
       });
 
     },
@@ -726,7 +743,7 @@ export default {
       if (this.form.rulesTags.length >=7) {
         return this.$message.error('当前规则已达上限,无法添加规则');
       }
-      this.form.rulesTags.push({tags:[],week:[1,2,3,4,5,6,7],startTime:null,endTime:null,remarks:null})
+      this.form.rulesTags.push({tags:[],week:[1,2,3,4,5,6,7],startTime:null,endTime:null,remarks:null,dayOrWeek:0,days:null})
     },
 
     //选择标签弹窗
@@ -814,7 +831,7 @@ export default {
         createName: null,
         applyUsers: null,
         ruleName: null,
-        rulesTags: [{tags:[],week:[1,2,3,4,5,6,7],startTime:null,endTime:null,remarks:null}],
+        rulesTags: [{tags:[],week:[1,2,3,4,5,6,7],startTime:null,endTime:null,remarks:null,dayOrWeek:0,days:null}],
         totalNumTags: null,
         numTagsTaday: null,
         isApply: null,
@@ -828,7 +845,7 @@ export default {
       this.tagListFormIndex=[];
 
       //规则
-      this.form.rulesTags=[{tags:[],week:[1,2,3,4,5,6,7],startTime:null,endTime:null,remarks:null}];
+      this.form.rulesTags=[{tags:[],week:[1,2,3,4,5,6,7],startTime:null,endTime:null,remarks:null,dayOrWeek:0,days:null}];
       this.resetForm("form");
     },
     /** 搜索按钮操作 */

+ 4 - 1
src/views/qw/autoTags/dayPartingIndexDetails.vue

@@ -51,9 +51,12 @@
                 <div style="margin-left: 10%;margin-top: 1%">
                   <span class="spanSize">|规则 {{index +1}} </span>
                   <div class="spanSize">客户在 每
-                    <span v-for="id in rtList.week" :key="id" style="display: inline;">
+                    <span v-for="id in rtList.week" :key="id" style="display: inline;" v-if="rtList.dayOrWeek == 0">
                       <el-tag :disable-transitions="false"  v-for="list in weekOptions" :key="list.value" style="margin: 3px;" v-if="list.value==id">{{list.label}}</el-tag>
                     </span>
+                    <span style="display: inline;" v-if="rtList.dayOrWeek == 1">
+                      <el-tag>{{rtList.days[0]}}</el-tag> 到 <el-tag>{{rtList.days[1]}}</el-tag>
+                    </span>
                     <span>【{{rtList.startTime}}~{{rtList.endTime}}】</span>
                     时,打上标签
                     <span v-for="tagId in rtList.tags" :key="tagId" style="display: inline;">

+ 1 - 0
src/views/qw/autoTags/groupIndex.vue

@@ -518,6 +518,7 @@ export default {
       this.queryTagParams.corpId=this.queryParams.corpId;
       searchTags(this.queryTagParams).then(response => {
         this.tagGroupList = response.rows;
+        this.tagTotal = response.total;
       });
 
     },

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

@@ -933,6 +933,7 @@ export default {
 
       searchTags(this.queryTagParams).then(response => {
         this.tagGroupList = response.rows;
+        this.tagTotal = response.total;
       });
       // searchTags({name:name,corpId:this.queryParams.corpId}).then(response => {
       //   this.tagGroupList = response.rows;

+ 3 - 1
src/views/qw/externalContact/deptIndex.vue

@@ -57,7 +57,7 @@
       </el-form-item>
 
       <el-form-item label="性别" prop="gender">
-        <el-select v-model="queryParams.gender" placeholder="状态" clearable size="small">
+        <el-select v-model="queryParams.gender" placeholder="性别" clearable size="small">
           <el-option
             v-for="dict in genderOptions"
             :key="dict.dictValue"
@@ -1602,6 +1602,7 @@ export default {
 
       searchTags(this.queryTagParams).then(response => {
         this.tagGroupList = response.rows;
+        this.tagTotal = response.total;
       });
 
       // searchTags({name:name,corpId:this.queryParams.corpId}).then(response => {
@@ -1700,6 +1701,7 @@ export default {
     resetQuery() {
       this.resetForm("queryForm");
       this.queryParams.corpId= this.myQwCompanyList[0].dictValue;
+      this.queryParams.transferStatus = null;
       this.selectTags=[];
       this.createTime=null;
       this.queryParams.sTime=null;

+ 250 - 6
src/views/qw/externalContact/index.vue

@@ -74,7 +74,7 @@
         />
       </el-form-item>
       <el-form-item label="性别" prop="gender">
-        <el-select v-model="queryParams.gender" placeholder="状态" clearable size="small">
+        <el-select v-model="queryParams.gender" placeholder="性别" clearable size="small">
           <el-option
             v-for="dict in genderOptions"
             :key="dict.dictValue"
@@ -83,6 +83,12 @@
           />
         </el-select>
       </el-form-item>
+      <el-form-item label="是否重粉" prop="userRepeat">
+        <el-select v-model="queryParams.userRepeat" placeholder="重粉" clearable size="small">
+          <el-option label="否" :value="0"/>
+          <el-option label="是" :value="1"/>
+        </el-select>
+      </el-form-item>
       <el-form-item label="客户等级" prop="level">
         <el-select v-model="queryParams.level" placeholder="客户等级" clearable size="small">
           <el-option
@@ -422,6 +428,20 @@
         </template>
       </el-table-column>
       <el-table-column label="企业id" align="center" prop="corpId" />
+      <el-table-column label="重粉看课历史" width="100px" align="center" fixed="right">
+        <template slot-scope="scope">
+          <div v-if="scope.row.fsUserId">
+            <el-tag type="success" v-if="scope.row.userRepeat == 0">正常</el-tag>
+            <el-tag type="danger" v-if="scope.row.userRepeat == 1">重粉</el-tag>
+            <el-button
+              size="mini"
+              type="text"
+              @click="showLog(scope.row)"
+            >重粉看课历史
+            </el-button>
+          </div>
+        </template>
+      </el-table-column>
       <el-table-column label="是否绑定会员" width="100px" align="center" fixed="right">
         <template slot-scope="scope">
           <el-tag v-if="scope.row.fsUserId" >已绑定</el-tag>
@@ -498,7 +518,24 @@
              @click="handleMemberdetails(scope.row)"
              v-if="scope.row.fsUserId"
              >
-             <span>会员详细</span>
+             <span>会员详情</span>
+          </el-button>
+          <el-button
+             size="mini"
+             type="text"
+             @click="handleInfoCollection(scope.row)"
+             v-if="false"
+             >
+             <span>信息采集</span>
+          </el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleChangeStatus(scope.row)"
+            v-hasPermi="['qw:externalContact:changeStatus']"
+          >
+            修改状态
           </el-button>
         </template>
       </el-table-column>
@@ -754,6 +791,74 @@
       <mycustomer ref="mycustomer"  @bindCustomerId="bindCustomerId"></mycustomer>
     </el-dialog>
 
+    <!-- 重粉看课记录   -->
+    <el-drawer title="重粉看课历史" :visible.sync="log.open" size="75%" append-to-body>
+      <div style="padding: 10px">
+        <el-form :model="log.queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
+          <el-form-item label="所属项目" prop="project">
+            <el-select v-model="log.queryParams.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">
+            <el-select filterable v-model="log.queryParams.courseId" placeholder="请选择课程" clearable size="small"
+                       @change="courseChange(log.queryParams.courseId)">
+              <el-option
+                v-for="dict in courseLists"
+                :key="dict.dictValue"
+                :label="dict.dictLabel"
+                :value="dict.dictValue"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="小节" prop="videoId">
+            <el-select filterable v-model="log.queryParams.videoId" placeholder="请选择小节" clearable size="small">
+              <el-option
+                v-for="dict in videoList"
+                :key="dict.dictValue"
+                :label="dict.dictLabel"
+                :value="dict.dictValue"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQueryWatchLog">搜索</el-button>
+          </el-form-item>
+        </el-form>
+        <el-table v-loading="log.loading" :data="log.list">
+          <el-table-column label="编号" align="center" prop="id"/>
+          <el-table-column label="所属企微主体" align="center" prop="corpName"/>
+          <el-table-column label="所属企微" align="center" prop="qwUserName"/>
+          <!--          <el-table-column label="企微" align="center" prop="qwUserName"/>-->
+          <el-table-column label="项目" align="center" prop="projectName"/>
+          <el-table-column label="课程" align="center" prop="courseName"/>
+          <el-table-column label="小节" align="aligner" prop="videoName"/>
+          <el-table-column label="记录时间" align="center" prop="createTime"/>
+          <el-table-column label="是否完课" align="center" prop="logType">
+            <template slot-scope="scope">
+              <el-tag v-if="scope.row.logType == 2" type="success">已完课</el-tag>
+              <el-tag v-else type="success">未完课</el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column label="完课时间" align="center" prop="finishTime"/>
+        </el-table>
+
+        <pagination
+          v-show="log.total>0"
+          :total="log.total"
+          :page.sync="log.queryParams.pageNum"
+          :limit.sync="log.queryParams.pageSize"
+          @pagination="logList"
+        />
+      </div>
+    </el-drawer>
+
 <!--    设置一个课程sop-->
     <el-dialog :title="setSop.title" :visible.sync="setSop.open"  width="1200px" append-to-body>
       <SopDialog ref="SopDialog"  @bindCourseSop="bindCourseSop"></SopDialog>
@@ -766,6 +871,29 @@
 	<el-dialog :title="info.title" :visible.sync="info.open"   width="1100px" append-to-body>
 	  <info  ref="Details" />
 	</el-dialog>
+
+  <el-dialog :title="collection.title" :visible.sync="collection.open"   width="1100px" append-to-body>
+	  <collection   ref="collection" />
+	</el-dialog>
+
+  <el-dialog title="修改客户状态" :visible.sync="statusDialog.open" width="500px" append-to-body>
+    <el-form ref="statusForm" :model="statusForm" :rules="statusRules" label-width="100px">
+      <el-form-item label="状态" prop="status">
+        <el-select v-model="statusForm.status" placeholder="请选择状态" 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>
+    <div slot="footer" class="dialog-footer">
+      <el-button @click="statusDialog.open = false">取 消</el-button>
+      <el-button type="primary" @click="submitStatusForm">提 交</el-button>
+    </div>
+  </el-dialog>
   </div>
 </template>
 
@@ -785,7 +913,7 @@ import {
   setCustomerCourseSop,
   getCustomerCourseSop,
   setCustomerCourseSopList,
-  unBindUserId, updateExternalContactCall
+  unBindUserId, updateExternalContactCall,updateExternalContactStatus,getWatchLogList
 } from '@/api/qw/externalContact'
 import {getMyQwUserList, getMyQwCompanyList, updateUser,getQwUserListLikeName} from "@/api/qw/user";
 import {listTag, getTag, searchTags,} from "@/api/qw/tag";
@@ -794,15 +922,50 @@ import mycustomer from '@/views/qw/externalContact/mycustomer'
 import customerDetails from '@/views/qw/externalContact/customerDetails'
 import SopDialog from '@/views/course/sop/SopDialog.vue'
 import  selectUser  from "@/views/qw/externalContact/selectUser.vue";
+import  collection  from "@/views/qw/externalContact/collection.vue";
 import info from "@/views/qw/externalContact/info.vue";
 import { editTalk } from "@/api/qw/externalContactInfo";
 import PaginationMore from "../../../components/PaginationMore/index.vue";
 import userDetails from '@/views/store/components/userDetails.vue';
+import {courseList, videoList} from "@/api/course/courseRedPacketLog";
+import Collection from './collection.vue';
 export default {
   name: "ExternalContact",
-  components:{PaginationMore, mycustomer,customerDetails,SopDialog,selectUser,info,userDetails},
+  components:{PaginationMore, mycustomer,customerDetails,SopDialog,selectUser,info,userDetails,collection},
   data() {
     return {
+      projectOptions: [],
+      courseLists: [],
+      videoList: [],
+      //重粉记录的参数
+      log: {
+        open: false,
+        loading: true,
+        list: [],
+        total: 0,
+        queryParams: {
+          pageNum: 1,
+          pageSize: 10,
+          externalUserId: null,
+          fsUserId: null,
+          projectId: null,
+          courseId: null,
+          videoId: null,
+        },
+      },
+      statusDialog: {
+        open: false,
+        title: "修改客户状态"
+      },
+      statusForm: {
+        id: null,
+        status: null
+      },
+      statusRules: {
+        status: [
+          {required: true, message: '状态不能为空', trigger: 'change'}
+        ]
+      },
       notesOpen: {
         type: 1,
         nameType: 3,
@@ -823,6 +986,10 @@ export default {
         title:"用户信息",
         open:false,
       },
+      collection:{
+        titile:"信息采集",
+        open:false,
+      },
       // ...其他已有数据
       qwUserSuggestions: [],       // 已展示的数据
       showQwUserDropdown: false,   // 控制下拉框显示
@@ -974,7 +1141,8 @@ export default {
         level:null,
         wayId:null,
         levelType:null,
-        companyUser:null
+        companyUser:null,
+        userRepeat: null
       },
       selectTags:[],
       // 表单参数
@@ -984,10 +1152,14 @@ export default {
       statusOptions:[],
       // 表单校验
       rules: {
-      }
+      },
+      userId:null,
     };
   },
   created() {
+    this.getDicts("sys_course_project").then(response => {
+      this.projectOptions = response.data;
+    });
 
     this.getDicts("sys_qw_externalContact_type").then(response => {
       this.typeOptions = response.data;
@@ -1034,12 +1206,55 @@ export default {
 
   },
   methods: {
+    /** 重粉查看操作 */
+    showLog(row) {
+      this.log.queryParams.fsUserId = row.fsUserId;
+      this.log.open = true;
+      this.log.loading = true;
+      courseList().then(response => {
+        this.courseLists = response.list;
+        this.logList();
+      })
+    },
+    handleQueryWatchLog() {
+      this.log.queryParams.pageNum = 1;
+      this.log.queryParams.pageSize = 10;
+      this.logList();
+    },
+    logList() {
+      getWatchLogList(this.log.queryParams).then(e => {
+        this.log.loading = false;
+        this.log.list = e.rows;
+        this.log.total = e.total;
+      });
+    },
+    courseChange(row) {
+      this.log.queryParams.videoId = null;
+      if (row === '') {
+        this.videoList = [];
+        return
+      }
+      videoList(row).then(response => {
+        this.videoList = response.list
+      });
+    },
     handleMemberdetails(row){
             this.show.open=true;
             setTimeout(() => {
                  this.$refs.userDetails.getDetails(row.fsUserId);
             }, 1);
     },
+    handleInfoCollection(row){
+      this.collection.title = "信息采集";
+      this.collection.open = true;
+      this.userId = row.fsUserId;
+      setTimeout(() => {
+                 this.$refs.collection.getCollectionInfo(row.fsUserId);
+            }, 1);
+    },
+    closeCollection(){
+      this.collection.open = false;
+    },
     onQwUserNameClear() {
       this.queryParams.qwUserId = null;  // 同时清空 qwUserId
     },
@@ -1132,6 +1347,33 @@ export default {
         });
         this.getList();
      },
+     handleChangeStatus(row) {
+      this.statusForm = {
+        id: row.id,
+        status: String(row.status) // 保证与 dictValue 类型一致
+      };
+      this.statusDialog.open = true;
+    },
+    submitStatusForm() {
+      this.$refs["statusForm"].validate(valid => {
+        if (valid) {
+          const params = {
+            id: this.statusForm.id,
+            status: this.statusForm.status
+          };
+
+          // 调用接口更新状态
+          updateExternalContactStatus(params).then(response => {
+            this.msgSuccess("状态修改成功");
+            this.statusDialog.open = false;
+            this.getList(); // 刷新列表
+          }).catch(error => {
+            console.error('状态修改失败:', error);
+            this.$message.error('状态修改失败');
+          });
+        }
+      });
+    },
     /** 查询企业微信客户列表 */
     getList() {
       this.loading = true;
@@ -1512,6 +1754,7 @@ export default {
 
       searchTags(this.queryTagParams).then(response => {
         this.tagGroupList = response.rows;
+        this.tagTotal = response.total;
       });
 
     },
@@ -1569,6 +1812,7 @@ export default {
     /** 重置按钮操作 */
     resetQuery() {
       this.resetForm("queryForm");
+      this.queryParams.transferStatus=null;
       this.queryParams.corpId= this.myQwCompanyList[0].dictValue;
       this.selectTags=[];
 	   this.createTime=null;

+ 274 - 4
src/views/qw/externalContact/myExternalContact.vue

@@ -34,7 +34,7 @@
       </el-form-item>
 
       <el-form-item label="性别" prop="gender">
-        <el-select v-model="queryParams.gender" placeholder="状态" clearable size="small">
+        <el-select v-model="queryParams.gender" placeholder="性别" clearable size="small">
           <el-option
             v-for="dict in genderOptions"
             :key="dict.dictValue"
@@ -76,6 +76,12 @@
           />
         </el-select>
       </el-form-item>
+      <el-form-item label="是否重粉" prop="userRepeat">
+        <el-select v-model="queryParams.userRepeat" placeholder="重粉" clearable size="small">
+          <el-option label="否" :value="0"/>
+          <el-option label="是" :value="1"/>
+        </el-select>
+      </el-form-item>
       <el-form-item label="客户等级" prop="level">
         <el-select v-model="queryParams.level" placeholder="客户等级" clearable size="small">
           <el-option
@@ -402,6 +408,20 @@
         </template>
       </el-table-column>
       <el-table-column label="企业id" align="center" prop="corpId" />
+      <el-table-column label="重粉看课历史" width="100px" align="center" fixed="right">
+        <template slot-scope="scope">
+          <div v-if="scope.row.fsUserId">
+            <el-tag type="success" v-if="scope.row.userRepeat == 0">正常</el-tag>
+            <el-tag type="danger" v-if="scope.row.userRepeat == 1">重粉</el-tag>
+            <el-button
+              size="mini"
+              type="text"
+              @click="showLog(scope.row)"
+            >重粉看课历史
+            </el-button>
+          </div>
+        </template>
+      </el-table-column>
       <el-table-column label="是否绑定会员" width="100px" align="center" fixed="right">
         <template slot-scope="scope">
           <el-tag v-if="scope.row.fsUserId" >已绑定</el-tag>
@@ -464,6 +484,23 @@
           >
             <span>初诊单</span>
           </el-button>
+          <el-button
+             size="mini"
+             type="text"
+             @click="handleMemberdetails(scope.row)"
+             v-if="scope.row.fsUserId"
+             >
+             <span>会员详情</span>
+          </el-button>
+          <el-button
+
+             size="mini"
+             type="text"
+             @click="handleInfoCollection(scope.row)"
+          v-if="false"
+             >
+             <span>信息采集</span>
+          </el-button>
 
 <!--          <el-button v-if="scope.row.customerId"-->
 <!--            size="mini"-->
@@ -483,6 +520,15 @@
                      icon="el-icon-setting"
                      @click="setCourseSOP(scope.row)"
           >设置课程SOP</el-button> -->
+        <el-button
+          size="mini"
+          type="text"
+          icon="el-icon-edit"
+          @click="handleChangeStatus(scope.row)"
+          v-hasPermi="['qw:externalContact:changeStatus']"
+        >
+          修改状态
+        </el-button>
         </template>
       </el-table-column>
     </el-table>
@@ -813,6 +859,79 @@
       </div>
     </el-dialog>
 
+    <!-- 重粉看课记录   -->
+    <el-drawer title="重粉看课历史" :visible.sync="log.open" size="75%" append-to-body>
+      <div style="padding: 10px">
+        <el-form :model="log.queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
+          <el-form-item label="所属项目" prop="project">
+            <el-select v-model="log.queryParams.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">
+            <el-select filterable v-model="log.queryParams.courseId" placeholder="请选择课程" clearable size="small"
+                       @change="courseChange(log.queryParams.courseId)">
+              <el-option
+                v-for="dict in courseLists"
+                :key="dict.dictValue"
+                :label="dict.dictLabel"
+                :value="dict.dictValue"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="小节" prop="videoId">
+            <el-select filterable v-model="log.queryParams.videoId" placeholder="请选择小节" clearable size="small">
+              <el-option
+                v-for="dict in videoList"
+                :key="dict.dictValue"
+                :label="dict.dictLabel"
+                :value="dict.dictValue"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQueryWatchLog">搜索</el-button>
+          </el-form-item>
+        </el-form>
+        <el-table v-loading="log.loading" :data="log.list">
+          <el-table-column label="编号" align="center" prop="id"/>
+          <el-table-column label="所属企微主体" align="center" prop="corpName"/>
+          <el-table-column label="所属企微" align="center" prop="qwUserName"/>
+          <el-table-column label="项目" align="center" prop="projectName"/>
+          <el-table-column label="课程" align="center" prop="courseName"/>
+          <el-table-column label="小节" align="aligner" prop="videoName"/>
+          <el-table-column label="记录时间" align="center" prop="createTime"/>
+          <el-table-column label="是否完课" align="center" prop="logType">
+            <template slot-scope="scope">
+              <el-tag v-if="scope.row.logType == 2" type="success">已完课</el-tag>
+              <el-tag v-else type="success">未完课</el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column label="完课时间" align="center" prop="finishTime"/>
+        </el-table>
+
+        <pagination
+          v-show="log.total>0"
+          :total="log.total"
+          :page.sync="log.queryParams.pageNum"
+          :limit.sync="log.queryParams.pageSize"
+          @pagination="logList"
+        />
+      </div>
+    </el-drawer>
+
+    <el-drawer
+        :with-header="false"
+        size="75%"
+          :title="member.title" :visible.sync="member.open">
+      <userDetails  ref="userDetails" />
+    </el-drawer>
+
     <!-- 绑定客户   -->
     <el-dialog :title="bindCustomer.title" :visible.sync="bindCustomer.open"  width="1200px" append-to-body>
       <mycustomer ref="mycustomer"  @bindCustomerId="bindCustomerId"></mycustomer>
@@ -831,6 +950,27 @@
     <el-dialog :title="user.title" :visible.sync="user.open" width="800px" append-to-body>
       <selectUser ref="selectUser" @bindMiniCustomerId="bindMiniCustomerId"></selectUser>
     </el-dialog>
+    <el-dialog :title="collection.title" :visible.sync="collection.open"   width="1100px" append-to-body>
+	  <collection   ref="collection" />
+	</el-dialog>
+  <el-dialog title="修改客户状态" :visible.sync="statusDialog.open" width="500px" append-to-body>
+    <el-form ref="statusForm" :model="statusForm" :rules="statusRules" label-width="100px">
+      <el-form-item label="状态" prop="status">
+        <el-select v-model="statusForm.status" placeholder="请选择状态" 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>
+    <div slot="footer" class="dialog-footer">
+      <el-button @click="statusDialog.open = false">取 消</el-button>
+      <el-button type="primary" @click="submitStatusForm">提 交</el-button>
+    </div>
+  </el-dialog>
   </div>
 </template>
 
@@ -852,7 +992,7 @@ import {
   setCustomerCourseSop,
   getCustomerCourseSop,
   setCustomerCourseSopList,
-  syncMyExternalContact, unBindUserId, updateExternalContactCall,exportMyExternalContact
+  syncMyExternalContact, unBindUserId, updateExternalContactCall,exportMyExternalContact,updateExternalContactStatus,getWatchLogList
 } from '@/api/qw/externalContact'
 import info from "@/views/qw/externalContact/info.vue";
 import {getMyQwUserList, getMyQwCompanyList, handleInputAuthAppKey, updateUser} from "@/api/qw/user";
@@ -867,11 +1007,54 @@ import { editTalk,editAllTalk } from "@/api/qw/externalContactInfo";
 import {createLinkUrl} from "@/api/course/sopCourseLink";
 import {docList} from "@/api/doctor/doctor";
 import PaginationMore from "../../../components/PaginationMore/index.vue";
+import Collection from './collection.vue';
+import {courseList, videoList} from "@/api/course/courseRedPacketLog";
+import userDetails from '@/views/store/components/userDetails.vue';
 export default {
   name: "ExternalContact",
-  components:{PaginationMore, mycustomer,customerDetails,SopDialog,selectUser,info},
+  components:{PaginationMore, mycustomer,customerDetails,SopDialog,selectUser,info,Collection,userDetails},
   data() {
     return {
+      member:{
+        title:"客户详情",
+        open:false,
+      },
+      projectOptions: [],
+      courseLists: [],
+      videoList: [],
+      //重粉记录的参数
+      log: {
+        open: false,
+        loading: true,
+        list: [],
+        total: 0,
+        queryParams: {
+          pageNum: 1,
+          pageSize: 10,
+          externalUserId: null,
+          fsUserId: null,
+          projectId: null,
+          courseId: null,
+          videoId: null,
+        },
+      },
+      statusDialog: {
+        open: false,
+        title: "修改客户状态"
+      },
+      statusForm: {
+        id: null,
+        status: null
+      },
+      statusRules: {
+        status: [
+          {required: true, message: '状态不能为空', trigger: 'change'}
+        ]
+      },
+      collection:{
+        titile:"信息采集",
+        open:false,
+      },
       doctorList:[],
       diagnosisForm:{},
       diagnosisOpen:false,
@@ -1023,7 +1206,9 @@ export default {
         lossTime:null,
         createTime:null,
         level:null,
-        levelType:null
+        levelType:null,
+        companyUser:null,
+        userRepeat: null
       },
 
       queryTagParams:{
@@ -1054,6 +1239,9 @@ export default {
     };
   },
   created() {
+    this.getDicts("sys_course_project").then(response => {
+      this.projectOptions = response.data;
+    });
 
     this.getDicts("sys_qw_externalContact_type").then(response => {
       this.typeOptions = response.data;
@@ -1088,6 +1276,58 @@ export default {
 
   },
   methods: {
+    /** 重粉查看操作 */
+    showLog(row) {
+      this.log.queryParams.fsUserId = row.fsUserId;
+      this.log.open = true;
+      this.log.loading = true;
+      courseList().then(response => {
+        this.courseLists = response.list;
+        this.log.queryParams.externalUserId = row.id;
+        this.logList();
+      })
+    },
+    handleQueryWatchLog() {
+      this.log.queryParams.pageNum = 1;
+      this.log.queryParams.pageSize = 10;
+      this.logList();
+    },
+    logList() {
+      getWatchLogList(this.log.queryParams).then(e => {
+        this.log.loading = false;
+        this.log.list = e.rows;
+        this.log.total = e.total;
+      });
+    },
+    courseChange(row) {
+      this.log.queryParams.videoId = null;
+      if (row === '') {
+        this.videoList = [];
+        return
+      }
+      videoList(row).then(response => {
+        this.videoList = response.list
+      });
+    },
+
+    handleMemberdetails(row){
+            this.member.open=true;
+            setTimeout(() => {
+                 this.$refs.userDetails.getDetails(row.fsUserId);
+            }, 1);
+    },
+
+    handleInfoCollection(row){
+      this.collection.title = "信息采集";
+      this.collection.open = true;
+      this.userId = row.fsUserId;
+      setTimeout(() => {
+                 this.$refs.collection.getCollectionInfo(row.fsUserId);
+            }, 1);
+    },
+    closeCollection(){
+      this.collection.open = false;
+    },
     doctorChange(val){
       for(const doctor of this.doctorList) {
         if(doctor.id == val) {
@@ -1255,6 +1495,34 @@ export default {
 
     },
 
+    handleChangeStatus(row) {
+      this.statusForm = {
+        id: row.id,
+        status: String(row.status) // 保证与 dictValue 类型一致
+      };
+      this.statusDialog.open = true;
+    },
+    submitStatusForm() {
+      this.$refs["statusForm"].validate(valid => {
+        if (valid) {
+          const params = {
+            id: this.statusForm.id,
+            status: this.statusForm.status
+          };
+
+          // 调用接口更新状态
+          updateExternalContactStatus(params).then(response => {
+            this.msgSuccess("状态修改成功");
+            this.statusDialog.open = false;
+            this.getList(); // 刷新列表
+          }).catch(error => {
+            console.error('状态修改失败:', error);
+            this.$message.error('状态修改失败');
+          });
+        }
+      });
+    },
+
     /** 查询企业微信客户列表 */
     getList() {
       this.loading = true;
@@ -1284,6 +1552,7 @@ export default {
 
       searchTags(this.queryTagParams).then(response => {
         this.tagGroupList = response.rows;
+        this.tagTotal = response.total;
       });
 
       // searchTags({name:name,corpId:this.queryParams.corpId}).then(response => {
@@ -1623,6 +1892,7 @@ export default {
       this.resetForm("queryForm");
       this.queryParams.qwUserId=this.myQwUserList[0].dictValue;
       this.queryParams.corpId=this.myQwUserList[0].corpId;
+      this.queryParams.transferStatus = null;
       this.selectTags=[];
 	   this.createTime=null;
 	  this.queryParams.sTime=null;

+ 12 - 0
src/views/qw/externalContact/selectUser.vue

@@ -28,6 +28,18 @@
     </el-form>
 
     <el-table  height="500" border v-loading="loading" :data="customerList" ref="customerList" >
+      <el-table-column label="用户ID" align="center" prop="userId" />
+      <el-table-column label="企微客户头像" align="center" prop="avatar" width="100px">
+        <template slot-scope="scope">
+          <el-popover
+            placement="right"
+            title=""
+            trigger="hover">
+            <img slot="reference" :src="scope.row.avatar" width="60px">
+            <img :src="scope.row.avatar" style="max-width: 200px;">
+          </el-popover>
+        </template>
+      </el-table-column>
       <el-table-column label="用户昵称" align="center" prop="nickName" />
       <el-table-column label="手机号码" align="center" prop="phone" />
       <el-table-column label="操作"   align="center" fixed="right" width="120px" class-name="small-padding fixed-width">

+ 20 - 4
src/views/qw/externalContactTransfer/companyTransfer.vue

@@ -213,6 +213,15 @@
             </template>
           </el-input>
         </el-form-item>
+        <el-form-item label="清除标签" prop="needClearTag">
+          <el-radio v-model="form.needClearTag" :label="0">不清除</el-radio>
+          <el-radio v-model="form.needClearTag" :label="1">清除</el-radio>
+          <div style="color: #999;font-size: 14px;display: flex;align-items: center;">
+            <i class="el-icon-info"></i>
+            <span v-if="form.needClearTag === 0">不清除标签:客户转接后会保留原标签,不会被清除。</span>
+            <span v-else>清除标签:客户转接后原标签不会保留,仅会存在自动添加的标签。</span>
+          </div>
+        </el-form-item>
         <el-form-item label="消息内容" prop="content">
           <el-input v-model="form.content" placeholder="请输入内容" />
           <div style="color: #999;font-size: 14px;display: flex;align-items: center;">
@@ -221,7 +230,7 @@
           </div>
         </el-form-item>
         <el-card>
-          <companyTransferQwUserSelect :corpId="queryParams.corpId" :companyId="companyId" @selectUser="selectUser"/>
+          <companyTransferQwUserSelect ref="companyTransferSelect" @selectUser="selectUser"/>
         </el-card>
       </el-form>
       <div slot="footer" class="dialog-footer">
@@ -279,7 +288,8 @@ export default {
       form: {
         qwUserId: null,
         nickName: null,
-        content: null
+        content: null,
+        needClearTag: 0
       },
       rules: {
         nickName: [
@@ -339,6 +349,7 @@ export default {
       };
       this.selectTags=[];
       this.resetForm("queryForm");
+      this.queryParams.transferStatus = null;
       this.queryParams.corpId = this.myQwCompanyList?.[0]?.dictValue || null
       this.getList();
     },
@@ -390,7 +401,8 @@ export default {
       this.form = {
         qwUserId: null,
         nickName: null,
-        content: null
+        content: null,
+        needClearTag: 0
       };
       this.resetForm("form");
     },
@@ -404,6 +416,9 @@ export default {
       this.submitLoading = false
       this.title = "分配客户"
       this.open = true
+      this.$nextTick(() => {
+        this.$refs.companyTransferSelect.load(this.queryParams.corpId, this.companyId)
+      })
     },
     selectUser(selection) {
       this.form.qwUserId = selection.id
@@ -426,7 +441,8 @@ export default {
             ids: this.ids,
             qwUserId: this.form.qwUserId,
             content: this.form.content,
-            transferType: 1
+            transferType: 1,
+            needClearTag: this.form.needClearTag
           }
 
           companyTransfer(params).then(() => {

+ 13 - 21
src/views/qw/externalContactTransfer/companyTransferQwUserSelect.vue

@@ -61,16 +61,6 @@ import { getCompanyListByCorId } from '@/api/company/company'
 
 export default {
   name: "companyTransferQwUserSelect",
-  props: {
-    corpId: {
-      type: String,
-      default: null
-    },
-    companyId: {
-      type: Number,
-      default: null
-    }
-  },
   data() {
     return {
       // 遮罩层
@@ -92,17 +82,19 @@ export default {
       },
     };
   },
-  created() {
-    getCompanyListByCorId(this.corpId).then(response => {
-      this.companyList = response.data.filter(item => item.dictValue !== this.companyId)
-      if (!Array.isArray(this.companyList) || this.companyList.length === 0) {
-        return
-      }
-      this.queryParams.companyId = this.companyList[0].dictValue;
-      this.handleQuery();
-    })
-  },
   methods: {
+    load(corpId, companyId) {
+      getCompanyListByCorId(corpId).then(response => {
+        this.companyList = response.data.filter(item => item.dictValue !== companyId)
+        if (!Array.isArray(this.companyList) || this.companyList.length === 0) {
+          this.userList = []
+          return
+        }
+        this.queryParams.corpId = corpId;
+        this.queryParams.companyId = this.companyList[0].dictValue;
+        this.handleQuery();
+      })
+    },
     /** 查询企微用户列表 */
     getList() {
       this.loading = true;
@@ -120,8 +112,8 @@ export default {
     /** 搜索按钮操作 */
     handleQuery() {
       this.queryParams.pageNum = 1;
-      this.queryParams.corpId = this.corpId;
       if (!Array.isArray(this.companyList) || this.companyList.length === 0) {
+        this.userList = []
         return
       }
       this.getList();

+ 16 - 2
src/views/qw/externalContactTransfer/deptTransferIndex.vue

@@ -245,6 +245,15 @@
               </el-button>
             </template>
           </el-input>
+        </el-form-item>
+        <el-form-item label="清除标签" prop="needClearTag">
+          <el-radio v-model="form.needClearTag" :label="0">不清除</el-radio>
+          <el-radio v-model="form.needClearTag" :label="1">清除</el-radio>
+          <div style="color: #999;font-size: 14px;display: flex;align-items: center;">
+            <i class="el-icon-info"></i>
+            <span v-if="form.needClearTag === 0">不清除标签:客户转接后会保留原标签,不会被清除。</span>
+            <span v-else>清除标签:客户转接后原标签不会保留,仅会存在自动添加的标签。</span>
+          </div>
         </el-form-item>
 		<el-form-item label="消息内容" prop="content">
 		  <el-input v-model="form.content" placeholder="请输入内容" />
@@ -333,7 +342,9 @@ export default {
         transferStatus:null
       },
       // 表单参数
-      form: {},
+      form: {
+        needClearTag: 0
+      },
       tagList:[],
       transferStatusOptions:[],
       statusOptions:[],
@@ -423,7 +434,8 @@ export default {
         addWay: null,
         operUserid: null,
         corpId: null,
-        companyId: null
+        companyId: null,
+        needClearTag: 0
       };
       this.resetForm("form");
     },
@@ -443,6 +455,7 @@ export default {
     resetQuery() {
       this.selectTags=[];
       this.resetForm("queryForm");
+      this.queryParams.transferStatus = null;
       this.handleQuery();
     },
     // 多选框选中数据
@@ -484,6 +497,7 @@ export default {
               userId:this.form.userId,
               corpId:this.queryParams.corpId,
 			  content:this.form.content,
+              needClearTag: 0
             }
             transfer(form).then(response => {
               this.msgSuccess(response.msg);

+ 15 - 2
src/views/qw/externalContactTransfer/index.vue

@@ -254,6 +254,15 @@
               </el-button>
             </template>
           </el-input>
+        </el-form-item>
+        <el-form-item label="清除标签" prop="needClearTag">
+          <el-radio v-model="form.needClearTag" :label="0">不清除</el-radio>
+          <el-radio v-model="form.needClearTag" :label="1">清除</el-radio>
+          <div style="color: #999;font-size: 14px;display: flex;align-items: center;">
+            <i class="el-icon-info"></i>
+            <span v-if="form.needClearTag === 0">不清除标签:客户转接后会保留原标签,不会被清除。</span>
+            <span v-else>清除标签:客户转接后原标签不会保留,仅会存在自动添加的标签。</span>
+          </div>
         </el-form-item>
 		<el-form-item label="消息内容" prop="content">
 		  <el-input v-model="form.content" placeholder="请输入内容" />
@@ -344,7 +353,9 @@ export default {
         transferStatus:null
       },
       // 表单参数
-      form: {},
+      form: {
+        needClearTag: 0
+      },
       tagList:[],
       transferStatusOptions:[],
       statusOptions:[],
@@ -434,7 +445,8 @@ export default {
         addWay: null,
         operUserid: null,
         corpId: null,
-        companyId: null
+        companyId: null,
+        needClearTag: 0
       };
       this.resetForm("form");
     },
@@ -505,6 +517,7 @@ export default {
               userId:this.form.userId,
               corpId:this.queryParams.corpId,
 			        content:this.form.content,
+              needClearTag: this.form.needClearTag
             }
             transfer(form).then(response => {
               this.msgSuccess(response.msg);

+ 47 - 20
src/views/qw/externalContactTransferLog/companyTransferDetail.vue

@@ -1,19 +1,20 @@
 <template>
   <div class="app-container">
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="企微公司" prop="corpId">
+        <el-select clearable v-model="queryParams.corpId" placeholder="请选择企微公司"  size="small" @change="updateCorpId()">
+          <el-option
+            v-for="dict in myQwCompanyList"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
       <el-form-item label="公司名称" prop="companyName">
         <el-input
           v-model="queryParams.companyName"
-          placeholder="请输入销售公司名称"
-          clearable
-          size="small"
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="主体名称" prop="corpName">
-        <el-input
-          v-model="queryParams.corpName"
-          placeholder="请输入企微主体名称"
+          placeholder="请输入接替销售公司名称"
           clearable
           size="small"
           @keyup.enter.native="handleQuery"
@@ -22,7 +23,7 @@
       <el-form-item label="销售名称" prop="companyUserName">
         <el-input
           v-model="queryParams.companyUserName"
-          placeholder="请输入销售名称"
+          placeholder="请输入接替销售名称"
           clearable
           size="small"
           @keyup.enter.native="handleQuery"
@@ -40,12 +41,13 @@
         </el-select>
       </el-form-item>
       <el-form-item>
-        <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
 
     <el-table v-loading="loading" :data="list" border>
+      <el-table-column label="ID" align="center" prop="id" width="55"/>
       <el-table-column label="主体名称" align="center" prop="corpName" />
       <el-table-column label="接替公司名称" align="center" prop="companyName" />
       <el-table-column label="接替销售名称" align="center" prop="companyUserName" />
@@ -95,6 +97,7 @@
         </el-col>
       </el-row>
       <el-table v-loading="detailLoading" :data="userList" border>
+        <el-table-column label="ID" align="center" prop="id" />
         <el-table-column label="客户名称" align="center" prop="externalUserName" />
         <el-table-column label="原公司名称" align="center" prop="companyName" />
         <el-table-column label="原销售名称" align="center" prop="companyUserName" />
@@ -106,12 +109,21 @@
           </template>
         </el-table-column>
       </el-table>
+
+      <pagination
+        v-show="userTotal>0"
+        :total="userTotal"
+        :page.sync="userQueryParams.pageNum"
+        :limit.sync="userQueryParams.pageSize"
+        @pagination="getUserList"
+      />
     </el-drawer>
   </div>
 </template>
 
 <script>
 import { detail, listExternalContactTransferAudit, sync } from '@/api/qw/externalContactTransferAudit'
+import { getMyQwCompanyList } from '@/api/qw/user'
 
 export default {
   name: "companyTransferDetail",
@@ -126,6 +138,7 @@ export default {
         corpName: null,
         companyUserName: null,
         status: 0,
+        corpId: null,
       },
       total: 0,
       list: [],
@@ -134,12 +147,21 @@ export default {
       dialogAuditVisible: false,
       dialogViewVisible: false,
       detailLoading: false,
+      userQueryParams: {
+        pageNum: 1,
+        pageSize: 10,
+      },
       userList: [],
+      userTotal: 0,
       currentAudit: {},
       syncLoading: false,
+      myQwCompanyList:[],
     }
   },
   created() {
+    getMyQwCompanyList().then(response => {
+      this.myQwCompanyList = response.data;
+    });
     this.getDicts("sys_qw_transfer_audit_status").then((response) => {
       this.auditStatusOptions = response.data;
     });
@@ -149,6 +171,9 @@ export default {
     this.handleQuery()
   },
   methods: {
+    updateCorpId() {
+      this.handleQuery()
+    },
     handleQuery() {
       this.queryParams.pageNum = 1;
       this.getList();
@@ -161,6 +186,7 @@ export default {
         corpName: null,
         companyUserName: null,
         status: 0,
+        corpId: null,
       };
       this.resetForm("queryForm");
       this.handleQuery();
@@ -185,22 +211,23 @@ export default {
     },
     handleView(row) {
       this.currentAudit = row
+      this.userQueryParams.pageNum = 1
+      this.getUserList()
+      this.dialogViewVisible = true
+    },
+    getUserList() {
       this.detailLoading = true
-      detail(row.id).then(response => {
-        this.userList = response.data
+      detail(this.currentAudit.id, this.userQueryParams).then(response => {
+        this.userList = response.rows
+        this.userTotal = response.total
         this.detailLoading = false
       })
-      this.dialogViewVisible = true
     },
     sync() {
       const id = this.currentAudit.id
       this.syncLoading = true
       sync(id).then(() => {
-        this.detailLoading = true
-        detail(id).then(response => {
-          this.userList = response.data
-          this.detailLoading = false
-        })
+        this.getUserList()
         this.syncLoading = false
       })
     },

+ 11 - 0
src/views/qw/externalContactTransferLog/deptTransferLogIndex.vue

@@ -22,6 +22,16 @@
         />
       </el-form-item>
 
+      <el-form-item label="客户姓名" prop="name">
+        <el-input
+          style="width:220px"
+          v-model="queryParams.name"
+          placeholder="请输入客户姓名"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
 
 
       <el-form-item label="状态" prop="status">
@@ -172,6 +182,7 @@ export default {
         externalContactId: null,
         status: null,
         companyUserNickName:null,
+        name:null,
       },
       // 表单参数
       form: {},

+ 12 - 0
src/views/qw/externalContactTransferLog/index.vue

@@ -23,6 +23,17 @@
       </el-form-item>
 
 
+      <el-form-item label="客户姓名" prop="name">
+        <el-input
+          style="width:220px"
+          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">
@@ -172,6 +183,7 @@ export default {
         externalContactId: null,
         status: null,
         companyUserNickName:null,
+        name:null,
       },
       // 表单参数
       form: {},

+ 12 - 0
src/views/qw/externalContactTransferLog/my.vue

@@ -22,6 +22,17 @@
         />
       </el-form-item>
 
+      <el-form-item label="客户姓名" prop="name">
+        <el-input
+          style="width:220px"
+          v-model="queryParams.name"
+          placeholder="请输入客户姓名"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+
 
 
       <el-form-item label="状态" prop="status">
@@ -172,6 +183,7 @@ export default {
         externalContactId: null,
         status: null,
         companyUserNickName:null,
+        name:null,
       },
       // 表单参数
       form: {},

+ 20 - 4
src/views/qw/externalContactUnassigned/companyUnassigned.vue

@@ -206,8 +206,17 @@
             </template>
           </el-input>
         </el-form-item>
+        <el-form-item label="清除标签" prop="needClearTag">
+          <el-radio v-model="form.needClearTag" :label="0">不清除</el-radio>
+          <el-radio v-model="form.needClearTag" :label="1">清除</el-radio>
+          <div style="color: #999;font-size: 14px;display: flex;align-items: center;">
+            <i class="el-icon-info"></i>
+            <span v-if="form.needClearTag === 0">不清除标签:客户转接后会保留原标签,不会被清除。</span>
+            <span v-else>清除标签:客户转接后原标签不会保留,仅会存在自动添加的标签。</span>
+          </div>
+        </el-form-item>
         <el-card>
-          <companyTransferQwUserSelect :corpId="queryParams.corpId" :companyId="companyId" @selectUser="selectUser"/>
+          <companyTransferQwUserSelect ref="companyTransferSelect" @selectUser="selectUser"/>
         </el-card>
       </el-form>
       <div slot="footer" class="dialog-footer">
@@ -266,7 +275,8 @@ export default {
         nickName: null,
         content: null,
         type: 0,
-        oldQwUserId: null
+        oldQwUserId: null,
+        needClearTag: 0
       },
       rules: {
         nickName: [
@@ -322,6 +332,7 @@ export default {
       this.qwUserNameList = null
       this.isQwUserISNull = false
       this.resetForm("queryForm")
+      this.queryParams.transferStatus = null;
       this.queryParams.corpId = this.myQwCompanyList?.[0]?.dictValue || null
       this.getList()
     },
@@ -399,7 +410,8 @@ export default {
         nickName: null,
         content: null,
         type: 0,
-        oldQwUserId: null
+        oldQwUserId: null,
+        needClearTag: 0
       };
       this.resetForm("form");
     },
@@ -421,6 +433,9 @@ export default {
       this.submitLoading = false
       this.title = "分配该员工所有客户"
       this.open = true
+      this.$nextTick(() => {
+        this.$refs.companyTransferSelect.load(this.queryParams.corpId, this.companyId)
+      })
     },
     selectUser(selection) {
       this.form.qwUserId = selection.id
@@ -445,7 +460,8 @@ export default {
             content: this.form.content,
             type: this.form.type,
             transferType: 2,
-            oldQwUserId: this.form.oldQwUserId
+            oldQwUserId: this.form.oldQwUserId,
+            needClearTag: this.form.needClearTag
           }
 
           companyTransfer(params).then(() => {

+ 14 - 1
src/views/qw/externalContactUnassigned/deptUnassignedIndex.vue

@@ -207,6 +207,15 @@
           </el-input>
 
 
+        </el-form-item>
+        <el-form-item label="清除标签" prop="needClearTag">
+          <el-radio v-model="form.needClearTag" :label="0">不清除</el-radio>
+          <el-radio v-model="form.needClearTag" :label="1">清除</el-radio>
+          <div style="color: #999;font-size: 14px;display: flex;align-items: center;">
+            <i class="el-icon-info"></i>
+            <span v-if="form.needClearTag === 0">不清除标签:客户转接后会保留原标签,不会被清除。</span>
+            <span v-else>清除标签:客户转接后原标签不会保留,仅会存在自动添加的标签。</span>
+          </div>
         </el-form-item>
         <el-card>
         <qwUserSelectOne ref="qwUserSelectOne" @selectUser="selectUser"></qwUserSelectOne>
@@ -295,6 +304,7 @@ export default {
       nickName:null,
       // 表单参数
       form: {
+        needClearTag: 0
       },
       tagList:[],
       transferStatusOptions:[],
@@ -376,7 +386,8 @@ export default {
         addWay: null,
         operUserid: null,
         corpId: null,
-        companyId: null
+        companyId: null,
+        needClearTag: 0
       };
       this.resetForm("form");
     },
@@ -394,6 +405,7 @@ export default {
     /** 重置按钮操作 */
     resetQuery() {
       this.resetForm("queryForm");
+      this.queryParams.transferStatus = null;
       this.queryParams.corpId= this.myQwCompanyList[0].dictValue
       this.handleQuery();
     },
@@ -434,6 +446,7 @@ export default {
               ids:this.ids,
               userId:this.form.userId,
               corpId:this.queryParams.corpId,
+              needClearTag: this.form.needClearTag
             }
             resignedTransfer(form).then(response => {
               this.msgSuccess(response.msg);

+ 14 - 1
src/views/qw/externalContactUnassigned/index.vue

@@ -221,6 +221,15 @@
           </el-input>
 
 
+        </el-form-item>
+        <el-form-item label="清除标签" prop="needClearTag">
+          <el-radio v-model="form.needClearTag" :label="0">不清除</el-radio>
+          <el-radio v-model="form.needClearTag" :label="1">清除</el-radio>
+          <div style="color: #999;font-size: 14px;display: flex;align-items: center;">
+            <i class="el-icon-info"></i>
+            <span v-if="form.needClearTag === 0">不清除标签:客户转接后会保留原标签,不会被清除。</span>
+            <span v-else>清除标签:客户转接后原标签不会保留,仅会存在自动添加的标签。</span>
+          </div>
         </el-form-item>
         <el-card>
         <qwUserSelectOne ref="qwUserSelectOne" @selectUser="selectUser"></qwUserSelectOne>
@@ -306,6 +315,7 @@ export default {
       nickName:null,
       // 表单参数
       form: {
+        needClearTag: 0
       },
       tagList:[],
       transferStatusOptions:[],
@@ -404,7 +414,8 @@ export default {
         addWay: null,
         operUserid: null,
         corpId: null,
-        companyId: null
+        companyId: null,
+        needClearTag: 0
       };
       this.resetForm("form");
     },
@@ -425,6 +436,7 @@ export default {
        this.isQwUserISNull = false;
       this.resetForm("queryForm");
       this.queryParams.corpId= this.myQwCompanyList[0].dictValue
+      this.queryParams.transferStatus = null;
       this.handleQuery();
     },
     // 多选框选中数据
@@ -480,6 +492,7 @@ export default {
               ids:this.ids,
               userId:this.form.userId,
               corpId:this.queryParams.corpId,
+              needClearTag: this.form.needClearTag
             }
             resignedTransfer(form).then(response => {
               this.msgSuccess(response.msg);

+ 3 - 2
src/views/qw/friendWelcome/indexNew.vue

@@ -28,7 +28,7 @@
       </el-form-item>
       <el-form-item label="创建时间" prop="createdTime">
         <el-date-picker clearable size="small"
-          v-model="queryParams.createdTime"
+          v-model="queryParams.createTime"
           type="date"
           value-format="yyyy-MM-dd"
           placeholder="选择创建时间">
@@ -36,7 +36,7 @@
       </el-form-item>
       <el-form-item label="更新时间" prop="updateTieme">
         <el-date-picker clearable size="small"
-          v-model="queryParams.updateTieme"
+          v-model="queryParams.updateTime"
           type="date"
           value-format="yyyy-MM-dd"
           placeholder="选择更新时间">
@@ -1132,6 +1132,7 @@ export default {
     },
     /** 重置按钮操作 */
     resetQuery() {
+      this.queryParams.createTime = null;
       this.resetForm("queryForm");
       this.queryParams.corpId= this.myQwCompanyList[0].dictValue
       getQwAllUserList(this.queryParams.corpId).then(response => {

+ 4 - 4
src/views/qw/friendWelcome/myIndexNew.vue

@@ -27,17 +27,17 @@
                      :value="dict.dictValue"/>
         </el-select>
       </el-form-item>
-      <el-form-item label="创建时间" prop="createdTime">
+      <el-form-item label="创建时间" prop="createTime">
         <el-date-picker clearable size="small"
-                        v-model="queryParams.createdTime"
+                        v-model="queryParams.createTime"
                         type="date"
                         value-format="yyyy-MM-dd"
                         placeholder="选择创建时间">
         </el-date-picker>
       </el-form-item>
-      <el-form-item label="更新时间" prop="updateTieme">
+      <el-form-item label="更新时间" prop="updateTime">
         <el-date-picker clearable size="small"
-                        v-model="queryParams.updateTieme"
+                        v-model="queryParams.updateTime"
                         type="date"
                         value-format="yyyy-MM-dd"
                         placeholder="选择更新时间">

+ 81 - 17
src/views/qw/friendWelcome/myWelcome.vue

@@ -26,22 +26,22 @@
           <el-option v-for="dict in allowSelectOptions" :key="dict.dictValue" :label="dict.dictLabel"  :value="dict.dictValue"/>
         </el-select>
       </el-form-item>
-      <el-form-item label="创建时间" prop="createdTime">
-        <el-date-picker clearable size="small"
-          v-model="queryParams.createdTime"
-          type="date"
-          value-format="yyyy-MM-dd"
-          placeholder="选择创建时间">
-        </el-date-picker>
-      </el-form-item>
-      <el-form-item label="更新时间" prop="updateTieme">
-        <el-date-picker clearable size="small"
-          v-model="queryParams.updateTieme"
-          type="date"
-          value-format="yyyy-MM-dd"
-          placeholder="选择更新时间">
-        </el-date-picker>
-      </el-form-item>
+<!--      <el-form-item label="创建时间" prop="createTime">-->
+<!--        <el-date-picker clearable size="small"-->
+<!--          v-model="queryParams.createTime"-->
+<!--          type="date"-->
+<!--          value-format="yyyy-MM-dd"-->
+<!--          placeholder="选择创建时间">-->
+<!--        </el-date-picker>-->
+<!--      </el-form-item>-->
+<!--      <el-form-item label="更新时间" prop="updateTime">-->
+<!--        <el-date-picker clearable size="small"-->
+<!--          v-model="queryParams.updateTime"-->
+<!--          type="date"-->
+<!--          value-format="yyyy-MM-dd"-->
+<!--          placeholder="选择更新时间">-->
+<!--        </el-date-picker>-->
+<!--      </el-form-item>-->
       <el-form-item label="是否发送欢迎语" prop="isSendMsg">
         <el-select v-model="queryParams.isSendMsg" placeholder="请选择" clearable size="small" >
           <el-option v-for="dict in allowSelectOptions" :key="dict.dictValue" :label="dict.dictLabel"  :value="dict.dictValue"/>
@@ -196,6 +196,7 @@
                   <div slot="header" style="display: flex;justify-content: space-between;align-items: center; ">
                     <div style="flex: 1;">
                     <span v-if="item.msgtype === 'image'">【图片】: {{ item.image.pic_url }}</span>
+                     <span v-if="item.msgtype === 'video'">【视频】: {{ item.video.url }}</span>
                     <span v-if="item.msgtype === 'link'">【链接】: {{ item.link.title }}-{{item.link.desc}}</span>
                     <span v-if="item.msgtype === 'miniprogram'">【小程序】: {{ item.miniprogram.title }}</span>
                     </div>
@@ -225,6 +226,9 @@
                 <el-dropdown-item command="image">
                   <i class="el-icon-picture" style="margin-right: 10px;"></i>图片
                 </el-dropdown-item>
+                <el-dropdown-item command="video">
+                  <i class="el-icon-video-camera" style="margin-right: 10px;"></i>视频
+                </el-dropdown-item>
                 <el-dropdown-item command="link">
                   <i class="el-icon-link" style="margin-right: 10px;"></i>链接
                 </el-dropdown-item>
@@ -349,6 +353,9 @@
                               <el-dropdown-item command="image">
                                 <i class="el-icon-picture" style="margin-right: 10px;"></i>图片
                               </el-dropdown-item>
+                              <el-dropdown-item command="video">
+                                <i class="el-icon-video-camera" style="margin-right: 10px;"></i>视频
+                              </el-dropdown-item>
                               <el-dropdown-item command="link">
                                 <i class="el-icon-link" style="margin-right: 10px;"></i>链接
                               </el-dropdown-item>
@@ -397,6 +404,25 @@
           <el-form-item label="图片:" prop="imagePicUrl">
             <ImageUpload v-model="fileFrom.imagePicUrl"  type="image" :num="10" :width="150" :height="150"  disabled/>
           </el-form-item>
+        </div>
+          <div v-if="welcomeItem.type==='video'">
+          <el-form-item label="视频:" prop="videoUrl">
+
+            <el-upload
+              v-model="fileFrom.videoUrl"
+              class="avatar-uploader"
+              :action="uploadUrl"
+              :show-file-list="false"
+              :on-success="(res, file) => handleAvatarSuccessVideo(res, file, fileFrom)"
+              :before-upload="beforeAvatarUploadVideo">
+              <i class="el-icon-plus avatar-uploader-icon"></i>
+            </el-upload>
+            {{fileFrom.videoUrl}}
+            <video v-if="fileFrom.videoUrl"
+                   :src="fileFrom.videoUrl"
+                   controls style="width: 200px;height: 100px">
+            </video>
+          </el-form-item>
         </div>
         <div v-if="welcomeItem.type==='link'">
 
@@ -524,6 +550,8 @@ export default {
   components: { ImageUpload, qwUserList,ImageUploadWeclome},
   data() {
     return {
+      
+      uploadUrl: process.env.VUE_APP_BASE_API + "/common/uploadOSS2",
       // 遮罩层
       loading: true,
       // 导出遮罩层
@@ -798,6 +826,8 @@ export default {
       switch (command) {
         case 'image':
           return '添加图片';
+        case 'video':
+          return '添加视频';
         case 'link':
           return '添加链接';
         case 'miniprogram':
@@ -899,6 +929,8 @@ export default {
       };
       if (item.msgtype === 'image') {
         this.fileFrom.imagePicUrl = item.image.pic_url;
+      } else if (item.msgtype === 'video') {
+        this.fileFrom.videoUrl = item.video.url;
       } else if (item.msgtype === 'link') {
         this.fileFrom.linkTitle = item.link.title;
         this.fileFrom.linkPicUrl = item.link.picurl;
@@ -977,6 +1009,13 @@ export default {
             pic_url: this.fileFrom.imagePicUrl
           }
         };
+      } else if (type === 'video') {
+        attachment = {
+          msgtype: 'video',
+          video: {
+            url:this.fileFrom.videoUrl,
+          }
+        };
       } else if (type === 'link') {
         attachment = {
           msgtype: 'link',
@@ -1275,7 +1314,32 @@ export default {
           this.download(response.msg);
           this.exportLoading = false;
         }).catch(() => {});
-    }
+    },
+     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;
+    },
   }
 };
 </script>

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

@@ -918,6 +918,7 @@ export default {
 
       searchTags(this.queryTagParams).then(response => {
         this.tagGroupList = response.rows;
+        this.tagTotal = response.total;
       });
       // searchTags({name:name,corpId:this.queryParams.corpId}).then(response => {
       //   this.tagGroupList = response.rows;

+ 11 - 7
src/views/qw/qwUserVoiceLog/index.vue

@@ -77,7 +77,7 @@
 
       <el-form-item label="标签" prop="tagIds">
         <div @click="hangleChangeTags()" style="cursor: pointer; border: 1px solid #e6e6e6; background-color: white; overflow: hidden; flex-grow: 1;width: 250px">
-          <div style="min-height: 35px; max-height: 200px; overflow-y: auto;">
+          <div style="min-height: 35px; max-height: 200px; overflow-y: auto;" :key="selectTags.length">
             <el-tag type="success"
                     closable
                     :disable-transitions="false"
@@ -415,7 +415,6 @@ export default {
       this.$forceUpdate();
     },
     handleSearchTags(name){
-
       if (!name){
         return this.$message.error("请输入要搜索的标签")
       }
@@ -424,6 +423,7 @@ export default {
 
       searchTags(this.queryTagParams).then(response => {
         this.tagGroupList = response.rows;
+        this.tagTotal = response.total;
       });
 
       // searchTags({name:name,corpId:this.queryParams.corpId}).then(response => {
@@ -560,19 +560,23 @@ export default {
       }else {
         this.queryParams.tagIds=null;
       }
-
-
       this.queryParams.pageNum = 1;
       this.getList();
     },
     /** 重置按钮操作 */
     resetQuery() {
       this.resetForm("queryForm");
-      this.queryParams.corpId= this.myQwCompanyList[0].dictValue;
+      if (this.myQwCompanyList && this.myQwCompanyList.length > 0) {
+        this.queryParams.corpId = this.myQwCompanyList[0].dictValue;
+      } else {
+        this.queryParams.corpId = null; // 或设置默认值
+      }
       this.selectTags=[];
       this.createTime=null;
-      this.queryParams.sTime=null;
-      this.queryParams.eTime=null;
+      this.queryParams.beginTime=null;
+      this.queryParams.endTime=null;
+      this.queryParams.tagIds=null;
+      this.$forceUpdate();
       this.handleQuery();
     },
     // 多选框选中数据

+ 10 - 2
src/views/qw/qwUserVoiceLogTotal/index.vue

@@ -83,7 +83,7 @@
             </el-form-item>-->
       <el-form-item label="标签" prop="tagIds">
         <div @click="hangleChangeTags()" style="cursor: pointer; border: 1px solid #e6e6e6; background-color: white; overflow: hidden; flex-grow: 1;width: 250px">
-          <div style="min-height: 35px; max-height: 200px; overflow-y: auto;">
+          <div style="min-height: 35px; max-height: 200px; overflow-y: auto;" :key="selectTags.length">
             <el-tag type="success"
                     closable
                     :disable-transitions="false"
@@ -431,6 +431,7 @@ export default {
 
       searchTags(this.queryTagParams).then(response => {
         this.tagGroupList = response.rows;
+        this.tagTotal = response.total;
       });
 
       // searchTags({name:name,corpId:this.queryParams.corpId}).then(response => {
@@ -634,11 +635,18 @@ export default {
       this.createTime=null;
       this.queryParams.beginTime = null;
       this.queryParams.endTime = null;
-      this.queryParams.corpId= this.myQwCompanyList[0].dictValue;
+      this.queryParams.deptId = null;
+      if (this.myQwCompanyList && this.myQwCompanyList.length > 0) {
+        this.queryParams.corpId= this.myQwCompanyList[0].dictValue;
+      } else {
+        this.queryParams.corpId = null; // 或设置默认值
+      }
       this.selectTags=[];
       this.createTime=null;
       this.queryParams.sTime=null;
       this.queryParams.eTime=null;
+      this.queryParams.tagIds=null;
+      this.$forceUpdate();
       this.handleQuery();
     },
     // 多选框选中数据

+ 33 - 1
src/views/qw/sopLogs/sopLogsList.vue

@@ -175,6 +175,7 @@
                 <span v-if="item.contentType == 5">文件</span>
                 <span v-if="item.contentType == 6">视频</span>
                 <span v-if="item.contentType == 7">语音</span>
+                <span v-if="item.contentType == 8">视频号</span>
                 <span v-if="item.contentType == 9">APP</span>
                 <span v-if="item.contentType == 10">自定义小程序</span>
                 <span v-if="item.contentType == 4"><el-button size="mini" type="primary" @click="generateShortLink(item)" style="margin-left: 330px;">生成短链</el-button></span>
@@ -314,7 +315,11 @@
       :limit.sync="queryParams.pageSize"
       @pagination="getList"
     />
+    <el-dialog :title="videoNumOptions.title" :visible.sync="videoNumOptions.open" width="1500px" append-to-body>
+      <userVideo ref="QwUserVideo" @videoResult="qwUserVideoResult"></userVideo>
+    </el-dialog>
   </div>
+
 </template>
 
 <script>
@@ -327,11 +332,12 @@ import {
   generateShortLink
 } from '../../../api/qw/sopLogs'
 import {delSopUserLogsInfo} from "@/api/qw/sopUserLogsInfo";
+import userVideo from "@/views/qw/userVideo/userVideo";
 
 export default {
   name: "sopLogsList",
   props:{
-    rowDetailFrom:{},
+    rowDetailFrom:{userVideo},
   },
 
   watch:{
@@ -345,6 +351,12 @@ export default {
   },
   data() {
     return {
+      videoNumOptions: {
+        title: '选择视频号',
+        open: false,
+        content: null,
+        contentIndex: null,
+      },
       copyText:"",
       shortLoading:false,
       shortLinkDialogVisible:false,
@@ -432,6 +444,25 @@ export default {
 
   },
   methods: {
+
+    qwUserVideoResult(val) {
+
+      // 根据选中的内容,将返回的数据更新到相应的表单项
+      const content = this.videoNumOptions.content;
+      const setList = content[this.videoNumOptions.contentIndex];
+      setList.nickname = val.nickname;
+      setList.avatar = val.avatar;
+      setList.coverUrl = val.coverUrl;
+      setList.thumbUrl = val.thumbUrl;
+      setList.desc = val.desc;
+      setList.url = val.url;
+      setList.extras = val.extras;
+      setList.videoId = val.id;
+      console.info(setList)
+
+      this.videoNumOptions.open = false;
+
+    },
     /** 查询企业微信SOP  定时任务列表 */
     getList(val) {
       this.queryParams.sopId = val.id || this.rowDetailFrom.id;
@@ -538,6 +569,7 @@ export default {
       this.resetForm("queryForm");
       this.queryParams.scheduleStartTime=null;
       this.queryParams.scheduleEndTime=null;
+      this.scheduleTime = null;
       this.handleQuery();
     },
     // 多选框选中数据

+ 1 - 1
src/views/qw/sopTemp/index.vue

@@ -111,7 +111,7 @@
     <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">
+      <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>

+ 24 - 1
src/views/qw/sopTemp/updateSopTemp.vue

@@ -708,6 +708,7 @@ export default {
   components: {ImageUpload, userVideo, draggable},
   data() {
     return {
+      projectFrom:process.env.VUE_APP_PROJECT_FROM,
       addTag: [{
         addTag: [],
         inputVisible: false,
@@ -734,7 +735,7 @@ export default {
       ruleList: [],
       ids: [],
       startTimeRange: [],
-      courseTypeList: ['1','3', '4', '9','10'],
+      courseTypeList: ['1','2', '4','5','6', '7','8','9','10'],
       sysFsSopWatchStatus: [],
       //消息内容类型 企微版
       sysQwSopContentType: [],
@@ -1567,6 +1568,7 @@ export default {
       //如果是链接的才上
       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) {
@@ -1593,9 +1595,19 @@ export default {
 
             if (content.setting[i].contentType == 3 || content.setting[i].contentType == 9) {
               this.$set(content.setting[i], 'linkDescribe', selectedVideo.dictLabel);
+
+              if (this.projectFrom == 'sxjz' && selectedVideo.dictImgUrl) {
+                this.$set(content.setting[i], 'linkImageUrl', selectedVideo.dictImgUrl);
+              }
+
             }
             if (content.setting[i].contentType == 4 || content.setting[i].contentType == 10 ) {
               this.$set(content.setting[i], 'miniprogramTitle', this.truncateTextByByteLength(selectedVideo.dictLabel, 60));
+
+              if (this.projectFrom == 'sxjz' && selectedVideo.dictImgUrl) {
+                this.$set(content.setting[i], 'miniprogramPicUrl', selectedVideo.dictImgUrl);
+              }
+
             }
           }
         }
@@ -1627,9 +1639,20 @@ export default {
 
           if (content.setting[i].contentType == 3 || content.setting[i].contentType == 9) {
             this.$set(content.setting[i], 'linkDescribe', selectedVideo.dictLabel);
+
+            if (this.projectFrom == 'sxjz' && selectedVideo.dictImgUrl) {
+              this.$set(content.setting[i], 'linkImageUrl', selectedVideo.dictImgUrl);
+            }
+
           }
+
           if (content.setting[i].contentType == 4 || content.setting[i].contentType == 10) {
             this.$set(content.setting[i], 'miniprogramTitle', this.truncateTextByByteLength(selectedVideo.dictLabel, 60));
+
+            if (this.projectFrom == 'sxjz' && selectedVideo.dictImgUrl) {
+              this.$set(content.setting[i], 'miniprogramPicUrl', selectedVideo.dictImgUrl);
+            }
+
           }
 
 

+ 55 - 12
src/views/qw/sopUserLogs/sopUserLogsSchedule.vue

@@ -35,16 +35,16 @@
                         placeholder="选择营期时间">
         </el-date-picker>
       </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 sopUserLogsDelStatus"
-            :key="dict.dictValue"
-            :label="dict.dictLabel"
-            :value="dict.dictValue"
-          />
-        </el-select>
-      </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 sopUserLogsDelStatus"-->
+<!--            :key="dict.dictValue"-->
+<!--            :label="dict.dictLabel"-->
+<!--            :value="dict.dictValue"-->
+<!--          />-->
+<!--        </el-select>-->
+<!--      </el-form-item>-->
       <el-form-item label="客户名称" prop="externalUserName">
         <el-input
           v-model="queryParams.externalUserName"
@@ -133,6 +133,17 @@
             v-hasPermi="['qw:sopUserLogs:remove']"
           >批量删除营期</el-button>
         </el-tooltip>
+      </el-col>
+                  <el-col :span="1.5">
+        <el-tooltip class="item" effect="dark" content="批量更换实际发送人" placement="top" >
+          <el-button
+            type="primary"
+            icon="el-icon-s-custom"
+            size="medium"
+            :disabled="multiple"
+            @click="handleUpdateSender"
+          >批量更换实际发送人</el-button>
+        </el-tooltip>
       </el-col>
     </el-row>
     <div style="color: #999;font-size: 14px;display: flex;align-items: center;margin-bottom: 5px" v-if="filterMode == 1">
@@ -152,6 +163,7 @@
       <el-table-column label="营期编号" align="center" prop="id" />
       <el-table-column label="企微员工账号" align="center" prop="qwUserId" />
       <el-table-column label="企微员工名称" align="center" prop="qwUserName" />
+      <el-table-column label="实际发送人" align="center" prop="actualQwUserName" />
       <el-table-column label="群聊" align="center" prop="chatName" v-if="filterMode == 2" />
       <el-table-column label="营期时间" align="center" prop="startTime" width="180">
         <template slot-scope="scope">
@@ -239,6 +251,10 @@
       <qwUserList ref="QwUserList" @selectUserList="selectUserList"></qwUserList>
     </el-dialog>
 
+        <el-dialog :title="listUser.title" :visible.sync="listUser.open" width="1300px"   append-to-body>
+      <qwUserSelectOne ref="QwUserListSender" @selectUser="selectSenderSingle"></qwUserSelectOne>
+    </el-dialog>
+
     <el-dialog title="追加群聊" :visible.sync="addGroupData.open"  width="800px" append-to-body>
       <el-form ref="msgForm" :model="addGroupData.form" label-width="100px">
         <el-form-item label="选择员工" prop="qwUserIds" style="margin-top: 2%">
@@ -292,7 +308,7 @@ import {
   repairSopUserLogs,
   getSelectChat,
   addGroupChat,
-  updateLogDate
+  updateLogDate,UpdateTimeSopUserLogs,replaceUser
 } from "../../../api/qw/sopUserLogs";
 import sopLogsDetails from "@/views/qw/sopLogs/sopLogsList.vue";
 import SopUserLogsInfoDetails from "@/views/qw/sopUserLogsInfo/sopUserLogsInfoDetails.vue";
@@ -300,11 +316,12 @@ import sendMsgOpenTool from "../../../views/qw/sopUserLogsInfo/sendMsgOpenTool.v
 import {listAll as chatListAll} from "@/api/qw/groupChat";
 import companyUserList from "@/views/company/companyUser/companyUserList.vue";
 import qwUserList from "@/views/qw/user/qwUserList.vue";
+import qwUserSelectOne from "@/views/qw/user/qwUserSelectOne.vue";
 import {getQwAllUserList, listUser} from "@/api/company/companyUser";
 
 export default {
   name: "sopUserLogsSchedule",
-  components: {qwUserList, companyUserList, SopUserLogsInfoDetails, sopLogsDetails,sendMsgOpenTool},
+  components: {qwUserList, qwUserSelectOne, companyUserList, SopUserLogsInfoDetails, sopLogsDetails,sendMsgOpenTool},
   props:{
     rowDetailFrom:{},
   },
@@ -316,6 +333,10 @@ export default {
       companyUserLists:[],
       sopName:'',
       tempId:'',
+      listUser: {
+        title: '选择发送人',
+        open: false
+      },
       // 遮罩层
       loading: true,
       // 导出遮罩层
@@ -420,6 +441,19 @@ export default {
 
   },
   methods: {
+        selectSenderSingle(user){
+      this.listUser.open=false;
+      const data = { ids: this.ids };
+      // 仅在选中员工时携带 actualQwUserId 与 actualQwId,否则两字段均不传,后端按清空处理
+      if (user && user.qwUserId) {
+        data.actualQwUserId = user.qwUserId;
+        data.actualQwId     = user.id;
+      }
+      replaceUser(data).then(res => {
+        this.msgSuccess(user && user.qwUserId ? "修改成功" : "已清空实际发送人");
+        this.getList();
+      })
+    },
     /** 查询sopUserLogs列表 */
     getList() {
       this.loading = true;
@@ -470,6 +504,15 @@ export default {
 
     },
 
+        /**
+     * 批量更换实际发送人
+     */
+    handleUpdateSender() {
+      this.listUser.open = true;
+      this.$nextTick(() => {
+        this.$refs.QwUserListSender.getDetails(this.queryParams.corpId);
+      })
+    },
     /**
      *  删除营期
      */

+ 93 - 1
src/views/qw/sopUserLogsInfo/sendMsgOpenTool.vue

@@ -164,7 +164,44 @@
                           @input="handleInputVideoText(item.value,item)"/>
                       </div>
                       <div v-if="item.contentType == 8">
+                        <el-button type="primary"
+                                   style="margin-bottom: 1%"
+                                   @click="hanldeSelectVideoNum(setting,index)">
+                          选择视频号
+                        </el-button>
+                        <el-card class="box-card" v-if="item.coverUrl">
+                          <el-form-item label="封面标题:" label-width="100px">
+                            <el-input v-model="item.nickname"
+                                      style="width: 90%;margin-bottom: 1%" disabled/>
+                          </el-form-item>
+                          <el-form-item label="头像:" label-width="100px">
+                            <el-image
+                              v-if="item.avatar != null"
+                              :src="item.avatar"
+                              :preview-src-list="[item.avatar]"
+                              :style="{ width: '50px', height: '50px' }"
+                            ></el-image>
+                          </el-form-item>
+                          <el-form-item label="封面:" label-width="100px">
+                            <el-image
+                              v-if="item.coverUrl != null"
+                              :src="item.coverUrl"
+                              :preview-src-list="[item.coverUrl]"
+                              :style="{ width: '200px', height: '200px' }"
+                            ></el-image>
 
+                          </el-form-item>
+                          <el-form-item label="简介:" label-width="100px">
+                            <el-input type="textarea" :rows="3"
+                                      v-model="item.desc"
+                                      style="width: 90%;margin-top: 1%;" disabled/>
+                          </el-form-item>
+                          <el-form-item label="视频地址:" label-width="100px"
+                                        style="margin-top: 1%">
+                            <el-input v-model="item.url"
+                                      style="width: 90%;" disabled/>
+                          </el-form-item>
+                        </el-card>
                       </div>
 
                     </el-form-item>
@@ -226,6 +263,9 @@
           <el-button @click="cancelMsgForm">取 消</el-button>
         </div>
     </el-dialog>
+    <el-dialog :title="videoNumOptions.title" :visible.sync="videoNumOptions.open" width="1500px" append-to-body>
+      <userVideo ref="QwUserVideo" @videoResult="qwUserVideoResult"></userVideo>
+    </el-dialog>
   </div>
 </template>
 
@@ -233,13 +273,21 @@
 import { sendMsgSopType,} from "../../../api/qw/sopUserLogsInfo";
 import ImageUpload from "@/views/qw/sop/ImageUpload.vue";
 import {courseList, videoList} from "@/api/qw/sop";
+import userVideo from "@/views/qw/userVideo/userVideo";
 
 
 export default {
   name: "sendMsgOpenTool",
-  components: {ImageUpload},
+  components: {ImageUpload,userVideo},
   data() {
     return {
+      projectFrom:process.env.VUE_APP_PROJECT_FROM,
+      videoNumOptions: {
+        title: '选择视频号',
+        open: false,
+        content: null,
+        contentIndex: null,
+      },
       //上传语音的遮罩层
       voiceLoading :false,
       uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
@@ -341,6 +389,24 @@ export default {
   },
   methods: {
 
+    qwUserVideoResult(val) {
+
+      // 根据选中的内容,将返回的数据更新到相应的表单项
+      const content = this.videoNumOptions.content;
+      const setList = content[this.videoNumOptions.contentIndex];
+      setList.nickname = val.nickname;
+      setList.avatar = val.avatar;
+      setList.coverUrl = val.coverUrl;
+      setList.thumbUrl = val.thumbUrl;
+      setList.desc = val.desc;
+      setList.url = val.url;
+      setList.extras = val.extras;
+      setList.videoId = val.id;
+      console.info(setList)
+
+      this.videoNumOptions.open = false;
+
+    },
     oneClickGroupSending(val,type,corpId){
 
       this.sendMsgOpen.open= true;
@@ -383,10 +449,21 @@ export default {
           if (selectedVideo && this.msgForm.videoId != null) {
             if (this.setting[i].contentType == 3 || this.setting[i].contentType == 9 ){
               this.$set(this.setting[i], 'linkDescribe', selectedVideo.dictLabel);
+
+              if (this.projectFrom == 'sxjz' && selectedVideo.dictImgUrl) {
+                this.$set(this.setting[i], 'linkImageUrl', selectedVideo.dictImgUrl);
+              }
+
             }
 
             if (this.setting[i].contentType == 4 || this.setting[i].contentType == 10 ){
               this.$set(this.setting[i], 'miniprogramTitle', selectedVideo.dictLabel);
+
+              if (this.projectFrom == 'sxjz' && selectedVideo.dictImgUrl) {
+                this.$set(this.setting[i], 'miniprogramPicUrl', selectedVideo.dictImgUrl);
+
+              }
+
             }
 
 
@@ -452,6 +529,12 @@ export default {
       this.$set(content, 'value', filteredValue);
 
     },
+    //选择视频号
+    hanldeSelectVideoNum(content, index) {
+      this.videoNumOptions.content = content;
+      this.videoNumOptions.contentIndex = index;
+      this.videoNumOptions.open = true;
+    },
 
     delSetList(index){
       this.setting.splice(index,1)
@@ -620,9 +703,18 @@ export default {
 
             if (this.setting[i].contentType == 3 || this.setting[i].contentType == 9){
               this.$set(this.setting[i], 'linkDescribe', selectedVideo.dictLabel);
+
+              if (this.projectFrom == 'sxjz' && selectedVideo.dictImgUrl) {
+                this.$set(this.setting[i], 'linkImageUrl', selectedVideo.dictImgUrl);
+              }
             }
             if (this.setting[i].contentType == 4 || this.setting[i].contentType == 10){
               this.$set(this.setting[i], 'miniprogramTitle', selectedVideo.dictLabel);
+
+              if (this.projectFrom == 'sxjz' && selectedVideo.dictImgUrl) {
+                this.$set(this.setting[i], 'miniprogramPicUrl', selectedVideo.dictImgUrl);
+              }
+
             }
 
           }

+ 95 - 1
src/views/qw/sopUserLogsInfo/sendMsgSopOpenTool.vue

@@ -164,7 +164,44 @@
                           @input="handleInputVideoText(item.value,item)"/>
                       </div>
                       <div v-if="item.contentType == 8">
+                        <el-button type="primary"
+                                   style="margin-bottom: 1%"
+                                   @click="hanldeSelectVideoNum(setting,index)">
+                          选择视频号
+                        </el-button>
+                        <el-card class="box-card" v-if="item.coverUrl">
+                          <el-form-item label="封面标题:" label-width="100px">
+                            <el-input v-model="item.nickname"
+                                      style="width: 90%;margin-bottom: 1%" disabled/>
+                          </el-form-item>
+                          <el-form-item label="头像:" label-width="100px">
+                            <el-image
+                              v-if="item.avatar != null"
+                              :src="item.avatar"
+                              :preview-src-list="[item.avatar]"
+                              :style="{ width: '50px', height: '50px' }"
+                            ></el-image>
+                          </el-form-item>
+                          <el-form-item label="封面:" label-width="100px">
+                            <el-image
+                              v-if="item.coverUrl != null"
+                              :src="item.coverUrl"
+                              :preview-src-list="[item.coverUrl]"
+                              :style="{ width: '200px', height: '200px' }"
+                            ></el-image>
 
+                          </el-form-item>
+                          <el-form-item label="简介:" label-width="100px">
+                            <el-input type="textarea" :rows="3"
+                                      v-model="item.desc"
+                                      style="width: 90%;margin-top: 1%;" disabled/>
+                          </el-form-item>
+                          <el-form-item label="视频地址:" label-width="100px"
+                                        style="margin-top: 1%">
+                            <el-input v-model="item.url"
+                                      style="width: 90%;" disabled/>
+                          </el-form-item>
+                        </el-card>
                       </div>
 
                     </el-form-item>
@@ -226,6 +263,9 @@
           <el-button @click="cancelMsgForm">取 消</el-button>
         </div>
     </el-dialog>
+    <el-dialog :title="videoNumOptions.title" :visible.sync="videoNumOptions.open" width="1500px" append-to-body>
+      <userVideo ref="QwUserVideo" @videoResult="qwUserVideoResult"></userVideo>
+    </el-dialog>
   </div>
 </template>
 
@@ -233,13 +273,21 @@
 import { sendMsg,} from "../../../api/qw/sopUserLogsInfo";
 import ImageUpload from "@/views/qw/sop/ImageUpload.vue";
 import {courseList, videoList} from "@/api/qw/sop";
+import userVideo from "@/views/qw/userVideo/userVideo";
 
 
 export default {
   name: "sendMsgSopOpenTool",
-  components: {ImageUpload},
+  components: {ImageUpload,userVideo},
   data() {
     return {
+      projectFrom:process.env.VUE_APP_PROJECT_FROM,
+      videoNumOptions: {
+        title: '选择视频号',
+        open: false,
+        content: null,
+        contentIndex: null,
+      },
       //上传语音的遮罩层
       voiceLoading :false,
       uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
@@ -342,6 +390,24 @@ export default {
   },
   methods: {
 
+    qwUserVideoResult(val) {
+
+      // 根据选中的内容,将返回的数据更新到相应的表单项
+      const content = this.videoNumOptions.content;
+      const setList = content[this.videoNumOptions.contentIndex];
+      setList.nickname = val.nickname;
+      setList.avatar = val.avatar;
+      setList.coverUrl = val.coverUrl;
+      setList.thumbUrl = val.thumbUrl;
+      setList.desc = val.desc;
+      setList.url = val.url;
+      setList.extras = val.extras;
+      setList.videoId = val.id;
+      console.info(setList)
+
+      this.videoNumOptions.open = false;
+
+    },
     oneClickGroupSending(val,type,corpId){
 
       this.sendMsgOpen.open= true;
@@ -384,13 +450,23 @@ export default {
           if (selectedVideo && this.msgForm.videoId != null) {
             if (this.setting[i].contentType == 3 || this.setting[i].contentType == 9 ){
               this.$set(this.setting[i], 'linkDescribe', selectedVideo.dictLabel);
+
+              if (this.projectFrom == 'sxjz' && selectedVideo.dictImgUrl) {
+                this.$set(this.setting[i], 'linkImageUrl', selectedVideo.dictImgUrl);
+              }
             }
 
             if (this.setting[i].contentType == 4 || this.setting[i].contentType == 10 ){
               this.$set(this.setting[i], 'miniprogramTitle', selectedVideo.dictLabel);
+
+              if (this.projectFrom == 'sxjz' && selectedVideo.dictImgUrl) {
+                this.$set(this.setting[i], 'miniprogramPicUrl', selectedVideo.dictImgUrl);
+              }
+
             }
 
 
+
           }
         }
       }
@@ -467,6 +543,12 @@ export default {
 
     },
 
+    //选择视频号
+    hanldeSelectVideoNum(content, index) {
+      this.videoNumOptions.content = content;
+      this.videoNumOptions.contentIndex = index;
+      this.videoNumOptions.open = true;
+    },
 
     handleKeydown(event, index) {
       const item = this.setting[index];
@@ -600,9 +682,11 @@ export default {
             if (this.setting[i].contentType == 3 || this.setting[i].contentType == 9){
               this.$set(this.setting[i], 'linkTitle', selectedCourse.dictLabel);
               this.$set(this.setting[i], 'linkImageUrl', selectedCourse.dictImgUrl);
+
             }
             if (this.setting[i].contentType == 4 || this.setting[i].contentType == 10){
               this.$set(this.setting[i], 'miniprogramPicUrl', selectedCourse.dictImgUrl);
+
             }
 
 
@@ -621,9 +705,19 @@ export default {
 
             if (this.setting[i].contentType == 3 || this.setting[i].contentType == 9){
               this.$set(this.setting[i], 'linkDescribe', selectedVideo.dictLabel);
+
+              if (this.projectFrom == 'sxjz' && selectedVideo.dictImgUrl) {
+                this.$set(this.setting[i], 'linkImageUrl', selectedVideo.dictImgUrl);
+              }
+
             }
             if (this.setting[i].contentType == 4 || this.setting[i].contentType == 10){
               this.$set(this.setting[i], 'miniprogramTitle', selectedVideo.dictLabel);
+
+              if (this.projectFrom == 'sxjz' && selectedVideo.dictImgUrl) {
+                this.$set(this.setting[i], 'miniprogramPicUrl', selectedVideo.dictImgUrl);
+              }
+
             }
 
           }

+ 29 - 1
src/views/qw/sopUserLogsInfo/sopUserLogsInfoDetails.vue

@@ -559,6 +559,9 @@ export default {
   components: {PaginationMore, userVideo, ImageUpload},
   data() {
     return {
+
+      projectFrom:process.env.VUE_APP_PROJECT_FROM,
+
       statusOptions:[],
       //上传语音的遮罩层
       voiceLoading :false,
@@ -888,10 +891,19 @@ export default {
           if (selectedVideo && this.msgForm.videoId != null) {
             if (this.setting[i].contentType == 3 || this.setting[i].contentType == 9 ){
               this.$set(this.setting[i], 'linkDescribe', selectedVideo.dictLabel);
+
+              if (this.projectFrom == 'sxjz' && selectedVideo.dictImgUrl) {
+                this.$set(this.setting[i], 'linkImageUrl', selectedVideo.dictImgUrl);
+              }
             }
 
             if (this.setting[i].contentType == 4){
               this.$set(this.setting[i], 'miniprogramTitle', selectedVideo.dictLabel);
+
+              if (this.projectFrom == 'sxjz' && selectedVideo.dictImgUrl) {
+                this.$set(this.setting[i], 'miniprogramPicUrl', selectedVideo.dictImgUrl);
+
+              }
             }
 
 
@@ -1109,7 +1121,6 @@ export default {
               this.$set(this.setting[i], 'miniprogramPicUrl', selectedCourse.dictImgUrl);
             }
 
-
           }
 
         }
@@ -1125,9 +1136,26 @@ export default {
 
             if (this.setting[i].contentType == 3 || this.setting[i].contentType == 9){
               this.$set(this.setting[i], 'linkDescribe', selectedVideo.dictLabel);
+
+              if (this.msgForm.videoId != null) {
+                const  selectedVideo = this.videoList.find(course => parseInt(course.dictValue) === this.msgForm.videoId);
+
+                if (this.projectFrom == 'sxjz' && selectedVideo.dictImgUrl) {
+                  this.$set(this.setting[i], 'linkImageUrl', selectedVideo.dictImgUrl);
+                }
+
+              }
             }
             if (this.setting[i].contentType == 4){
               this.$set(this.setting[i], 'miniprogramTitle', selectedVideo.dictLabel);
+              if (this.msgForm.videoId != null) {
+                const  selectedVideo = this.videoList.find(course => parseInt(course.dictValue) === this.msgForm.videoId);
+
+                if (this.projectFrom == 'sxjz' && selectedVideo.dictImgUrl) {
+                  this.$set(this.setting[i], 'miniprogramPicUrl', selectedVideo.dictImgUrl);
+                }
+
+              }
             }
 
           }

+ 2 - 18
src/views/qw/user/cuDeptIdIndex.vue

@@ -77,7 +77,7 @@
       <el-table-column label="企微账号" align="center" prop="qwUserId" />
       <el-table-column label="企微昵称" align="center" prop="qwUserName" />
       <el-table-column label="员工称呼" align="center" prop="welcomeText" />
-      <el-table-column label="所属部门" align="center" prop="isDel">
+      <el-table-column label="员工状态" align="center" prop="isDel">
         <template slot-scope="scope">
           <el-tag v-if="scope.row.isDel == 0" type="success">正常</el-tag>
           <el-tag v-else type="error">离职</el-tag>
@@ -105,28 +105,12 @@
       <el-table-column label="vid" align="center" prop="vid" />
       <el-table-column label="uid" align="center" prop="uid" />
       <el-table-column label="serverId" align="center" prop="serverId" />
-     <el-table-column label="ai状态" align="center" prop="loginStatus">
+     <el-table-column label="ipad状态" align="center" prop="loginStatus">
         <template slot-scope="scope">
           <el-tag v-if="scope.row.ipadStatus == 1" type="success">在线</el-tag>
           <el-tag v-else type="danger">离线</el-tag>
         </template>
       </el-table-column>
-<!--      <el-table-column label="插件状态" align="center" prop="toolStatus">-->
-<!--        <template slot-scope="scope">-->
-<!--          <el-tag v-if="scope.row.toolStatus == 1" type="success">在线</el-tag>-->
-<!--          <el-tag v-else type="danger">离线</el-tag>-->
-<!--        </template>-->
-<!--      </el-table-column>-->
-<!--      <el-table-column label="插件版本" align="center" prop="version"/>-->
-      <el-table-column label="服务器地址" align="center" prop="loginCodeUrl">
-        <template slot-scope="scope">
-          <el-tooltip class="item" effect="dark" :content="scope.row.loginCodeUrl" placement="top">
-            <div style="display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; text-overflow: ellipsis;">
-              <span>{{ scope.row.loginCodeUrl }}</span>
-            </div>
-          </el-tooltip>
-        </template>
-      </el-table-column>
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="120px" fixed="right">
         <template slot-scope="scope">
           <el-button

+ 56 - 19
src/views/qw/user/index.vue

@@ -12,6 +12,9 @@
           />
         </el-select>
       </el-form-item>
+      <el-form-item label="企微部门">
+        <treeselect :clearable="false"  v-model="queryParams.deptId"  :options="deptOptions" :show-count="true" placeholder="请选择归属部门"/>
+      </el-form-item>
       <el-form-item label="企微账号" prop="qwUserId">
         <el-input
           v-model="queryParams.qwUserId"
@@ -89,7 +92,9 @@
       <el-table-column label="企微账号" align="center" prop="qwUserId" />
       <el-table-column label="企微昵称" align="center" prop="qwUserName" />
       <el-table-column label="员工称呼" align="center" prop="welcomeText" />
-      <el-table-column label="所属部门" align="center" prop="isDel">
+      // 部门
+      <el-table-column label="部门" align="center" prop="departmentName" />
+      <el-table-column label="员工状态" align="center" prop="isDel">
         <template slot-scope="scope">
           <el-tag v-if="scope.row.isDel == 0" type="success">正常</el-tag>
           <el-tag v-else type="error">离职</el-tag>
@@ -117,7 +122,7 @@
       <el-table-column label="vid" align="center" prop="vid" />
       <el-table-column label="uid" align="center" prop="uid" />
       <el-table-column label="serverId" align="center" prop="serverId" />
-      <el-table-column label="ai状态" align="center" prop="loginStatus">
+      <el-table-column label="ipad状态" align="center" prop="loginStatus">
         <template slot-scope="scope">
           <el-tag v-if="scope.row.ipadStatus == 1" type="success">在线</el-tag>
           <el-tag v-else type="danger">离线</el-tag>
@@ -130,15 +135,15 @@
       <!--        </template>-->
       <!--      </el-table-column>-->
       <!--      <el-table-column label="插件版本" align="center" prop="version"/>-->
-      <el-table-column label="服务器地址" align="center" prop="loginCodeUrl">
-        <template slot-scope="scope">
-          <el-tooltip class="item" effect="dark" :content="scope.row.loginCodeUrl" placement="top">
-            <div style="display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; text-overflow: ellipsis;">
-              <span>{{ scope.row.loginCodeUrl }}</span>
-            </div>
-          </el-tooltip>
-        </template>
-      </el-table-column>
+<!--      <el-table-column label="服务器地址" align="center" prop="loginCodeUrl">-->
+<!--        <template slot-scope="scope">-->
+<!--          <el-tooltip class="item" effect="dark" :content="scope.row.loginCodeUrl" placement="top">-->
+<!--            <div style="display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; text-overflow: ellipsis;">-->
+<!--              <span>{{ scope.row.loginCodeUrl }}</span>-->
+<!--            </div>-->
+<!--          </el-tooltip>-->
+<!--        </template>-->
+<!--      </el-table-column>-->
 
       <el-table-column label="自动发课" align="center" prop="isAuto">
         <template slot-scope="scope">
@@ -538,7 +543,6 @@
 import {
   updateIsAuto,
   updateUser,
-  exportUser,
   getMyQwCompanyList,
   relieveFastGptRoleById,
   loginQwIpad,
@@ -553,15 +557,19 @@ import {
   outLoginQwIpad,
   changeVideoStatus,
   handleAllocateRemoteHost,
-  qwBindCloudHost, qwUnbindCloudHost, handleAuthAppKey, handleInputAuthAppKey, selectCloudAP, staffListUser
+  qwBindCloudHost, qwUnbindCloudHost, handleAuthAppKey, handleInputAuthAppKey, selectCloudAP, staffListUser, exportStaff
 } from '../../../api/qw/user'
 import fastGptRole from "@/views/fastGpt/fastGptRole/fastGptRole";
+import { treeselect } from "@/api/qw/qwDept";
+import Treeselect from "@riophae/vue-treeselect";
+import "@riophae/vue-treeselect/dist/vue-treeselect.css";
 
 export default {
   name: "cuDeptIdIndex",
-  components: { fastGptRole},
+  components: { fastGptRole,Treeselect},
   data() {
     return {
+      deptOptions:[], // 企微部门
       isAutoOptions:[],
       updateIp:{
         open:false,
@@ -671,6 +679,7 @@ export default {
         qwUserId: null,
         corpId: null,
         qwUserName: null,
+        deptId:null,
       },
       qwUserId:null,
       companyUserList:[],
@@ -699,13 +708,14 @@ export default {
     });
     getMyQwCompanyList().then(response => {
       this.myQwCompanyList = response.data;
-      if(this.myQwCompanyList!=null){
+      if(this.myQwCompanyList && this.myQwCompanyList.length>0){
         this.queryParams.corpId=this.myQwCompanyList[0].dictValue;
+        // 查询部门下拉树结构
+        this.getTreeselect()
         this.getList();
       }
     });
-
-
+    this.loading = false;
   },
   watch: {
     // 监听弹窗的可见性变化
@@ -718,6 +728,25 @@ export default {
     },
   },
   methods: {
+    /** 查询部门下拉树结构 */
+    getTreeselect() {
+      var that=this;
+      let query = {
+        corpId: this.queryParams.corpId
+      }
+      // 企微主体不能为空
+      if(!query.corpId){
+        this.$message.error("请选择企微主体");
+        return;
+      }
+      treeselect(query).then((response) => {
+        this.deptOptions = response.data;
+        console.log(this.deptOptions)
+        if(response.data!=null&&response.data.length>0){
+          this.queryParams.deptId=response.data[0].id;
+        }
+      });
+    },
     getList() {
       this.loading = true;
       staffListUser(this.queryParams).then(response => {
@@ -729,6 +758,7 @@ export default {
     },
     updateCorpId() {
       this.reset();
+      this.getTreeselect();
       this.getList();
     },
     changeVideoStatus(val){
@@ -1204,13 +1234,13 @@ export default {
     /** 导出按钮操作 */
     handleExport() {
       const queryParams = this.queryParams;
-      this.$confirm('是否确认导出所有企微用户数据项?', "警告", {
+      this.$confirm('是否确认导出所有企微员工数据项?', "警告", {
         confirmButtonText: "确定",
         cancelButtonText: "取消",
         type: "warning"
       }).then(() => {
         this.exportLoading = true;
-        return exportUser(queryParams);
+        return exportStaff(queryParams);
       }).then(response => {
         this.download(response.msg);
         this.exportLoading = false;
@@ -1263,4 +1293,11 @@ export default {
   width: 80%;
 }
 
+.vue-treeselect{
+  width: 217px;
+  height: 36px;
+}
+
 </style>
+
+

+ 1272 - 0
src/views/qw/user/myIndex.vue

@@ -0,0 +1,1272 @@
+<template>
+
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
+      <el-form-item label="企微主体" prop="corpId">
+        <el-select v-model="queryParams.corpId" placeholder="企微主体" size="small" @change="updateCorpId()">
+          <el-option
+            v-for="dict in myQwCompanyList"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="企微账号" prop="qwUserId">
+        <el-input
+          v-model="queryParams.qwUserId"
+          placeholder="请输入企微账号"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+
+      <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="appKey">
+        <el-input
+          v-model="queryParams.appKey"
+          placeholder="请输入授权码"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="员工状态" prop="isDel">
+        <el-select v-model="queryParams.isDel" placeholder="请选择员工状态" clearable>
+          <el-option
+            v-for="item in optionsStatus"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value">
+          </el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          :disabled="multiple"
+          plain
+          icon="el-icon-edit"
+          size="mini"
+          @click="updateSendType"
+        >修改发送方式
+        </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:user:export']"
+        >导出</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table border v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="企微成员ID" align="center" prop="id" />
+      <el-table-column label="企微账号" align="center" prop="qwUserId" />
+      <el-table-column label="企微昵称" align="center" prop="qwUserName" />
+      <el-table-column label="员工称呼" align="center" prop="welcomeText" />
+      <el-table-column label="员工状态" align="center" prop="isDel">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.isDel == 0" type="success">正常</el-tag>
+          <el-tag v-else type="error">离职</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="联系我二维码" align="center" prop="contactWay" >
+        <template slot-scope="scope">
+          <el-image
+            v-if="scope.row.contactWay!=null"
+            style="width: 100px; height: 100px"
+            :src="scope.row.contactWay"
+            fit="contain"
+            @click="openImageViewer(scope.row.contactWay)"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="绑定的AI客服" align="center" prop="fastGptRoleName" />
+      <el-table-column label="授权码" align="center" prop="appKey" />
+      <el-table-column label="发送方式" align="center" prop="sendMsgType">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.sendMsgType == 0">方式一</el-tag>
+          <el-tag v-if="scope.row.sendMsgType == 1" type="success">方式二</el-tag>
+          <el-tag v-if="scope.row.sendMsgType == 2" type="warning">掉线通知</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="vid" align="center" prop="vid" />
+      <el-table-column label="uid" align="center" prop="uid" />
+      <el-table-column label="serverId" align="center" prop="serverId" />
+      <el-table-column label="ipad状态" align="center" prop="loginStatus">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.ipadStatus == 1" type="success">在线</el-tag>
+          <el-tag v-else type="danger">离线</el-tag>
+        </template>
+      </el-table-column>
+      <!--      <el-table-column label="插件状态" align="center" prop="toolStatus">-->
+      <!--        <template slot-scope="scope">-->
+      <!--          <el-tag v-if="scope.row.toolStatus == 1" type="success">在线</el-tag>-->
+      <!--          <el-tag v-else type="danger">离线</el-tag>-->
+      <!--        </template>-->
+      <!--      </el-table-column>-->
+      <!--      <el-table-column label="插件版本" align="center" prop="version"/>-->
+<!--      <el-table-column label="服务器地址" align="center" prop="loginCodeUrl">-->
+<!--        <template slot-scope="scope">-->
+<!--          <el-tooltip class="item" effect="dark" :content="scope.row.loginCodeUrl" placement="top">-->
+<!--            <div style="display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; text-overflow: ellipsis;">-->
+<!--              <span>{{ scope.row.loginCodeUrl }}</span>-->
+<!--            </div>-->
+<!--          </el-tooltip>-->
+<!--        </template>-->
+<!--      </el-table-column>-->
+
+      <el-table-column label="自动发课" align="center" prop="isAuto">
+        <template slot-scope="scope">
+          <dict-tag :options="isAutoOptions" :value="scope.row.isAuto"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="120px" fixed="right">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-user-solid"
+            plain
+            @click="handleAppellation(scope.row)"
+          >
+            修改员工称呼
+          </el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-user-solid"
+            plain
+            @click="handleAutoRemark(scope.row)"
+          >
+            完课备注修改
+          </el-button>
+
+          <el-button
+            v-if="scope.row.serverStatus==1&&scope.row.ipadStatus!=1"
+            size="mini"
+            type="text"
+            icon="el-icon-sunny"
+            plain
+            @click="handleLoginQwCode(scope.row)"
+            v-hasPermi="['qw:user:login']"
+          >
+            登录企微
+          </el-button>
+          <el-button
+            v-if="scope.row.serverStatus==1&&scope.row.ipadStatus==1"
+            size="mini"
+            type="text"
+            icon="el-icon-moon"
+            plain
+            @click="handleLoginOutQwStatus(scope.row)"
+            v-hasPermi="['qw:user:login']"
+          >
+            退出企微
+          </el-button>
+          <el-button
+            v-if="scope.row.ipadStatus==1"
+            size="mini"
+            type="text"
+            icon="el-icon-moon"
+            plain
+            @click="handleTwoCode(scope.row)"
+            v-hasPermi="['qw:user:login']">
+            二次验证
+          </el-button>
+          <el-button
+            v-if="scope.row.serverStatus!=1"
+            size="mini"
+            type="text"
+            icon="el-icon-moon"
+            plain
+            @click="handleGetQwIpad(scope.row)"
+            v-hasPermi="['qw:user:login']"
+          >
+            获取Ai主机
+          </el-button>
+          <el-button
+            v-if="scope.row.serverStatus==1 && scope.row.ipadStatus!=1"
+            size="mini"
+            type="text"
+            icon="el-icon-moon"
+            plain
+            @click="handleDelQwIpad(scope.row)"
+            v-hasPermi="['qw:user:login']"
+          >
+            解绑Ai主机
+          </el-button>
+          <el-button
+            v-if="scope.row.isAuto=='00'"
+            size="mini"
+            type="text"
+            icon="el-icon-moon"
+            plain
+            @click="handleUpdateIsAuto(scope.row,'01')"
+            v-hasPermi="['qw:user:isauto']"
+          >
+            启用插件
+          </el-button>
+          <el-button
+            v-if="scope.row.isAuto=='01'"
+            size="mini"
+            type="text"
+            icon="el-icon-moon"
+            plain
+            @click="handleUpdateIsAuto(scope.row,'00')"
+            v-hasPermi="['qw:user:isauto']"
+          >
+            禁用插件
+          </el-button>
+        </template>
+      </el-table-column>
+      <el-table-column label="主机" align="center" class-name="small-padding fixed-width" width="110px" fixed="right">
+        <template slot-scope="scope">
+
+          <el-button
+            v-if="scope.row.appKey==null"
+            size="mini"
+            type="text"
+            icon="el-icon-s-check"
+            plain
+            v-hasPermi="['qw:user:authAppKey']"
+            @click="uploadAuthorizeKey2(scope.row)"
+          >授权key
+          </el-button>
+          <el-button
+            v-if="scope.row.loginCodeUrl==null && scope.row.appKey !=null"
+            size="mini"
+            type="text"
+            icon="el-icon-sunny"
+            plain
+            @click="handleBindCloudHost(scope.row)"
+            v-hasPermi="['qw:user:loginIp']"
+          >
+            绑定主机
+          </el-button>
+          <!--          <el-button-->
+          <!--            v-if="scope.row.loginCodeUrl!=null"-->
+          <!--            size="mini"-->
+          <!--            type="text"-->
+          <!--            icon="el-icon-video-camera-solid"-->
+          <!--            plain-->
+          <!--            @click="handleCloudAP(scope.row.loginCodeUrl)"-->
+          <!--            v-hasPermi="['qw:user:cloudAP']"-->
+          <!--          >-->
+          <!--            获取主机帐密-->
+          <!--          </el-button>-->
+          <el-button
+            v-if="scope.row.loginCodeUrl!=null"
+            size="mini"
+            type="text"
+            icon="el-icon-moon"
+            plain
+            @click="handleUnbindCloudHost(scope.row)"
+            v-hasPermi="['qw:user:loginIpOut']"
+          >
+            解除主机
+          </el-button>
+          <el-button
+            size="mini"
+            type="text"
+            :icon="scope.row.videoGetStatus == 0 ? 'el-icon-circle-check' : 'el-icon-remove-outline'"
+            plain
+            @click="changeVideoStatus(scope.row)"
+          >
+            {{ scope.row.videoGetStatus == 0 ? "开启" : "禁用" }}视频号接收
+          </el-button>
+        </template>
+      </el-table-column>
+      <el-table-column label="AI客服" align="center" class-name="small-padding fixed-width" width="100px" fixed="right">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-connection"
+            plain
+            v-if="scope.row.fastGptRoleName!=null"
+            @click="bindFastGptRole(scope.row)"
+          >换绑AI客服</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            plain
+            icon="el-icon-link"
+            v-else
+            @click="bindFastGptRole(scope.row)"
+          >绑定AI客服</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-unlock"
+            plain
+            v-if="scope.row.fastGptRoleName!=null"
+            @click="relieveFastGptRole(scope.row)"
+          >解绑AI客服</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"
+    />
+
+    <!-- 绑定AI客服-->
+    <el-dialog :title="bindAiTitle" :visible.sync="bindAiOpen" width="1200px" append-to-body>
+      <fast-gpt-role ref="fastGptRole" @refreshFastGptList="refreshFastGptList" ></fast-gpt-role>
+    </el-dialog>
+
+    <!--    <el-dialog :visible.sync="updateIp.open" width="600px" append-to-body>-->
+    <!--      <el-form ref="updateIpForm" :model="updateIpForm" :rules="updateIpRule" label-width="100px">-->
+    <!--        <el-form-item label="新云主机IP" prop="Ip">-->
+    <!--          <el-input v-model="updateIpForm.newIp" placeholder="请输入新IP" />-->
+    <!--        </el-form-item>-->
+    <!--      </el-form>-->
+    <!--      <div slot="footer" class="dialog-footer" >-->
+    <!--        <el-button type="primary" @click="submitUpdateIpForm">确 定</el-button>-->
+    <!--      </div>-->
+    <!--    </el-dialog>-->
+
+    <el-dialog title="云主机信息" :visible.sync="cloudAPOpen.open" append-to-body>
+      <el-card class="box-card">
+        <div slot="header" class="clearfix">
+          <span>账号:{{cloudAPOpen.admin}}</span>
+        </div>
+        <div slot="header" class="clearfix">
+          <span>密码:{{cloudAPOpen.passWord}}</span>
+        </div>
+      </el-card>
+    </el-dialog>
+
+    <el-dialog :title="callOpen.title" :visible.sync="callOpen.open" width="500px" append-to-body>
+      <el-form ref="callOpenFrom" :model="callOpenFrom" :rules="callOpenRule" label-width="110px">
+        <el-form-item label="员工称呼" prop="welcomeText">
+          <el-input v-model="callOpenFrom.welcomeText" placeholder="请输入员工称呼" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer" >
+        <el-button type="primary" @click="submitCallOpenFrom">确 定</el-button>
+      </div>
+    </el-dialog>
+
+
+    <el-dialog :title="editRemarkOpen.title" :visible.sync="editRemarkOpen.open" width="500px" append-to-body>
+      <el-form ref="callOpenFrom" :model="editRemarkOpen" label-width="110px">
+        <el-radio-group v-model="editRemarkOpen.isSendMsg">
+          <el-card>
+            <el-row :gutter="20">
+              <el-col>
+                <el-radio
+                  :label="1"
+                  style="font-size: 16px; margin: 10px 0;"
+                >添加【完课备注】在最【旧备注-(前面)】</el-radio>
+              </el-col>
+              <el-col>
+                <el-radio
+                  :label="2"
+                  style="font-size: 16px; margin: 10px 0;"
+                >添加【完课备注】在最【旧备注-(后面)】</el-radio>
+              </el-col>
+              <el-col>
+                <el-radio
+                  :label="3"
+                  style="font-size: 16px; margin: 10px 0;"
+                >使用简洁版备注【*日期完】,在【旧备注-前面】</el-radio>
+              </el-col>
+              <el-col>
+                <el-radio
+                  :label="4"
+                  style="font-size: 16px; margin: 10px 0;"
+                >使用简洁版备注【*日期完】,在【旧备注-后面】</el-radio>
+              </el-col>
+              <el-col>
+                <el-radio
+                  :label="5"
+                  style="font-size: 16px; margin: 10px 0;"
+                >不用完课备注</el-radio>
+              </el-col>
+            </el-row>
+          </el-card>
+        </el-radio-group>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitEditRemarkOpenFrom">确 定</el-button>
+      </div>
+    </el-dialog>
+
+    <el-dialog title="授权key" :visible.sync="authorizeKeyOpen" width="500px" append-to-body>
+      <el-form ref="authorizeKeyFrom" :model="authorizeKeyFrom" :rules="authorizeKeyRule" label-width="110px">
+        <el-form-item label="授权的key值" prop="appKey">
+          <el-input v-model="authorizeKeyFrom.appKey" placeholder="请输入授权key" type="Number"/>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer" >
+        <el-button type="primary" @click="submitAuthorizeKeyForm">确 定</el-button>
+      </div>
+    </el-dialog>
+    <!--二维码   -->
+    <el-dialog
+      title="企微二次认证"
+      :visible.sync="qwLoginTwo.open"
+      width="600px"
+      append-to-body
+      custom-class="qr-login-dialog"
+    >
+      <div class="qr-login-container">
+        <div class="image-wrapper" v-loading="imageLoading" >
+          <el-image
+            :src="'data:image/png;base64,' +qwLoginTwo.codeUrl"
+            style="display: block; margin: 0 auto; width: 300px; height: 300px;"
+          />
+        </div>
+        <p class="qr-login-instructions">二次验证二维码</p>
+      </div>
+      <div slot="footer" class="dialog-footer" >
+        <el-button type="primary" @click="qwLoginTwo.open=false">确 定</el-button>
+      </div>
+    </el-dialog>
+
+    <el-dialog
+      :title="qwLogin.title"
+      :visible.sync="qwLogin.open"
+      width="600px"
+      append-to-body
+      custom-class="qr-login-dialog"
+    >
+      <div class="qr-login-container">
+        <div class="image-wrapper" v-loading="imageLoading" >
+          <el-image
+            :src="'data:image/png;base64,' +qwLogin.codeUrl"
+            style="display: block; margin: 0 auto; width: 300px; height: 300px;"
+          />
+        </div>
+        <p class="qr-login-instructions">使用企业微信扫码授权登录</p>
+      </div>
+    </el-dialog>
+
+    <el-dialog
+      title="输入企微验证码"
+      :visible.sync="qwCode.open"
+      width="600px"
+      append-to-body>
+      <el-form  :model="qwCode"  label-width="80px" @submit.native.prevent="handleSubmit">
+        <el-form-item label="验证码" prop="companyName">
+          <el-input v-model="qwCode.code" placeholder="输入企微6位验证码" />
+        </el-form-item>
+      </el-form>
+
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitCodeForm">确 定</el-button>
+
+      </div>
+    </el-dialog>
+
+
+
+    <!-- 大图预览对话框 -->
+    <el-dialog
+      :visible.sync="dialogVisible"
+      :modal="false"
+      width="1200"
+      append-to-body>
+      <img
+        :src="this.dialogImageUrl"
+        style="display: block; max-width: 100%; margin: 0 auto"
+      />
+    </el-dialog>
+    <el-dialog
+      :visible.sync="updateSendOpen"
+      width="1000px"
+      append-to-body>
+      <el-form label-width="80px">
+        <p>是否修改企微账号发送方式:
+          <el-tag style="margin-left: 10px" v-for="name in names">{{ name }}</el-tag>
+        </p>
+        <el-form-item label="发送方式" prop="type">
+          <el-radio-group v-model="type">
+            <el-radio
+              :label="0"
+            >方式一
+            </el-radio>
+            <el-radio
+              :label="1"
+            >方式二
+            </el-radio>
+            <el-radio
+              :label="2"
+            >掉线通知
+            </el-radio>
+          </el-radio-group>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitUpdateSendTypeForm">确 定</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import {
+  updateIsAuto,
+  updateUser,
+  getMyQwCompanyList,
+  relieveFastGptRoleById,
+  loginQwIpad,
+  loginQwCodeMsg,
+  twoCode,
+  twoCodeStatus,
+  qrCodeStatus,
+  updateSendType,
+  getQwIpad,
+  delQwIpad,
+  qrCodeVerify,
+  outLoginQwIpad,
+  changeVideoStatus,
+  handleAllocateRemoteHost,
+  qwBindCloudHost,
+  qwUnbindCloudHost,
+  handleAuthAppKey,
+  handleInputAuthAppKey,
+  selectCloudAP,
+  staffListUser,
+  exportStaff,
+  myStaffListUser
+} from '../../../api/qw/user'
+import fastGptRole from "@/views/fastGpt/fastGptRole/fastGptRole";
+
+export default {
+  name: "myIndex",
+  components: { fastGptRole},
+  data() {
+    return {
+      isAutoOptions:[],
+      updateIp:{
+        open:false,
+        title: "修改云主机IP"
+      },
+      updateIpForm:{
+        id:null,
+        newIp:null,
+      },
+      authorizeKeyOpen:false,
+      authorizeKeyFrom:{
+        id:null,
+        appKey:null,
+        qwUserId:null,
+        qwUserName:null
+      },
+      updateIpRule:{},
+      newIp:null,
+      //放大图片
+      dialogImageUrl:null,
+      dialogVisible:false,
+      optionsStatus: [{
+        value: 0,
+        label: '正常'
+      }, {
+        value: 2,
+        label: '离职'
+      }],
+      // 遮罩层
+      loading: true,
+      names: [],
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      updateSendOpen: false,
+      // 总条数
+      total: 0,
+      //公司列表
+      myQwCompanyList:[],
+      // 企微用户表格数据
+      userList: [],
+      allowSelectOptions:[],
+      // 弹出层标题
+      bindAiTitle: "",
+      bindAiOpen: false,
+      qwLogin:{
+        title:"",
+        open:false,
+        codeUrl:null,
+        code:null,
+        appKey:null,
+      },
+      qwLoginTwo:{
+        title:"",
+        open:false,
+        codeUrl:null,
+        code:null,
+        appKey:null,
+      },
+      qwCode:{
+        title:"",
+        open:false,
+        code:null,
+      },
+      cloudAPOpen:{
+        open:false,
+        admin:null,
+        passWord:null,
+      },
+      callOpen:{
+        open:false,
+        title: '修改员工称呼',
+
+      },
+      callOpenFrom:{
+        id:null,
+        welcomeText:null,
+      },
+      isAutoForm:{
+        id:null,
+        isAuto:null,
+      },
+
+      editRemarkOpen: {
+        open: false,
+        title: '修改员工自动给完课客户打备注的规则',
+        id:null,
+        isSendMsg:null,
+      },
+
+      twoCodeInterval:null,
+      type: 0,
+      loginQwInterval:null,
+
+      imageLoading: true, // 控制加载状态
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        qwUserId: null,
+        corpId: null,
+        qwUserName: null,
+      },
+      qwUserId:null,
+      companyUserList:[],
+      // 表单参数
+      form: {
+        isSendMsg: '2',
+      },
+      authorizeKeyRule:{
+        appKey:[{required:true,message:"授权码不能为空",trigger:"blur"}]
+      },
+      callOpenRule:{
+        welcomeText:[{required:true,message:"员工称呼不能为空",trigger:"blur"}]
+      },
+      // 表单校验
+      rules: {
+      },
+      //欢迎语表单校验
+      weclomeRules:{
+        welcomeText:[{required:true,message:"消息文本不能为空",trigger:"blur"}]
+      },
+    };
+  },
+  created() {
+    this.getDicts("qw_user_is_auto").then(response => {
+      this.isAutoOptions = response.data;
+    });
+    getMyQwCompanyList().then(response => {
+      this.myQwCompanyList = response.data;
+      if(this.myQwCompanyList!=null){
+        this.queryParams.corpId=this.myQwCompanyList[0].dictValue;
+        this.getList();
+      }
+    });
+
+
+  },
+  watch: {
+    // 监听弹窗的可见性变化
+    'qwLogin.open'(newVal) {
+      if (!newVal) {
+        // 如果弹窗关闭,清除定时器
+        clearInterval(this.loginQwInterval);
+
+      }
+    },
+  },
+  methods: {
+    getList() {
+      this.loading = true;
+      myStaffListUser(this.queryParams).then(response => {
+        this.userList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+
+    },
+    updateCorpId() {
+      this.reset();
+      this.getList();
+    },
+    changeVideoStatus(val){
+      changeVideoStatus(val.id).then(res => {
+        this.$message.success("修改状态成功");
+        this.getList()
+      })
+    },
+
+    //绑定AI客服
+    bindFastGptRole(row) {
+      this.bindAiTitle = "绑定AI客服";
+      this.bindAiOpen = true;
+      setTimeout(() => {
+        this.$refs.fastGptRole.handleBindAiData(row)
+      }, 200);
+
+    },
+    handleAppellation(val) {
+      this.callOpen.open = true;
+      this.callOpenFrom.welcomeText = val.welcomeText;
+      this.callOpenFrom.id = val.id;
+    },
+
+    handleAutoRemark(val) {
+      this.editRemarkOpen.open = true;
+      this.editRemarkOpen.id = val.id;
+      this.editRemarkOpen.isSendMsg = val.isSendMsg;
+    },
+    //登录
+    handleLoginQwCode(val) {
+      if (val.appKey == null || val.appKey === '') {
+        return this.$message.warning("没有授权码,无法登录企业微信,请授权");
+      }
+      loginQwIpad({qwUserId: val.id}).then(res => {
+        this.qwUserId = val.id;
+        this.qwLogin.code = null;
+        this.imageLoading = false;
+        console.log(res)
+        if (res.msg == "success") {
+          this.qwLogin.codeUrl = res.qrCode64
+          this.qwLogin.open = true;
+          this.loginQwPolling();
+        } else {
+          this.$message.success(res.msg);
+          this.getList()
+        }
+
+      })
+    },
+
+    handleTwoCode(val) {
+
+      twoCode({qwUserId: val.id}).then(res => {
+        console.log(res)
+        this.qwLoginTwo.open = true;
+        this.qwLoginTwo.codeUrl = res.qrCode
+
+      });
+
+
+    },
+    twoCodePolling() {
+      this.twoCodeInterval = setInterval(() => {
+        twoCodeStatus({qwUserId: this.qwUserId}).then(res => {
+          console.log(res)
+
+          if (res.msg == 104001) {
+            this.$message.success('登录成功');
+            this.clearDl()
+            clearInterval(this.loginQwInterval);
+          } else if (res.msg == 100004) {
+            this.clearDl()
+          }
+        });
+      }, 3000);
+    },
+    loginQwPolling() {
+      this.loginQwInterval = setInterval(() => {
+        qrCodeStatus({qwUserId: this.qwUserId}).then(res => {
+          console.log(res)
+          if (res.msg == 22) {
+            this.$message.success('账号企业不一致请重新扫码登录');
+            this.clearDl();
+          }
+          if (res.msg == 104001) {
+            this.$message.success('登录成功');
+            this.clearDl()
+
+          } else if (res.msg == 100004) {
+            this.qwCode.open = true;
+            clearInterval(this.loginQwInterval);
+          }
+        });
+      }, 3000);
+    },
+    submitCodeForm() {
+
+      qrCodeVerify({code: this.qwCode.code, qwUserId: this.qwUserId}).then(res => {
+        console.log(res)
+
+        this.$message.success('验证成功账号信息确认中。。。。');
+        this.qwCode.open = false;
+        this.loginQwInterval = setTimeout(() => {
+          qrCodeStatus({qwUserId: this.qwUserId}).then(res => {
+            console.log(res);
+            if (res.msg == 23) {
+              this.$message.error('账号不一致请重新扫码登录');
+              this.clearDl();
+            }
+            if (res.msg == 22) {
+              this.$message.error('账号企业不一致请重新扫码登录');
+              this.clearDl();
+            }
+            if (res.msg == 104001) {
+              this.$message.success('登录成功');
+              this.clearDl();
+            }
+          });
+        }, 4000);
+      });
+    },
+
+    clearDl() {
+      this.qwCode.open = false;
+      this.qwLogin.open = false;
+      clearInterval(this.loginQwInterval);
+      this.getList()
+    },
+
+
+    //退出
+    handleLoginOutQwStatus(val) {
+      outLoginQwIpad({qwUserId: val.id}).then(res => {
+
+        this.$message.success("退出登录成功");
+        this.getList()
+      })
+
+    },
+
+
+    handleGetQwIpad(val) {
+      getQwIpad({qwUserId: val.id}).then(res => {
+        this.$message.success("获取主机成功");
+        this.getList();
+      }).catch(error => {
+        console.log(error);
+        if (error.code === 501) {
+          this.$confirm(
+            '当前区域没有多余的名额,将为你分配异地名额,会导致企业微信需要扫脸重新登录,并且半个小时后需要进行验证',
+            '提示',
+            {
+              confirmButtonText: '确定',
+              cancelButtonText: '取消',
+              type: 'warning',
+              dangerouslyUseHTMLString: true
+            }
+          ).then(() => {
+            return handleAllocateRemoteHost({qwUserId: val.id});
+          }).then(res => {
+            this.$message.success('异地主机分配成功');
+            this.getList();
+          }).catch(() => {
+            this.$message.info('已取消异地主机分配');
+          });
+        } else {
+          this.$message.error('获取主机失败');
+        }
+      });
+    },
+    handleDelQwIpad(val) {
+      delQwIpad({qwUserId: val.id}).then(res => {
+        this.$message.success("解绑主机成功");
+        this.getList()
+      })
+    },
+
+    handleUpdateIsAuto(val,code) {
+      this.isAutoForm={
+        id:val.id,
+        isAuto:code
+      }
+      updateIsAuto(this.isAutoForm).then(res => {
+        if (code==='00'){
+          this.$message.success('禁用成功');
+        }else {
+          this.$message.success('启用成功');
+        }
+        this.getList();
+      });
+
+
+    },
+    //传验证码
+    handleLoginQwCodeMsg() {
+      loginQwCodeMsg({appKey: this.qwLogin.appKey, code: this.qwLogin.code}).then(res => {
+        this.qwLogin.open = false;
+        this.$message.success("登录成功");
+      })
+    },
+
+    validateCode() {
+      // 只允许输入数字并限制长度为6
+      this.qwLogin.code = this.qwLogin.code.replace(/\D/g, "").slice(0, 6);
+    },
+
+    handleCloudAP(urlAP) {
+
+      selectCloudAP({ipAddress: urlAP}).then(res => {
+        this.cloudAPOpen.open = true
+        this.cloudAPOpen.admin = res.data.apAdmin;
+        this.cloudAPOpen.passWord = res.data.apPassword;
+      })
+    },
+
+    handleUnbindCloudHost(val) {
+
+      const appKey = val.appKey;
+
+      this.$confirm(
+        '确定要给企微账号:<span style="color: green;">' + val.qwUserId + '' +
+        '</span><br>企微昵称:<span style="color: red;">【' + val.qwUserName + '】</span>' +
+        '</span><br><span style="color: orange;">解绑【Ps:解绑后此云主机可能会分配给他人】</span></span>',
+        "警告",
+        {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+          dangerouslyUseHTMLString: true // 允许使用 HTML 字符串
+        }
+      ).then(() => {
+        return qwUnbindCloudHost(appKey);
+      }).then(response => {
+        this.$message.success('解绑成功');
+      }).finally(res => {
+        this.getList();
+      })
+
+    },
+
+
+    handleAuthorizeKey(val) {
+      this.authorizeKeyFrom.id = val.id;
+      this.authorizeKeyFrom.qwUserId = val.qwUserId;
+      this.authorizeKeyFrom.qwUserName = val.qwUserName;
+      this.authorizeKeyOpen = true;
+
+    },
+
+    submitAuthorizeKeyForm() {
+      this.$refs["authorizeKeyFrom"].validate(valid => {
+        if (valid) {
+          if (this.authorizeKeyFrom.id != null && this.authorizeKeyFrom.appKey != null) {
+            this.uploadAuthorizeKey();
+          }
+        }
+      });
+    },
+    submitCallOpenFrom() {
+
+      this.$refs["callOpenFrom"].validate(valid => {
+        if (valid) {
+
+          if (this.callOpenFrom.id != null && this.callOpenFrom.welcomeText != null) {
+            updateUser(this.callOpenFrom).then(res => {
+              this.$message.success('修改成功');
+              this.callOpen.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+
+    submitEditRemarkOpenFrom() {
+      if (this.editRemarkOpen.id != null && this.editRemarkOpen.isSendMsg != null) {
+        updateUser(this.editRemarkOpen).then(res => {
+          this.$message.success('修改成功');
+          this.editRemarkOpen.open = false;
+          this.getList();
+        });
+      } else {
+        this.$message.error("请选择条件")
+      }
+    },
+
+
+    uploadAuthorizeKey() {
+      this.$confirm(
+        '确定要给企微账号:<span style="color: green;">' + this.authorizeKeyFrom.qwUserId + '' +
+        '</span><br>企微昵称:<span style="color: red;">【' + this.authorizeKeyFrom.qwUserName + '】</span>' +
+        '</span><br>授权key:<span style="color: #04adf6;">【' + this.authorizeKeyFrom.appKey + '】</span>?',
+        "警告",
+        {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+          dangerouslyUseHTMLString: true // 允许使用 HTML 字符串
+        }
+      ).then(() => {
+        this.authorizeKeyOpen = false;
+        return handleInputAuthAppKey(this.authorizeKeyFrom);
+      }).then(response => {
+        this.msgSuccess("授权key完成");
+      }).finally(res => {
+        this.resetAuthorizeKeyFrom();
+        this.getList();
+      })
+
+    },
+
+    uploadAuthorizeKey2(val) {
+      const id = val.id;
+      this.$confirm(
+        '确定要给企微账号:<span style="color: green;">' + val.qwUserId + '' +
+        '</span><br>企微昵称:<span style="color: red;">【' + val.qwUserName + '】</span>' +
+        '</span><br>授权key</span>?',
+        "警告",
+        {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+          dangerouslyUseHTMLString: true // 允许使用 HTML 字符串
+        }
+      ).then(() => {
+        return handleAuthAppKey({id: id});
+      }).then(response => {
+        this.msgSuccess("授权key完成");
+      }).finally(res => {
+        this.resetAuthorizeKeyFrom();
+        this.getList();
+      })
+    },
+
+    handleBindCloudHost(val) {
+
+      if (val.appKey == null || val.appKey == '') {
+        return this.$message.warning('没有授权码,无法绑定主机,请联系管理员');
+      }
+
+      const appKey = val.appKey;
+
+      this.$confirm(
+        '确定要给企微账号:<span style="color: green;">' + val.qwUserId + '' +
+        '</span><br>企微昵称:<span style="color: red;">【' + val.qwUserName + '】</span>' +
+        '</span><br><span style="color: dodgerblue;">绑定云主机?</span></span>',
+        "警告",
+        {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+          dangerouslyUseHTMLString: true // 允许使用 HTML 字符串
+        }
+      ).then(() => {
+        return qwBindCloudHost(appKey);
+      }).then(response => {
+        this.$message.success('绑定成功,请登录云主机进行配置~~');
+      }).finally(res => {
+        this.getList();
+      })
+
+
+    },
+
+    openImageViewer(url) {
+      // 打开大图预览对话框
+      this.dialogImageUrl = url
+      this.dialogVisible = true;
+    },
+    //刷新页面
+    refreshFastGptList() {
+      this.bindAiOpen = false;
+      this.getList();
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        qwUserId: null,
+        corpId: null,
+        qwUserName: null,
+      };
+      this.resetForm("form");
+
+    },
+
+    //重置授权
+    resetAuthorizeKeyFrom() {
+      this.authorizeKeyFrom = {
+        id: null,
+        appKey: null,
+        qwUserId: null,
+        qwUserName: null
+      };
+    },
+    //重置登录
+    resetQwLogin() {
+      this.qwLogin = {
+        title: "",
+        open: false,
+        codeUrl: null,
+        code: null,
+        corpId: null,
+        qwUserId: null,
+      }
+      this.qwLogin.open = false;
+      this.loading = false;
+      this.getList();
+    },
+    //解绑AI客服
+    relieveFastGptRole(row) {
+      this.$confirm('是否确认解绑AI客服?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function () {
+        return relieveFastGptRoleById(row.id);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("解绑成功");
+      })
+
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.queryParams.corpId = this.myQwCompanyList[0].dictValue;
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.names = selection.map(item => item.qwUserName)
+      this.single = selection.length !== 1
+      this.multiple = !selection.length
+    },
+
+
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != null) {
+
+            // updateUser(this.form).then(response => {
+            //   this.msgSuccess("绑定成功");
+            //   this.open = false;
+            //   this.getList();
+            //
+            // });
+          } else {
+            // addUser(this.form).then(response => {
+            //   this.msgSuccess("新增成功");
+            //   this.open = false;
+            //   this.getList();
+            // });
+          }
+        }
+      });
+    },
+
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有企微员工数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        this.exportLoading = true;
+        return exportStaff(queryParams);
+      }).then(response => {
+        this.download(response.msg);
+        this.exportLoading = false;
+      }).catch(() => {
+      });
+    },
+    updateSendType() {
+      this.updateSendOpen = true;
+    },
+    cleanUpdateSendType() {
+      this.updateSendOpen = false;
+    },
+    submitUpdateSendTypeForm(){
+      updateSendType({ids: this.ids, type: this.type}).then(e => {
+        this.updateSendOpen = false;
+        this.$message.success("修改成功");
+        this.getList();
+      });
+    },
+  }
+};
+</script>
+<style>
+.text-container {
+  max-height: 7.5em; /* 设置最大高度为6行,根据字体大小调整 */
+  overflow-y: auto; /* 内容超出时显示滚动条 */
+  line-height: 1.5em; /* 行高设置,确保每行高度一致 */
+}
+
+.qr-login-dialog .el-dialog__body {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  text-align: center;
+  padding: 20px;
+}
+
+.qr-login-container {
+  width: 100%;
+}
+
+.qr-login-instructions {
+  font-size: 14px;
+  color: #666;
+  margin: 10px 0;
+}
+
+.verification-code-input {
+  margin-top: 15px;
+  width: 80%;
+}
+
+</style>

+ 9 - 1
src/views/qw/user/qwUserSelectOne.vue

@@ -41,6 +41,11 @@
       @pagination="handlePaginationChange"
     />
 
+        <div style="margin-top: 30px;display: flex;justify-content: center;gap: 12px">
+      <el-button plain @click="handleClear">不选(清空发送人)</el-button>
+      <el-button type="warning" icon="el-icon-check" @click="confirmSelect">确定选择</el-button>
+    </div>
+
   </div>
 </template>
 
@@ -167,7 +172,10 @@ export default {
     handleSelectionChange(selection) {
       this.$emit("selectUser",selection);
     },
-
+    // 用户点击“不选”
+    handleClear() {
+      this.$emit("selectUser",{});   // 传空对象,表示清空
+    },
   }
 };
 </script>

+ 15 - 0
src/views/users/user/transfer.vue

@@ -12,6 +12,16 @@
           </el-option>
         </el-select>
       </el-form-item>
+      <el-form-item label="项目" prop="projectId">
+        <el-select  v-model="queryParams.projectId" placeholder="请选择项目" clearable size="small" >
+          <el-option
+            v-for="item in projectOptions"
+            :key="item.dictValue"
+            :label="item.dictLabel"
+            :value="item.dictValue"
+          />
+        </el-select>
+      </el-form-item>
       <el-form-item label="会员ID" prop="userId">
         <el-input
           style="width: 200px"
@@ -74,6 +84,11 @@
       </el-table-column>
       <el-table-column label="会员ID" align="center" prop="userId" />
       <el-table-column label="昵称" align="center" prop="nickname" />
+      <el-table-column label="头像" align="center">
+        <template slot-scope="scope">
+          <img  :src="scope.row.avatar" style="width:50px;height:50px" />
+        </template>
+      </el-table-column>
       <el-table-column label="所属销售" align="center" prop="companyUserNickName" />
       <el-table-column label="手机号码" align="center" prop="phone" />
       <el-table-column label="状态" align="center" prop="statusText" >