Selaa lähdekoodia

Merge branch 'master' into bjcz_his_scrm

吴树波 3 viikkoa sitten
vanhempi
commit
ad64b6dc9e
100 muutettua tiedostoa jossa 11349 lisäystä ja 695 poistoa
  1. 3 0
      .env.development
  2. 19 0
      .env.prod-bly
  3. 4 10
      .env.prod-cqtyt
  4. 22 0
      .env.prod-czt
  5. 22 0
      .env.prod-ddgy
  6. 22 0
      .env.prod-hat
  7. 22 0
      .env.prod-hst
  8. 23 0
      .env.prod-jnlzjk
  9. 22 0
      .env.prod-knt
  10. 22 0
      .env.prod-nmgyt
  11. 4 1
      .env.prod-sft
  12. 1 1
      .env.prod-sxjz
  13. 1 1
      .env.prod-syysy
  14. 9 0
      package.json
  15. 6 0
      src/api/company/company.js
  16. 36 0
      src/api/company/companyUser.js
  17. 11 0
      src/api/company/userIntegralLogs.js
  18. 7 0
      src/api/course/qw/courseWatchLog.js
  19. 9 0
      src/api/doctor/doctor.js
  20. 19 0
      src/api/hisStore/answer.js
  21. 1 1
      src/api/hisStore/city.js
  22. 70 0
      src/api/hisStore/collection.js
  23. 58 0
      src/api/hisStore/integralGoods.js
  24. 80 0
      src/api/hisStore/integralOrder.js
  25. 16 0
      src/api/hisStore/statistics.js
  26. 33 2
      src/api/hisStore/storeAfterSales.js
  27. 7 1
      src/api/hisStore/storeOrder.js
  28. 9 0
      src/api/hisStore/storeOrderAudit.js
  29. 6 6
      src/api/hisStore/userOnlineState.js
  30. 8 0
      src/api/qw/QwWorkTask.js
  31. 36 1
      src/api/qw/externalContact.js
  32. 23 0
      src/api/qw/externalContactTransferAudit.js
  33. 9 0
      src/api/qw/qwUser.js
  34. 4 4
      src/api/qw/qwUserVoiceLog.js
  35. 20 1
      src/api/qw/sop.js
  36. 8 0
      src/api/qw/user.js
  37. 71 0
      src/api/store/healthTongue.js
  38. 7 0
      src/api/store/package.js
  39. 6 0
      src/api/store/packageOrder.js
  40. 18 0
      src/api/store/statistics.js
  41. 7 1
      src/api/store/storeOrder.js
  42. BIN
      src/assets/logo/bly.png
  43. BIN
      src/assets/logo/cqtyt.jpg
  44. BIN
      src/assets/logo/czt.png
  45. BIN
      src/assets/logo/ddgy.jpg
  46. BIN
      src/assets/logo/hat.png
  47. BIN
      src/assets/logo/hst_logo.png
  48. BIN
      src/assets/logo/img.png
  49. BIN
      src/assets/logo/jnlzjk.png
  50. BIN
      src/assets/logo/knt.jpg
  51. BIN
      src/assets/logo/nmgyt.jpg
  52. 56 11
      src/layout/components/Navbar.vue
  53. 139 44
      src/views/company/companyConfig/index.vue
  54. 11 9
      src/views/company/companyDept/index.vue
  55. 489 158
      src/views/company/companyUser/index.vue
  56. 125 56
      src/views/company/components/selectQwUser.vue
  57. 184 0
      src/views/company/components/selectQwUserOlder.vue
  58. 93 0
      src/views/components/healthTongueDetails.vue
  59. 305 4
      src/views/course/courseFinishTemp/index.vue
  60. 295 4
      src/views/course/courseFinishTempParent/index.vue
  61. 10 31
      src/views/course/courseUserStatistics/qw/index.vue
  62. 307 31
      src/views/course/courseWatchLog/deptWatchLog.vue
  63. 495 42
      src/views/course/courseWatchLog/index.vue
  64. 4 4
      src/views/course/courseWatchLog/myCourseWatchLog.vue
  65. 23 3
      src/views/course/courseWatchLog/qw/statistics.vue
  66. 343 43
      src/views/course/courseWatchLog/watchLog.vue
  67. 199 0
      src/views/hisStore/components/OrderSummaryTable.vue
  68. 318 0
      src/views/hisStore/components/integralOrderDetails.vue
  69. 94 27
      src/views/hisStore/components/productAfterSalesOrder.vue
  70. 156 28
      src/views/hisStore/components/productOrder.vue
  71. 625 0
      src/views/hisStore/integralOrder/addOrder.vue
  72. 546 0
      src/views/hisStore/integralOrder/index.vue
  73. 112 0
      src/views/hisStore/integralOrder/integralProductSelect.vue
  74. 561 0
      src/views/hisStore/statistics/storeOrderStatistics.vue
  75. 512 0
      src/views/hisStore/statistics/storeProduct.vue
  76. 475 0
      src/views/hisStore/storeOrderAudit/audit.vue
  77. 66 7
      src/views/qw/autoTags/dayPartingIndex.vue
  78. 4 1
      src/views/qw/autoTags/dayPartingIndexDetails.vue
  79. 189 32
      src/views/qw/contactWay/index.vue
  80. 192 0
      src/views/qw/externalContact/collection.vue
  81. 53 3
      src/views/qw/externalContact/deptIndex.vue
  82. 349 8
      src/views/qw/externalContact/index.vue
  83. 345 10
      src/views/qw/externalContact/myExternalContact.vue
  84. 445 0
      src/views/qw/externalContactTransfer/companyTransfer.vue
  85. 144 0
      src/views/qw/externalContactTransfer/companyTransferQwUserSelect.vue
  86. 48 3
      src/views/qw/externalContactTransfer/deptTransferIndex.vue
  87. 114 83
      src/views/qw/externalContactTransfer/index.vue
  88. 604 0
      src/views/qw/externalContactTransfer/index旧.vue
  89. 213 0
      src/views/qw/externalContactTransferLog/companyTransferDetail.vue
  90. 42 0
      src/views/qw/externalContactTransferLog/index.vue
  91. 464 0
      src/views/qw/externalContactUnassigned/companyUnassigned.vue
  92. 47 2
      src/views/qw/externalContactUnassigned/deptUnassignedIndex.vue
  93. 48 2
      src/views/qw/externalContactUnassigned/index.vue
  94. 13 1
      src/views/qw/friendWelcome/deptFriendWelcome.vue
  95. 12 0
      src/views/qw/friendWelcome/indexNew.vue
  96. 13 2
      src/views/qw/friendWelcome/myWelcome.vue
  97. 336 3
      src/views/qw/qwUserVoiceLog/index.vue
  98. 339 4
      src/views/qw/qwUserVoiceLogTotal/index.vue
  99. 8 8
      src/views/qw/sop/addSop.vue
  100. 3 1
      src/views/qw/sop/deptSop.vue

+ 3 - 0
.env.development

@@ -25,3 +25,6 @@ VUE_CLI_BABEL_TRANSPILE_MODULES = true
 VUE_APP_PATIENT_INFO = '客户信息'
 # 添加病人
 VUE_APP_ADD_PATIENT = '添加信息'
+
+#1、正常搜索下拉框 2、查询出200条数据,然后搜索这200条以内的
+VUE_APP_COURSE_COMPANY_NAME = 2

+ 19 - 0
.env.prod-bly

@@ -0,0 +1,19 @@
+# 页面标题
+VUE_APP_TITLE = 倍力优SCRM管理系统
+# 公司名称
+VUE_APP_COMPANY_NAME = 倍力优(北京)健康产业科技有限公司
+# ICP备案号
+VUE_APP_ICP_RECORD = 京ICP备18042618号-2
+# ICP网站访问地址
+VUE_APP_ICP_URL =https://beian.miit.gov.cn
+# 网站LOG
+VUE_APP_LOG_URL =@/assets/logo/bly.png
+
+# 生产环境配置
+ENV = 'production'
+
+#FS管理系统/生产环境
+VUE_APP_BASE_API = '/prod-api'
+
+# 路由懒加载
+VUE_CLI_BABEL_TRANSPILE_MODULES = true

+ 4 - 10
.env.prod-fcky → .env.prod-cqtyt

@@ -1,13 +1,13 @@
 # 页面标题
-VUE_APP_TITLE =客户管理系统
+VUE_APP_TITLE =重庆泰医堂
 # 公司名称
-VUE_APP_COMPANY_NAME =蜂巢快药(北京)医药连锁有限公司
+VUE_APP_COMPANY_NAME =泰安高新区泰医堂中医医院有限公司
 # ICP备案号
-VUE_APP_ICP_RECORD =京ICP备2025120013号
+VUE_APP_ICP_RECORD =鲁ICP备2025154120号-1
 # ICP网站访问地址
 VUE_APP_ICP_URL =https://beian.miit.gov.cn
 # 网站LOG
-VUE_APP_LOG_URL =@/assets/logo/fcky.png
+VUE_APP_LOG_URL =@/assets/logo/cqtyt.jpg
 
 # 生产环境配置
 ENV = 'production'
@@ -15,14 +15,8 @@ ENV = 'production'
 # FS管理系统/开发环境
 VUE_APP_BASE_API = '/prod-api'
 
-
 #默认 1、会员 2、企微
 VUE_APP_COURSE_DEFAULT = 1
 
 # 路由懒加载
 VUE_CLI_BABEL_TRANSPILE_MODULES = true
-
-# 患者信息
-VUE_APP_PATIENT_INFO = '客户信息'
-# 添加病人
-VUE_APP_ADD_PATIENT = '添加信息'

+ 22 - 0
.env.prod-czt

@@ -0,0 +1,22 @@
+# 页面标题
+VUE_APP_TITLE =内蒙古纯正堂SCRM销售端
+# 公司名称
+VUE_APP_COMPANY_NAME =内蒙古纯正堂大药房有限公司
+# ICP备案号
+VUE_APP_ICP_RECORD =京ICP备2024053040号-5
+# ICP网站访问地址
+VUE_APP_ICP_URL =https://beian.miit.gov.cn
+# 网站LOG
+VUE_APP_LOG_URL =@/assets/logo/czt.png
+
+# 生产环境配置
+ENV = 'production'
+
+# FS管理系统/开发环境
+VUE_APP_BASE_API = '/prod-api'
+
+#默认 1、会员 2、企微
+VUE_APP_COURSE_DEFAULT = 2
+
+# 路由懒加载
+VUE_CLI_BABEL_TRANSPILE_MODULES = true

+ 22 - 0
.env.prod-ddgy

@@ -0,0 +1,22 @@
+# 页面标题
+VUE_APP_TITLE =叮当国医SCRM销售端
+# 公司名称
+VUE_APP_COMPANY_NAME =云联融智互联网医院有限公司
+# ICP备案号
+VUE_APP_ICP_RECORD =渝ICP备2024031984号-1
+# ICP网站访问地址
+VUE_APP_ICP_URL =https://beian.miit.gov.cn
+# 网站LOG
+VUE_APP_LOG_URL =@/assets/logo/ddgy.jpg
+
+# 生产环境配置
+ENV = 'production'
+
+# FS管理系统/开发环境
+VUE_APP_BASE_API = '/prod-api'
+
+#默认 1、会员 2、企微
+VUE_APP_COURSE_DEFAULT = 1
+
+# 路由懒加载
+VUE_CLI_BABEL_TRANSPILE_MODULES = true

+ 22 - 0
.env.prod-hat

@@ -0,0 +1,22 @@
+# 页面标题
+VUE_APP_TITLE =恒安图SCRM销售端
+# 公司名称
+VUE_APP_COMPANY_NAME =云联融智互联网医院有限公司
+# ICP备案号
+VUE_APP_ICP_RECORD =渝ICP备2024031984号-1
+# ICP网站访问地址
+VUE_APP_ICP_URL =https://beian.miit.gov.cn
+# 网站LOG
+VUE_APP_LOG_URL =@/assets/logo/hat.png
+
+# 生产环境配置
+ENV = 'production'
+
+# FS管理系统/开发环境
+VUE_APP_BASE_API = '/prod-api'
+
+#默认 1、会员 2、企微
+VUE_APP_COURSE_DEFAULT = 2
+
+# 路由懒加载
+VUE_CLI_BABEL_TRANSPILE_MODULES = true

+ 22 - 0
.env.prod-hst

@@ -0,0 +1,22 @@
+# 页面标题
+VUE_APP_TITLE =鸿森堂SCRM销售端
+# 公司名称
+VUE_APP_COMPANY_NAME =成都温江鸿森堂智慧互联网医院有限公司
+# ICP备案号
+VUE_APP_ICP_RECORD =蜀ICP备2025160310号-3
+# ICP网站访问地址
+VUE_APP_ICP_URL =https://beian.miit.gov.cn
+# 网站LOG
+VUE_APP_LOG_URL =@/assets/logo/hst_logo.png
+
+# 生产环境配置
+ENV = 'production'
+
+# FS管理系统/开发环境
+VUE_APP_BASE_API = '/prod-api'
+
+#默认 1、会员 2、企微
+VUE_APP_COURSE_DEFAULT = 2
+
+# 路由懒加载
+VUE_CLI_BABEL_TRANSPILE_MODULES = true

+ 23 - 0
.env.prod-jnlzjk

@@ -0,0 +1,23 @@
+# 页面标题
+VUE_APP_TITLE =联志健康SCRM销售端
+# 公司名称
+VUE_APP_COMPANY_NAME =济南联志健康
+# ICP备案号
+VUE_APP_ICP_RECORD =渝ICP备2024031984号-2
+# ICP网站访问地址
+VUE_APP_ICP_URL =https://beian.miit.gov.cn
+# 网站LOG
+VUE_APP_LOG_URL =@/assets/logo/jnlzjk.png
+
+# 生产环境配置
+ENV = 'production'
+
+# FS管理系统/开发环境
+VUE_APP_BASE_API = '/prod-api'
+
+
+#默认 1、会员 2、企微
+VUE_APP_COURSE_DEFAULT = 1
+
+# 路由懒加载
+VUE_CLI_BABEL_TRANSPILE_MODULES = true

+ 22 - 0
.env.prod-knt

@@ -0,0 +1,22 @@
+# 页面标题
+VUE_APP_TITLE = 康年堂SCRM销售端
+# 公司名称
+VUE_APP_COMPANY_NAME = 陕西康年堂医药连锁有限公司
+# ICP备案号
+VUE_APP_ICP_RECORD = 陕ICP备2023011686号-5
+# ICP网站访问地址
+VUE_APP_ICP_URL =https://beian.miit.gov.cn
+# 网站LOG
+VUE_APP_LOG_URL =@/assets/logo/knt.jpg
+
+# 生产环境配置
+ENV = 'production'
+
+#FS管理系统/生产环境
+VUE_APP_BASE_API = '/prod-api'
+
+# 路由懒加载
+VUE_CLI_BABEL_TRANSPILE_MODULES = true
+
+#默认 1、会员 2、企微
+VUE_APP_COURSE_DEFAULT = 1

+ 22 - 0
.env.prod-nmgyt

@@ -0,0 +1,22 @@
+# 页面标题
+VUE_APP_TITLE =内蒙古一帖医药
+# 公司名称
+VUE_APP_COMPANY_NAME =内蒙古好药师蒙一堂大药房有限公司
+# ICP备案号
+VUE_APP_ICP_RECORD =蒙ICP备2023002294号
+# ICP网站访问地址
+VUE_APP_ICP_URL =https://beian.miit.gov.cn
+# 网站LOG
+VUE_APP_LOG_URL =@/assets/logo/nmgyt.jpg
+
+# 生产环境配置
+ENV = 'production'
+
+# FS管理系统/开发环境
+VUE_APP_BASE_API = '/prod-api'
+
+#默认 1、会员 2、企微
+VUE_APP_COURSE_DEFAULT = 2
+
+# 路由懒加载
+VUE_CLI_BABEL_TRANSPILE_MODULES = true

+ 4 - 1
.env.prod-sft

@@ -16,7 +16,10 @@ ENV = 'production'
 VUE_APP_BASE_API = '/prod-api'
 
 #默认 1、会员 2、企微
-VUE_APP_COURSE_DEFAULT = 2
+VUE_APP_COURSE_DEFAULT = 1
 
 # 路由懒加载
 VUE_CLI_BABEL_TRANSPILE_MODULES = true
+
+#1、正常搜索下拉框 2、查询出200条数据,然后搜索这200条以内的
+VUE_APP_COURSE_COMPANY_NAME = 2

+ 1 - 1
.env.prod-sxjz

@@ -1,5 +1,5 @@
 # 页面标题
-VUE_APP_TITLE =互联网医院管理系统
+VUE_APP_TITLE =挑宝SCRM客服系统
 # 公司名称
 VUE_APP_COMPANY_NAME =银川鑫泰互联网医院有限公司
 # ICP备案号

+ 1 - 1
.env.prod-syysy

@@ -1,5 +1,5 @@
 # 页面标题
-VUE_APP_TITLE =互联网医院管理系统
+VUE_APP_TITLE =益寿缘SCRM客服系统
 # 公司名称
 VUE_APP_COMPANY_NAME =沈阳铁西益寿缘中医院有限责任公司
 # ICP备案号

+ 9 - 0
package.json

@@ -16,6 +16,7 @@
     "build:prod-test": "vue-cli-service build --mode prod-test",
     "build:prod-sxjz": "vue-cli-service build --mode prod-sxjz",
     "build:prod-jnmy": "vue-cli-service build --mode prod-jnmy",
+    "build:prod-knt": "vue-cli-service build --mode prod-knt",
     "build:prod-hdt": "vue-cli-service build --mode prod-hdt",
     "build:prod-yzt": "vue-cli-service build --mode prod-yzt",
     "build:prod-xfk": "vue-cli-service build --mode prod-xfk",
@@ -28,8 +29,12 @@
     "build:prod-whhm": "vue-cli-service build --mode prod-whhm",
     "build:prod-drk": "vue-cli-service build --mode prod-drk",
     "build:prod-zkzh": "vue-cli-service build --mode prod-zkzh",
+    "build:prod-nmgyt": "vue-cli-service build --mode prod-nmgyt",
+    "build:prod-cqtyt": "vue-cli-service build --mode prod-cqtyt",
+    "build:prod-bly": "vue-cli-service build --mode prod-bly",
     "build:prod-qdtst": "vue-cli-service build --mode prod-qdtst",
     "build:prod-bjczwh": "vue-cli-service build --mode prod-bjczwh",
+    "build:prod-jnlzjk": "vue-cli-service build --mode prod-jnlzjk",
     "build:prod-jkj": "vue-cli-service build --mode prod-jkj",
     "build:prod-cqxzt": "vue-cli-service build --mode prod-cqxzt",
     "build:prod-bjyjb": "vue-cli-service build --mode prod-bjyjb",
@@ -37,6 +42,10 @@
     "build:prod-fby": "vue-cli-service build --mode prod-fby",
     "build:prod-syysy": "vue-cli-service build --mode prod-syysy",
     "build:prod-hyt": "vue-cli-service build --mode prod-hyt",
+    "build:prod-hst": "vue-cli-service build --mode prod-hst",
+    "build:prod-hat": "vue-cli-service build --mode prod-hat",
+    "build:prod-ddgy": "vue-cli-service build --mode prod-ddgy",
+    "build:prod-czt": "vue-cli-service build --mode prod-czt",
     "preview": "node build/index.js --preview",
     "lint": "eslint --ext .js,.vue src",
     "test:unit": "jest --clearCache && vue-cli-service test:unit",

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

@@ -43,3 +43,9 @@ export function allList(query) {
   })
 }
 
+export function getCompanyListByCorId(corId) {
+  return request({
+    url: '/company/company/getCompanyListByCorId/' + corId,
+    method: 'get'
+  })
+}

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

@@ -87,6 +87,16 @@ export function addUser(data) {
   })
 }
 
+//创建二维码
+export function addCodeUrl(data) {
+  return request({
+    url: '/company/user/addCodeUrl',
+    method: 'post',
+    data: data
+  })
+}
+
+
 // 修改用户
 export function updateUser(data) {
   return request({
@@ -271,3 +281,29 @@ export function isAllowedAllRegister(status, data) {
     data: data
   })
 }
+
+// 绑定医生
+export function bindDoctorId(data) {
+  return request({
+    url: '/company/user/bindDoctorId',
+    method: 'post',
+    data: data
+  })
+}
+
+//解绑医生
+export function unBindDoctorId(userId) {
+  return request({
+    url: '/company/user/unBindDoctorId/'+userId,
+    method: 'get'
+  })
+}
+
+//批量修改用户的角色
+export function updateBatchUserRoles(data) {
+  return request({
+    url: '/company/user/updateBatchUserRoles',
+    method: 'post',
+    data: data
+  })
+}

+ 11 - 0
src/api/company/userIntegralLogs.js

@@ -0,0 +1,11 @@
+import request from '@/utils/request'
+
+// 查询积分记录列表
+export function listUserIntegralLogs(query) {
+  return request({
+    url: '/company/companyIntegral/list',
+    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',

+ 9 - 0
src/api/doctor/doctor.js

@@ -141,3 +141,12 @@ export function exportDoctor(query) {
     params: query
   })
 }
+
+// 查询医生管理列表
+export function listDoctorVO(query) {
+  return request({
+    url: '/his/doctor/getDocVoList',
+    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',
+  })
+}

+ 1 - 1
src/api/hisStore/city.js

@@ -10,7 +10,7 @@ export function listCity(query) {
 }
 export function getCitys(query) {
   return request({
-    url: '/store/store/city/getCitys',
+    url: '/store/city/getCitys',
     method: 'get',
     params: query
   })

+ 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',
+  })
+}

+ 58 - 0
src/api/hisStore/integralGoods.js

@@ -0,0 +1,58 @@
+import request from '@/utils/request'
+
+// 查询积分商品列表
+export function listIntegralGoods(query) {
+  return request({
+    url: '/his/integralGoods/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询积分商品详细
+export function getIntegralGoods(goodsId) {
+  return request({
+    url: '/his/integralGoods/' + goodsId,
+    method: 'get'
+  })
+}
+export function importTemplate() {
+  return request({
+    url: '/his/integralGoods/importTemplate',
+    method: 'get'
+  })
+}
+// 新增积分商品
+export function addIntegralGoods(data) {
+  return request({
+    url: '/his/integralGoods',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改积分商品
+export function updateIntegralGoods(data) {
+  return request({
+    url: '/his/integralGoods',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除积分商品
+export function delIntegralGoods(goodsId) {
+  return request({
+    url: '/his/integralGoods/' + goodsId,
+    method: 'delete'
+  })
+}
+
+// 导出积分商品
+export function exportIntegralGoods(query) {
+  return request({
+    url: '/his/integralGoods/export',
+    method: 'get',
+    params: query
+  })
+}

+ 80 - 0
src/api/hisStore/integralOrder.js

@@ -0,0 +1,80 @@
+import request from '@/utils/request'
+
+// 查询积分商品订单列表
+export function listIntegralOrder(query) {
+  return request({
+    url: '/his/integralOrder/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询积分商品订单详细
+export function getIntegralOrder(orderId) {
+  return request({
+    url: '/his/integralOrder/' + orderId,
+    method: 'get'
+  })
+}
+export function getExpress(orderId) {
+  return request({
+    url: '/his/integralOrder/getExpress/' + orderId,
+    method: 'get'
+  })
+}
+export function importTemplate() {
+  return request({
+    url: '/his/integralOrder/importTemplate',
+    method: 'get'
+  })
+}
+export function sendgoods(data) {
+  return request({
+    url: '/his/integralOrder/sendGoods',
+    method: 'put',
+    data: data
+  })
+}
+// 新增积分商品订单
+export function addIntegralOrder(data) {
+  return request({
+    url: '/his/integralOrder',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改积分商品订单
+export function updateIntegralOrder(data) {
+  return request({
+    url: '/his/integralOrder',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除积分商品订单
+export function delIntegralOrder(orderId) {
+  return request({
+    url: '/his/integralOrder/' + orderId,
+    method: 'delete'
+  })
+}
+
+// 导出积分商品订单
+export function exportIntegralOrder(query) {
+  return request({
+    url: '/his/integralOrder/export',
+    method: 'get',
+    params: query
+  })
+}
+
+
+// 查询收货人电话
+export function getOrderUserPhone(orderId) {
+  return request({
+    url: '/his/integralOrder/queryPhone/' + orderId,
+    method: 'get'
+  })
+}

+ 16 - 0
src/api/hisStore/statistics.js

@@ -31,3 +31,19 @@ export function exportStorePayment(query) {
   })
 }
 
+export function storeProduct(query) {
+  return request({
+    url: '/store/store/statistics/storeProduct',
+    method: 'get',
+    params: query
+  })
+}
+
+ export function storeOrderStatistics(params) {
+    return request({
+      url: '/store/store/statistics/storeOrderStatistics',
+      method: 'get',
+      params
+    });
+  }
+

+ 33 - 2
src/api/hisStore/storeAfterSales.js

@@ -62,6 +62,30 @@ export function exportStoreAfterSales(query) {
 
 
 
+// export function cancel(data) {
+//   return request({
+//     url: '/store/store/storeAfterSales/cancel',
+//     method: 'post',
+//     data: data
+//   })
+// }
+
+// export function refund(data) {
+//   return request({
+//     url: '/store/store/storeAfterSales/refund',
+//     method: 'post',
+//     data: data
+//   })
+// }
+
+export function audit(data) {
+  return request({
+    url: '/store/store/storeAfterSales/audit',
+    method: 'post',
+    data: data
+  })
+}
+
 export function cancel(data) {
   return request({
     url: '/store/store/storeAfterSales/cancel',
@@ -78,9 +102,16 @@ export function refund(data) {
   })
 }
 
-export function audit(data) {
+export function audit1(data) {
   return request({
-    url: '/store/store/storeAfterSales/audit',
+    url: '/store/store/storeAfterSales/audit1',
+    method: 'post',
+    data: data
+  })
+}
+export function audit2(data) {
+  return request({
+    url: '/store/store/storeAfterSales/audit2',
     method: 'post',
     data: data
   })

+ 7 - 1
src/api/hisStore/storeOrder.js

@@ -85,7 +85,13 @@ export function updateStoreOrder(data) {
     data: data
   })
 }
-
+export function updateAddressErpFsStoreOrder(data) {
+  return request({
+    url: '/store/store/storeOrder/updateAddressErpFsStoreOrder',
+    method: 'put',
+    data: data
+  })
+}
 // 删除订单
 export function delStoreOrder(id) {
   return request({

+ 9 - 0
src/api/hisStore/storeOrderAudit.js

@@ -17,3 +17,12 @@ export const audit = (data) => {
     data: data
   })
 }
+
+// 批量审核
+export const auditBatch = (data) => {
+  return request({
+    url: '/store/storeOrderAudit/auditBatch',
+    method: 'get',
+    params: data
+  })
+}

+ 6 - 6
src/api/hisStore/userOnlineState.js

@@ -3,7 +3,7 @@ import request from '@/utils/request'
 // 查询用户上线情况列表
 export function listUserOnlineState(query) {
   return request({
-    url: '/store/store/userOnlineState/list',
+    url: '/store/userOnlineState/list',
     method: 'get',
     params: query
   })
@@ -12,7 +12,7 @@ export function listUserOnlineState(query) {
 // 查询用户上线情况详细
 export function getUserOnlineState(userId) {
   return request({
-    url: '/store/store/userOnlineState/' + userId,
+    url: '/store/userOnlineState/' + userId,
     method: 'get'
   })
 }
@@ -20,7 +20,7 @@ export function getUserOnlineState(userId) {
 // 新增用户上线情况
 export function addUserOnlineState(data) {
   return request({
-    url: '/store/store/userOnlineState',
+    url: '/store/userOnlineState',
     method: 'post',
     data: data
   })
@@ -29,7 +29,7 @@ export function addUserOnlineState(data) {
 // 修改用户上线情况
 export function updateUserOnlineState(data) {
   return request({
-    url: '/store/store/userOnlineState',
+    url: '/store/userOnlineState',
     method: 'put',
     data: data
   })
@@ -38,7 +38,7 @@ export function updateUserOnlineState(data) {
 // 删除用户上线情况
 export function delUserOnlineState(userId) {
   return request({
-    url: '/store/store/userOnlineState/' + userId,
+    url: '/store/userOnlineState/' + userId,
     method: 'delete'
   })
 }
@@ -46,7 +46,7 @@ export function delUserOnlineState(userId) {
 // 导出用户上线情况
 export function exportUserOnlineState(query) {
   return request({
-    url: '/store/store/userOnlineState/export',
+    url: '/store/userOnlineState/export',
     method: 'get',
     params: query
   })

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

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

+ 36 - 1
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({
@@ -25,6 +34,15 @@ export function myDeptExtList(query) {
     params: query
   })
 }
+
+export function companyExtList(query) {
+  return request({
+    url: '/qw/externalContact/companyExtList',
+    method: 'get',
+    params: query
+  })
+}
+
 export function myList(query) {
   return request({
     url: '/qw/externalContact/myList',
@@ -149,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({
@@ -296,4 +323,12 @@ export function delLossStatisticsExport(query) {
     method: 'get',
     params: query
   })
-}
+}
+
+export function companyTransfer(data) {
+  return request({
+    url: '/qw/externalContact/companyTransfer',
+    method: 'post',
+    data: data
+  })
+}

+ 23 - 0
src/api/qw/externalContactTransferAudit.js

@@ -0,0 +1,23 @@
+import request from '@/utils/request'
+
+export function listExternalContactTransferAudit(query) {
+  return request({
+    url: '/qw/externalContactTransferCompanyAudit/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function detail(id) {
+  return request({
+    url: '/qw/externalContactTransferCompanyAudit/detail/' + id,
+    method: 'get'
+  })
+}
+
+export function sync(id) {
+  return request({
+    url: '/qw/externalContactTransferCompanyAudit/sync/' + id,
+    method: 'post'
+  })
+}

+ 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
+  })
+}

+ 4 - 4
src/api/qw/qwUserVoiceLog.js

@@ -1,11 +1,11 @@
 import request from '@/utils/request'
 
 // 查询企微用户通话记录列表
-export function listQwUserVoiceLog(query) {
+export function listQwUserVoiceLog(data) {
   return request({
-    url: '/qw/qwUserVoiceLog/list',
-    method: 'get',
-    params: query
+    url: '/qw/qwUserVoiceLog/newList',
+    method: 'post',
+    data: data
   })
 }
 

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

@@ -9,6 +9,15 @@ export function listSop(query) {
   })
 }
 
+// 查询所有的企微sop列表信息
+export function infoSop(query) {
+  return request({
+    url: '/qw/sop/info',
+    method: 'get',
+    params: query
+  })
+}
+
 export function listMySop(query) {
   return request({
     url: '/qw/sop/myList',
@@ -40,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'
   })
 }
@@ -96,6 +107,14 @@ export function delSop(id) {
   })
 }
 
+// 删除企微sop
+export function delSopLogs(id) {
+  return request({
+    url: '/qw/sop/delSopLogs/' + id,
+    method: 'delete'
+  })
+}
+
 //批量执行企微sop
 export function executeSop(ids) {
   return request({

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

@@ -390,3 +390,11 @@ export function qwRestartCloudHost(params) {
     params: params
   })
 }
+
+export function companyQwUserlist(params) {
+  return request({
+    url: '/qw/user/companyQwUserlist',
+    method: 'get',
+    params: params
+  })
+}

+ 71 - 0
src/api/store/healthTongue.js

@@ -0,0 +1,71 @@
+import request from '@/utils/request'
+
+// 查询归属公司会员舌苔列表
+export function listHealthTongue(query) {
+  return request({
+    url: '/store/healthTongue/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询归属我的会员舌苔列表
+export function myListHealthTongue(query) {
+  return request({
+    url: '/store/healthTongue/myList',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询舌苔详细
+export function getHealthTongue(id) {
+  return request({
+    url: '/store/healthTongue/' + id,
+    method: 'get'
+  })
+}
+
+// 新增舌苔
+export function addHealthTongue(data) {
+  return request({
+    url: '/store/healthTongue',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改舌苔
+export function updateHealthTongue(data) {
+  return request({
+    url: '/store/healthTongue',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除舌苔
+export function delHealthTongue(id) {
+  return request({
+    url: '/store/healthTongue/' + id,
+    method: 'delete'
+  })
+}
+
+// 导出舌苔
+export function exportHealthTongue(query) {
+  return request({
+    url: '/store/healthTongue/export',
+    method: 'get',
+    params: query
+  })
+}
+
+// 导出舌苔
+export function myExportHealthTongue(query) {
+  return request({
+    url: '/store/healthTongue/myExport',
+    method: 'get',
+    params: query
+  })
+}

+ 7 - 0
src/api/store/package.js

@@ -63,3 +63,10 @@ export function exportPackage(query) {
     params: query
   })
 }
+
+export function allPrivatePackage() {
+  return request({
+    url: '/store/package/privatePackageList',
+    method: 'get'
+  })
+}

+ 6 - 0
src/api/store/packageOrder.js

@@ -96,3 +96,9 @@ export function getUserPhone(orderId) {
     method: 'get'
   })
 }
+export function getWxaCodePackageOrderUnLimit(orderId) {
+  return request({
+    url: '/store/packageOrder/getWxaCodePackageOrderUnLimit/'+orderId,
+    method: 'get',
+  })
+}

+ 18 - 0
src/api/store/statistics.js

@@ -76,3 +76,21 @@ export function exportStorePayment(query) {
   })
 }
 
+export function storeProduct(query) {
+  return request({
+    url: '/store/store/statistics/storeProduct',
+    method: 'get',
+    params: query
+  })
+}
+
+export function storeOrderStatistics(query) {
+  return request({
+    url: '/store/store/statistics/storeOrder',
+    method: 'get',
+    params: query
+  })
+}
+
+
+

+ 7 - 1
src/api/store/storeOrder.js

@@ -138,7 +138,13 @@ export function updateStoreOrder(data) {
     data: data
   })
 }
-
+export function updateAddressErpFsStoreOrder(data) {
+  return request({
+    url: '/store/storeOrder/updateAddressErpFsStoreOrder',
+    method: 'put',
+    data: data
+  })
+}
 export function bindCustomer(data) {
   return request({
     url: '/store/storeOrder/bindCustomer',

BIN
src/assets/logo/bly.png


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/jnlzjk.png


BIN
src/assets/logo/knt.jpg


BIN
src/assets/logo/nmgyt.jpg


+ 56 - 11
src/layout/components/Navbar.vue

@@ -107,32 +107,77 @@ export default {
   data() {
     return {
       msgCount:0,
+      previousMsgCount: 0, // 保存上一次的消息计数
       msg:{
         open:false,
         title:'通知消息'
       },
+      msgPollingTimer: null, // 轮询定时器
     }
   },
   created() {
     this.getMsgCount();
+    // 启动消息轮询,每30秒检查一次新消息
+    this.startMsgPolling();
+  },
+  beforeDestroy() {
+    // 组件销毁前清除定时器
+    this.stopMsgPolling();
   },
   methods: {
+    startMsgPolling() {
+      // 清除已存在的定时器
+      if (this.msgPollingTimer) {
+        clearInterval(this.msgPollingTimer);
+      }
+
+      // 设置定时器,每30秒获取一次消息计数
+      this.msgPollingTimer = setInterval(() => {
+        this.getMsgCount();
+      }, 30000); // 30秒轮询一次
+    },
+    stopMsgPolling() {
+      if (this.msgPollingTimer) {
+        clearInterval(this.msgPollingTimer);
+        this.msgPollingTimer = null;
+      }
+    },
     getMsgCount(){
-      // getMsgCount().then(response => {
-      //     this.msgCount = response.counts;
-      //     if(this.msgCount>0){
-      //       this.$notify({
-      //         title: '提示',
-      //         message: '您有'+this.msgCount+"条消息通知",
-      //         position: 'top-right'
-      //       });
-      //     }
-
-      // });
+      getMsg().then(response => {
+        // 计算所有未读消息总数
+        let totalCount = 0;
+        response.counts.forEach(item => {
+          totalCount += item.total;
+        });
+
+        // 保存旧的消息计数
+        this.previousMsgCount = this.msgCount;
+        // 更新消息计数
+        this.msgCount = totalCount;
+
+        // 如果消息计数增加了,显示通知弹框
+        if (this.msgCount > this.previousMsgCount && this.msgCount > 0) {
+          this.$notify({
+            title: '提示',
+            message: '您有'+this.msgCount+"条消息通知",
+            position: 'top-right',
+            type: 'info'
+          });
+        }
+
+      }).catch(error => {
+        console.error("获取消息计数失败:", error);
+      });
     },
     openMsg(){
       console.log(11);
       this.msg.open=true;
+      // 每次打开消息界面时刷新数据
+      if (this.$refs.msg) {
+        this.$refs.msg.getMsg();
+      }
+      // 打开消息后重新获取消息计数来更新角标
+      this.getMsgCount();
     },
     toggleSideBar() {
       this.$store.dispatch('app/toggleSideBar')

+ 139 - 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,7 @@ export default {
     this.getConfigKey("sys:qw:config");
     this.getConfigKey("customer:config");
     this.getConfigKey("sys:AiKf:config");
+    this.getConfigKey("company:admin:show");
     this.getDicts("sys_company_status").then((response) => {
       this.statusOptions = response.data;
     });
@@ -379,13 +449,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 +493,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 +522,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 +529,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>

+ 11 - 9
src/views/company/companyDept/index.vue

@@ -48,6 +48,8 @@
       default-expand-all
       :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
     >
+
+      <el-table-column prop="deptId" label="部门编号" width="260"></el-table-column>
       <el-table-column prop="deptName" label="部门名称" width="260"></el-table-column>
       <el-table-column prop="orderNum" label="排序" width="200"></el-table-column>
       <el-table-column prop="status" label="状态" :formatter="statusFormat" width="100"></el-table-column>
@@ -58,17 +60,17 @@
       </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" 
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
             @click="handleUpdate(scope.row)"
             v-hasPermi="['company:dept:edit']"
           >修改</el-button>
-          <el-button 
-            size="mini" 
-            type="text" 
-            icon="el-icon-plus" 
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-plus"
             @click="handleAdd(scope.row)"
             v-hasPermi="['company:dept:add']"
           >新增</el-button>
@@ -319,4 +321,4 @@ export default {
     }
   }
 };
-</script>
+</script>

+ 489 - 158
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,12 +46,15 @@
           <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>
-          <!-- <el-col :span="1.5">
+          <el-col :span="1.5">
             <el-button  plain type="info" icon="el-icon-upload2" size="mini" @click="handleImport" v-hasPermi="['company:user:import']">导入</el-button>
-          </el-col> -->
+          </el-col>
           <el-col :span="1.5">
             <el-button  plain type="warning" icon="el-icon-download" size="mini" @click="handleExport" v-hasPermi="['company:user:export']">导出</el-button>
           </el-col>
@@ -80,7 +83,7 @@
               size="mini"
               :disabled="multiple"
               @click="handerCompanyUserAreaList"
-            >批量设置销售所属区域(原有的暂用)</el-button>
+            >批量设置销售所属区域</el-button>
           </el-col>
           <el-col :span="1.5">
             <el-button
@@ -100,6 +103,14 @@
               @click="handleAllowedAllRegister"
             >允许注册会员开关</el-button>
           </el-col>
+          <el-col :span="1.5">
+            <el-button
+              type="primary"
+              plain
+              size="mini"
+              @click="handleBindCompanyUserCode"
+            >生成注册/绑定销售二维码</el-button>
+          </el-col>
           <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
         </el-row>
 
@@ -110,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">
               <!-- 显示已上传的二维码 -->
@@ -151,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">
@@ -164,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
@@ -177,6 +203,30 @@
           </el-table-column>
           <el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
             <template slot-scope="scope">
+              <el-button
+                size="mini"
+                type="text"
+                icon="el-icon-connection"
+                plain
+                v-if="scope.row.doctorId!=null"
+                @click="handleUpdateDoctor(scope.row)"
+              >换绑医生</el-button>
+              <el-button
+                size="mini"
+                type="text"
+                plain
+                icon="el-icon-link"
+                v-else
+                @click="handleUpdateDoctor(scope.row)"
+              >绑定医生</el-button>
+              <el-button
+                size="mini"
+                type="text"
+                icon="el-icon-unlock"
+                plain
+                v-if="scope.row.doctorId!=null"
+                @click="handleUnBindUserId(scope.row)"
+              >解绑医生</el-button>
               <el-button
                 v-if="scope.row.qwStatus == 0"
                 size="mini"
@@ -205,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"
@@ -225,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="企微公司"  >
@@ -245,8 +314,6 @@
             />
           </el-select>
         </el-form-item>
-
-
       </el-form>
       <div slot="footer" class="dialog-footer">
         <el-button type="primary" @click="synSubmitForm">确 定</el-button>
@@ -255,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="企微公司"  >
@@ -406,29 +472,64 @@
       </div>
     </el-dialog>
 
-<!--    &lt;!&ndash; 用户导入对话框 &ndash;&gt;-->
-<!--    <el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>-->
-<!--      <el-upload ref="upload" :limit="1" accept=".xlsx, .xls" :headers="upload.headers" :action="upload.url + '?updateSupport=' + upload.updateSupport" :disabled="upload.isUploading" :on-progress="handleFileUploadProgress" :on-success="handleFileSuccess" :auto-upload="false" drag>-->
-<!--        <i class="el-icon-upload"></i>-->
-<!--        <div class="el-upload__text">-->
-<!--          将文件拖到此处,或-->
-<!--          <em>点击上传</em>-->
-<!--        </div>-->
-<!--        <div class="el-upload__tip" slot="tip">-->
-<!--          <el-checkbox v-model="upload.updateSupport" />是否更新已经存在的用户数据-->
-<!--          <el-link type="info" style="font-size:12px" @click="importTemplate">下载模板</el-link>-->
-<!--        </div>-->
-<!--        <div class="el-upload__tip" style="color:red" slot="tip">提示:仅允许导入“xls”或“xlsx”格式文件!</div>-->
-<!--      </el-upload>-->
-<!--      <div slot="footer" class="dialog-footer">-->
-<!--        <el-button type="primary" @click="submitFileForm">确 定</el-button>-->
-<!--        <el-button @click="upload.open = false">取 消</el-button>-->
-<!--      </div>-->
-<!--    </el-dialog>-->
-    <el-dialog :title="user.title" :visible.sync="user.open" width="1000px" append-to-body>
-      <selectUser ref="selectUser" @bindQwUser="bindQwUser"></selectUser>
+    <el-dialog :title="title" :visible.sync="bindCompanyOpen" width="700px" append-to-body>
+      <el-form ref="formBindCompany" :model="formBindCompany" :rules="bindCompanyRules" label-width="80px">
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="企微主体" prop="corpId">
+              <el-select v-model="formBindCompany.corpId" placeholder="企微主体" size="small">
+                <el-option
+                  v-for="dict in myQwCompanyList"
+                  :key="dict.dictValue"
+                  :label="dict.dictLabel"
+                  :value="dict.dictValue"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="归属部门" prop="deptId">
+              <treeselect v-model="formBindCompany.deptId" :options="deptOptions" :show-count="true" placeholder="请选择归属部门" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="角色" prop="roleIds">
+              <el-select v-model="formBindCompany.roleIds" multiple placeholder="请选择">
+                <el-option v-for="item in roleOptions" :key="item.roleId" :label="item.roleName" :value="item.roleId "></el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="销售区域" prop="addressId">
+              <el-select v-model="formBindCompany.addressId"  filterable placeholder="请选择所属销售的区域" style="width: 200px;">
+                <el-option
+                  v-for="item in citysAreaList"
+                  :key="item.cityId"
+                  :label="item.cityName"
+                  :value="item.cityId"
+                ></el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitBingCompanyForm">确 定</el-button>
+        <el-button @click="cancelBind">取 消</el-button>
+      </div>
     </el-dialog>
 
+    <!-- 修改 selectUser 组件的引用 -->
+    <el-dialog :title="user.title" :visible.sync="user.open" width="1000px" append-to-body>
+      <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;">
@@ -470,6 +571,48 @@
         <el-button @click="allowedAllRegisterOpen = false">取 消</el-button>
       </div>
     </el-dialog>
+
+    <el-dialog :title="bindCompanyUrl.title" v-if="bindCompanyUrl.open"  :visible.sync="bindCompanyUrl.open" width="450px"  append-to-body>
+      <div style="padding-bottom:15px;" >
+        <img :src="bindCompanyUrl.url" width="400px">
+      </div>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="downloadImage(bindCompanyUrl.url, bindCompanyUrl.name+'.png')">下载二维码</el-button>
+      </div>
+    </el-dialog>
+
+    <el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
+      <el-upload
+        ref="upload"
+        :limit="1"
+        accept=".xlsx, .xls"
+        :headers="upload.headers"
+        :action="upload.url + '?updateSupport=' + upload.updateSupport"
+        :disabled="upload.isUploading"
+        :on-progress="handleFileUploadProgress"
+        :on-success="handleFileSuccess"
+        :auto-upload="false"
+        drag
+      >
+        <i class="el-icon-upload"></i>
+        <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
+        <div class="el-upload__tip text-center" slot="tip">
+          <div class="el-upload__tip" slot="tip">
+          </div>
+          <span>仅允许导入xls、xlsx格式文件。</span>
+          <el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;" @click="importTemplate">下载模板</el-link>
+        </div>
+      </el-upload>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitFileForm">确 定</el-button>
+        <el-button @click="upload.open = false">取 消</el-button>
+      </div>
+    </el-dialog>
+
+    <el-dialog :title="doctor.title" :visible.sync="doctor.open" width="800px" append-to-body>
+      <selectDoctor ref="selectDoctor" @bindCompanyUserDoctorId="bindCompanyUserDoctorId"></selectDoctor>
+    </el-dialog>
+
   </div>
 </template>
 
@@ -488,7 +631,7 @@ import {
   generateSubDomain,
   setIsRegisterMember,
   updateCompanyUserAreaList,
-  isAllowedAllRegister
+  isAllowedAllRegister, unBindDoctorId, bindDoctorId,updateBatchUserRoles
 } from "@/api/company/companyUser";
 import { getToken } from "@/utils/auth";
 import { treeselect } from "@/api/company/companyDept";
@@ -500,11 +643,21 @@ import { getMyQwUserList,getMyQwCompanyList } from "@/api/qw/user";
 import  selectUser  from "@/views/company/components/selectQwUser.vue";
 import { getConfigByKey } from "@/api/company/companyConfig";
 import axios from "axios";
+import {addCodeUrl} from "../../../api/company/companyUser";
+import selectDoctor from "@/views/qw/user/selectDoctor.vue";
 export default {
   name: "User",
-  components: { Treeselect ,selectUser},
+  components: {selectDoctor, Treeselect ,selectUser},
   data() {
     return {
+      doctor: {
+        open: false,
+        title: '绑定医生'
+      },
+      doctorForm: {
+        userId: null,
+        doctorId: null
+      },
       uploadUrl: process.env.VUE_APP_BASE_API+"/company/user/common/uploadOSS",
       // 遮罩层
       loading: false,
@@ -538,6 +691,7 @@ export default {
       deptOptions: undefined,
       // 是否显示弹出层
       open: false,
+      bindCompanyOpen: false,
       qwOpen:false,
       // 部门名称
       deptName: undefined,
@@ -569,26 +723,35 @@ export default {
       citysAreaList:[],
       // 表单参数
       form: {},
+      formBindCompany: {},
+
+      bindCompanyUrl:{
+        open:false,
+        title:"绑定/注册销售二维码",
+        name:null,
+        url:null,
+      },
+
       form1: {},
       defaultProps: {
         children: "children",
         label: "label",
       },
-      // // 用户导入参数
-      // upload: {
-      //   // 是否显示弹出层(用户导入)
-      //   open: false,
-      //   // 弹出层标题(用户导入)
-      //   title: "",
-      //   // 是否禁用上传
-      //   isUploading: false,
-      //   // 是否更新已经存在的用户数据
-      //   updateSupport: 0,
-      //   // 设置上传的请求头部
-      //   headers: { Authorization: "Bearer " + getToken() },
-      //   // 上传的地址
-      //   url: process.env.VUE_APP_BASE_API + "/system/user/importData",
-      // },
+      // 用户导入参数
+      upload: {
+        // 是否显示弹出层(用户导入)
+        open: false,
+        // 弹出层标题(用户导入)
+        title: "",
+        // 是否禁用上传
+        isUploading: false,
+        // 是否更新已经存在的用户数据
+        updateSupport: 0,
+        // 设置上传的请求头部
+        headers: { Authorization: "Bearer " + getToken() },
+        // 上传的地址
+        url: process.env.VUE_APP_BASE_API + "/company/user/importCompanyUser",
+      },
       // 查询参数
       queryParams: {
         pageNum: 1,
@@ -646,11 +809,33 @@ export default {
           },
         ],
       },
+      // 表单校验
+      bindCompanyRules: {
+        deptId: [
+          { required: true, message: "归属部门不能为空", trigger: "blur" },
+        ],
+        addressId: [
+          { required: true, message: "销售所属区域不能为空", trigger: "blur" },
+        ],
+        roleIds: [
+          { required: true, message: "角色不能为空", trigger: "blur" },
+        ],
+        corpId: [
+          { required: true, message: "角色不能为空", trigger: "blur" },
+        ],
+      },
       // 是否允许注册会员开关
       allowedAllRegisterOpen: false,
       allowedAllRegisterForm: {
         status: true
       },
+      // 在 data() 中添加
+      batchRoleDialogVisible: false,
+      selectedRoleIds: [],
+      batchRoleForm: {
+        userIds: [],
+        roleIds: []
+      },
     };
   },
   watch: {
@@ -683,7 +868,7 @@ export default {
   },
   methods: {
     onDomainBlur() {
-      if (this.form.domain!=null){
+      if (this.form.domain != null) {
         let value = this.form.domain.trim();
 
         // 强制只保留第一个 http://
@@ -702,11 +887,98 @@ export default {
         // 正则校验最终格式,提醒用户
         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');
+          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;
@@ -715,14 +987,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);
@@ -756,13 +1026,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";
         });
     },
@@ -771,33 +1041,37 @@ export default {
       this.open = false;
       this.reset();
     },
+    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() {
@@ -820,6 +1094,15 @@ export default {
       };
       this.resetForm("form");
     },
+
+    resetBindCompany() {
+      this.formBindCompany = {
+        deptId: null,
+        addressId: null,
+        roleIds: [],
+      };
+      this.resetForm("formBindCompany");
+    },
     /** 搜索按钮操作 */
     handleQuery() {
       this.queryParams.page = 1;
@@ -850,40 +1133,18 @@ export default {
         console.log(" this.form1 ", this.form1)
       });
     },
-    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;
-      });
 
+    handleBindCompanyUserCode() {
+      this.reset();
+      this.getTreeselect();
+      getUser().then((response) => {
+        this.postOptions = response.posts;
+        this.roleOptions = response.roles;
+        this.bindCompanyOpen = true;
+        this.title = "创建 新增/绑定销售 的二维码";
+      });
     },
 
-    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();
@@ -925,18 +1186,19 @@ 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)) {
-        this.$message.error('请输入正确格式的域名,如:http://xxx.xxx.com');
-        return;
-      }
+      // const domainPattern = /^http:\/\/([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/;
+      // if (this.form.domain!=null && !domainPattern.test(this.form.domain)) {
+      //   this.$message.error('请输入正确格式的域名,如:http://xxx.xxx.com');
+      //   return;
+      // }
 
       this.$refs["form"].validate((valid) => {
         if (valid) {
@@ -960,12 +1222,45 @@ export default {
         }
       });
     },
+
+    downloadImage(imageSrc, fileName) {
+      const link = document.createElement('a');
+      link.href = imageSrc;
+      link.download = fileName || '绑定或新增销售.png';
+      document.body.appendChild(link);
+      link.click();
+      document.body.removeChild(link);
+    },
+
+    submitBingCompanyForm: function() {
+
+      this.$refs["formBindCompany"].validate((valid) => {
+        if (valid) {
+
+          let loadingRock = this.$loading({
+            lock: true,
+            text: '生成二维码中~~请不要刷新页面!!',
+            spinner: 'el-icon-loading',
+            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();
+          })
+        }
+      });
+    },
     /**
-    * 同步企业微信员工
-    */
+     * 同步企业微信员工
+     */
     synSubmitForm() {
-      this.synOpen=false;
-      this.loading=true;
+      this.synOpen = false;
+      this.loading = true;
       /*this.msgSuccess("");
 
       let loadingRock = this.$loading({
@@ -975,63 +1270,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()
       });
     },
@@ -1047,14 +1339,15 @@ export default {
           type: "warning",
         }
       )
-        .then(function () {
+        .then(function() {
           return delUser(userIds);
         })
         .then(() => {
           this.getList();
           this.msgSuccess("删除成功");
         })
-        .catch(function () {});
+        .catch(function() {
+        });
     },
     /** 导出按钮操作 */
     handleExport() {
@@ -1064,19 +1357,20 @@ export default {
         cancelButtonText: "取消",
         type: "warning",
       })
-        .then(function () {
+        .then(function() {
           return exportUser(queryParams);
         })
         .then((response) => {
           this.download(response.msg);
         })
-        .catch(function () {});
+        .catch(function() {
+        });
+    },
+    /** 导入按钮操作 */
+    handleImport() {
+      this.upload.title = "用户导入";
+      this.upload.open = true;
     },
-    // /** 导入按钮操作 */
-    // handleImport() {
-    //   this.upload.title = "用户导入";
-    //   this.upload.open = true;
-    // },
     /** 下载模板操作 */
     importTemplate() {
       importTemplate().then((response) => {
@@ -1099,15 +1393,15 @@ 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
       });
     },
     /** 设置单独注册会员按钮操作 */
@@ -1117,7 +1411,7 @@ export default {
     },
     /** 提交设置单独注册会员 */
     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;
@@ -1132,8 +1426,8 @@ export default {
       // 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;
@@ -1179,7 +1473,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', '');
@@ -1221,7 +1515,7 @@ export default {
     },
     requestUpload() {
     },
-    beforeUpload(){
+    beforeUpload() {
       console.log(file.type)
       const isPic =
         file.type === 'image/jpeg' ||
@@ -1239,6 +1533,43 @@ export default {
       }
       return isPic && isLt2M
     },
+    handleUpdateDoctor(row) {
+      this.doctor.title = "绑定医生"
+      this.doctor.open = true;
+      this.doctorForm.userId = row.userId;
+    },
+    bindCompanyUserDoctorId(row) {
+      console.log(row)
+      this.doctorForm.doctorId = row;
+      bindDoctorId(this.doctorForm).then(res => {
+        if (res.code == 200) {
+          this.$message.success('绑定成功')
+        } else {
+          this.$message.error('绑定失败:', res.msg)
+        }
+        this.getList()
+        this.doctor.open = false;
+      })
+    },
+    handleUnBindUserId(val) {
+      this.$confirm(
+        '确认解绑医生:<span style="color: green;">' + val.nickName + '' +
+        '</span> 的医生?',
+        {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+          dangerouslyUseHTMLString: true // 允许使用 HTML 字符串
+        }
+      ).then(() => {
+        return unBindDoctorId(val.userId);
+      }).then(response => {
+        this.getList();
+        this.msgSuccess("解绑成功");
+      }).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>-->

+ 93 - 0
src/views/components/healthTongueDetails.vue

@@ -0,0 +1,93 @@
+<template>
+    <div style="background-color: #f0f2f5; padding-bottom: 20px; min-height: 100%; " >
+      <div style="padding: 20px; background-color: #fff;">
+       舌苔检测详情
+      </div>
+<div class="contentx" >
+  <div class="desct"> 检测信息</div>
+ <el-descriptions title="" :column="3" border>
+            <el-descriptions-item label="姓名" ><span v-if="item!=null">{{item.name}}</span></el-descriptions-item>
+            <el-descriptions-item label="年龄" ><span v-if="item!=null">{{item.age}}</span></el-descriptions-item>
+            <el-descriptions-item label="性别" ><span v-if="item!=null"><dict-tag :options="sexOptions" :value="item.sex"/></span></el-descriptions-item>
+            <el-descriptions-item label="体质" ><span v-if="item!=null">{{item.typeName}}</span></el-descriptions-item>
+            <el-descriptions-item label="剥苔" ><span v-if="item!=null"><dict-tag :options="orOptions" :value="item.botai"/></span></el-descriptions-item>
+            <el-descriptions-item label="剥苔详细" ><span v-if="item!=null">{{item.botaiDesc}}</span></el-descriptions-item>
+            <el-descriptions-item label="齿痕" ><span v-if="item!=null"><dict-tag :options="orOptions" :value="item.chihen"/></span></el-descriptions-item>
+            <el-descriptions-item label="齿痕详细" ><span v-if="item!=null">{{item.chihenDesc}}</span></el-descriptions-item>
+            <el-descriptions-item label="裂纹" ><span v-if="item!=null"><dict-tag :options="orOptions" :value="item.liewen"/></span></el-descriptions-item>
+            <el-descriptions-item label="裂纹详细" ><span v-if="item!=null">{{item.liewenDesc}}</span></el-descriptions-item>
+            <el-descriptions-item label="舌脉" ><span v-if="item!=null">{{item.shemianName}}</span></el-descriptions-item>
+            <el-descriptions-item label="舌脉详细" ><span v-if="item!=null">{{item.shemianDesc}}</span></el-descriptions-item>
+            <el-descriptions-item label="苔色" ><span v-if="item!=null">{{item.taiseName}}</span></el-descriptions-item>
+            <el-descriptions-item label="苔色详细" ><span v-if="item!=null"> {{item.taiseDesc}}</span></el-descriptions-item>
+            <el-descriptions-item label="舌苔图片" ><span v-if="item!=null"> <img :src="item.tongueUrl" style="max-width: 150px;"></span></el-descriptions-item>
+  </el-descriptions>
+
+ </div>
+ <div class="contentx" v-if="conditioningPlanJson!=null">
+        <div class="desct"> 体质解析</div>
+        <el-descriptions title="" :column="1" border >
+            <el-descriptions-item :label="form.name" v-for=" form in conditioningPlanJson" ><span >{{form.value}}</span></el-descriptions-item>
+        </el-descriptions>
+  </div>
+    </div>
+</template>
+
+<script>
+import { listHealthTongue, getHealthTongue, delHealthTongue, addHealthTongue, updateHealthTongue, exportHealthTongue } from "@/api/store/healthTongue";
+
+  export default {
+    name: "healthTongueDetail",
+    data() {
+      return {
+        item:null,
+        orOptions:[],
+        sexOptions:[],
+        conditioningPlanJson:null,
+      }
+    },
+    created() {
+      this.getDicts("sys_company_or").then(response => {
+        this.orOptions = response.data;
+      });
+      this.getDicts("sys_sex").then(response => {
+        this.sexOptions = response.data;
+      });
+    },
+    methods: {
+      getDetails(orderId) {
+        this.item=null;
+        getHealthTongue(orderId).then(response => {
+          this.item = response.data;
+          if(response.data.typeJson!=null){
+            this.conditioningPlanJson=JSON.parse(response.data.typeJson)
+          };
+        });
+      },
+    }
+  }
+</script>
+<style>
+
+  .contentx{
+      height: 100%;
+      background-color: #fff;
+      padding: 0px 20px 20px;
+
+
+      margin: 20px;
+  }
+  .el-descriptions-item__label.is-bordered-label{
+    font-weight: normal;
+  }
+  .el-descriptions-item__content {
+    max-width: 150px;
+    min-width: 100px;
+  }
+  .desct{
+      padding-top: 20px;
+      padding-bottom: 20px;
+      color: #524b4a;
+      font-weight: bold;
+    }
+</style>

+ 305 - 4
src/views/course/courseFinishTemp/index.vue

@@ -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,9 @@
         <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>
 
@@ -430,12 +597,21 @@ 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",
@@ -493,6 +669,7 @@ export default {
       chatSetting: [],
       // 表单参数
       form: {
+        companyUserIds: [],
         setting: null,
         chatSetting: null,
         videoIdSet: null,
@@ -531,6 +708,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 => {
@@ -673,6 +945,12 @@ export default {
       this.open = true;
       this.title = "添加完课模板";
     },
+    //选择视频号
+    hanldeSelectVideoNum(content, index) {
+      this.videoNumOptions.content = content;
+      this.videoNumOptions.contentIndex = index;
+      this.videoNumOptions.open = true;
+    },
 
     /**
      * 查看完课模板
@@ -718,11 +996,22 @@ 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;
       });
     },
     /** 提交按钮 */
@@ -736,7 +1025,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) {

+ 295 - 4
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;
@@ -514,6 +768,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 +819,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) {

+ 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) {

+ 307 - 31
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"
@@ -28,16 +38,29 @@
           @keyup.enter.native="handleQuery"
         />
       </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">
           <el-option
             v-for="item in companyUserOptions"
-            :key="item.value"
-            :label="item.label"
-            :value="item.value">
+            :key="item.dictValue"
+            :label="item.dictLabel"
+            :value="item.dictValue">
+          </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"
+                   :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>
@@ -61,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"
@@ -89,7 +132,7 @@
 <!--        />-->
 <!--      </el-form-item>-->
       <!-- 营期时间 -->
-      <el-form-item label="营期时间" prop="scheduleTime">
+      <!-- <el-form-item label="营期时间" prop="scheduleTime">
         <el-input
           v-model="scheduleTimeText"
           placeholder="请选择营期时间"
@@ -103,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="请选择创建时间"
@@ -119,9 +174,21 @@
           @change="createChange"
           :key="createCalendarKey"
         />
+      </el-form-item> -->
+        <el-form-item label="创建时间" prop="createTime">
+        <el-date-picker
+          v-model="createTimeText"
+          type="daterange"
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          value-format="yyyy-MM-dd"
+          style="width: 240px"
+           @change="createChange"
+        />
       </el-form-item>
       <!-- 最新更新时间 -->
-      <el-form-item label="最新更新时间" prop="updateTime">
+      <!-- <el-form-item label="最新更新时间" prop="updateTime">
         <el-input
           v-model="updateTimeText"
           placeholder="请选择更新时间"
@@ -135,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="请选择进线时间"
@@ -151,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>
@@ -352,6 +460,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)
 
 
@@ -359,21 +469,34 @@ export default {
   name: "CourseWatchLog",
   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,
@@ -429,9 +552,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);
 
           // 获取明天的日期(不包括今天)
@@ -466,8 +589,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: {},
@@ -479,10 +606,11 @@ export default {
         name: undefined,
         hasNextPage: false,
         pageNum: 1,
-        pageSize: 10
+        pageSize: 200
       },
       companyUserOptionsLoading: false,
       companyUserOptions: [],
+      companyUserOptionsByAll: [],
     };
   },
   created() {
@@ -493,25 +621,33 @@ export default {
     this.getDicts("sys_course_watch_log_type").then(response => {
       this.logTypeOptions = response.data;
     });
+    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 '';
@@ -593,10 +729,19 @@ 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;
+
+      // 重置SOP搜索
+      this.handleSopClear();
+      // 统一重置日历组件
+      this.resetCalendars();
+
       this.handleQuery();
     },
     // 多选框选中数据
@@ -709,7 +854,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;
@@ -720,11 +865,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;
       }
@@ -732,9 +877,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 = createTime[0] || null;
+        this.queryParams.eTime = createTime[1] || null;
       } else {
         this.createTimeText = '';
         this.queryParams.sTime = null;
@@ -746,8 +891,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;
@@ -774,22 +919,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 参数
@@ -808,12 +1018,23 @@ 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){
+
+        getCompanyUserListLikeName(this.companyUserOptionsParams).then(response => {
+          this.companyUserOptionsByAll = [...this.companyUserOptions, ...response.data.list]
+          this.companyUserOptionsParams.hasNextPage = response.data.hasNextPage
+          this.companyUserOptionsLoading = false;
+        });
+      }else {
+        getCompanyUserListLikeName(this.companyUserOptionsParams).then(response => {
+          this.companyUserOptions = [...this.companyUserOptions, ...response.data.list]
+          this.companyUserOptionsParams.hasNextPage = response.data.hasNextPage
+          this.companyUserOptionsLoading = false;
+        });
+      }
+
     },
     /**
      * 加载更多员工选项
@@ -826,6 +1047,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>

+ 495 - 42
src/views/course/courseWatchLog/index.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="nickName" v-if="queryParams.sendType == 1">
         <el-input
           v-model="queryParams.nickName"
@@ -38,15 +48,6 @@
           @keyup.enter.native="handleQuery"
         />
       </el-form-item>
-      <el-form-item label="所属销售" prop="companyUserName" v-if="queryParams.sendType == 1">
-        <el-input
-          v-model="queryParams.companyUserName"
-          placeholder="请输入所属销售"
-          clearable
-          size="small"
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
       <el-form-item label="课程" prop="courseId">
         <el-select filterable  v-model="queryParams.courseId" placeholder="请选择课程"  clearable size="small" @change="courseChange(queryParams.courseId)">
           <el-option
@@ -67,8 +68,74 @@
           />
         </el-select>
       </el-form-item>
+      <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"
+                   @change="handleCompanyUserChange"
+                   :loading="companyUserOptionsLoading"
+                   @visible-change="handleCompanyUserDropdownVisible"
+        >
+          <el-option
+            v-for="item in companyUserOptions"
+            :key="item.dictValue"
+            :label="item.dictLabel"
+            :value="item.dictValue">
+          </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">
+        <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
+        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="营期时间" prop="scheduleTime">
+      <!-- <el-form-item label="营期时间" prop="scheduleTime">
         <el-input
           v-model="scheduleTimeText"
           placeholder="请选择营期时间"
@@ -82,9 +149,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="请选择创建时间"
@@ -98,9 +177,22 @@
           @change="createChange"
           :key="createCalendarKey"
         />
+      </el-form-item> -->
+
+      <el-form-item label="创建时间" prop="createTime">
+        <el-date-picker
+          v-model="createTimeText"
+          type="daterange"
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          value-format="yyyy-MM-dd"
+          style="width: 240px"
+           @change="createChange"
+        />
       </el-form-item>
       <!-- 最新更新时间 -->
-      <el-form-item label="最新更新时间" prop="updateTime">
+      <!-- <el-form-item label="最新更新时间" prop="updateTime">
         <el-input
           v-model="updateTimeText"
           placeholder="请选择更新时间"
@@ -114,9 +206,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="请选择进线时间"
@@ -130,12 +234,42 @@
           @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="isVip">
+
+
+      <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="请选择是否为会员"
+          placeholder="请选择是否注册"
           clearable size="small">
           <el-option
             v-for="dict in isVipList"
@@ -163,7 +297,7 @@
           @click="handleExport"
           v-hasPermi="['course:courseWatchLog:export']"
         >导出</el-button>
-        <el-col :span="1.5">
+        <el-col :span="1.5" v-if="queryParams.sendType == 2">
           <el-button
             type="primary"
             plain
@@ -172,7 +306,7 @@
             v-hasPermi="['qw:externalContact:addTag']"
           >批量添加标签</el-button>
         </el-col>
-        <el-col :span="1.5">
+        <el-col :span="1.5" v-if="queryParams.sendType == 2" >
           <el-button
             type="primary"
             plain
@@ -220,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">
@@ -458,34 +593,45 @@
 
 <script>
 import { listCourseWatchLog, getCourseWatchLog, delCourseWatchLog, addCourseWatchLog, updateCourseWatchLog, exportCourseWatchLog } from "@/api/course/courseWatchLog";
-import {courseList, myListCourseRedPacketLog, videoList} from '@/api/course/courseRedPacketLog'
-import {myListLogs} from "@/api/course/courseAnswerlogs";
+import {courseList, listCourseRedPacketLog, videoList} from '@/api/course/courseRedPacketLog'
+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,
@@ -500,9 +646,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);
 
           // 获取明天的日期(不包括今天)
@@ -516,6 +662,7 @@ export default {
       courseLists:[],
       videoList:[],
       logTypeOptions:[],
+      projectOptions:[],
       sendTypeOptions:[{
         dictLabel:"会员",dictValue:'1'
       },
@@ -592,10 +739,15 @@ export default {
         { dictLabel: '是', dictValue: 1 },
         { dictLabel: '否', dictValue: 0 }
       ],
+
+      // SOP搜索相关
+      sopSearchText: '', // SOP搜索框显示的文本
+      selectedSopId: null, // 选中的SOP ID
       // 查询参数
       queryParams: {
         pageNum: 1,
         pageSize: 10,
+        project: null,
         userId: null,
         nickName: null,
         videoId: null,
@@ -604,6 +756,7 @@ export default {
         externalUserName:null,
         duration: null,
         qwUserId: null,
+        qwUserName: null,
         companyUserId: null,
         companyId: null,
         courseId: null,
@@ -613,16 +766,39 @@ export default {
         upETime:null,
         qecSTime:null,
         qecETime:null,
+        periodSTime:null,
+        periodETime:null,
         scheduleStartTime: null,
         scheduleEndTime: null,
         sendType:process.env.VUE_APP_COURSE_DEFAULT,
         isVip: null,
+        sopId: null, // sopId
       },
       // 表单参数
       form: {},
       // 表单校验
       rules: {
       },
+      // 员工选项列表
+      companyUserOptionsParams: {
+        name: undefined,
+        hasNextPage: false,
+        pageNum: 1,
+        pageSize: 10
+      },
+      companyUserOptions: [],
+      companyUserOptionsByAll: [],
+      companyUserOptionsLoading: false,
+      // 企微信息
+      qwUserOptions: [],
+      // 员工选项列表
+      qwUserOptionsParams: {
+        name: undefined,
+        hasNextPage: false,
+        pageNum: 1,
+        pageSize: 10
+      },
+      qwUserOptionsLoading: false,
     };
   },
   created() {
@@ -630,28 +806,175 @@ 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: {
+    /**
+     * 处理所属销售下拉框显示状态变化
+     */
+    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;
+      }
+    },
+    /**
+     * 根据名称模糊查询用户列表
+     * @param query 参数
+     */
+    loadCompanyUserOptions(query) {
+      this.companyUserOptions = [];
+
+      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()
+    },
+    /**
+     * 加载更多员工选项
+     */
+    loadMoreCompanyUserOptions() {
+      if (!this.companyUserOptionsParams.hasNextPage) {
+        return;
+      }
+
+      this.companyUserOptionsParams.pageNum += 1
+      this.getCompanyUserListLikeName()
+    },
+
+    /**
+     * 获取员工列表
+     */
+    getCompanyUserListLikeName(isAll) {
+
+      if (isAll){
+
+        getCompanyUserListLikeName(this.companyUserOptionsParams).then(response => {
+          this.companyUserOptionsByAll = [...this.companyUserOptions, ...response.data.list]
+          this.companyUserOptionsParams.hasNextPage = response.data.hasNextPage
+          this.companyUserOptionsLoading = false;
+        });
+      }else {
+        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.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 '';
@@ -671,9 +994,9 @@ 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;
         console.log(this.queryParams.scheduleStartTime)
         console.log(this.queryParams.scheduleEndTime)
       } else {
@@ -685,9 +1008,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 = createTime[0] || null;
+        this.queryParams.eTime = createTime[1] || null;
       } else {
         this.createTimeText = '';
         this.queryParams.sTime = null;
@@ -698,9 +1021,9 @@ 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.updateTimeText = this.formatDateRange(updateTime);
+        this.queryParams.upSTime = updateTime[0] || null;
+        this.queryParams.upETime = updateTime[1] || null;
       } else {
         this.updateTimeText = '';
         this.queryParams.upSTime = null;
@@ -727,16 +1050,18 @@ 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;
+        console.log(this.queryParams.qecSTime);
+        console.log(this.queryParams.qecETime);
       } else {
         this.qecCreateTimeText = '';
         this.queryParams.qecSTime = null;
@@ -744,6 +1069,69 @@ export default {
       }
     },
 
+    //营期课程时间
+    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){
@@ -811,13 +1199,20 @@ 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.scheduleStartTime = null;
       this.queryParams.scheduleEndTime = null;
+      this.queryParams.sopId = null; // 重置SOP ID
+
+      // 重置SOP搜索
+      this.handleSopClear();
 
       // 统一重置日历组件
       this.resetCalendars();
@@ -903,7 +1298,7 @@ export default {
     },
     answerLogList() {
       this.loadingAnswerLog = true;
-      myListLogs(this.answerLogQueryParams).then(e => {
+      listLogs(this.answerLogQueryParams).then(e => {
         this.answerLogsList = e.rows;
         this.answerLogTotal = e.total;
         this.loadingAnswerLog = false;
@@ -917,8 +1312,8 @@ export default {
     },
     redLogList() {
       this.loadingRedLog = true;
-      console.info(this.redLogQueryParams)
-      myListCourseRedPacketLog(this.redLogQueryParams).then(e => {
+      console.info("-----index",this.redLogQueryParams)
+      listCourseRedPacketLog(this.redLogQueryParams).then(e => {
         this.redLogsList = e.rows;
         this.redLogTotal = e.total;
         this.loadingRedLog = false;
@@ -1125,6 +1520,53 @@ export default {
       };
     },
 
+    /**
+     * 异步查询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([]);
+      });
+    },
+
+    /**
+     * 选择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 = '';
+    },
+
   }
 };
 </script>
@@ -1171,6 +1613,17 @@ export default {
   margin-left: 10px;
 }
 
+/* SOP搜索框样式 */
+.sop-item {
+  display: flex;
+  align-items: center;
+}
+
+.sop-name {
+  font-size: 14px;
+  color: #606266;
+}
+
 
 
 .button-new-tag {

+ 4 - 4
src/views/course/courseWatchLog/myCourseWatchLog.vue

@@ -92,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
@@ -103,7 +103,7 @@
           v-hasPermi="['course:courseWatchLog:myExport']"
         >导出</el-button>
       </el-col>
-      <el-col :span="1.5">
+      <el-col :span="1.5" v-if="queryParams.sendType == 2">
         <el-button
           type="primary"
           plain
@@ -112,7 +112,7 @@
           v-hasPermi="['qw:externalContact:addTag']"
         >批量添加标签</el-button>
       </el-col>
-      <el-col :span="1.5">
+      <el-col :span="1.5" v-if="queryParams.sendType == 2">
         <el-button
           type="primary"
           plain
@@ -382,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;
     });
 

+ 23 - 3
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" />
@@ -65,7 +80,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 +293,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;

+ 343 - 43
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()">
@@ -79,8 +89,29 @@
           />
         </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="营期时间" prop="scheduleTime">
+      <!-- <el-form-item label="营期时间" prop="scheduleTime">
         <el-input
           v-model="scheduleTimeText"
           placeholder="请选择营期时间"
@@ -94,9 +125,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="请选择创建时间"
@@ -110,9 +153,21 @@
           @change="createChange"
           :key="createCalendarKey"
         />
+      </el-form-item> -->
+        <el-form-item label="创建时间" prop="createTime">
+        <el-date-picker
+          v-model="createTimeText"
+          type="daterange"
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          value-format="yyyy-MM-dd"
+          style="width: 240px"
+           @change="createChange"
+        />
       </el-form-item>
       <!-- 最新更新时间 -->
-      <el-form-item label="最新更新时间" prop="updateTime">
+      <!-- <el-form-item label="最新更新时间" prop="updateTime">
         <el-input
           v-model="updateTimeText"
           placeholder="请选择更新时间"
@@ -126,9 +181,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="请选择进线时间"
@@ -142,12 +209,41 @@
           @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 label="是否为会员" prop="isVip">
+
+      <el-form-item label="是否注册" prop="isVip">
         <el-select
           filterable
           v-model="queryParams.isVip"
-          placeholder="请选择是否为会员"
+          placeholder="请选择是否注册"
           clearable size="small">
           <el-option
             v-for="dict in isVipList"
@@ -176,7 +272,7 @@
           v-hasPermi="['course:courseWatchLog:myExport']"
         >导出
         </el-button>
-        <el-col :span="1.5">
+        <el-col :span="1.5" v-if="queryParams.sendType == 2">
           <el-button
             type="primary"
             plain
@@ -185,7 +281,7 @@
             v-hasPermi="['qw:externalContact:addTag']"
           >批量添加标签</el-button>
         </el-col>
-        <el-col :span="1.5">
+        <el-col :span="1.5" v-if="queryParams.sendType == 2">
           <el-button
             type="primary"
             plain
@@ -202,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;      
+      &lt;!&ndash;
       <el-table-column label="会员ID" align="center" prop="userId" v-if="queryParams.sendType == 1"/>
       &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" 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 }}
@@ -243,7 +359,13 @@
 <!--      <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" />
@@ -477,27 +599,32 @@ 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,
@@ -512,9 +639,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);
 
           // 获取明天的日期(不包括今天)
@@ -556,11 +683,12 @@ export default {
         pageNum: 1,
         pageSize: 10,
       },
+      projectOptions:[],
       sendTypeOptions:[{
-        dictLabel:"会员",dictValue:'1'
+        dictLabel:'会员',dictValue:'1'
       },
         {
-          dictLabel:"企微",dictValue:'2'
+          dictLabel:'企微',dictValue:'2'
         }
       ],
 
@@ -606,9 +734,14 @@ export default {
         { dictLabel: '是', dictValue: 1 },
         { dictLabel: '否', dictValue: 0 }
       ],
+
+      // SOP搜索相关
+      sopSearchText: '', // SOP搜索框显示的文本
+      selectedSopId: null, // 选中的SOP ID
       // 查询参数
       queryParams: {
         pageNum: 1,
+        project: null,
         pageSize: 10,
         userId: null,
         videoId: null,
@@ -627,10 +760,13 @@ export default {
         upETime:null,
         qecSTime:null,
         qecETime:null,
+        periodSTime:null,
+        periodETime:null,
         scheduleStartTime: null,
         scheduleEndTime: null,
         sendType:process.env.VUE_APP_COURSE_DEFAULT,
         isVip: null,
+        sopId: null, // sopId
       },
       // 表单参数
       form: {},
@@ -653,25 +789,57 @@ export default {
     getMyQwUserList().then(response => {
       this.myQwUserList = response.data;
     });
+    this.getDicts("sys_course_project").then(response => {
+      this.projectOptions = response.data;
+    });
   },
   methods: {
+
+    handleSendTypeChange() {
+      // 重置相关参数
+      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 '';
@@ -710,13 +878,13 @@ 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;
         console.log(this.queryParams.scheduleStartTime)
         console.log(this.queryParams.scheduleEndTime)
       } else {
-        this.scheduleTimeText = '';
+        this.scheduleTimeText = [];
         this.queryParams.scheduleStartTime = null;
         this.queryParams.scheduleEndTime = null;
       }
@@ -724,11 +892,11 @@ 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 = createTime[0] || null;
+        this.queryParams.eTime = createTime[1] || null;
       } else {
-        this.createTimeText = '';
+        this.createTimeText = [];
         this.queryParams.sTime = null;
         this.queryParams.eTime = null;
       }
@@ -737,11 +905,11 @@ 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.updateTimeText = this.formatDateRange(updateTime);
+        this.queryParams.upSTime = updateTime[0] || null;
+        this.queryParams.upETime = updateTime[1] || null;
       } else {
-        this.updateTimeText = '';
+        this.updateTimeText = [];
         this.queryParams.upSTime = null;
         this.queryParams.upETime = null;
       }
@@ -766,7 +934,7 @@ export default {
           this.$message.error('进线时间选择范围不能超过7天');
           // 清空选择
           this.qecCreateTime = [];
-          this.qecCreateTimeText = '';
+          this.qecCreateTimeText = [];
           this.queryParams.qecSTime = null;
           this.queryParams.qecETime = null;
           this.qecCalendarKey++;
@@ -774,14 +942,79 @@ export default {
         }
 
         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.queryParams.qecSTime = qecCreateTime[0] || null;
+        this.queryParams.qecETime = qecCreateTime[1] || null;
       } else {
-        this.qecCreateTimeText = '';
+        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;
@@ -839,12 +1072,21 @@ 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
+
+      // 重置SOP搜索
+      this.handleSopClear();
       // 统一重置日历组件
       this.resetCalendars();
 
@@ -943,7 +1185,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;
@@ -1145,6 +1387,53 @@ export default {
         tagIds:[]
       };
     },
+
+    /**
+     * 异步查询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([]);
+      });
+    },
+
+    /**
+     * 选择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 = '';
+    },
   }
 };
 </script>
@@ -1220,4 +1509,15 @@ export default {
   background: rgba(0, 0, 0, 0.2);
   border-radius: 3px;
 }
+
+/* SOP搜索框样式 */
+.sop-item {
+  display: flex;
+  align-items: center;
+}
+
+.sop-name {
+  font-size: 14px;
+  color: #606266;
+}
 </style>

+ 199 - 0
src/views/hisStore/components/OrderSummaryTable.vue

@@ -0,0 +1,199 @@
+<!-- src/components/OrderSummaryTable.vue -->
+<template>
+  <div class="table-container">
+    <h3 class="table-title">{{ title }}</h3>
+    <el-table
+      :data="tableData"
+      border
+      stripe
+      style="width: 100%; margin-top: 20px;"
+      :row-class-name="tableRowClassName"
+      :span-method="objectSpanMethod"
+      show-summary
+      :summary-method="getSummaries"
+      >
+      <el-table-column prop="name" :label="nameLable" width="150" class-name="sticky-column" align="center">
+        <template slot-scope="scope">
+          <span :class="{'group-name': scope.row.isGroup}">{{ scope.row.name }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column prop="totalCalls" label="总单数" width="80" align="center"></el-table-column>
+      <el-table-column prop="totalAmount" label="总金额" width="100" align="center">
+        <template slot-scope="scope">
+          {{ scope.row.totalAmount ? scope.row.totalAmount.toFixed(2) : '0.00' }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="validAmount" label="成单金额" width="100" align="center">
+        <template slot-scope="scope">
+          {{ scope.row.validAmount ? scope.row.validAmount.toFixed(2) : '0.00' }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="waitingOrders" label="待审数" width="80" align="center"></el-table-column>
+      <el-table-column prop="waitingAmount" label="待审金额" width="100" align="center">
+        <template slot-scope="scope">
+          {{ scope.row.waitingAmount ? scope.row.waitingAmount.toFixed(2) : '0.00' }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="unPassedOrders" label="未过数" width="80" align="center"></el-table-column>
+      <el-table-column prop="unPassedAmount" label="未过金额" width="100" align="center">
+        <template slot-scope="scope">
+          {{ scope.row.unpassedAmount ? scope.row.unpassedAmount.toFixed(2) : '0.00' }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="cancelOrders" label="取消数" width="80" align="center"></el-table-column>
+      <el-table-column prop="cancelAmount" label="取消金额" width="100" align="center">
+        <template slot-scope="scope">
+          {{ scope.row.cancelAmount ? scope.row.cancelAmount.toFixed(2) : '0.00' }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="completeOrders" label="成交数" width="120" align="center"></el-table-column>
+      <el-table-column prop="completeAmount" label="成交金额" width="130" align="center">
+        <template slot-scope="scope">
+          {{ scope.row.completeAmount ? scope.row.completeAmount.toFixed(2) : '0.00' }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="unshippedOrders" label="成交未发货数" width="120" align="center"></el-table-column>
+      <el-table-column prop="unshippedAmount" label="成交未发货金额" width="130" align="center">
+        <template slot-scope="scope">
+          {{ scope.row.unshippedAmount ? scope.row.unshippedAmount.toFixed(2) : '0.00' }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="shippedOrders" label="发货数" width="80" align="center"></el-table-column>
+      <el-table-column prop="shippedAmount" label="发货金额" width="100" align="center">
+        <template slot-scope="scope">
+          {{ scope.row.shippedAmount ? scope.row.shippedAmount.toFixed(2) : '0.00' }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="transitOrders" label="在途数" width="80" align="center"></el-table-column>
+      <el-table-column prop="transitAmount" label="在途金额" width="100" align="center">
+        <template slot-scope="scope">
+          {{ scope.row.transitAmount ? scope.row.transitAmount.toFixed(2) : '0.00' }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="receivedOrders" label="签收数" width="80" align="center"></el-table-column>
+      <el-table-column prop="receivedAmount" label="签收金额" width="100" align="center">
+        <template slot-scope="scope">
+          {{ scope.row.receivedAmount ? scope.row.receivedAmount.toFixed(2) : '0.00' }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="returnOrders" label="退货数" width="80" align="center"></el-table-column>
+      <el-table-column prop="returnAmount" label="退货金额" width="100" align="center">
+        <template slot-scope="scope">
+          {{ scope.row.returnAmount ? scope.row.returnAmount.toFixed(2) : '0.00' }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="paybackOrders" label="回款数" width="80" align="center"></el-table-column>
+      <el-table-column prop="paybackAmount" label="回款金额" width="100" align="center">
+        <template slot-scope="scope">
+          {{ scope.row.paybackAmount ? scope.row.paybackAmount.toFixed(2) : '0.00' }}
+        </template>
+      </el-table-column>
+    </el-table>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'OrderSummaryTable',
+  props: {
+    title: {
+      type: String,
+      required: true
+    },
+    tableData: {
+      type: Array,
+      default: () => []
+    },
+    nameLable: {  // 新增 prop
+      type: String,
+      default: '姓名/工号'
+    }
+  },
+  watch: {
+    tableData: {
+      handler(newVal) {
+        console.log('接收到的tableData:', newVal);
+        console.log('tableData长度:', newVal ? newVal.length : 0);
+      },
+      immediate: true
+    }
+  },
+  mounted() {
+    console.log('组件挂载时的tableData:', this.tableData);
+    console.log('tableData长度:', this.tableData ? this.tableData.length : 0);
+  },
+  methods: {
+    // 表格行样式
+    tableRowClassName({ row, rowIndex }) {
+      if (row.isGroup) {
+        return 'group-row';
+      }
+      return '';
+    },
+    // 表格合并方法
+    objectSpanMethod({ row, column, rowIndex, columnIndex }) {
+      return {
+        rowspan: 1,
+        colspan: 1
+      };
+    },
+    // 合计方法
+    getSummaries(param) {
+      const { columns, data } = param;
+      const sums = [];
+      const sumFields = ['totalCalls', 'totalAmount', 'validAmount', 'waitingOrders', 'waitingAmount',
+        'unPassedOrders', 'unPassedAmount', 'cancelOrders', 'cancelAmount', 'completeOrders',
+        'completeAmount', 'unshippedOrders', 'unshippedAmount', 'shippedOrders', 'shippedAmount',
+        'transitOrders', 'transitAmount', 'receivedOrders', 'receivedAmount', 'returnOrders',
+        'returnAmount', 'paybackOrders', 'paybackAmount'];
+      
+      columns.forEach((column, index) => {
+        if (index === 0) {
+          sums[index] = '合计';
+          return;
+        }
+        
+        if (sumFields.includes(column.property)) {
+          const values = data.map(item => Number(item[column.property]));
+          if (!values.every(value => Number.isNaN(value))) {
+            const sum = values.reduce((prev, curr) => {
+              const value = Number(curr);
+              if (!Number.isNaN(value)) {
+                return prev + value;
+              } else {
+                return prev;
+              }
+            }, 0);
+            
+            // 对于金额字段保留两位小数
+            if (column.property && column.property.includes('Amount')) {
+              sums[index] = sum.toFixed(2);
+            } else {
+              sums[index] = sum;
+            }
+          } else {
+            sums[index] = '0';
+          }
+        } else {
+          sums[index] = '';
+        }
+      });
+
+      return sums;
+    }
+  } 
+}
+</script>
+
+<style scoped>
+.table-container {
+  margin-top: 30px;
+}
+
+.table-title {
+  font-size: 16px;
+  font-weight: bold;
+  color: #333;
+  margin-bottom: 10px;
+}
+</style>

+ 318 - 0
src/views/hisStore/components/integralOrderDetails.vue

@@ -0,0 +1,318 @@
+<template>
+    <div style="background-color: #f0f2f5; padding-bottom: 20px; min-height: 100%; " >
+      <div style="padding: 20px; background-color: #fff;">
+         积分订单详情
+      </div>
+<div class="contentx" v-if="item!=null">
+        <div class="desct"> 积分订单信息</div>
+        <div class="order-status" v-if="item!=null && item.status != 5" >
+                  <el-steps  :active="item.status==4?0:item.status" align-center finish-status="success">
+                    <el-step title="待支付"></el-step>
+                    <el-step title="待发货"></el-step>
+                    <el-step title="待收货"></el-step>
+                    <el-step title="已完成"></el-step>
+                  </el-steps>
+        </div>
+         <el-card shadow="never" style="margin-top: 15px">
+           <div class="operate-container"  v-if="item!=null">
+             <span style="margin-left: 20px" class="color-danger">订单状态:
+                <el-tag prop="status" v-for="(ite, index) in statusOptions"    v-if="item.status==ite.dictValue">{{ite.dictLabel}}</el-tag>
+             </span>
+             <div class="operate-button-container"  v-if="item.status==1" v-hasPermi="['his:integralOrder:sendGoods']">
+                <el-button size="mini" @click="sendVisible=true" >发货</el-button>
+             </div>
+            <div class="operate-button-container"   v-if="item.deliverySn!=null" v-hasPermi="['his:integralOrder:express']">
+              <el-button size="mini" @click="showExpress()" >查看物流</el-button>
+            </div>
+
+            <div class="operate-button-container"  v-hasPermi="['his:integralOrder:edit']">
+              <el-button size="mini" @click="updateOrder()" >修改订单</el-button>
+            </div>
+           </div>
+           <div class="desct">
+            基本信息
+           </div>
+           <el-descriptions title="" :column="3" border>
+             <el-descriptions-item label="订单编号"><span v-if="item!=null">{{item.orderCode}}</span></el-descriptions-item>
+              <el-descriptions-item label="会员ID"><span v-if="item!=null">{{item.userId}}</span></el-descriptions-item>
+             <el-descriptions-item label="会员"><span v-if="item.nickName!=null">{{item.nickName}}({{item.phone}})</span></el-descriptions-item>
+             <el-descriptions-item label="用户名称"><span v-if="item!=null">{{item.userName}}</span></el-descriptions-item>
+             <el-descriptions-item label="用户电话"><span v-if="item!=null">{{item.userPhone}}</span>
+              <el-button icon="el-icon-search" size="mini" @click="handlePhone()" style="margin-left: 20px;" circle v-hasPermi="['his:integralOrder:queryPhone']"></el-button>
+            </el-descriptions-item>
+              <el-descriptions-item label="用户地址"><span v-if="item!=null">{{item.userAddress}}</span></el-descriptions-item>
+             <el-descriptions-item label="支付积分"><span v-if="item!=null">{{item.integral}}</span></el-descriptions-item>
+             <el-descriptions-item label="状态"><span v-if="item!=null"> <dict-tag :options="statusOptions" :value="item.status"/></span></el-descriptions-item>
+             <el-descriptions-item label="快递公司编号"><span v-if="item!=null">{{item.deliveryCode}}</span></el-descriptions-item>
+             <el-descriptions-item label="快递名称"><span v-if="item!=null">{{item.deliveryName}}</span></el-descriptions-item>
+             <el-descriptions-item label="快递单号"><span v-if="item!=null">{{item.deliverySn}}</span></el-descriptions-item>
+             <el-descriptions-item label="发货时间"><span v-if="item!=null">{{item.deliveryTime}}</span></el-descriptions-item>
+             <el-descriptions-item label="提交时间"><span v-if="item!=null">{{item.createTime}}</span></el-descriptions-item>
+           </el-descriptions>
+         </el-card>
+
+    </div>
+      <div class="contentx" v-if="item!=null" style="padding-bottom: 70px;">
+       <div class="desct">
+           商品信息
+          </div>
+       <el-table
+              border
+              v-if="prod!=null"
+              :data="prod"
+              size="small"
+              style="margin-top: 20px" >
+              <el-table-column label="商品图片"  align="center">
+                <template slot-scope="scope">
+                  <img :src="scope.row.imgUrl" style="height: 80px">
+                </template>
+              </el-table-column>
+              <el-table-column label="商品名称"  align="center">
+                <template slot-scope="scope">
+                  <p>{{scope.row.goodsName}}</p>
+                </template>
+              </el-table-column>
+              <el-table-column label="积分" align="center">
+                <template slot-scope="scope">
+                  <p>¥{{scope.row.integral}}</p>
+                </template>
+              </el-table-column>
+              <el-table-column label="金额" align="center">
+               <template slot-scope="scope">
+                 <p>¥{{scope.row.cash || 0}}</p>
+               </template>
+              </el-table-column>
+
+            </el-table>
+       </div>
+      <el-dialog
+           width="50%"
+           title="发货"
+           :visible.sync="sendVisible"
+           append-to-body @close="sendCancel">
+      <el-form ref="form" :model="form" :rules="rules" label-width="120px">
+          <el-form-item label="快递公司编号" prop="deliveryCode">
+                    <el-input v-model="form.deliveryCode" placeholder="请输入快递公司编号" />
+                  </el-form-item>
+                  <el-form-item label="快递名称" prop="deliveryName">
+                    <el-input v-model="form.deliveryName" placeholder="请输入快递名称" />
+                  </el-form-item>
+                  <el-form-item label="快递单号" prop="deliverySn">
+                    <el-input v-model="form.deliverySn" placeholder="请输入快递单号" />
+                  </el-form-item>
+        </el-form>
+        <div slot="footer" class="dialog-footer">
+                          <el-button type="primary" @click="sendGoods">确 定</el-button>
+                          <el-button @click="sendCancel">取 消</el-button>
+        </div>
+
+          </el-dialog>
+
+          <el-dialog :title="expressDialog.title" :visible.sync="expressDialog.open" width="600px" append-to-body>
+            <el-table style="margin-top: 20px;width: 100%"
+                      ref="orderHistoryTable"
+                      :data="traces" border>
+              <el-table-column label="操作时间"  width="160" align="center">
+                <template slot-scope="scope">
+                  {{scope.row.AcceptTime}}
+                </template>
+              </el-table-column>
+               <el-table-column label="位置" align="center">
+                <template slot-scope="scope">
+                  {{scope.row.Location}}
+                </template>
+              </el-table-column>
+              <el-table-column label="描述" align="center">
+                <template slot-scope="scope">
+                  {{scope.row.AcceptStation}}
+                </template>
+              </el-table-column>
+            </el-table>
+          </el-dialog>
+
+          <el-dialog :title="editOrder.title" :visible.sync="editOrder.open" width="600px" append-to-body>
+            <el-form ref="editForm" :model="editForm" :rules="editRules" label-width="100px">
+              <el-form-item label="订单状态" prop="status" >
+                <el-select v-model="editForm.status" placeholder="请选择状态" clearable size="small" filterable>
+                    <el-option
+                      v-for="dict in statusOptions "
+                      :key="dict.dictValue"
+                      :label="dict.dictLabel"
+                      :value="dict.dictValue"
+                    />
+                  </el-select>
+                </el-form-item>
+              <el-form-item label="详情地址" prop="userAddress"  >
+                <el-input v-model="editForm.userAddress" placeholder="请输入" />
+              </el-form-item>
+              <el-form-item label="备注" prop="remark"  >
+                <el-input v-model="editForm.remark" placeholder="请输入备注" />
+              </el-form-item>
+            </el-form>
+            <div slot="footer" class="dialog-footer">
+              <el-button type="primary" @click="submitEditForm">确 定</el-button>
+            </div>
+          </el-dialog>
+    </div>
+</template>
+
+<script>
+import {getExpress, listIntegralOrder, sendgoods,getIntegralOrder, delIntegralOrder, addIntegralOrder, updateIntegralOrder, exportIntegralOrder,getOrderUserPhone } from "@/api/hisStore/integralOrder";
+  export default {
+    name: "integralOrder",
+    data() {
+      return {
+        expressDialog:{
+          title:"物流信息",
+          open:false,
+        },
+        editOrder:{
+          title:"修改订单",
+          open:false,
+        },
+        editForm:{
+          orderId:null,
+          status:null,
+          userAddress:null,
+          remark:"",
+        },
+        editRules:{},
+        item:null,
+        express:null,
+        traces:[],
+        rules:{
+      deliveryCode: [
+        { required: true, message: '请输入快递公司编号', trigger: 'blur' }
+      ],
+      deliveryName: [
+        { required: true, message: '请输入快递名称', trigger: 'blur' }
+      ],
+      deliverySn: [
+        { required: true, message: '请输入快递单号', trigger: 'blur' }
+      ]
+    },
+        sendVisible:false,
+        form: {
+          deliveryCode: null,
+          deliveryName:null,
+          deliverySn:null,
+          orderId:null,
+        }
+      }
+    },
+    created() {
+      this.getDicts("sys_integral_order_status").then(response => {
+        this.statusOptions = response.data;
+      });
+    },
+    methods: {
+      handlePhone(){
+        const orderId = this.item.orderId;
+        getOrderUserPhone(orderId).then(response =>{
+            this.item.userPhone = response.userPhone;
+        })
+      },
+      updateOrder(){
+        this.editOrder.open=true;
+        this.editForm.orderId=this.item.orderId;
+        this.editForm.remark=this.item.remark;
+        this.editForm.status = this.item.status.toString();
+        this.editForm.userAddress = this.item.userAddress.toString();
+      },
+     //修改订单状态
+     submitEditForm(){
+        this.$refs["editForm"].validate(valid => {
+        if (valid) {
+          updateIntegralOrder(this.editForm).then(response => {
+            if (response.code === 200) {
+              this.msgSuccess("操作成功");
+              this.editOrder.open = false;
+              this.getDetails(this.item.orderId);
+              }
+            });
+          }
+        });
+      },
+      sendCancel(){
+           this.sendVisible = false;
+           this.form={
+             deliveryCode: null,
+             deliveryName:null,
+             deliverySn:null,
+             orderId:null,
+           }
+      },
+      showExpress(){
+        this.expressDialog.open=true;
+        getExpress(this.item.orderId).then(response => {
+            this.express = response.data;
+            if(this.express!=null&&this.express.Traces!=null){
+                this.traces=this.express.Traces
+            }
+        });
+      },
+          sendGoods(){
+              this.form.orderId=this.item.orderId;
+              sendgoods(this.form).then(response => {
+                    this.msgSuccess("修改成功");
+                    this.sendVisible = false;
+                    getIntegralOrder(this.item.orderId).then(response => {
+                        this.item = response.data;
+                        this.$parent.$parent.getList();
+                    });
+
+                    this.form={
+                    deliveryCode: null,
+                    deliveryName:null,
+                    deliverySn:null,
+                    orderId:null,
+                  }
+              });
+            },
+      getDetails(orderId) {
+        this.item=null;
+        getIntegralOrder(orderId).then(response => {
+            this.item = response.data;
+            this.prod=[JSON.parse(this.item.itemJson)];
+        });
+      },
+    }
+  }
+</script>
+<style>
+  .contentx{
+      height: 100%;
+      background-color: #fff;
+      padding: 0px 20px 20px;
+
+
+      margin: 20px;
+  }
+  .el-descriptions-item__label.is-bordered-label{
+    font-weight: normal;
+  }
+  .el-descriptions-item__content {
+    max-width: 150px;
+    min-width: 100px;
+  }
+  .desct{
+      padding-top: 20px;
+      padding-bottom: 20px;
+      color: #524b4a;
+      font-weight: bold;
+    }
+.operate-container {
+  background: #F2F6FC;
+  height: 60px;
+  margin: -20px -20px 0;
+  line-height: 60px;
+}
+.order-content{
+  margin: 10px;
+
+}
+.operate-button-container {
+  float: right;
+  margin-right: 20px
+}
+</style>

+ 94 - 27
src/views/hisStore/components/productAfterSalesOrder.vue

@@ -16,7 +16,11 @@
             <el-tag prop="status" v-for="(item, index) in salesStatusOptions"    v-if="afterSales.salesStatus==item.dictValue">{{item.dictLabel}}</el-tag>
           </span>
           <div class="operate-button-container"  >
-            <el-button size="mini" v-hasPermi="['store:storeAfterSales:edit']" v-show="afterSales.salesStatus==0&&afterSales.status===1"  @click="addDelivery">编辑物流</el-button>
+            <el-button size="mini"  v-hasPermi="['store:storeAfterSales:edit']" v-show="afterSales.salesStatus==0&&afterSales.status===1"  @click="addDelivery">编辑物流</el-button>
+            <el-button size="mini"  v-hasPermi="['store:storeAfterSales:audit1']" v-show="afterSales.salesStatus==0&&afterSales.status===0" @click="handleAudit1">平台审核</el-button>
+            <el-button size="mini"  v-hasPermi="['store:storeAfterSales:audit2']" v-show="afterSales.salesStatus==0&&afterSales.status===2" @click="handleAudit2">仓库审核</el-button>
+            <el-button size="mini"  v-hasPermi="['store:storeAfterSales:refund']" @click="handleRefund"  v-show="afterSales.salesStatus==0&&afterSales.status===3">财务审核</el-button>
+            <el-button size="mini"  v-hasPermi="['store:storeAfterSales:cancel']" @click="cancel"  v-show="afterSales.salesStatus==0">撤销</el-button>
             <el-button size="mini"  @click="showOrder">查看订单</el-button>
          </div>
         </div>
@@ -204,7 +208,7 @@
 </template>
 
 <script>
-import {getStoreAfterSales,cancel,refund,audit,updateStoreAfterSales } from "@/api/hisStore/storeAfterSales";
+import {getStoreAfterSales,cancel,refund,audit1,audit2,updateStoreAfterSales} from "@/api/hisStore/storeAfterSales";
 
 import productOrder from "./productOrder";
 export default {
@@ -312,53 +316,116 @@ export default {
         this.$refs.productOrder.getOrder(orderId);
       }, 500);
     },
-    handleAudit(){
-        this.audit.open=true;
-        this.form.serviceType=this.afterSales.serviceType;
-        this.form.salesId=this.afterSales.id;
-        this.form.refundAmount=this.afterSales.refundAmount;
-    },
-    submitAuditForm() {
-      this.$refs["form"].validate(valid => {
-        if (valid) {
-           audit(this.form).then(response => {
-              if (response.code === 200) {
-                this.audit.open = false;
-                this.getStoreAfterSales(this.afterSales.id);
-                this.msgSuccess("操作成功");
-              }
-            });
-        }
-      });
-    },
-    cancel(){
+    handleAudit1(){
       var id=this.afterSales.id;
-      this.$confirm('是否确认取消订单?', "警告", {
+      this.$confirm('确定审请通过?', "警告", {
           confirmButtonText: "确定",
           cancelButtonText: "取消",
           type: "warning"
         }).then(function() {
           var data={salesId:id}
-          return cancel(data);
+          return audit1(data);
         }).then(() => {
           this.getStoreAfterSales(id);
           this.msgSuccess("操作成功");
         }).catch(function() {});
+
     },
-    refund(){
+    handleAudit2(){
       var id=this.afterSales.id;
-      this.$confirm('是否确认收货,确认后将自动退款给客户', "警告", {
+      this.$confirm('确定审请通过?', "警告", {
           confirmButtonText: "确定",
           cancelButtonText: "取消",
           type: "warning"
         }).then(function() {
           var data={salesId:id}
-          return refund(data);
+          return audit2(data);
         }).then(() => {
           this.getStoreAfterSales(id);
           this.msgSuccess("操作成功");
         }).catch(function() {});
+
+    },
+    handleRefund(){
+        this.audit.open=true;
+        this.form.salesId=this.afterSales.id;
+        this.form.refundAmount=this.afterSales.refundAmount;
+    },
+    submitAuditForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          refund(this.form).then(response => {
+              if (response.code === 200) {
+                this.audit.open = false;
+                this.getStoreAfterSales(this.afterSales.id);
+                this.msgSuccess("操作成功");
+              }
+            });
+        }
+      });
+    },
+    cancel(){
+      var id=this.afterSales.id;
+      this.$prompt('是否确定取消订单?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+      }).then(({ value }) => {
+        var data={salesId:id,remark:value};
+        return cancel(data);
+      }).then(() => {
+        this.getStoreAfterSales(id);
+        this.msgSuccess("操作成功");
+      }).catch(() => {
+
+      });
     },
+    // handleAudit(){
+    //     this.audit.open=true;
+    //     this.form.serviceType=this.afterSales.serviceType;
+    //     this.form.salesId=this.afterSales.id;
+    //     this.form.refundAmount=this.afterSales.refundAmount;
+    // },
+    // submitAuditForm() {
+    //   this.$refs["form"].validate(valid => {
+    //     if (valid) {
+    //        audit(this.form).then(response => {
+    //           if (response.code === 200) {
+    //             this.audit.open = false;
+    //             this.getStoreAfterSales(this.afterSales.id);
+    //             this.msgSuccess("操作成功");
+    //           }
+    //         });
+    //     }
+    //   });
+    // },
+    // cancel(){
+    //   var id=this.afterSales.id;
+    //   this.$confirm('是否确认取消订单?', "警告", {
+    //       confirmButtonText: "确定",
+    //       cancelButtonText: "取消",
+    //       type: "warning"
+    //     }).then(function() {
+    //       var data={salesId:id}
+    //       return cancel(data);
+    //     }).then(() => {
+    //       this.getStoreAfterSales(id);
+    //       this.msgSuccess("操作成功");
+    //     }).catch(function() {});
+    // },
+    // refund(){
+    //   var id=this.afterSales.id;
+    //   this.$confirm('是否确认收货,确认后将自动退款给客户', "警告", {
+    //       confirmButtonText: "确定",
+    //       cancelButtonText: "取消",
+    //       type: "warning"
+    //     }).then(function() {
+    //       var data={salesId:id}
+    //       return refund(data);
+    //     }).then(() => {
+    //       this.getStoreAfterSales(id);
+    //       this.msgSuccess("操作成功");
+    //     }).catch(function() {});
+    // },
     getStoreAfterSales(id){
         getStoreAfterSales(id).then(response => {
             this.afterSales = response.afterSales;

+ 156 - 28
src/views/hisStore/components/productOrder.vue

@@ -356,10 +356,10 @@
     </el-dialog>
     <el-dialog :title="editAddress.title" :visible.sync="editAddress.open" width="600px" append-to-body>
       <el-form ref="editAddressForm" :model="editAddressForm" :rules="editAddressRules" label-width="100px">
-        <el-form-item label="收件人" prop="realName">
+        <el-form-item label="收件人" prop="realName" required>
           <el-input v-model="editAddressForm.realName" placeholder="请输入收件人" />
         </el-form-item>
-          <el-form-item label="联系电话" prop="source">
+          <el-form-item label="联系电话" prop="userPhone" required>
           <el-input v-model="editAddressForm.userPhone" placeholder="请输入联系电话" />
         </el-form-item>
         <el-form-item label="收货地址" prop="district">
@@ -396,7 +396,7 @@
           </el-col>
         </el-row>
         </el-form-item>
-        <el-form-item label="详细地址" prop="detail">
+        <el-form-item label="详细地址" prop="detail" required>
           <el-input v-model="editAddressForm.detail" placeholder="请输入收货人详细地址" />
         </el-form-item>
       </el-form>
@@ -494,7 +494,20 @@ import { getTcmScheduleList } from "@/api/company/tcmScheduleReport";
 import {getCustomerListBySearch } from "@/api/crm/customer";
 import ImageUpload from '@/components/ImageUpload'
 import Material from '@/components/Material'
-import {bindCustomer,getExpress, listStoreOrder, getStoreOrder, delStoreOrder, addStoreOrder, updateStoreOrder, exportStoreOrder,uploadCredentials, getStoreOrderAddress,getUserPhone} from "@/api/hisStore/storeOrder";
+import {
+  bindCustomer,
+  getExpress,
+  listStoreOrder,
+  getStoreOrder,
+  delStoreOrder,
+  addStoreOrder,
+  updateStoreOrder,
+  exportStoreOrder,
+  uploadCredentials,
+  getStoreOrderAddress,
+  getUserPhone,
+  updateAddressErpFsStoreOrder
+} from "@/api/hisStore/storeOrder";
 import {getCitys} from "@/api/hisStore/city";
 import customerDetails from '../../crm/components/customerDetails.vue';
 import addSms from '../../crm/components/addSms.vue';
@@ -757,42 +770,157 @@ export default {
           this.province=res.data.filter(item => item.level===0 )
         })
     },
-    handleEditAddress() {
-        this.getCityList();
-        this.editAddressForm.id=this.order.id;
-        this.editAddressForm.realName=this.order.realName;
-        this.editAddressForm.userPhone=this.order.userPhone;
-        var address=this.order.userAddress.split(' ')
-        var province=this.citys.find((item)=>{
-          return item.name==address[0]&&item.level==0;
-        })
-        if(province!=null){
-          this.editAddressForm.provinceId=province.cityId;
-          this.city=this.citys.filter(item => item.parentId===province.cityId&&item.level==1 )
-        }
-        var city=this.citys.find((item)=>{
-          return item.name==address[1]&&item.level==1;
-        })
+    flattenCityData(data, level = 0) {
+      let result = [];
+      data.forEach(item => {
+        // 转换字段名
+        const cityItem = {
+          cityId: item.value,
+          name: item.label,
+          parentId: item.pid,
+          level: level
+        };
+        result.push(cityItem);
 
-        if(city!=null){
-          this.editAddressForm.cityId=city.cityId;
-          this.district=this.citys.filter(item => item.parentId===city.cityId&&item.level==2 )
+        // 递归处理子节点
+        if (item.children && item.children.length > 0) {
+          result = result.concat(this.flattenCityData(item.children, level + 1));
         }
-        var district=this.citys.find((item)=>{
-          return item.name==address[2]&&item.level==2;
+      });
+      return result;
+    },
+    handleEditAddress() {
+      let loading = this.$loading({
+        lock: true,
+        text: "请求中...",
+        background: "rgba(0, 0, 0, 0.7)",
+      });
+      const orderId = this.order.id;
+
+      getStoreOrderAddress(orderId).then(addressResponse => {
+        // 更新解密后的地址
+        this.order.userAddress = addressResponse.address;
+
+      }).then(res=>{
+        const id = this.order.id;
+        return getUserPhone(id).then(response =>{
+          this.order.userPhone = response.userPhone;
         })
-        if(district!=null){
-          this.editAddressForm.districtId=district.cityId;
+      }).then(res=>{
+        return getCitys();
+      }).then(res => {
+        this.citys = this.flattenCityData(res.data);
+        this.province = this.citys.filter(item => item.level === 0);
+
+        this.editAddressForm = {
+          id: this.order.id,
+          realName: this.order.realName,
+          userPhone: this.order.userPhone,
+          provinceId: null,
+          cityId: null,
+          districtId: null,
+          province: '',
+          city: '',
+          district: '',
+          detail: ''
+        };
+
+        // 解析地址
+        if (this.order.userAddress) {
+          var addressParts = this.order.userAddress.split(' ');
+
+          // 查找省份
+          if (addressParts.length > 0) {
+            var province = this.citys.find((item) => {
+              return item.name === addressParts[0] && item.level === 0;
+            });
+            if (province != null) {
+              this.editAddressForm.provinceId = province.cityId;
+              this.editAddressForm.province = province.name;
+
+              // 检查是否为直辖市(北京、上海、天津、重庆)
+              const isDirectMunicipality = ['北京市', '上海市', '天津市', '重庆市'].includes(province.name);
+
+              if (isDirectMunicipality) {
+                // 直辖市处理:先找到市级节点(市辖区)
+                if (addressParts.length > 1) {
+                  // 查找市级节点(第2部分,如"市辖区")
+                  var cityLevel = this.citys.find((item) => {
+                    return item.name === addressParts[1] && item.level === 1 && item.parentId === province.cityId;
+                  });
+
+                  if (cityLevel != null) {
+                    this.editAddressForm.cityId = cityLevel.cityId;
+                    this.editAddressForm.city = cityLevel.name;
+                    this.city = this.citys.filter(item => item.parentId === province.cityId && item.level === 1);
+
+                    // 使用市级节点的cityId作为parentId查找区县
+                    if (addressParts.length > 2) {
+                      var district = this.citys.find((item) => {
+                        return item.name === addressParts[2] && item.level === 2 && item.parentId === cityLevel.cityId;
+                      });
+
+                      if (district != null) {
+                        this.editAddressForm.districtId = district.cityId;
+                        this.editAddressForm.district = district.name;
+                        this.district = this.citys.filter(item => item.parentId === cityLevel.cityId && item.level === 2);
+                      }
+                    }
+
+                    // 提取详细地址(第4部分及之后)
+                    if (addressParts.length > 3) {
+                      this.editAddressForm.detail = addressParts.slice(3).join(' ');
+                    }
+                  }
+                }
+              } else {
+                // 普通省份处理:正常匹配市、区
+                this.city = this.citys.filter(item => item.parentId === province.cityId && item.level === 1);
+
+                // 查找城市
+                if (addressParts.length > 1) {
+                  var city = this.citys.find((item) => {
+                    return item.name === addressParts[1] && item.level === 1;
+                  });
+                  if (city != null) {
+                    this.editAddressForm.cityId = city.cityId;
+                    this.editAddressForm.city = city.name;
+                    this.district = this.citys.filter(item => item.parentId === city.cityId && item.level === 2);
+                  }
+                }
+                // 查找区县
+                if (addressParts.length > 2) {
+                  var district = this.citys.find((item) => {
+                    return item.name === addressParts[2] && item.level === 2;
+                  });
+                  if (district != null) {
+                    this.editAddressForm.districtId = district.cityId;
+                    this.editAddressForm.district = district.name;
+                  }
+                }
+                // 提取详细地址(第4部分及之后)
+                if (addressParts.length > 3) {
+                  this.editAddressForm.detail = addressParts.slice(3).join(' ');
+                }
+              }
+            }
+          }
         }
 
         this.editAddress.open = true;
+      }).catch(error => {
+        this.msgError("加载数据失败");
+        console.error(error);
+      }).finally(()=>{
+        loading.close();
+      })
     },
     /** 提交按钮 */
     submitEditAddressForm() {
       this.$refs["editAddressForm"].validate(valid => {
         if (valid) {
            this.editAddressForm.userAddress=this.editAddressForm.province+" "+this.editAddressForm.city+" "+this.editAddressForm.district+" "+this.editAddressForm.detail;
-            updateStoreOrder(this.editAddressForm).then(response => {
+            updateAddressErpFsStoreOrder(this.editAddressForm).then(response => {
               if (response.code === 200) {
                 this.msgSuccess("修改成功");
                 this.editAddress.open = false;

+ 625 - 0
src/views/hisStore/integralOrder/addOrder.vue

@@ -0,0 +1,625 @@
+<template>
+  <div class="app-container">
+    <!-- 添加一个加载状态,在获取createOrderType之前显示 -->
+    <div v-if="!formReady" style="text-align: center; padding: 50px;">
+      <i class="el-icon-loading"></i> 正在加载...
+    </div>
+
+    <!-- 只有在formReady为true时才显示表单 -->
+    <el-form v-if="formReady" ref="form" :model="form" :rules="rules" label-width="120px">
+      <el-form-item label="会员信息" prop="userId">
+        <el-table border style="margin-top:5px;" v-loading="userloading" :data="users">
+          <el-table-column label="ID" align="center" prop="userId" />
+          <el-table-column label="会员头像" align="center" width="80">
+            <template slot-scope="scope">
+              <el-popover placement="right" title="" trigger="hover">
+                <img slot="reference" :src="scope.row.avatar" width="50">
+                <img :src="scope.row.avatar" style="max-width: 120px;">
+              </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" prop="status">
+            <template slot-scope="scope">
+              <el-tag prop="status" v-for="(item, index) in userStatusOptions" v-if="scope.row.status==item.dictValue">{{item.dictLabel}}</el-tag>
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-form-item>
+
+      <el-form-item label="收货信息" prop="addressId">
+        <el-row>
+          <el-col>
+            <el-button plain type="primary" icon="el-icon-plus" @click="handleAddUserAddress()">添加收货地址</el-button>
+          </el-col>
+        </el-row>
+        <el-radio-group v-model="form.addressId" style="width:100%">
+          <el-table border style="margin-top:5px;" v-loading="addressloading" :data="address">
+            <el-table-column label="ID" align="center"  >
+              <template slot-scope="scope">
+                <el-radio :label="scope.row.addressId"></el-radio>
+              </template>
+            </el-table-column>
+            <el-table-column label="收货人姓名" align="center" prop="realName" />
+            <el-table-column label="收货人电话" align="center" prop="phone" />
+            <el-table-column label="地址" align="center" prop="detail">
+              <template slot-scope="scope">
+                {{scope.row.province}} {{scope.row.city}} {{scope.row.district}} {{scope.row.detail}}
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-radio-group>
+      </el-form-item>
+
+      <el-form-item label="商品列表">
+        <el-row>
+          <el-col>
+            <el-button plain type="primary" icon="el-icon-plus" @click="handleAddProduct">添加商品</el-button>
+          </el-col>
+        </el-row>
+        <el-table border :key="tablekey" width="100%" style="margin-top:5px;" :data="products">
+          <el-table-column label="商品名称" align="center" prop="goodsName" />
+          <el-table-column label="商品图片" align="center" width="100">
+            <template slot-scope="scope">
+              <el-popover placement="right" title="" trigger="hover">
+                <img slot="reference" :src="scope.row.imgUrl" width="50">
+                <img :src="scope.row.imgUrl" style="max-width: 50px;">
+              </el-popover>
+            </template>
+          </el-table-column>
+          <el-table-column label="库存" align="center" prop="stock" />
+          <el-table-column label="所需积分" align="center" prop="integral" />
+          <el-table-column label="需支付金额" align="center" prop="cash" />
+          <el-table-column label="商品编号" align="center" prop="barCode" />
+
+          <el-table-column label="操作" align="center" width="100px">
+            <template slot-scope="scope">
+              <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)">删除</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+
+      </el-form-item>
+      <el-form-item label="订单备注" prop="mark">
+        <el-input type="textarea" rows="2" v-model="form.mark" placeholder="" />
+      </el-form-item>
+    </el-form>
+
+    <div v-if="formReady" slot="footer" class="dialog-footer" style="float: right; margin-bottom: 20px;">
+      <el-button type="primary" @click="submitForm">确 定</el-button>
+      <el-button @click="cancel">取 消</el-button>
+    </div>
+
+    <!-- 对话框部分保持不变 -->
+    <el-dialog :title="product.title" v-if="product.open" :visible.sync="product.open" width="1000px" append-to-body>
+      <product-select @selectProduct="selectProduct" />
+    </el-dialog>
+    <el-dialog :title="user.title" v-if="user.open" :visible.sync="user.open" width="500px" append-to-body>
+      <add-user @addUser="addUser" />
+    </el-dialog>
+    <el-dialog :title="userAddress.title" v-if="userAddress.open" :visible.sync="userAddress.open" width="800px" append-to-body>
+      <add-user-address ref="addUserAddress" @addUserAddress="addUserAddress" />
+    </el-dialog>
+    <el-dialog :title="payQr.title" v-if="payQr.open" :visible.sync="payQr.open" width="240px" append-to-body>
+      <div style="padding-bottom:15px;">
+        <div class="qrcode" ref="qrCodeUrl"></div>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { addIntegralOrder } from "@/api/hisStore/integralOrder";
+import { getUserList } from "@/api/users/user";
+import { getAddressList } from "@/api/users/userAddress";
+import { getTcmScheduleList } from "@/api/company/tcmScheduleReport";
+import productSelect from "@/views/hisStore/integralOrder/integralProductSelect";
+import addUserAddress from "@/views/hisStore/components/addUserAddress";
+import config from "@/utils/config";
+import QRCode from 'qrcodejs2'
+import { treeselect } from "@/api/company/companyDept";
+import Treeselect from "@riophae/vue-treeselect";
+import "@riophae/vue-treeselect/dist/vue-treeselect.css";
+
+export default {
+  components: { Treeselect,productSelect,addUserAddress },
+  name: "StoreOrder",
+  props: {
+    customerId: {
+      type: Number,
+      required: false
+    },
+    userId: {
+      type: Number,
+      required: false
+    }
+  },
+  data() {
+    return {
+      // 添加表单准备状态
+      formReady: false,
+      createOrderType: null,
+      // 部门树选项
+      deptOptions: undefined,
+      // 部门名称
+      deptName: undefined,
+      defaultProps: {
+        children: "children",
+        label: "label",
+      },
+      deliveryPayStatusOptions:[],
+      deliveryStatusOptions:[],
+      dateRange: [],
+      orderTypeOptions:[],
+      orderMediumOptions:[],
+      payTypeOptions:[],
+      payQr:{
+        open:false,
+        title:"付款二维码"
+      },
+      user:{
+        open:false,
+        title:"创建会员"
+      },
+      userAddress:{
+        open:false,
+        title:"创建收货地址"
+      },
+      tablekey:false,
+      totalMoney:0.00,
+      products:[],
+      product:{
+        open:false,
+        title:"商品选择"
+      },
+      phone:null,
+      address:[],
+      addressloading: false,
+      userloading: false,
+      users:[],
+      userStatusOptions:[],
+      show:{
+        open:false,
+        title:"订单详情"
+      },
+      activeName:"00",
+      statusOptions:[],
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 订单表格数据
+      storeOrderList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      createTimeRange:[],
+      payTimeRange:[],
+      deliveryImportTimeRange:[],
+      scheduleOptions:[],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        orderCode: null,
+        extendOrderId: null,
+        userId: null,
+        realName: null,
+        userPhone: null,
+        userAddress: null,
+        cartId: null,
+        freightPrice: null,
+        totalNum: null,
+        totalPrice: null,
+        totalPostage: null,
+        payPrice: null,
+        payPostage: null,
+        deductionPrice: null,
+        couponId: null,
+        couponPrice: null,
+        paid: null,
+        payTime: null,
+        payType: null,
+        status: null,
+        refundStatus: null,
+        refundReasonWapImg: null,
+        refundReasonWapExplain: null,
+        refundReasonTime: null,
+        refundReasonWap: null,
+        refundReason: null,
+        refundPrice: null,
+        deliverySn: null,
+        deliveryName: null,
+        deliveryType: null,
+        deliveryId: null,
+        gainIntegral: null,
+        useIntegral: null,
+        payIntegral: null,
+        backIntegral: null,
+        mark: null,
+        isDel: null,
+        cost: null,
+        verifyCode: null,
+        storeId: null,
+        shippingType: null,
+        isChannel: null,
+        isRemind: null,
+        isSysDel: null
+      },
+      // 表单参数
+      form: {
+        addressId:null,
+        userId:null,
+        products:[],
+      },
+      // 表单校验
+      rules: {
+        userId: [
+          { required: true, message: "会员信息不能为空", trigger: "submit" }
+        ],
+        addressId: [
+          { required: true, message: "收货信息不能为空", trigger: "submit" }
+        ],
+        products: [
+          { required: true, message: "商品不能为空", trigger: "submit" }
+        ],
+      }
+    };
+  },
+  watch: {
+    // 根据名称筛选部门树
+    deptName(val) {
+      this.$refs.tree.filter(val);
+    },
+  },
+  created() {
+    // 先获取createOrderType,然后再初始化其他数据
+    this.initializeForm();
+  },
+  methods: {
+    // 初始化表单的方法
+    async initializeForm() {
+      try {
+
+        // 2. 然后并行加载其他数据
+        await Promise.all([
+          this.loadDictionaries(),
+          this.loadOtherData(),
+          this.initUser()
+        ]);
+
+        // 3. 所有数据加载完成后,设置表单为准备状态
+        this.formReady = true;
+        console.log('Form is ready to display');
+
+      } catch (error) {
+        console.error('Error initializing form:', error);
+        this.$message.error('表单初始化失败');
+      }
+    },
+
+    // 加载字典数据
+    async loadDictionaries() {
+      const dictPromises = [
+        this.getDicts("crm_customer_source").then((response) => {
+          this.orderMediumOptions = response.data;
+        }),
+        this.getDicts("store_order_type").then((response) => {
+          this.orderTypeOptions = response.data;
+        }),
+        this.getDicts("store_pay_type").then((response) => {
+          this.payTypeOptions = response.data;
+        }),
+        this.getDicts("user_status").then((response) => {
+          this.userStatusOptions = response.data;
+        }),
+        this.getDicts("store_order_status").then((response) => {
+          this.statusOptions = response.data;
+        }),
+        this.getDicts("store_order_delivery_status").then((response) => {
+          this.deliveryStatusOptions = response.data;
+        }),
+        this.getDicts("store_delivery_pay_status").then((response) => {
+          this.deliveryPayStatusOptions = response.data;
+        })
+      ];
+
+      await Promise.all(dictPromises);
+    },
+
+    // 加载其他数据
+    async loadOtherData() {
+      await Promise.all([
+        this.getTreeselect(),
+        getTcmScheduleList().then(response => {
+          this.scheduleOptions = response.data;
+        })
+      ]);
+    },
+
+    initUser(){
+      return new Promise((resolve) => {
+        if(this.userId != null){
+          var data={userId:this.userId}
+          this.userloading = true;
+          this.users=[];
+          this.address=[];
+          getUserList(data).then(response => {
+            this.users = response.data;
+            this.userloading = false;
+            if(this.users!=null&&this.users.length==1){
+              this.form.userId=this.users[0].userId;
+              this.getAddressList(this.form.userId).then(() => {
+                resolve();
+              });
+            } else {
+              resolve();
+            }
+          }).catch(() => {
+            resolve();
+          });
+        } else {
+          resolve();
+        }
+      });
+    },
+
+    /** 查询部门下拉树结构 */
+    getTreeselect() {
+      return treeselect().then((response) => {
+        this.deptOptions = response.data;
+      });
+    },
+
+    // 修改getAddressList方法,返回Promise
+    getAddressList(userId){
+      var data={userId:userId}
+      this.addressloading = true;
+      this.address=[];
+      return getAddressList(data).then(response => {
+        this.address = response.data;
+        this.addressloading = false;
+      });
+    },
+
+    // 其他方法保持不变...
+    // 筛选节点
+    filterNode(value, data) {
+      if (!value) return true;
+      return data.label.indexOf(value) !== -1;
+    },
+    // 节点单击事件
+    handleNodeClick(data) {
+      this.queryParams.deptId = data.id;
+      this.getList();
+    },
+    handleGenPayUrl(row){
+      this.payQr.open=true;
+      setTimeout(() => {
+        var qrcode = new QRCode(this.$refs.qrCodeUrl, {
+            text: config.payQRUrl+row.id,
+            width: 200,
+            height: 200,
+            colorDark: '#000000',
+            colorLight: '#ffffff',
+            correctLevel: QRCode.CorrectLevel.H
+        })
+      }, 200);
+    },
+    handleAddUser(){
+      this.user.open=true;
+    },
+    handleAddUserAddress(){
+      this.userAddress.open=true;
+      setTimeout(() => {
+        this.$refs.addUserAddress.init(this.form.userId);
+      }, 500);
+    },
+    addUserAddress(){
+      this.userAddress.open=false;
+      //获取地址
+      this.getAddressList(this.form.userId);
+    },
+    addUser(){
+      this.user.open=false;
+    },
+    compute(){
+      this.totalMoney=0;
+      var that=this;
+      this.products.forEach (function (value) {
+          that.totalMoney += value.money;
+      });
+    },
+    handleProductCountChange(row){
+      this.tablekey = !this.tablekey
+      row.money=row.count*row.price;
+      this.$forceUpdate();
+      this.compute();
+    },
+
+    selectProduct(row){
+      for(var i=0;i<this.products.length;i++){
+        if(this.products[i].id==row.id){
+          this.$message.warning("请删除商品在重新选择!")
+          return;
+        }
+      }
+      row.count=1;
+      row.money=row.count*row.price;
+      this.products.push(row);
+      this.$message.success("商品"+ row.goodsName + "添加成功")
+      this.compute();
+      this.product.open=false;
+    },
+    handleAddProduct(){
+      this.product.open=true;
+    },
+    searchUser(){
+      if(this.phone==null||this.phone==""){
+        return;
+      }
+      var data={phone:this.phone}
+      this.userloading = true;
+      this.users=[];
+      this.address=[];
+      getUserList(data).then(response => {
+        this.users = response.data;
+        this.userloading = false;
+        if(this.users!=null&&this.users.length==1){
+          this.form.userId=this.users[0].userId;
+          this.getAddressList(this.form.userId)
+        }
+      });
+    },
+    handleDetails(row){
+      this.show.open=true;
+      const orderId = row.id ;
+      setTimeout(() => {
+        this.$refs.order.getOrder(orderId);
+      }, 500);
+    },
+    handleClick(tab, event) {
+       if(tab.name=="all"){
+        this.queryParams.status==null;
+      }
+      else{
+         this.queryParams.status=tab.name;
+      }
+      this.getList();
+    },
+    /** 查询订单列表 */
+    getList() {
+      if(this.queryParams.status=='00'){
+        this.queryParams.status=null;
+      }
+      if(this.createTimeRange!=null&&this.createTimeRange.length==2){
+        this.queryParams.createTimeRange=this.createTimeRange[0]+"--"+this.createTimeRange[1]
+      }
+      else{
+        this.queryParams.createTimeRange=null;
+      }
+      if(this.payTimeRange!=null&&this.payTimeRange.length==2){
+        this.queryParams.payTimeRange=this.payTimeRange[0]+"--"+this.payTimeRange[1]
+      }
+      else{
+        this.queryParams.payTimeRange=null;
+      }
+      if(this.deliveryImportTimeRange!=null&&this.deliveryImportTimeRange.length==2){
+        this.queryParams.deliveryImportTimeRange=this.deliveryImportTimeRange[0]+"--"+this.deliveryImportTimeRange[1]
+      }
+      else{
+        this.queryParams.deliveryImportTimeRange=null;
+      }
+      this.loading = true;
+
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+      this.$emit("closePackage")
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        addressId:null,
+        userId:null,
+        products:null,
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "创建订单";
+    },
+
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if(this.products.length==0){
+          this.msgError("请选择商品");
+          return;
+        }
+        this.form.products=this.products;
+        if (valid) {
+          this.form.goodsId = this.form.products[0].goodsId;
+          // console.log("this.form:{}",this.form.products[0].goodsId)
+          addIntegralOrder(this.form).then(response => {
+            if (response.code === 200) {
+              this.msgSuccess("创建成功");
+              this.$emit("closePackage")
+            }
+          });
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+       this.products.splice(this.products.findIndex(item => item.id === row.id), 1)
+       this.compute();
+    },
+  }
+};
+</script>
+
+<style scoped lang="scss">
+.items{
+  margin: 5px 0px;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  justify-content: flex-start;
+  .pic{
+    width:60px;
+    height:60px;
+  }
+  .goods-content{
+    margin-left: 10px;
+    max-width: 200px;
+    text-align: left;
+    .goods-title{
+      overflow:hidden;
+      white-space: nowrap;
+      text-overflow: ellipsis;
+      -o-text-overflow:ellipsis;
+    }
+  }
+}
+.el-message-box__message p{
+  max-height: 400px;
+  overflow:scroll;
+}
+.import-msg{
+  height: 500px;
+  overflow: auto;
+}
+</style>
+
+<style>
+.el-descriptions-item__label.is-bordered-label{
+  font-weight: normal;
+}
+</style>

+ 546 - 0
src/views/hisStore/integralOrder/index.vue

@@ -0,0 +1,546 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="订单编号" prop="orderCode">
+        <el-input
+          v-model="queryParams.orderCode"
+          placeholder="请输入订单编号"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="用户名称" prop="userName">
+        <el-input
+          v-model="queryParams.userName"
+          placeholder="请输入用户名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="用户电话" prop="userPhone">
+        <el-input
+          v-model="queryParams.userPhone"
+          placeholder="请输入用户电话"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+
+      <el-form-item label="状态" prop="status">
+        <el-select v-model="queryParams.status" placeholder="状态" clearable size="small">
+          <el-option
+            v-for="dict in statusOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="快递单号" prop="deliverySn">
+        <el-input
+          v-model="queryParams.deliverySn"
+          placeholder="请输入快递单号"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </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="change"></el-date-picker>
+              </el-form-item>
+      <el-form-item label="销售公司" prop="companyId">
+        <el-select v-model="queryParams.companyId" placeholder="销售公司"  size="small" @change="getAllUserlist(queryParams.companyId)" clearable>
+          <el-option
+            v-for="dict in qwCompanyList"
+            :key="dict.companyId"
+            :label="dict.companyName"
+            :value="dict.companyId"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="销售账号" prop="userId">
+        <el-select v-model="queryParams.companyUserId" placeholder="销售账号"  size="small" clearable>
+          <el-option
+            v-for="dict in companyUserNameList"
+            :key="dict.userId"
+            :label="dict.userName"
+            :value="dict.userId"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="企微微信" prop="companyUserName">
+        <el-select v-model="queryParams.qwUserId" placeholder="企微微信"  size="small" clearable>
+          <el-option
+            v-for="dict in qwUserList"
+            :key="dict.id"
+            :label="dict.qwUserName"
+            :value="dict.id"
+          />
+        </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="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          :loading="exportLoading"
+          @click="handleExport"
+          v-hasPermi="['his:integralOrder:export']"
+        >导出</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="info"
+          plain
+          icon="el-icon-upload2"
+          size="mini"
+          @click="handleImport"
+          v-hasPermi="['his:integralOrder:exportDeliver']"
+        >导入发货</el-button>
+      </el-col>
+
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+    <el-tabs type="card" v-model="actName" @tab-click="handleClickX">
+      <el-tab-pane label="全部订单" name="10"></el-tab-pane>
+      <el-tab-pane v-for="(item,index) in statusOptions" :label="item.dictLabel" :name="item.dictValue"></el-tab-pane>
+    </el-tabs>
+    <el-table v-loading="loading" border :data="integralOrderList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="订单编号" align="center" prop="orderCode" />
+      <el-table-column label="用户名称" align="center" prop="userName" />
+      <el-table-column label="用户电话" align="center" prop="userPhone" />
+      <el-table-column label="用户地址" align="center" prop="userAddress" show-overflow-tooltip />
+      <el-table-column label="支付积分" align="center" prop="integral" />
+      <el-table-column label="支付金额" align="center" prop="cash" />
+      <el-table-column label="状态" align="center" prop="status">
+        <template slot-scope="scope">
+          <dict-tag :options="statusOptions" :value="scope.row.status"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="快递公司编号" align="center" prop="deliveryCode" />
+      <el-table-column label="快递名称" align="center" prop="deliveryName" />
+      <el-table-column label="快递单号" align="center" prop="deliverySn" />
+      <el-table-column label="发货时间" align="center" prop="deliveryTime" width="180"/>
+      <el-table-column label="提交时间" align="center" prop="createTime" width="180"/>
+      <el-table-column label="销售公司ID" align="center" prop="companyId" width="180"/>
+      <el-table-column label="销售ID" align="center" prop="companyUserId" width="180"/>
+      <el-table-column label="企业微信ID" align="center" prop="qwUserId" width="180"/>
+      <el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip/>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right">
+        <template slot-scope="scope">
+          <el-button
+             size="mini"
+             type="text"
+             @click="handledetails(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"
+    />
+
+    <!-- 添加或修改积分商品订单对话框 -->
+    <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="orderCode">
+          <el-input v-model="form.orderCode" placeholder="请输入订单编号" />
+        </el-form-item>
+        <el-form-item label="用户id" prop="userId">
+          <el-input v-model="form.userId" placeholder="请输入用户id" />
+        </el-form-item>
+        <el-form-item label="用户名称" prop="userName">
+          <el-input v-model="form.userName" placeholder="请输入用户名称" />
+        </el-form-item>
+        <el-form-item label="用户电话" prop="userPhone">
+          <el-input v-model="form.userPhone" placeholder="请输入用户电话" />
+        </el-form-item>
+        <el-form-item label="用户地址" prop="userAddress">
+          <el-input v-model="form.userAddress" placeholder="请输入用户地址" />
+        </el-form-item>
+        <el-form-item label="商品信息" prop="itemJson">
+          <el-input v-model="form.itemJson" type="textarea" placeholder="请输入内容" />
+        </el-form-item>
+        <el-form-item label="支付积分" prop="integral">
+          <el-input v-model="form.integral" placeholder="请输入支付积分" />
+        </el-form-item>
+        <el-form-item label="1:待发货;2:待收货;3:已完成" prop="status">
+          <el-select v-model="form.status" placeholder="请选择1:待发货;2:待收货;3:已完成">
+            <el-option
+              v-for="dict in statusOptions"
+              :key="dict.dictValue"
+              :label="dict.dictLabel"
+              :value="dict.dictValue"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="快递公司编号" prop="deliveryCode">
+          <el-input v-model="form.deliveryCode" placeholder="请输入快递公司编号" />
+        </el-form-item>
+        <el-form-item label="快递名称" prop="deliveryName">
+          <el-input v-model="form.deliveryName" placeholder="请输入快递名称" />
+        </el-form-item>
+        <el-form-item label="快递单号" prop="deliverySn">
+          <el-input v-model="form.deliverySn" placeholder="请输入快递单号" />
+        </el-form-item>
+        <el-form-item label="发货时间" prop="deliveryTime">
+          <el-date-picker clearable size="small"
+            v-model="form.deliveryTime"
+            type="date"
+            value-format="yyyy-MM-dd"
+            placeholder="选择发货时间">
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+          <el-input v-model="form.remark" 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>
+    <el-drawer
+    	:with-header="false"
+            size="75%"
+             :title="show.title" :visible.sync="show.open">
+         <integralOrderDetails  ref="Details" />
+       </el-drawer>
+   <el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
+         <el-upload
+           ref="upload"
+           :limit="1"
+           accept=".xlsx, .xls"
+           :headers="upload.headers"
+           :action="upload.url + '?updateSupport=' + upload.updateSupport"
+           :disabled="upload.isUploading"
+           :on-progress="handleFileUploadProgress"
+           :on-success="handleFileSuccess"
+           :auto-upload="false"
+           drag
+         >
+           <i class="el-icon-upload"></i>
+           <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
+           <div class="el-upload__tip text-center" slot="tip">
+             <div class="el-upload__tip" slot="tip">
+             </div>
+             <span>仅允许导入xls、xlsx格式文件。</span>
+             <el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;" @click="importTemplate">下载模板</el-link>
+           </div>
+         </el-upload>
+       <div slot="footer" class="dialog-footer">
+         <el-button type="primary" @click="submitFileForm">确 定</el-button>
+         <el-button @click="upload.open = false">取 消</el-button>
+       </div>
+     </el-dialog>
+  </div>
+</template>
+
+<script>
+import {importTemplate, listIntegralOrder,importExpressTemplate, getIntegralOrder, delIntegralOrder, addIntegralOrder, updateIntegralOrder, exportIntegralOrder } from "@/api/hisStore/integralOrder";
+import integralOrderDetails from '../components/integralOrderDetails.vue';
+import { getToken } from "@/utils/auth";
+import {getCompanyList} from "@/api/company/company";
+import {getAllUserlist} from "@/api/company/companyUser";
+import {getQwUserInfo} from "@/api/qw/qwUser";
+
+export default {
+  name: "IntegralOrder",
+  components: { integralOrderDetails },
+  data() {
+    return {
+      show:{
+              open:false,
+            },
+      upload: {
+        // 是否显示弹出层
+        open: false,
+        // 弹出层标题
+        title: "",
+        // 是否禁用上传
+        isUploading: false,
+        // 是否更新已经存在的用户数据
+        updateSupport: 0,
+        // 设置上传的请求头部
+        headers: { Authorization: "Bearer " + getToken() },
+        // 上传的地址
+        url: process.env.VUE_APP_BASE_API + "/his/integralOrder/importData"
+      },
+      // 遮罩层
+      loading: true,
+      actName:"10",
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 积分商品订单表格数据
+      integralOrderList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 1:待发货;2:待收货;3:已完成字典
+      statusOptions: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        orderCode: null,
+        userName: null,
+        userPhone: null,
+        integral: null,
+        status: null,
+        deliverySn: null,
+        createTime: null,
+        sTime:null,
+        eTime:null,
+        companyUserId:null,
+        qwUserId:null,
+        companyId:null,
+      },
+       createTime:null,
+      qwCompanyList:[],
+      companyUserNameList:[],
+      qwUserList:[],
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        userId: [
+          { required: true, message: "用户id不能为空", trigger: "blur" }
+        ],
+        status: [
+          { required: true, message: "1:待发货;2:待收货;3:已完成不能为空", trigger: "change" }
+        ],
+      }
+    };
+  },
+  created() {
+    this.getList();
+    this.getDicts("sys_integral_order_status").then(response => {
+      this.statusOptions = response.data;
+    });
+
+
+    //获取企业
+    getCompanyList().then(response => {
+      this.qwCompanyList = response.data;
+    });
+  },
+  methods: {
+    change(){
+          if(this.createTime!=null){
+            this.queryParams.sTime=this.createTime[0];
+            this.queryParams.eTime=this.createTime[1];
+          }else{
+            this.queryParams.sTime=null;
+            this.queryParams.eTime=null;
+          }
+
+        },
+    handleImport() {
+        this.upload.title = "导入";
+        this.upload.open = true;
+       },
+    importTemplate() {
+      importTemplate().then(response => {
+        this.download(response.msg);
+      });
+    },
+    // 文件上传中处理
+    handleFileUploadProgress(event, file, fileList) {
+      this.upload.isUploading = true;
+    },
+    // 文件上传成功处理
+    handleFileSuccess(response, file, fileList) {
+      this.upload.open = false;
+      this.upload.isUploading = false;
+      this.$refs.upload.clearFiles();
+      this.$alert(response.msg, "导入结果", { dangerouslyUseHTMLString: true });
+      this.getList();
+    },
+    // 提交上传文件
+    submitFileForm() {
+      this.$refs.upload.submit();
+    },
+    handledetails(row){
+        this.show.open=true;
+        setTimeout(() => {
+             this.$refs.Details.getDetails(row.orderId);
+        }, 1);
+    },
+    /** 查询积分商品订单列表 */
+    getList() {
+      this.loading = true;
+      listIntegralOrder(this.queryParams).then(response => {
+        this.integralOrderList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        orderId: null,
+        orderCode: null,
+        userId: null,
+        userName: null,
+        userPhone: null,
+        userAddress: null,
+        itemJson: null,
+        integral: null,
+        status: null,
+        deliveryCode: null,
+        deliveryName: null,
+        deliverySn: null,
+        deliveryTime: null,
+        createTime: null,
+        remark: null,
+        sTime:null,
+        eTime:null,
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.createTime=null;
+      this.queryParams.sTime=null;
+      this.queryParams.eTime=null;
+      this.queryParams.qwUserId=null;
+      this.queryParams.companyId=null;
+      this.queryParams.companyUserId=null;
+      this.handleQuery();
+
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.orderId)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加积分商品订单";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const orderId = row.orderId || this.ids
+      getIntegralOrder(orderId).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改积分商品订单";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.orderId != null) {
+            updateIntegralOrder(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addIntegralOrder(this.form).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    handleClickX(tab, event) {
+     if(tab.name=="10"){
+       this.queryParams.status=null;
+     }else{
+       this.queryParams.status=tab.name;
+     }
+      this.handleQuery();
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const orderIds = row.orderId || this.ids;
+      this.$confirm('是否确认删除积分商品订单编号为"' + orderIds + '"的数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return delIntegralOrder(orderIds);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有积分商品订单数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportIntegralOrder(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    },
+    //选择企业后触发
+    getAllUserlist(companyId){
+      if(companyId){
+        getAllUserlist({companyId}).then(response => {
+          this.companyUserNameList=response.data;
+        });
+        //企业微信
+        getQwUserInfo({companyId}).then(response => {
+          this.qwUserList=response.data;
+        })
+      }
+    }
+  }
+};
+</script>

+ 112 - 0
src/views/hisStore/integralOrder/integralProductSelect.vue

@@ -0,0 +1,112 @@
+<template>
+  <div >
+    <el-form :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
+      <el-form-item label="商品名称" prop="goodsName">
+        <el-input
+          style="width:200px"
+          v-model="queryParams.goodsName"
+          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-form-item>
+    </el-form>
+    <el-table border v-loading="loading" :data="list">
+      <el-table-column label="商品图片" align="center" width="100">
+        <template slot-scope="scope">
+          <el-popover
+            placement="right"
+            title=""
+            trigger="hover"
+          >
+            <img slot="reference" :src="scope.row.imgUrl" width="80">
+            <img :src="scope.row.imgUrl" style="max-width: 80px;">
+          </el-popover>
+        </template>
+      </el-table-column>
+      <el-table-column label="商品分类" align="center" prop="goodType">
+        <template slot-scope="scope">
+          <dict-tag :options="typeOptions" :value="scope.row.status"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="商品名称" show-overflow-tooltip align="center" prop="goodsName" />
+      <el-table-column label="库存" align="center" prop="stock" />
+      <el-table-column label="所需积分" align="center" prop="integral" />
+      <el-table-column label="需支付金额" align="center" prop="cash" />
+      <el-table-column label="商品编号" align="center" prop="barCode" />
+
+      <el-table-column label="操作" align="center" width="100px" >
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            @click="handleSelect(scope.row)"
+          >选择</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <pagination
+      style="padding-bottom:10px;"
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </div>
+</template>
+
+<script>
+import { listIntegralGoods } from "@/api/hisStore/integralGoods";
+export default {
+  name: "selectProduct",
+  data() {
+    return {
+      loading: true,
+      list:[],
+      typeOptions:[],
+      total: 0,
+      queryParams: {
+        goodsName:"",
+        pageNum: 1,
+        pageSize: 10
+      }
+    };
+  },
+  created() {
+     this.getList();
+
+    this.getDicts("sys_integral_goods_type").then(response => {
+      this.typeOptions = response.data;
+    });
+
+  },
+
+  methods: {
+    handleSelect(row){
+      this.$emit('selectProduct',row);
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    getList(){
+      this.loading = true;
+      this.queryParams.status=1
+      listIntegralGoods(this.queryParams).then(response => {
+        this.list = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    }
+  }
+};
+</script>
+<style scoped>
+
+</style>

+ 561 - 0
src/views/hisStore/statistics/storeOrderStatistics.vue

@@ -0,0 +1,561 @@
+<template>
+  <div class="app-container">
+    <div class="app-content">
+      <div class="title">
+        商城订单统计
+      </div>
+      <el-form class="search-form" :inline="true" >
+
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <el-form-item label="时间:">
+              <el-select v-model="value" placeholder="请选择日期">
+                <el-option
+                  v-for="item in options"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value">
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+             <el-form-item label="公司名:" prop="companyId">
+              <el-select filterable v-model="companyId" @change="companyChange" placeholder="请选择公司名" clearable size="small">
+                <el-option
+                  v-for="item in companys"
+                  :key="item.companyId"
+                  :label="item.companyName"
+                  :value="item.companyId"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="部门:">
+              <treeselect :clearable="true"  v-model="deptId"  :options="deptOptions" :show-count="true"  placeholder="请选择归属部门" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="员工:">
+              <el-select filterable v-model="userIds" placeholder="请选择员工" clearable size="small">
+                <el-option
+                  v-for="item in users"
+                  :key="item.userId"
+                  :label="item.nickName"
+                  :value="item.userId">
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <el-form-item label="订单类型:">
+              <el-select v-model="orderType" placeholder="请选择订单类型" clearable size="small">
+                <el-option
+                  v-for="item in orderTypeOptions"
+                  :key="item.dictLabel"
+                  :label="item.dictLabel"
+                  :value="item.dictValue">
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="媒体来源:">
+              <el-select v-model="orderMedium" placeholder="请选择媒体来源" clearable size="small">
+                <el-option
+                  v-for="item in orderMediumOptions"
+                  :key="item.dictLabel"
+                  :label="item.dictLabel"
+                  :value="item.dictValue">
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+
+
+
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <!-- 查询方式 下单时间 发货时间 -->
+            <el-form-item label="下单时间:">
+              <el-select v-model="queryType" placeholder="查询方式" size="small">
+                <el-option
+                  v-for="item in queryTypes"
+                  :key="item.dictLabel"
+                  :label="item.dictLabel"
+                  :value="item.dictValue">
+                </el-option>
+              </el-select>
+            </el-form-item>
+
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="筛选日期" prop="createTime">
+              <el-date-picker clearable size="small" style="width: 220px"
+                v-model="dateRange"
+                type="daterange"
+                value-format="yyyy-MM-dd"
+                        start-placeholder="开始日期" end-placeholder="结束日期"
+                        >
+              </el-date-picker>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <!-- 实收金额 金额等于0 金额大于0 -->
+            <el-form-item label="实收金额:">
+              <el-select v-model="amountType" placeholder="请选择金额" clearable size="small">
+                <el-option
+                  v-for="item in amountTypes"
+                  :key="item.dictLabel"
+                  :label="item.dictLabel"
+                  :value="item.dictValue">
+                </el-option>
+              </el-select>
+            </el-form-item>
+
+          </el-col>
+          <el-col :span="6">
+
+          </el-col>
+        </el-row>
+
+
+        
+        <el-form-item>
+                    <el-button type="primary" icon="el-icon-search" plain   @click="storeOrderStatistics">搜索</el-button>
+        </el-form-item>
+      </el-form>
+      <div class="data-box">
+        <!-- <div class="echart-box">
+          <div id="echart-customer"></div>
+        </div> -->
+
+        <!-- 新增的数据表格 -->
+        <div class="table-container">
+          <!-- 使用组件替换原有表格 -->
+          <order-summary-table 
+            title="员工下单汇总" 
+            :table-data="tableData" 
+            nameLable="员工姓名" />
+            
+          <order-summary-table 
+            title="公司下单汇总" 
+            :table-data="companyTableData" 
+            nameLable="公司" />
+            
+          <order-summary-table 
+            title="部门下单汇总" 
+            :table-data="deptTableData" 
+            nameLable="部门"/>
+        </div>
+      </div>
+    </div>
+
+  </div>
+</template>
+
+<script>
+import OrderSummaryTable from '../components/OrderSummaryTable';
+import { storeOrderStatistics } from "@/api/hisStore/statistics";
+import { getUserListByDeptId} from "@/api/company/companyUser";
+// import echarts from 'echarts'
+import resize from '../../dashboard/mixins/resize'
+import { treeselect } from "@/api/company/companyDept";
+import Treeselect from "@riophae/vue-treeselect";
+import "@riophae/vue-treeselect/dist/vue-treeselect.css";
+import { getCompanyList } from "@/api/company/company";
+export default {
+  name: 'storeOrderStatatic',
+  mixins: [resize],
+  components: { Treeselect,OrderSummaryTable },
+  watch: {
+    // 监听deptId
+    'deptId': 'currDeptChange'
+  },
+  data() {
+    return {
+      queryTypes:[
+        { dictLabel: '下单时间', dictValue: 1 },
+        { dictLabel: '发货时间', dictValue: 2 }
+      ],
+      amountTypes:[
+        { dictLabel: '金额等于0', dictValue: 1 },
+        { dictLabel: '金额大于0', dictValue: 2 }
+      ],
+      queryType: 1,
+      amountType: null,
+      companys: [],
+      deptOptions: [],
+      companyId: undefined,
+      deptId: undefined,
+      userIds: undefined,
+      users: [],
+      dateRange: [],
+      chart: null,
+      orderTypeOptions:[],// 订单类型
+      orderType:'',
+      orderMediumOptions:[], // 媒体来源
+      orderMedium:'',
+      options: [{
+        value: '1',
+        label: '今天'
+      }, {
+        value: '2',
+        label: '昨天'
+      }, {
+        value: '3',
+        label: '本周'
+      }, {
+        value: '4',
+        label: '上周'
+      }, {
+        value: '5',
+        label: '本月'
+      }, {
+        value: '6',
+        label: '上月'
+      }, {
+        value: '7',
+        label: '本季度'
+      }, {
+        value: '8',
+        label: '上季度'
+      }, {
+        value: '9',
+        label: '本年'
+      }, {
+        value: '10',
+        label: '上年'
+      }],
+      value: '5',
+      list: [],
+      dates: [],
+      orderCount: [],
+      payPrice: [],
+      // 新增表格数据
+      tableData: [],        // 员工下单汇总
+      companyTableData: [], // 公司下单汇总
+      deptTableData: []     // 部门下单汇总
+    }
+  },
+  created() {
+    getCompanyList().then(response => {
+      this.companys = response.data;
+      // if (this.companys != null && this.companys.length > 0) {
+      //   this.companyId = this.companys[0].companyId;
+      //   this.getTreeselect();
+      // }
+    });
+    // 获取订单类型
+    this.getDicts("store_order_type").then((response) => {
+      this.orderTypeOptions = response.data;
+    });
+    // 媒体来源
+    this.getDicts("crm_customer_source").then((response) => {
+      this.orderMediumOptions = response.data;
+    });
+  },
+  methods: {
+    companyChange(val) {
+      console.log(val);
+      this.companyId = val;
+      this.getTreeselect();
+    },
+    currDeptChange(val) {
+      console.log(val)
+      this.deptId = val;
+      this.getUserListByDeptId();
+    },
+    /** 查询部门下拉树结构 */
+    getTreeselect() {
+      var that = this;
+      var param = { companyId: this.companyId }
+      treeselect(param).then((response) => {
+        this.deptOptions = response.data;
+        console.log(this.deptOptions)
+        if (response.data != null && response.data.length > 0) {
+          // this.deptId = response.data[0].id;
+          that.storeOrderStatistics()
+        }
+      });
+    },
+    handleExport() {
+      var data;
+      if (this.userIds != undefined) {
+        data = { type: this.value, userIds: this.userIds + "", deptId: this.deptId }
+      }
+      else {
+        data = { type: this.value, deptId: this.deptId }
+      }
+      exportVoiceLogs(data).then((response) => {
+        console.log(response)
+        this.download(response.msg);
+      });
+    },
+    getUserListByDeptId() {
+      this.userIds = undefined;
+      var data = { deptId: this.deptId };
+      getUserListByDeptId(data).then(response => {
+        this.users = response.data;
+      });
+    },
+    storeOrderStatistics() {
+      var data={};
+      if(this.value){
+        data.type = this.value
+      }
+      if(this.userIds){
+        data.companyUserId = this.userIds
+      }
+
+      if(this.queryType){
+        data.queryType = this.queryType
+      }
+      if(this.amountType){
+        data.amountType = this.amountType
+      }
+
+      if(this.dateRange && this.dateRange.length>0){
+        data.type = null
+        data.startTime = this.dateRange[0]
+        data.endTime = this.dateRange[1]
+      }
+      if(this.orderType){
+        data.orderType = this.orderType
+      }
+      if(this.orderMedium){
+        data.orderMedium = this.orderMedium
+      }
+      if(this.companyId){
+        data.companyId = this.companyId
+      }
+      if(this.deptId){
+        data.deptId = this.deptId
+      }
+      storeOrderStatistics(data).then((response) => {
+        this.dates = response.dates;
+        this.orderCount = response.orderCount;
+        this.payPrice = response.payPrice;
+        //表格数据
+        this.tableData = response.userTableData || [];
+        this.companyTableData = response.companyTableData || [];
+        this.deptTableData = response.deptTableData || [];
+
+        // setTimeout(() => {
+        //   this.initEchart();
+        // }, 500);
+      });
+    },
+    initEchart() {
+      var option = {
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: {
+            // 坐标轴指示器,坐标轴触发有效
+            type: 'shadow'        // 默认为直线,可选为:'line' | 'shadow'
+          }
+        },
+        legend: {
+          data: ['订单数', '订单金额']
+        },
+        grid: {
+          left: '3%',
+          right: '4%',
+          bottom: '3%',
+          containLabel: true
+        },
+        xAxis: [
+          {
+            type: 'category',
+            data: this.dates
+          }
+        ],
+        yAxis: [
+          {
+            type: 'value',
+            axisLabel: {
+              formatter: '{value}个'
+            }
+          }
+        ],
+        series: [
+          {
+            name: '订单数',
+            type: 'bar',
+            emphasis: {
+              focus: 'series'
+            },
+            data: this.orderCount
+          },
+          {
+            name: '订单金额',
+            type: 'bar',
+            emphasis: {
+              focus: 'series'
+            },
+            data: this.payPrice
+          }
+        ]
+      };
+      this.chart = echarts.init(document.getElementById("echart-customer"));
+      this.chart.setOption(option, true);
+    },
+    // 表格行样式
+    tableRowClassName({ row, rowIndex }) {
+      if (row.isGroup) {
+        return 'group-row';
+      }
+      return '';
+    },
+    // 表格合并方法(如果需要的话)
+    objectSpanMethod({ row, column, rowIndex, columnIndex }) {
+      // 可以在这里实现单元格合并逻辑
+      return {
+        rowspan: 1,
+        colspan: 1
+      };
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.app-container {
+  border: 1px solid #e6e6e6;
+  padding: 12px;
+  height: calc(100vh - 24px); // 设置容器高度
+  display: flex;
+  flex-direction: column;
+  
+  .app-content {
+    background-color: white;
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    overflow: hidden;
+    
+    .title {
+      padding: 20px 30px 0px 30px;
+      font-size: 18px;
+      font-weight: bold;
+      color: black;
+    }
+    
+    .search-form {
+      margin: 20px 30px 0px 30px;
+      position: sticky;
+      top: 0;
+      background-color: white;
+      z-index: 100;
+      padding: 20px 30px 0px 30px;
+      margin: 0 30px;
+      box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+      flex-shrink: 0; // 防止收缩
+    }
+    
+    .data-box {
+      padding: 30px;
+      background-color: rgb(255, 255, 255);
+      flex: 1;
+      overflow-y: auto; // 添加垂直滚动条
+      height: 100%;
+      
+      .echart-box {
+        margin: 0 auto;
+        text-align: center;
+      }
+      
+      .el-select {
+        margin: 5px 10px;
+      }
+      
+      .table-box {
+        margin-top: 15px;
+        .export {
+          float: right;
+          margin: 10px 0px;
+        }
+      }
+      
+      // 新增表格样式
+      .table-container {
+        margin-top: 30px;
+        .table-title {
+          font-size: 16px;
+          font-weight: bold;
+          color: #333;
+          margin-bottom: 10px;
+        }
+      }
+    }
+  }
+}
+
+#echart-customer {
+  width: 100%;
+  height: 320px
+}
+
+.vue-treeselect {
+  width: 217px;
+  height: 36px;
+}
+
+// 表格相关样式
+::v-deep .group-row {
+  background-color: #e8f5e8 !important;
+  font-weight: bold;
+}
+
+::v-deep .group-name {
+  font-weight: bold;
+  color: #333;
+}
+
+::v-deep .el-table {
+  font-size: 12px;
+
+  .el-table__header-wrapper {
+    th {
+      background-color: #f5f7fa;
+      font-weight: bold;
+      color: #333;
+    }
+  }
+
+  .el-table__body-wrapper {
+    td {
+      padding: 8px 0;
+    }
+  }
+}
+
+::v-deep .sticky-column {
+  position: sticky;
+  left: 0;
+  z-index: 2;
+  background-color: white;
+
+  // 为表头也添加样式
+  &.is-header-column {
+    background-color: #f5f7fa;
+  }
+}
+
+// 确保表格容器允许sticky定位
+::v-deep .el-table__body-wrapper {
+  overflow-x: auto;
+}
+</style>
+
+<style>
+.vue-treeselect__control {
+  display: block;
+}
+</style>

+ 512 - 0
src/views/hisStore/statistics/storeProduct.vue

@@ -0,0 +1,512 @@
+<template>
+  <div class="app-container">
+    <div class="app-content">
+      <div class="title">
+        产品销售统计
+      </div>
+      
+      <el-form class="search-form" :inline="true">
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <el-form-item label="时间:">
+              <el-select v-model="value" placeholder="请选择日期">
+                <el-option
+                  v-for="item in options"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value">
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+             <el-form-item label="公司名:" prop="companyId">
+              <el-select filterable v-model="companyId" @change="companyChange" placeholder="请选择公司名" clearable size="small">
+                <el-option
+                  v-for="item in companys"
+                  :key="item.companyId"
+                  :label="item.companyName"
+                  :value="item.companyId"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="部门:">
+              <treeselect :clearable="false" v-model="deptId" :options="deptOptions" :show-count="true" placeholder="请选择归属部门" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="员工:">
+              <el-select filterable v-model="userIds" placeholder="请选择员工" clearable size="small">
+                <el-option
+                  v-for="item in users"
+                  :key="item.userId"
+                  :label="item.nickName"
+                  :value="item.userId">
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row> 
+        
+       
+        
+    
+        
+        
+        <el-form-item label="筛选日期" prop="createTime">
+          <el-date-picker clearable size="small" style="width: 205.4px"
+            v-model="dateRange"
+            type="daterange"
+            value-format="yyyy-MM-dd"
+            start-placeholder="开始日期" end-placeholder="结束日期">
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="cyan" icon="el-icon-search" @click="storeProduct">搜索</el-button>
+        </el-form-item>
+      </el-form>
+      
+      <div class="data-box">
+        <div class="echart-box">
+          <div id="echart-customer"></div>
+        </div>
+        
+        <!-- 产品销售统计数据表格 -->
+        <div class="table-container">
+          <h3 class="table-title">产品销售汇总</h3>
+          <!-- <div class="export">
+            <el-button type="primary" @click="handleExport">导出数据</el-button>
+          </div> -->
+          <el-table 
+            :data="tableData"
+            border
+            stripe
+            style="width: 100%; margin-top: 20px;"
+            :row-class-name="tableRowClassName"
+            :span-method="objectSpanMethod">
+            
+            <!-- 产品名称列 -->
+            <el-table-column prop="name" label="产品名称" width="100" align="center" class-name="sticky-column">
+              <template slot-scope="scope">
+                <span :class="{'group-name': scope.row.isGroup}">{{ scope.row.name }}</span>
+              </template>
+            </el-table-column>
+            
+            <!-- 动态生成订单类型列 - 使用多级表头 -->
+            <el-table-column 
+              v-for="orderType in orderTypeOptions" 
+              :key="orderType.dictValue"
+              :label="orderType.dictLabel" 
+              align="center">
+              
+              <!-- 数量子列 -->
+              <el-table-column 
+                label="数量" 
+                width="60" 
+                align="center">
+                <template slot-scope="scope">
+                  {{ getOrderData(scope.row, orderType.dictValue, 'count') }}
+                </template>
+              </el-table-column>
+              
+              <!-- 金额子列 -->
+              <el-table-column 
+                label="金额" 
+                width="80" 
+                align="center">
+                <template slot-scope="scope">
+                  {{ getOrderData(scope.row, orderType.dictValue, 'amount') }}
+                </template>
+              </el-table-column>
+              
+            </el-table-column>
+            
+          </el-table>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { storeProduct } from "@/api/hisStore/statistics";
+import { getUserListByDeptId } from "@/api/company/companyUser";
+import echarts from 'echarts';
+import resize from '../../dashboard/mixins/resize';
+import { treeselect } from "@/api/company/companyDept";
+import Treeselect from "@riophae/vue-treeselect";
+import "@riophae/vue-treeselect/dist/vue-treeselect.css";
+import { getCompanyList } from "@/api/company/company";
+
+export default {
+  name: 'ContentStatistics',
+  mixins: [resize],
+  components: { Treeselect },
+  watch: {
+    // 监听deptId
+    'deptId': 'currDeptChange'
+  },
+  data() {
+    return {
+      companys: [],
+      deptOptions: [],
+      companyId: undefined,
+      deptId: undefined,
+      userIds: undefined,
+      users: [],
+      dateRange: [],
+      chart: null,
+      options: [{
+        value: '1',
+        label: '今天'
+      }, {
+        value: '2',
+        label: '昨天'
+      }, {
+        value: '3',
+        label: '本周'
+      }, {
+        value: '4',
+        label: '上周'
+      }, {
+        value: '5',
+        label: '本月'
+      }, {
+        value: '6',
+        label: '上月'
+      }, {
+        value: '7',
+        label: '本季度'
+      }, {
+        value: '8',
+        label: '上季度'
+      }, {
+        value: '9',
+        label: '本年'
+      }, {
+        value: '10',
+        label: '上年'
+      }],
+      value: '5',
+      list: [],
+      dates: [],
+      orderCount: [], // 修改变量名
+      payPrice: [], // 修改变量名
+      // 订单类型选项 - 用作表头
+      orderTypeOptions: [],
+      // 产品销售统计表格数据
+      tableData: []
+    }
+  },
+  created() {
+    getCompanyList().then(response => {
+      this.companys = response.data;
+      if (this.companys != null && this.companys.length > 0) {
+        this.companyId = this.companys[0].companyId;
+        this.getTreeselect();
+      }
+    });
+    
+    // 获取订单类型字典
+    this.getDicts("store_order_type").then((response) => {
+      this.orderTypeOptions = response.data;
+    });
+  },
+  methods: {
+    companyChange(val) {
+      console.log(val);
+      this.companyId = val;
+      this.getTreeselect();
+    },
+    currDeptChange(val) {
+      console.log(val)
+      this.deptId = val;
+      this.getUserListByDeptId();
+    },
+    /** 查询部门下拉树结构 */
+    getTreeselect() {
+      var that = this;
+      var param = { companyId: this.companyId }
+      treeselect(param).then((response) => {
+        this.deptOptions = response.data;
+        console.log(this.deptOptions)
+        if (response.data != null && response.data.length > 0) {
+          this.deptId = response.data[0].id;
+          that.storeProduct()
+        }
+      });
+    },
+    handleExport() {
+      var data;
+      if (this.userIds != undefined) {
+        data = { type: this.value, userIds: this.userIds + "", deptId: this.deptId }
+      }
+      else {
+        data = { type: this.value, deptId: this.deptId }
+      }
+      // 导出产品销售统计数据
+      console.log('导出产品销售统计数据', data);
+    },
+    getUserListByDeptId() {
+      this.userIds = undefined;
+      var data = { deptId: this.deptId };
+      getUserListByDeptId(data).then(response => {
+        this.users = response.data;
+      });
+    },
+    storeProduct() {
+      var data;
+      if (this.userIds != undefined) {
+        data = { type: this.value, userIds: this.userIds + "", deptId: this.deptId }
+      }
+      else {
+        data = { type: this.value, deptId: this.deptId }
+      }
+      if(this.dateRange && this.dateRange.length > 0){
+        data.type = null
+        data.startTime = this.dateRange[0]
+        data.endTime = this.dateRange[1]
+      }
+      
+      storeProduct(data).then((response) => {
+        this.dates = response.dates;
+        this.orderCount = response.orderCount;
+        this.payPrice = response.payPrice;
+        
+        // 处理表格数据,补全缺失的订单类型数据
+        this.tableData = this.processTableData(response.tableData || []);
+        
+        setTimeout(() => {
+          this.initEchart();
+        }, 500);
+      });
+    },
+    
+    // 新增方法:处理表格数据,为每个产品补全所有订单类型的数据
+    processTableData(rawTableData) {
+      return rawTableData.map(item => {
+        // 创建一个新的 productCounts 对象,包含所有订单类型
+        const processedProductCounts = {};
+        
+        // 遍历所有订单类型选项,为每个类型设置默认值
+        this.orderTypeOptions.forEach(orderType => {
+          const dictValue = orderType.dictValue;
+          if (item.productCounts && item.productCounts[dictValue]) {
+            // 如果原数据中存在该订单类型的数据,使用原数据
+            processedProductCounts[dictValue] = item.productCounts[dictValue];
+          } else {
+            // 如果原数据中不存在该订单类型的数据,设置默认值
+            processedProductCounts[dictValue] = {
+              count: 0,
+              amount: 0.0
+            };
+          }
+        });
+        
+        return {
+          ...item,
+          productCounts: processedProductCounts
+        };
+      });
+    },
+    
+    initEchart() {
+      var option = {
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: {
+            type: 'shadow'
+          }
+        },
+        legend: {
+          data: ['订单数量', '金额']
+        },
+        grid: {
+          left: '3%',
+          right: '4%',
+          bottom: '3%',
+          containLabel: true
+        },
+        xAxis: [
+          {
+            type: 'category',
+            data: this.dates
+          }
+        ],
+        yAxis: [
+          {
+            type: 'value',
+            axisLabel: {
+              formatter: '{value}'
+            }
+          }
+        ],
+        series: [
+          {
+            name: '订单数量',
+            type: 'bar',
+            emphasis: {
+              focus: 'series'
+            },
+            data: this.orderCount // 修改为正确的变量名
+          },
+          {
+            name: '金额',
+            type: 'bar',
+            emphasis: {
+              focus: 'series'
+            },
+            data: this.payPrice // 修改为正确的变量名
+          }
+        ]
+      };
+      this.chart = echarts.init(document.getElementById("echart-customer"));
+      this.chart.setOption(option, true);
+    },
+    
+    // 修改获取订单数据的方法,适配新的数据结构
+    getOrderData(row, orderType, dataType) {
+      // 使用 productCounts 而不是 orderData
+      if (row.productCounts && row.productCounts[orderType]) {
+        const value = row.productCounts[orderType][dataType] || 0;
+        if (dataType === 'amount') {
+          return value.toFixed(2);
+        }
+        return value;
+      }
+      return dataType === 'amount' ? '0.00' : 0;
+    },
+    
+    // 表格行样式
+    tableRowClassName({ row, rowIndex }) {
+      if (row.isGroup) {
+        return 'group-row';
+      }
+      return '';
+    },
+    // 表格合并方法
+    objectSpanMethod({ row, column, rowIndex, columnIndex }) {
+      return {
+        rowspan: 1,
+        colspan: 1
+      };
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.app-container {
+  border: 1px solid #e6e6e6;
+  padding: 12px;
+  .app-content {
+    background-color: white;
+    .title {
+      padding: 20px 30px 0px 30px;
+      font-size: 18px;
+      font-weight: bold;
+      color: black;
+    }
+    .search-form {
+      margin: 20px 30px 0px 30px;
+    }
+    .data-box {
+      padding: 30px;
+      background-color: rgb(255, 255, 255);
+      height: 100%;
+      .echart-box {
+        margin: 0 auto;
+        text-align: center;
+      }
+      .el-select {
+        margin: 5px 10px;
+      }
+      .table-box {
+        margin-top: 15px;
+        .export {
+          float: right;
+          margin: 10px 0px;
+        }
+      }
+      // 表格样式
+      .table-container {
+        margin-top: 30px;
+        .table-title {
+          font-size: 16px;
+          font-weight: bold;
+          color: #333;
+          margin-bottom: 10px;
+        }
+        .export {
+          float: right;
+          margin: 10px 0px;
+        }
+      }
+    }
+  }
+}
+
+#echart-customer {
+  width: 100%;
+  height: 320px;
+}
+
+.vue-treeselect {
+  width: 217px;
+  height: 36px;
+}
+
+// 表格相关样式
+::v-deep .group-row {
+  background-color: #e8f5e8 !important;
+  font-weight: bold;
+}
+
+::v-deep .group-name {
+  font-weight: bold;
+  color: #333;
+}
+
+::v-deep .el-table {
+  font-size: 12px;
+  
+  .el-table__header-wrapper {
+    th {
+      background-color: #f5f7fa;
+      font-weight: bold;
+      color: #333;
+    }
+  }
+  
+  .el-table__body-wrapper {
+    td {
+      padding: 8px 0;
+    }
+  }
+}
+
+::v-deep .sticky-column {
+  position: sticky;
+  left: 0;
+  z-index: 2;
+  background-color: white;
+  
+  // 为表头也添加样式
+  &.is-header-column {
+    background-color: #f5f7fa;
+  }
+}
+
+// 确保表格容器允许sticky定位
+::v-deep .el-table__body-wrapper {
+  overflow-x: auto;
+}
+</style>
+
+<style>
+.vue-treeselect__control {
+  display: block;
+}
+</style>

+ 475 - 0
src/views/hisStore/storeOrderAudit/audit.vue

@@ -0,0 +1,475 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-row :gutter="20">
+        <el-col :span="6">
+          <el-form-item label="所属员工" prop="companyUserName">
+            <el-input
+              v-model="queryParams.companyUserName"
+              placeholder="请输入所属员工昵称"
+              clearable
+              size="small"
+              @keyup.enter.native="handleQuery"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="6">
+          <el-form-item label="收件人" prop="realName">
+            <el-input
+            style="width:220px"
+              v-model="queryParams.realName"
+              placeholder="请输入收件人"
+              clearable
+              size="small"
+              @keyup.enter.native="handleQuery"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="6">
+          <el-form-item label="手机号" prop="userPhone">
+            <el-input
+            style="width:220px"
+              v-model="queryParams.userPhone"
+              placeholder="请输入收件人手机号"
+              clearable
+              size="small"
+              @keyup.enter.native="handleQuery"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="6">
+          <el-form-item label="创建时间" prop="createTimeRange">
+            <el-date-picker
+            style="width:220px"
+              clearable size="small"
+              v-model="createTimeRange"
+              type="daterange"
+              value-format="yyyy-MM-dd"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期">
+            </el-date-picker>
+          </el-form-item>
+        </el-col>
+
+      </el-row>
+
+       <el-row :gutter="20">
+        <el-col :span="6">
+          <el-form-item label="订单金额" prop="orderAmountRange">
+            <el-input
+            style="width:100px"
+              v-model="queryParams.orderAmountSmall"
+              placeholder="请输入订单金额"
+              clearable
+              size="small"
+              @keyup.enter.native="handleQuery"
+            />
+            <span>-</span>
+            <el-input
+            style="width:100px"
+              v-model="queryParams.orderAmountBig"
+              placeholder="请输入订单金额"
+              clearable
+              size="small"
+              @keyup.enter.native="handleQuery"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="6">
+          <el-form-item label="订单类型" prop="orderType">
+            <el-select style="width:220px"  v-model="queryParams.orderType" placeholder="请选择订单类型" clearable size="small" >
+              <el-option
+                    v-for="item in orderTypeOptions"
+                    :key="item.dictValue"
+                    :label="item.dictLabel"
+                    :value="item.dictValue"
+                  />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="6">
+          <el-form-item label="审核时间" prop="auditRange">
+            <el-date-picker
+            style="width:220px"
+              clearable size="small"
+              v-model="auditRange"
+              type="daterange"
+              value-format="yyyy-MM-dd"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期">
+            </el-date-picker>
+          </el-form-item>
+        </el-col>
+        <el-col :span="6">
+          
+        </el-col>
+
+      </el-row>
+      
+      
+      
+      
+      <!-- 订单金额 范围两个输入 -->
+      
+      <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-row 布局将 tabs 和 批量操作按钮放在同一行 -->
+    <el-row :gutter="10" style="margin-bottom: 10px;">
+      <el-col :span="16">
+        <el-tabs type="card" v-model="activeName" @tab-click="handleClick">
+          <el-tab-pane label="全部" name="00"></el-tab-pane>
+          <el-tab-pane label="待审批" name="0"></el-tab-pane>
+          <el-tab-pane label="审批拒绝" name="1"></el-tab-pane>
+          <!-- <el-tab-pane label="待总后台审核" name="2"></el-tab-pane>
+          <el-tab-pane label="总后台审核拒绝" name="3"></el-tab-pane> -->
+          <el-tab-pane label="审核通过" name="4"></el-tab-pane>
+        </el-tabs>
+      </el-col>
+      <el-col :span="8" style="text-align: right;">
+        <div style="margin-top: 5px;">
+          <el-button 
+            type="primary" 
+            icon="el-icon-check" 
+            size="mini" 
+            :disabled="selectedRows.length === 0"
+            @click="handleBatchAudit"
+            v-hasPermi="['store:storeOrderAudit:audit']"
+          >
+            批量审核
+          </el-button>
+          <span style="margin-left: 10px; color: #999;">
+            已选择 {{ selectedRows.length }} 项
+          </span>
+        </div>
+      </el-col>
+    </el-row>
+
+    <el-table height="500" 
+    border 
+    v-loading="loading" 
+    :data="storeOrderAuditList" 
+    :key="tableKey"
+    class="scrollable-table"
+    @selection-change="handleSelectionChange"
+    >
+      <el-table-column type="selection" width="55" fixed="left"></el-table-column>
+      <el-table-column label="订单号" align="center" prop="orderCode" width="200" fixed="left" />
+      <el-table-column label="所属公司" align="center" prop="companyName" />
+      <el-table-column label="所属员工" align="center" prop="companyUserName" />
+      <el-table-column label="审核状态" align="center" prop="status" width="150">
+        <template slot-scope="scope">
+          <el-tag prop="status" v-for="item in statusOptions"
+                  :type="item.type"
+                  v-if="scope.row.auditStatus === item.dictValue">
+            {{item.dictLabel}}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="订单类型" align="center" prop="orderType" width="150" >
+          <template slot-scope="scope">
+              <el-tag prop="status" v-for="(item, index) in orderTypeOptions"    v-if="scope.row.orderType==item.dictValue">{{item.dictLabel}}</el-tag>
+          </template>
+      </el-table-column>
+      <el-table-column label="收件人" align="center" prop="realName" />
+      <el-table-column label="送货地址" align="center" prop="userAddress" show-overflow-tooltip/>
+      <el-table-column label="实收金额" align="center" prop="payPrice">
+        <template slot-scope="scope">
+          {{ scope.row.payPrice ? parseFloat(scope.row.payPrice).toFixed(2) : '0.00' }}
+        </template>
+      </el-table-column>
+      <el-table-column label="订单金额" align="center" prop="totalPrice">
+        <template slot-scope="scope">
+          {{ scope.row.totalPrice ? parseFloat(scope.row.totalPrice).toFixed(2) : '0.00' }}
+        </template>
+      </el-table-column>
+      <el-table-column label="手机号" align="center" prop="userPhone"  width="150"/>
+      <el-table-column label="审核时间" align="center" prop="companyAuditTime"  width="180"/>
+      <el-table-column label="审核人" align="center" prop="companyAuditUserName" />
+      <!-- <el-table-column label="总后台审核时间" align="center" prop="adminAuditTime" />
+      <el-table-column label="总后台审核人" align="center" prop="adminAuditUserName" /> -->
+      <el-table-column label="被拒原因" align="center" prop="reason" show-overflow-tooltip />
+      <!-- <el-table-column label="提交时间" align="center" prop="createTime" /> -->
+      <el-table-column label="创建时间" align="center" prop="orderCreateTime" width="180"/>
+      <el-table-column label="操作" fixed="right" align="center" width="80" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-popover
+            v-if="scope.row.auditStatus === 0"
+            placement="right"
+            trigger="click"
+            :ref="'popover_' + scope.row.id">
+            <el-button size="mini" type="success" @click="handlePass(scope.row)">通过</el-button>
+            <el-button size="mini" type="danger" @click="handleAudit(scope.row)">拒绝</el-button>
+            <el-button
+              slot="reference"
+              size="mini"
+              type="text"
+              v-hasPermi="['store:storeOrderAudit:audit']"
+            >审核订单</el-button>
+          </el-popover>
+        </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="show.title" :visible.sync="show.open" width="500px" append-to-body>
+      <el-form ref="auditForm" :model="auditForm" :rules="auditRules" label-width="80px">
+        <el-form-item label="拒绝原因" prop="reviewContent">
+          <el-input
+            type="textarea"
+            v-model="auditForm.reviewContent"
+            placeholder="请输入拒绝原因"
+          />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="handleReject">确定</el-button>
+        <el-button @click="cancelAudit">关闭</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 添加批量审核确认对话框 -->
+    <el-dialog 
+      title="批量审核确认" 
+      :visible.sync="batchAuditDialogVisible" 
+      width="500px" 
+      append-to-body
+    >
+      <p>确定要审核通过选中的 {{ selectedRows.length }} 个订单吗?</p>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="batchAuditDialogVisible = false">取 消</el-button>
+        <el-button type="primary" @click="confirmBatchAudit"
+        v-hasPermi="['store:storeOrderAudit:auditBatch']">确 定</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { getCompanyList } from "@/api/company/company";
+import {list, audit,auditBatch} from "@/api/hisStore/storeOrderAudit";
+
+export default {
+  name: "StoreOrderAudit",
+  data() {
+    const statusOptions = [
+      {
+        dictLabel: "待审批",
+        dictValue: 0,
+        type: ''
+      },
+      {
+        dictLabel: "审批拒绝",
+        dictValue: 1,
+        type: 'danger'
+      },
+      {
+        dictLabel: "审核通过",
+        dictValue: 4,
+        type: 'success'
+      }
+    ]
+
+    return {
+      
+      tableKey: 0,
+      showSearch: true,
+      companys: [],
+      total: 0,
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        companyUserName: null,
+        auditStatus: null,
+        realName:null,
+        userPhone: null,
+        orderAmountSmall: null,
+        orderAmountBig: null,
+        beginTime:null,// 审单日期
+        endTime:null,
+        orderType: null,
+        auditBeginTime:null,
+        auditEndTime:null,
+      },
+      selectedRows: [], // 添加这行来存储选中的行
+       batchAuditDialogVisible: false, // 批量审核对话框显示控制
+      createTimeRange:[],
+      auditRange:[], // 审核时间
+      orderTypeOptions:[], // 订单类型
+      activeName: '00',
+      loading: false,
+      storeOrderAuditList: [],
+      statusOptions: statusOptions,
+      show: {
+        open: false,
+        title: "审核订单"
+      },
+      auditForm: {
+        auditId: null,
+        reviewType: null,
+        reviewContent: ''
+      },
+      auditRules: {
+        reviewContent: [
+          { required: true, message: "请输入拒绝原因", trigger: "blur" }
+        ]
+      },
+      currentRowId: null,
+    }
+  },
+  created() {
+    this.getDicts("store_order_type").then((response) => {
+      this.orderTypeOptions = response.data;
+    });
+    this.getCompanyOptions()
+    this.handleQuery()
+  },
+  activated() {
+    this.tableKey = Date.now()
+  },
+  methods: {
+    // 确认批量审核
+    confirmBatchAudit() {
+      // 这里需要调用批量审核接口,假设有一个批量审核的API
+      
+        const ids = this.selectedRows.map(item => item.id).join(",");
+  
+      // 示例:调用批量审核API
+      auditBatch({ids}).then(response => {
+        this.$message.success("批量审核完成");
+        this.batchAuditDialogVisible = false;
+        this.getList(); // 刷新列表
+      }).catch(error => {
+        this.$message.error("批量审核失败");
+      });
+      
+      // 由于没有提供批量审核API,这里只是演示逻辑
+      console.log("批量审核订单IDs:", auditIds);
+      this.batchAuditDialogVisible = false;
+      this.getList(); // 实际使用时需要刷新列表
+    },
+     // 批量审核处理
+    handleBatchAudit() {
+      if (this.selectedRows.length === 0) {
+        this.$message.warning("请至少选择一条数据");
+        return;
+      }
+      this.batchAuditDialogVisible = true;
+    },
+    // 多选
+    handleSelectionChange(selection) {
+      this.selectedRows = selection;
+    },
+    getCompanyOptions() {
+      getCompanyList().then(response => {
+        this.companys = response.data
+      })
+    },
+    handleQuery() {
+      this.queryParams.pageNum = 1
+      this.getList()
+    },
+    resetQuery() {
+      // 手动重置时间范围
+      this.createTimeRange = [];
+      this.queryParams.beginTime = null;
+      this.queryParams.endTime = null;
+      
+      // 手动重置订单金额范围
+      this.queryParams.orderAmountSmall = null;
+      this.queryParams.orderAmountBig = null;
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    getList() {
+      this.loading = true
+      if(this.createTimeRange!=null&&this.createTimeRange.length==2){
+        this.queryParams.beginTime=this.createTimeRange[0]
+        this.queryParams.endTime=this.createTimeRange[1]
+      }
+      if(this.auditRange!=null&&this.auditRange.length==2){
+        this.queryParams.auditBeginTime=this.auditRange[0]
+        this.queryParams.auditEndTime=this.auditRange[1]
+      }
+      list(this.queryParams).then(response => {
+        const {rows, total} = response
+        this.storeOrderAuditList = rows
+        this.total = total
+        this.loading = false
+      })
+    },
+    handleClick(tab) {
+      this.queryParams.auditStatus = tab.name === '00' ? null : tab.name
+      this.handleQuery()
+    },
+    handleAudit(row) {
+      this.currentRowId = row.id
+      this.auditForm.auditId = row.id;
+      this.auditForm.reviewType = 0;
+      this.show.open = true;
+    },
+    handlePass(row) {
+      this.currentRowId = row.id
+      this.auditForm.auditId = row.id;
+      this.auditForm.reviewType = 1;
+      this.submitAudit();
+    },
+    handleReject() {
+      this.$refs["auditForm"].validate(valid => {
+        if (valid) {
+          this.submitAudit();
+        }
+      });
+    },
+    submitAudit() {
+      audit(this.auditForm).then(response => {
+        const {msg} = response
+        this.msgSuccess(msg);
+        this.show.open = false;
+        this.$refs['popover_' + this.currentRowId].doClose();
+        this.getList();
+        this.resetAuditForm();
+      });
+    },
+    cancelAudit() {
+      this.show.open = false;
+      this.resetAuditForm();
+    },
+    resetAuditForm() {
+      this.auditForm = {
+        auditId: null,
+        reviewType: null,
+        reviewContent: ''
+      };
+      this.$refs["auditForm"]?.resetFields();
+    }
+  }
+}
+</script>
+
+<style scoped>
+.scrollable-table {
+  display: block;
+  width: 100%;
+}
+
+.scrollable-table ::v-deep .el-table__body-wrapper {
+  overflow-x: auto;
+  overflow-y: auto;
+}
+
+.scrollable-table ::v-deep .el-table__header-wrapper {
+  overflow-x: auto;
+  overflow-y: hidden;
+}
+</style>

+ 66 - 7
src/views/qw/autoTags/dayPartingIndex.vue

@@ -66,8 +66,12 @@
       </el-table-column>
       <el-table-column label="添加的标签" align="center" prop="tagIdsName">
         <template slot-scope="scope">
-          <div v-for="name in scope.row.tagIdsName" style="display: inline;">
-            <el-tag type="success">{{ name }}</el-tag>
+          <div class="tag-container">
+            <div class="tag-list">
+              <div v-for="name in scope.row.tagIdsName" style="display: inline;">
+                <el-tag type="success">{{ name }}</el-tag>
+              </div>
+            </div>
           </div>
         </template>
       </el-table-column>
@@ -170,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',
@@ -519,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 数组
@@ -722,7 +742,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})
     },
 
     //选择标签弹窗
@@ -810,7 +830,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,
@@ -824,7 +844,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");
     },
     /** 搜索按钮操作 */
@@ -1027,5 +1047,44 @@ export default {
 .scroll-wrapper::-webkit-scrollbar-thumb {
   background: rgba(0, 0, 0, 0.2);
   border-radius: 3px;
+}
+ .tag-container {
+   max-height: 300px;
+   overflow-y: auto;
+   padding: 1px;
+   border: 1px solid #ebeef5;
+   border-radius: 1px;
+   background-color: #fafafa;
+ }
+.tag-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+}
+.scroll-hint {
+  text-align: center;
+  color: #909399;
+  font-size: 12px;
+  padding: 1px 0;
+}
+.container {
+  max-width: 800px;
+  margin: 0 auto;
+  padding: 10px;
+}
+.title {
+  text-align: center;
+  color: #303133;
+  margin-bottom: 30px;
+}
+.demo-table {
+  width: 100%;
+  margin-bottom: 30px;
+}
+.instructions {
+  background-color: #f5f7fa;
+  padding: 15px;
+  border-radius: 1px;
+  margin-bottom: 20px;
 }
 </style>

+ 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;">

+ 189 - 32
src/views/qw/contactWay/index.vue

@@ -110,6 +110,7 @@
         </el-row>
         <div style=" height: calc(100% - 40px); overflow-y: auto;">
           <el-table v-loading="loading" :data="contactWayList" @selection-change="handleSelectionChange" border>
+            <el-table-column label="id" align="center" prop="id" />
             <el-table-column label="名称" align="center" prop="name" />
             <el-table-column label="二维码" align="center" prop="qrCode" width="150px" >
               <template slot-scope="scope">
@@ -203,15 +204,27 @@
               <el-col :span="22">
                 <div style="background-color: #fbfbfb;padding: 15px;  border: 1px solid #e6e6e6; margin-bottom: 20px;">
                   <el-form :model="item"  label-width="80px">
-                    <el-form-item label="员工选择"  style="height: 50px;">
-                       <el-select v-model="item.userIds" remote multiple placeholder="请选择" filterable  style="width: 100%;">
-                         <el-option
-                           v-for="dict in companyUserList"
-                           :key="dict.qwUserId"
-                           :label="dict.qwUserName"
-                           :value="dict.qwUserId">
-                         </el-option>
-                       </el-select>
+                    <el-form-item label="员工选择">
+                      <div>
+                        <el-button
+                          size="medium"
+                          icon="el-icon-circle-plus-outline"
+                          plain
+                          @click="handleListUserForTimeSlot(index)">请选择使用员工
+                        </el-button>
+                      </div>
+                      <div style="margin-top: 10px;">
+                        <el-tag
+                          style="margin-left: 5px; margin-top: 5px;"
+                          size="medium"
+                          :key="user.id"
+                          v-for="user in item.userList || []"
+                          closable
+                          :disable-transitions="false"
+                          @close="handleCloseTimeSlotUser(index, user)">
+                          {{ user.qwUserName }}
+                        </el-tag>
+                      </div>
                     </el-form-item>
                     <el-form-item label="工作周期" prop="week" style="height: 50px;" >
                        <el-select v-model="item.week" remote multiple placeholder="请选择" filterable  style="width: 100%;">
@@ -255,14 +268,27 @@
            <el-link type="primary" class="el-icon-plus" :underline="false" @click='addUserTime()'>添加其他工作周期</el-link>
         </el-form-item>
         <el-form-item label=""  v-if="form.userType==1">
-         <el-select v-model="userIds" remote multiple placeholder="选择全天在线员工" filterable  style="width: 800px;" :change="userChange()">
-           <el-option
-             v-for="dict in companyUserList"
-             :label="dict.qwUserName"
-             :value="dict.qwUserId">
-           </el-option>
+          <div>
+            <el-button
+              size="medium"
+              icon="el-icon-circle-plus-outline"
+              plain
+              @click="handleListUser(form.type,form.sendType, true)">请选择使用员工
+            </el-button>
+          </div>
+          <div>
+            <el-tag
+              style="margin-left: 5px"
+              size="medium"
+              :key="user.id"
+              v-for="user in userSelectList"
+              closable
+              :disable-transitions="false"
+              @close="handleCloseGroupUser(user)">
+              {{ user.qwUserName }}
+            </el-tag>
+          </div>
 
-         </el-select>
         </el-form-item>
         <el-form-item label="员工添加上限"v-if="form.userType==1" prop="isUserLimit">
           <el-switch
@@ -594,7 +620,7 @@
           </div>
           <div v-if="fileFrom.videoId!=null">
             <el-form-item label="图文链接:"  label-width="100px" >
-              <el-tag type="warning" v-model="fileFrom.linkUrl='待生成'">选择的课程小节 即为卡片链接地址</el-tag>
+              <el-tag type="warning">选择的课程小节 即为卡片链接地址</el-tag>
             </el-form-item>
           </div>
           <div v-if="fileFrom.videoId!=null">
@@ -645,6 +671,9 @@
       </div>
     </el-dialog>
 
+    <el-dialog :title="listUser.title" :visible.sync="listUser.open" width="700px" append-to-body>
+      <qwUserList ref="QwUserList" @selectUserList="selectUserList"></qwUserList>
+    </el-dialog>
 
     </div>
         <el-drawer
@@ -666,9 +695,10 @@ import ImageUpload from '@/views/qw/material/ImageUpload.vue'
 import statisDetails from '@/views/qw/contactWay/statisDetails';
 import { getMyQwUserList,getMyQwCompanyList } from "@/api/qw/user";
 import {courseList, videoList} from "@/api/qw/sop";
+import qwUserList from "@/views/qw/user/qwUserList.vue";
 export default {
   name: "ContactWay",
-   components: { contactWayGroup,ImageUpload,statisDetails},
+   components: {qwUserList, contactWayGroup,ImageUpload,statisDetails},
   data() {
     return {
       // 遮罩层
@@ -678,7 +708,8 @@ export default {
       exportLoading: false,
       // 选中数组
       ids: [],
-      userTimeJson:[{userIds:null,week:[1,2,3,4,5,6,7],startTime:null,endTime:null}],
+      userTimeJson:[{userIds:null,userList:[],week:[1,2,3,4,5,6,7],startTime:null,endTime:null}],
+      currentEditingTimeSlotIndex: -1, // 当前正在编辑的时间段索引
       userIds:[],
       show:{
            title:"医院详情",
@@ -828,6 +859,13 @@ export default {
       tagGroupList:[],
       tagList:[],
       tagListForm:[],
+      // 选择使用员工相关参数
+      userSelectList: [],
+      //企业微信员工
+      listUser: {
+        title: "",
+        open: false
+      },
     };
   },
   created() {
@@ -1171,7 +1209,7 @@ export default {
       });
     },
     addUserTime(){
-      this.userTimeJson.push({userIds:null,week:[1,2,3,4,5,6,7],startTime:null,endTime:null})
+      this.userTimeJson.push({userIds:null,userList:[],week:[1,2,3,4,5,6,7],startTime:null,endTime:null})
     },
     delUserTime(index){
       this.userTimeJson.splice(index,1)
@@ -1269,9 +1307,11 @@ export default {
               userTimeJson: null,
               userType: 2
             };
-              this.userTimeJson=[{userIds:null,week:[1,2,3,4,5,6,7],startTime:null,endTime:null}];
+              this.userTimeJson=[{userIds:null,userList:[],week:[1,2,3,4,5,6,7],startTime:null,endTime:null}];
+              this.currentEditingTimeSlotIndex = -1;
               this.userLimitJson=[];
               this.userIds=[];
+              this.userSelectList=[];
               this.spareUserIds=[];
               this.tagListForm=[];
               this.closeWelcomeWord=[];
@@ -1331,15 +1371,42 @@ export default {
       getContactWay(id).then(response => {
         this.form = response.data;
         this.open = true;
-        if(this.form.userTimeJson!=null){
-          this.userTimeJson=JSON.parse(this.form.userTimeJson)
-        }else{ this.userTimeJson=[{userIds:null,week:[1,2,3,4,5,6,7],startTime:null,endTime:null}]}
+
+        // 根据 userType 区分处理
+        if(this.form.userType === 2) {
+          // 自动上下线模式 - 只回显 userTimeJson
+          if(this.form.userTimeJson!=null){
+            this.userTimeJson=JSON.parse(this.form.userTimeJson)
+            // 根据 userIds回显userList
+            this.userTimeJson.forEach(timeSlot => {
+              if (!timeSlot.userList) {
+                timeSlot.userList = [];
+              }
+              if (timeSlot.userIds && Array.isArray(timeSlot.userIds)) {
+                timeSlot.userList = timeSlot.userIds.map(qwUserId => {
+                  return this.companyUserList.find(u => u.qwUserId === qwUserId);
+                }).filter(u => u); // 过滤掉 undefined
+              }
+            });
+          }else{
+            this.userTimeJson=[{userIds:null,userList:[],week:[1,2,3,4,5,6,7],startTime:null,endTime:null}]
+          }
+        } else if(this.form.userType === 1) {
+          // 全天在线模式 - 只回显 userSelectList
+          if(this.form.userIds!=null){
+            this.userIds=JSON.parse(this.form.userIds)
+            this.userSelectList = this.userIds.map(qwUserId => {
+              return this.companyUserList.find(u => u.qwUserId === qwUserId);
+            }).filter(u => u); // 过滤掉 undefined
+          }else{
+            this.userIds=[]
+            this.userSelectList=[]
+          }
+        }
+
         if(this.form.userLimitJson!=null){
           this.userLimitJson=JSON.parse(this.form.userLimitJson)
         }else{ this.userLimitJson=[]}
-        if(this.form.userIds!=null){
-          this.userIds=JSON.parse(this.form.userIds)
-        }else{ this.userIds=[]}
         if(this.form.spareUserIds!=null){
           this.spareUserIds=JSON.parse(this.form.spareUserIds)
         }else{ this.spareUserIds=[]}
@@ -1361,10 +1428,23 @@ export default {
       this.$refs["form"].validate(valid => {
         if (valid) {
           this.form.corpId=this.queryParams.corpId
+          if(this.form.userType === 1){
+            if(Object.keys(this.userIds).length === 0){
+              return this.$message({
+                message: '请选择客服类型-使用员工',
+                type: 'warning'
+              });
+            }
+          }
           if(this.form.userType==2){
             var jsonUserIds=[];
             for (let i = 0; i < this.userTimeJson.length; i++) {
-                if(this.userTimeJson[i].userIds==null||this.userTimeJson[i].userIds==""){
+                // 确保 userIds 与 userList 同步
+                if(this.userTimeJson[i].userList && this.userTimeJson[i].userList.length > 0){
+                  this.userTimeJson[i].userIds = this.userTimeJson[i].userList.map(u => u.qwUserId);
+                }
+
+                if(this.userTimeJson[i].userIds==null||this.userTimeJson[i].userIds=="" || this.userTimeJson[i].userIds.length==0){
                   return this.$message('人员不能为空');
                 }
                 if(this.userTimeJson[i].week==null||this.userTimeJson[i].week==""){
@@ -1378,9 +1458,7 @@ export default {
                 }
 
                 for (let j = 0; j < this.userTimeJson[i].userIds.length; j++) {
-                  console.log("!jsonUserIds.find(item=>item==this.userTimeJson[i].userIds[j])")
                   if(!jsonUserIds.find(item=>item==this.userTimeJson[i].userIds[j])){
-
                     jsonUserIds.push(this.userTimeJson[i].userIds[j]);
                   }
                 }
@@ -1391,7 +1469,12 @@ export default {
           }
           this.form.closeWelcomeWord=JSON.stringify(this.closeWelcomeWord)
           this.form.userIds=JSON.stringify(this.userIds)
-          this.form.userTimeJson=JSON.stringify(this.userTimeJson)
+          // 提交前移除 userList 字段(因为后端只需要 userIds)
+          const userTimeJsonForSubmit = this.userTimeJson.map(item => {
+            const {userList, ...rest} = item;
+            return rest;
+          });
+          this.form.userTimeJson=JSON.stringify(userTimeJsonForSubmit)
           this.form.userLimitJson=JSON.stringify(this.userLimitJson)
           this.form.spareUserIds=JSON.stringify(this.spareUserIds)
           this.form.tags=JSON.stringify(this.tagListForm)
@@ -1440,7 +1523,81 @@ export default {
           this.download(response.msg);
           this.exportLoading = false;
         }).catch(() => {});
-    }
+    },
+    //选择企微员工时
+    handleListUser(type, sendType, selectOne) {
+      setTimeout(() => {
+        this.$refs.QwUserList.getDetails(this.form.corpId, type, sendType, selectOne);
+      }, 1);
+
+      this.listUser.title = "选择企微员工"
+      this.listUser.open = true;
+
+    },
+    //企业微信员工信息子组件返回
+    selectUserList(list) {
+      this.listUser.open = false;
+      console.log("选择的员工",list)
+
+      // 判断是全天在线还是自动上下线
+      if (this.currentEditingTimeSlotIndex === -1) {
+        // 全天在线模式
+        list.forEach(obj => {
+          if (!this.userSelectList.some(item => item.id === obj.id)) {
+            this.userSelectList.push(obj);  // 存储完整对象
+            this.userIds.push(obj.qwUserId);
+          }
+        });
+      } else {
+        // 自动上下线模式 - 为特定时间段添加员工
+        const timeSlot = this.userTimeJson[this.currentEditingTimeSlotIndex];
+        if (!timeSlot.userList) {
+          this.$set(timeSlot, 'userList', []);
+        }
+        if (!timeSlot.userIds) {
+          this.$set(timeSlot, 'userIds', []);
+        }
+
+        list.forEach(obj => {
+          if (!timeSlot.userList.some(item => item.id === obj.id)) {
+            timeSlot.userList.push(obj);
+            timeSlot.userIds.push(obj.qwUserId);
+          }
+        });
+
+        // 重置索引
+        this.currentEditingTimeSlotIndex = -1;
+      }
+    },
+    // 全天在线模式 - 删除员工标签
+    handleCloseGroupUser(user) {
+      const index = this.userSelectList.findIndex(t => t.id === user.id);
+      if (index !== -1) {
+        this.userSelectList.splice(index, 1);
+        this.userIds.splice(index, 1);
+      }
+    },
+    // 自动上下线模式 - 打开员工选择弹窗
+    handleListUserForTimeSlot(timeSlotIndex) {
+      this.currentEditingTimeSlotIndex = timeSlotIndex;
+      setTimeout(() => {
+        this.$refs.QwUserList.getDetails(this.form.corpId, this.form.type, this.form.sendType, true);
+      }, 1);
+      this.listUser.title = "选择企微员工"
+      this.listUser.open = true;
+    },
+    // 自动上下线模式 - 删除时间段员工标签
+    handleCloseTimeSlotUser(timeSlotIndex, user) {
+      const timeSlot = this.userTimeJson[timeSlotIndex];
+      const userListIndex = timeSlot.userList.findIndex(t => t.id === user.id);
+      if (userListIndex !== -1) {
+        // 使用 splice 删除并强制更新
+        timeSlot.userList.splice(userListIndex, 1);
+        timeSlot.userIds.splice(userListIndex, 1);
+        // 强制更新整个 userTimeJson 数组以触发响应式更新
+        this.$set(this.userTimeJson, timeSlotIndex, {...timeSlot});
+      }
+    },
   }
 };
 </script>

+ 192 - 0
src/views/qw/externalContact/collection.vue

@@ -0,0 +1,192 @@
+<template>
+    <div class="app-container">
+        <el-form ref="form" :model="form" label-width="140px">
+            <el-button v-if="form.id" size="mini" type="text" @click="handleShare" icon="el-icon-coin"
+                v-hasPermi="['hisStore:collection:WxaCodeCollectionUnLimit']">分享
+            </el-button>
+            <el-form-item label="信息模板" prop="questionId">
+                <el-select @change="selectQuestion" v-model="form.questionId" placeholder="请选择问答">
+                    <el-option v-for="dict in questionOptions" :key="dict.dictValue" :label="dict.dictLabel"
+                        :value="parseInt(dict.dictValue)" />
+                </el-select>
+            </el-form-item>
+            <div v-for="(answer, index) in form.answers">
+                <div style="margin-bottom: 20px;margin-top: 20px;">
+                    <span style="font-size: 15px;font-weight: bold;    margin-left: 31px">{{ answer.title }}</span>
+                </div>
+                <div style="margin-left: 31px;">
+                    <el-checkbox-group :disabled="form.answers[index].flag" v-model="form.answers[index].value" size="mini" >
+                        <el-checkbox v-for="dict in answer.options" :label="dict.value" >{{ dict.name }}</el-checkbox>
+                    </el-checkbox-group>
+                    <!-- <el-radio-group :disabled="form.answers[index].flag" v-model="form.answers[index].value">
+                        <el-radio v-for="dict in answer.options" :label="dict.value">{{ dict.name }}</el-radio>
+                    </el-radio-group> -->
+                </div>
+
+            </div>
+            <el-form-item label="是否关联套餐包" prop="isPackage">
+                <el-radio-group v-model="form.isPackage">
+                    <el-radio :label="0">否</el-radio>
+                    <el-radio :label="1">是</el-radio>
+                </el-radio-group>
+            </el-form-item>
+            <el-form-item v-if="form.isPackage == 1" label="套餐包" prop="packageId">
+                <el-select filterable v-model="form.packageId" placeholder="请选择套餐包">
+                    <el-option v-for="dict in privatePackageOptions" :key="dict.dictValue" :label="dict.dictLabel"
+                        :value="parseInt(dict.dictValue)" />
+                </el-select>
+            </el-form-item>
+            <el-form-item v-if="form.packageId && form.isPackage && form.isPackage == 1" label="支付类型" prop="payType">
+                <el-radio-group v-model="form.payType">
+                    <el-radio :label="1">全款</el-radio>
+                    <el-radio :label="2">物流代收</el-radio>
+                </el-radio-group>
+            </el-form-item>
+            <el-form-item v-if="form.packageId && form.payType == 2 && form.isPackage &&   form.isPackage == 1" label="物流代收金额" prop="amount">
+                <el-input v-model="form.amount" placeholder="请输入物流代收金额" type="number" />
+            </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 :title="collectionForm.title" v-if="collectionForm.open" :visible.sync="collectionForm.open"
+            width="450px" append-to-body>
+            <div style="padding-bottom:15px;">
+                <img :src="codeImage" width="400px">
+            </div>
+            <div slot="footer" class="dialog-footer">
+                <el-button @click="downloadImage(codeImage, collectionForm.name + '.png')">下载二维码</el-button>
+            </div>
+        </el-dialog>
+    </div>
+</template>
+
+<script>
+import { questionOptions, getAnswer } from "@/api/hisStore/answer";
+import { allPrivatePackage } from "@/api/store/package";
+import { getInfo, addCollection, updateCollection, getWxaCodeCollectionUnLimit } from "@/api/hisStore/collection";
+export default {
+    name: "collection",
+    components: {},
+    data() {
+        return {
+            form: {
+                answers: []
+            },
+            userId: null,
+            questionOptions: [],
+            privatePackageOptions: [],
+            collectionForm: {
+                open: false,
+                title: "用户信息采集分享",
+                name: null,
+            },
+            codeImage: null,
+        };
+    },
+    created() {
+        this.getQuestionOptions();
+        this.getAllPrivatePackge();
+    },
+    methods: {
+        downloadImage(imageSrc, fileName) {
+            const link = document.createElement('a');
+            link.href = imageSrc;
+            link.download = fileName || '付款二维码.png';
+            document.body.appendChild(link);
+            link.click();
+            document.body.removeChild(link);
+        },
+        handleShare() {
+            let loadingRock = this.$loading({
+                lock: true,
+                text: '生成二维码中~~请不要刷新页面!!',
+                spinner: 'el-icon-loading',
+                background: 'rgba(0, 0, 0, 0.7)'
+            });
+
+            getWxaCodeCollectionUnLimit(this.form.id).then(response => {
+                this.codeImage = response.url
+                this.collectionForm.open = true;
+                this.collectionForm.name = this.form.id;
+                loadingRock.close();
+            }).finally(res => {
+                loadingRock.close();
+            })
+        },
+        //获取问答模板
+        getQuestionOptions() {
+            console.log("this.userId", this.userId)
+            questionOptions().then(response => {
+                this.questionOptions = response.rows;
+            })
+        },
+        getAllPrivatePackge() {
+            allPrivatePackage().then(res => {
+                this.privatePackageOptions = res.rows;
+            })
+        },
+        getCollectionInfo(userId) {
+            const queryParams = {
+                userId: userId,
+            }
+            this.userId = userId;
+            getInfo(queryParams).then(res => {
+                this.form = res.data;
+            })
+        },
+        //选择问答模板
+        selectQuestion(val) {
+            console.log(val)
+            this.form = {
+                answers: []
+            };
+            const queryParams = {
+                userId: this.userId,
+                questionId: val
+            }
+            getInfo(queryParams).then(res => {
+                this.form = res.data;
+            })
+            // getAnswer(val).then(response =>{
+
+            //     this.form.answers = response.data.answers;
+            //     console.log(this.form)
+            // })
+        },
+
+        submitForm() {
+            console.log(this.form)
+            this.form.userId = this.userId;
+            this.$refs["form"].validate(valid => {
+                if (valid) {
+                    if (this.form.id != null) {
+                        updateCollection(this.form).then(res => {
+                            this.msgSuccess("修改成功");
+                            this.open = false;
+                            this.$parent.$parent.closeCollection();
+                        })
+
+                    } else {
+                        addCollection(this.form).then(res => {
+                            this.msgSuccess("添加成功");
+                            this.open = false;
+                            this.$parent.$parent.closeCollection();
+                        })
+                    }
+                }
+            });
+
+        },
+        cancel() {
+
+        },
+    },
+};
+
+
+
+</script>

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

@@ -332,10 +332,19 @@
       </el-table-column>
       <el-table-column label="备注" align="center" prop="remark" />
       <el-table-column label="描述信息" align="center" prop="description" />
-      <el-table-column label="标签" align="center" prop="tagIdsName" width="250px">
+      <el-table-column label="标签" align="center" prop="tagIdsName" width="300px">
         <template slot-scope="scope">
-          <div v-for="name in scope.row.tagIdsName"  style="display: inline;">
-            <el-tag type="success">{{ name }}</el-tag>
+          <div class="tag-container">
+            <div class="tag-list">
+              <el-tag
+                v-for="name in scope.row.tagIdsName"
+                :key="name"
+                type="success"
+                size="small"
+              >
+                {{ name }}
+              </el-tag>
+            </div>
           </div>
         </template>
       </el-table-column>
@@ -350,6 +359,7 @@
           <dict-tag :options="ratingType" :value="scope.row.level"/>
         </template>
       </el-table-column>
+      <el-table-column label="state参数" align="center" prop="state" width="100px" />
       <el-table-column label="等级状态" align="center" prop="levelType" width="120px" >
         <template slot-scope="scope">
           <dict-tag :options="ratingUpFall" :value="scope.row.levelType"/>
@@ -2094,4 +2104,44 @@ export default {
   background: rgba(0, 0, 0, 0.2);
   border-radius: 3px;
 }
+
+.tag-container {
+  max-height: 200px;
+  overflow-y: auto;
+  padding: 1px;
+  border: 1px solid #ebeef5;
+  border-radius: 1px;
+  background-color: #fafafa;
+}
+.tag-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+}
+.scroll-hint {
+  text-align: center;
+  color: #909399;
+  font-size: 12px;
+  padding: 1px 0;
+}
+.container {
+  max-width: 800px;
+  margin: 0 auto;
+  padding: 10px;
+}
+.title {
+  text-align: center;
+  color: #303133;
+  margin-bottom: 30px;
+}
+.demo-table {
+  width: 100%;
+  margin-bottom: 30px;
+}
+.instructions {
+  background-color: #f5f7fa;
+  padding: 15px;
+  border-radius: 1px;
+  margin-bottom: 20px;
+}
 </style>

+ 349 - 8
src/views/qw/externalContact/index.vue

@@ -20,6 +20,15 @@
           @keyup.enter.native="handleQuery"
         />
       </el-form-item>
+      <el-form-item label="活码id" prop="wayId">
+        <el-input
+          v-model="queryParams.wayId"
+          placeholder="请输入活码id"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
 
       <el-form-item label="销售企微昵称" prop="qwUserName">
         <el-input
@@ -74,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
@@ -344,14 +359,34 @@
       </el-table-column>
       <el-table-column label="备注" align="center" prop="remark" />
       <el-table-column label="描述信息" align="center" prop="description" />
-      <el-table-column label="标签" align="center" prop="tagIdsName" width="250px">
+      <el-table-column label="标签" align="center" prop="tagIdsName" width="300px">
+<!--        <template slot-scope="scope">-->
+<!--          <div v-for="name in scope.row.tagIdsName"  style="display: inline;">-->
+<!--          <el-tag type="success">{{ name }}</el-tag>-->
+<!--          </div>-->
+<!--        </template>-->
         <template slot-scope="scope">
-          <div v-for="name in scope.row.tagIdsName"  style="display: inline;">
-          <el-tag type="success">{{ name }}</el-tag>
+          <div class="tag-container">
+            <div class="tag-list">
+              <el-tag
+                v-for="name in scope.row.tagIdsName"
+                :key="name"
+                type="success"
+                size="small"
+              >
+                {{ name }}
+              </el-tag>
+            </div>
           </div>
         </template>
       </el-table-column>
-
+      <el-table-column label="是否回复" align="center" prop="isReply" width="120px" >
+        <template slot-scope="scope">
+          <span v-if="scope.row.isReply === 1"><el-tag type="success">已回复</el-tag></span>
+          <span v-else-if="scope.row.isReply === 0"><el-tag type="info">未回复</el-tag></span>
+          <span v-else>{{ scope.row.isReply }}</span>
+        </template>
+      </el-table-column>
       <el-table-column label="状态" align="center" prop="status" width="120px" >
         <template slot-scope="scope">
           <dict-tag :options="statusOptions" :value="scope.row.status"/>
@@ -362,6 +397,9 @@
           <dict-tag :options="ratingType" :value="scope.row.level"/>
         </template>
       </el-table-column>
+
+      <el-table-column label="state参数" align="center" prop="state" width="100px" />
+
       <el-table-column label="等级状态" align="center" prop="levelType" width="120px" >
         <template slot-scope="scope">
           <dict-tag :options="ratingUpFall" :value="scope.row.levelType"/>
@@ -390,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>
@@ -460,6 +512,31 @@
              @click="handledetails(scope.row)"
              >AI获取用户信息
           </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="scope.row.fsUserId"
+             >
+             <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>
     </el-table>
@@ -476,6 +553,13 @@
       <customer-details  ref="customerDetails" @refreshList="refreshList"/>
     </el-drawer>
 
+    <el-drawer
+        :with-header="false"
+        size="75%"
+          :title="show.title" :visible.sync="show.open">
+      <userDetails  ref="userDetails" />
+    </el-drawer>
+
 
     <!--  搜索标签   -->
     <el-dialog :title="changeTagDialog.title" :visible.sync="changeTagDialog.open" style="width:100%;height: 100%" append-to-body>
@@ -707,6 +791,70 @@
       <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="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>
@@ -719,6 +867,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>
 
@@ -738,7 +909,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";
@@ -747,14 +918,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},
+  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,
@@ -775,6 +982,10 @@ export default {
         title:"用户信息",
         open:false,
       },
+      collection:{
+        titile:"信息采集",
+        open:false,
+      },
       // ...其他已有数据
       qwUserSuggestions: [],       // 已展示的数据
       showQwUserDropdown: false,   // 控制下拉框显示
@@ -924,8 +1135,10 @@ export default {
         eTime:null,
         createTime:null,
         level:null,
+        wayId:null,
         levelType:null,
-        companyUser:null
+        companyUser:null,
+        userRepeat: null
       },
       selectTags:[],
       // 表单参数
@@ -935,10 +1148,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;
@@ -985,6 +1202,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
     },
@@ -1077,6 +1343,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;
@@ -1819,6 +2112,14 @@ export default {
       this.notesOpen.filter = false;
 
     },
+    notesCancel(){
+      this.notesOpen={
+        open: false,
+        notes: null,
+        type: 1,
+        nameType:3,
+      }
+    },
     notesSubmitForm() {
 
       if (this.notesOpen.notes == null || this.notesOpen.notes == "") {
@@ -1954,4 +2255,44 @@ export default {
   background: rgba(0, 0, 0, 0.2);
   border-radius: 3px;
 }
+
+.tag-container {
+  max-height: 200px;
+  overflow-y: auto;
+  padding: 1px;
+  border: 1px solid #ebeef5;
+  border-radius: 1px;
+  background-color: #fafafa;
+}
+.tag-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+}
+.scroll-hint {
+  text-align: center;
+  color: #909399;
+  font-size: 12px;
+  padding: 1px 0;
+}
+.container {
+  max-width: 800px;
+  margin: 0 auto;
+  padding: 10px;
+}
+.title {
+  text-align: center;
+  color: #303133;
+  margin-bottom: 30px;
+}
+.demo-table {
+  width: 100%;
+  margin-bottom: 30px;
+}
+.instructions {
+  background-color: #f5f7fa;
+  padding: 15px;
+  border-radius: 1px;
+  margin-bottom: 20px;
+}
 </style>

+ 345 - 10
src/views/qw/externalContact/myExternalContact.vue

@@ -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
@@ -258,7 +264,7 @@
           plain
           size="mini"
           @click="addUserTag"
-
+          v-hasPermi="['qw:externalContact:myAddTag']"
         >批量添加标签</el-button>
       </el-col>
       <el-col :span="1.5">
@@ -267,7 +273,7 @@
           plain
           size="mini"
           @click="delUserTag"
-
+          v-hasPermi="['qw:externalContact:myDelTag']"
         >批量移除标签</el-button>
       </el-col>
 	  <el-col :span="1.5">
@@ -336,13 +342,29 @@
       </el-table-column>
       <el-table-column label="备注" align="center" prop="remark" />
       <el-table-column label="描述信息" align="center" prop="description" />
-      <el-table-column label="标签" align="center" prop="tagIdsName" width="250px">
+      <el-table-column label="标签" align="center" prop="tagIdsName" width="300px">
         <template slot-scope="scope">
-          <div v-for="name in scope.row.tagIdsName" style="display: inline;">
-            <el-tag type="success">{{ name }}</el-tag>
+          <div class="tag-container">
+            <div class="tag-list">
+              <el-tag
+                v-for="name in scope.row.tagIdsName"
+                :key="name"
+                type="success"
+                size="small"
+              >
+                {{ name }}
+              </el-tag>
+            </div>
           </div>
         </template>
       </el-table-column>
+      <el-table-column label="是否回复" align="center" prop="isReply" width="120px" >
+        <template slot-scope="scope">
+          <span v-if="scope.row.isReply === 1"><el-tag type="success">已回复</el-tag></span>
+          <span v-else-if="scope.row.isReply === 0"><el-tag type="info">未回复</el-tag></span>
+          <span v-else>{{ scope.row.isReply }}</span>
+        </template>
+      </el-table-column>
       <el-table-column label="状态" align="center" prop="status" width="120px" >
         <template slot-scope="scope">
           <dict-tag :options="statusOptions" :value="scope.row.status"/>
@@ -353,6 +375,12 @@
           <dict-tag :options="ratingType" :value="scope.row.level"/>
         </template>
       </el-table-column>
+      <el-table-column label="下单次数" align="center" width="100px">
+        <template #default="scope">
+          {{ scope.row.orderCount && scope.row.orderCount !== 0 ? scope.row.orderCount : '' }}
+        </template>
+      </el-table-column>
+      <el-table-column label="state参数" align="center" prop="state" width="100px" />
       <el-table-column label="等级状态" align="center" prop="levelType" width="120px" >
         <template slot-scope="scope">
           <dict-tag :options="ratingUpFall" :value="scope.row.levelType"/>
@@ -380,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>
@@ -442,6 +484,22 @@
           >
             <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="scope.row.fsUserId"
+             >
+             <span>信息采集</span>
+          </el-button>
 
 <!--          <el-button v-if="scope.row.customerId"-->
 <!--            size="mini"-->
@@ -461,6 +519,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>
@@ -531,11 +598,11 @@
         <el-form-item label="身体状况" prop="physicalCondition">
           <el-input v-model="diagnosisForm.physicalCondition" type="textarea" placeholder="请输入内容" />
         </el-form-item>
-        
+
         <el-form-item label="初步诊断" prop="firstDiagnosis">
           <el-input v-model="diagnosisForm.firstDiagnosis" type="textarea" placeholder="请输入内容" />
         </el-form-item>
-        
+
         <el-form-item label="医生职称" prop="doctorDep">
           <el-input disabled v-model="diagnosisForm.doctorDep" placeholder="医生职称" />
         </el-form-item>
@@ -791,6 +858,77 @@
       </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="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>
@@ -809,6 +947,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>
 
@@ -830,7 +989,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";
@@ -845,11 +1004,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,
@@ -1001,7 +1203,9 @@ export default {
         lossTime:null,
         createTime:null,
         level:null,
-        levelType:null
+        levelType:null,
+        companyUser:null,
+        userRepeat: null
       },
 
       queryTagParams:{
@@ -1032,6 +1236,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;
@@ -1066,6 +1273,57 @@ 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.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) {
@@ -1233,6 +1491,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;
@@ -1963,6 +2249,14 @@ export default {
       this.notesOpen.filter = false;
 
     },
+    notesCancel(){
+      this.notesOpen={
+        open: false,
+        notes: null,
+        type: 1,
+        nameType:3,
+      }
+    },
     notesSubmitForm() {
 
       if (this.notesOpen.notes == null || this.notesOpen.notes == "") {
@@ -2066,4 +2360,45 @@ export default {
   background: rgba(0, 0, 0, 0.2);
   border-radius: 3px;
 }
+
+
+.tag-container {
+  max-height: 200px;
+  overflow-y: auto;
+  padding: 1px;
+  border: 1px solid #ebeef5;
+  border-radius: 1px;
+  background-color: #fafafa;
+}
+.tag-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+}
+.scroll-hint {
+  text-align: center;
+  color: #909399;
+  font-size: 12px;
+  padding: 1px 0;
+}
+.container {
+  max-width: 800px;
+  margin: 0 auto;
+  padding: 10px;
+}
+.title {
+  text-align: center;
+  color: #303133;
+  margin-bottom: 30px;
+}
+.demo-table {
+  width: 100%;
+  margin-bottom: 30px;
+}
+.instructions {
+  background-color: #f5f7fa;
+  padding: 15px;
+  border-radius: 1px;
+  margin-bottom: 20px;
+}
 </style>

+ 445 - 0
src/views/qw/externalContactTransfer/companyTransfer.vue

@@ -0,0 +1,445 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="110px">
+      <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="name">
+        <el-input
+          v-model="queryParams.name"
+          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="type">
+        <el-select v-model="queryParams.type" placeholder="请选择用户类别" clearable size="small">
+          <el-option
+            v-for="dict in typeOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="客户等级" prop="level">
+        <el-select v-model="queryParams.level" placeholder="客户等级" clearable size="small">
+          <el-option
+            v-for="dict in ratingType"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="性别" prop="gender">
+        <el-input
+          v-model="queryParams.gender"
+          placeholder="请输入性别"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="转接状态" prop="addWay">
+        <el-select v-model="queryParams.transferStatus" placeholder="转接状态" clearable size="small">
+          <el-option
+            v-for="dict in transferStatusOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="标签" prop="tagIds">
+        <el-select v-model="selectTags" remote multiple placeholder="请选择" filterable  style="width: 100%;">
+          <el-option
+            v-for="dict in tagList"
+            :label="dict.name"
+            :value="dict.tagId">
+          </el-option>
+        </el-select>
+      </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>
+        <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="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          :loading="exportLoading"
+          @click="handleExport"
+          v-hasPermi="['qw:externalContact:companyExport']"
+        >导出</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          size="mini"
+          @click="handleTransfer"
+          :disabled="multiple"
+          v-hasPermi="['qw:externalContact:companyTransfer']"
+        >分配客户</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="externalContactList" @selection-change="handleSelectionChange" border>
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="所属员工" align="center" prop="qwUserName" width="120px"/>
+      <el-table-column label="员工部门" align="center" prop="departmentName" width="120px"/>
+      <el-table-column label="客户名称" align="center" prop="name" />
+      <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="type">
+        <template slot-scope="scope">
+          <dict-tag :options="typeOptions" :value="scope.row.type"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="性别" align="center" prop="gender">
+        <template slot-scope="scope">
+          <dict-tag :options="genderOptions" :value="scope.row.gender"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="备注" align="center" prop="remark" />
+      <el-table-column label="描述信息" align="center" prop="description" />
+      <el-table-column label="标签" align="center" prop="tagIds" width="300px">
+        <template slot-scope="scope">
+          <div class="tag-container">
+            <div class="tag-list">
+              <div v-for="i in JSON.parse(scope.row.tagIds)" :key="i" style="display: inline;">
+                <el-tag type="success" v-for="ii in tagList" :key="ii.id" style="margin: 3px;" v-if="ii.tagId==i">{{ii.name}}</el-tag>
+              </div>
+            </div>
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column label="客户等级" align="center" prop="level" width="120px" >
+        <template slot-scope="scope">
+          <dict-tag :options="ratingType" :value="scope.row.level"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="添加时间" align="center" prop="createTime" width="100px"/>
+      <el-table-column label="状态" align="center" prop="status" width="100px">
+        <template slot-scope="scope">
+          <dict-tag :options="statusOptions" :value="scope.row.status"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="转接状态" align="center" prop="transferStatus" width="100px">
+        <template slot-scope="scope">
+          <dict-tag :options="transferStatusOptions" :value="scope.row.transferStatus"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="企业id" align="center" prop="corpId" />
+      <el-table-column label="备注电话号码" align="center" prop="remarkMobiles" width="150px">
+        <template slot-scope="scope">
+          <span v-for="i in JSON.parse(scope.row.remarkMobiles)" :key="i">{{i}}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="备注企业名称" align="center" prop="remarkCorpName" />
+      <el-table-column label="来源" align="center" prop="addWay" width="100px">
+        <template slot-scope="scope">
+          <dict-tag :options="addWayOptions" :value="scope.row.addWay"/>
+        </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">
+        <div style="background-color: rgb(239, 250, 255); margin: 10px;padding: 15px;">
+          <div>可将选中的客户转接给其他员工,进行后续服务</div>
+          <div>注意:90天内客户只能被转接一次,一个客户最多只能被转接两次</div>
+          <div>需等待总后台审核才会进行转接</div>
+        </div>
+
+        <el-form-item label="接替员工" prop="nickName">
+          <el-input style="width: 150px" disabled>
+            <template slot="prefix">
+              <el-button
+                plain
+                size="small"
+                type="success"
+                v-if="form.nickName">
+                {{ form.nickName }}
+              </el-button>
+            </template>
+          </el-input>
+        </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;">
+            <i class="el-icon-info"></i>
+            自定义转接的时候发给客户的消息内容(选填)ps:不填则是官方默认话术
+          </div>
+        </el-form-item>
+        <el-card>
+          <companyTransferQwUserSelect :corpId="queryParams.corpId" :companyId="companyId" @selectUser="selectUser"/>
+        </el-card>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary"
+                   @click="submitForm"
+                   :disabled="submitLoading"
+                   :loading="submitLoading">提交审核</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listTag } from '@/api/qw/tag'
+import { getMyQwCompanyList } from '@/api/qw/user'
+import { companyExtList, companyTransfer, exportExternalContact } from '@/api/qw/externalContact'
+import companyTransferQwUserSelect from '@/views/qw/externalContactTransfer/companyTransferQwUserSelect.vue'
+
+export default {
+  name: "companyTransfer",
+  components: { companyTransferQwUserSelect },
+  data() {
+    return {
+      loading: false,
+      showSearch: true,
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        corpId: null,
+        name: null,
+        qwUserName: null,
+        type: null,
+        level: null,
+        gender: null,
+        transferStatus: null,
+        tagIds: null,
+        createTime: null,
+        status: 0
+      },
+      myQwCompanyList:[],
+      tagList:[],
+      selectTags:[],
+      typeOptions: [],
+      ratingType: [],
+      transferStatusOptions:[],
+      exportLoading: false,
+      statusOptions: [],
+      genderOptions: [],
+      addWayOptions:[],
+      externalContactList: [],
+      total: 0,
+      ids: [],
+      multiple: true,
+      form: {
+        qwUserId: null,
+        nickName: null,
+        content: null
+      },
+      rules: {
+        nickName: [
+          { required: true, message: "接替员工不能为空", trigger: "blur" }
+        ]
+      },
+      open: false,
+      title: "",
+      companyId: null,
+      submitLoading: false,
+    };
+  },
+  created() {
+    // 从store中获取当前用户的companyId
+    this.companyId = this.$store.state.user.user?.companyId || null;
+
+    this.getQwCompanyList();
+    this.getDicts("sys_qw_externalContact_type").then(response => {
+      this.typeOptions = response.data;
+    });
+    this.getDicts("sys_user_sex").then(response => {
+      this.genderOptions = response.data;
+    });
+    this.getDicts("sys_qw_externalContact_addWay").then(response => {
+      this.addWayOptions = response.data;
+    });
+    this.getDicts("sys_qw_sop_rating_type").then(response => {
+      this.ratingType = response.data;
+    });
+    this.getDicts("sys_qw_external_contact_status").then(response => {
+      this.statusOptions = response.data;
+    });
+    this.getDicts("sys_qw_transfer_status").then(response => {
+      this.transferStatusOptions = response.data;
+    });
+  },
+  methods: {
+    handleQuery() {
+      this.queryParams.tagIds=this.selectTags.join(',')
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    resetQuery() {
+      this.queryParams = {
+        pageNum: 1,
+        pageSize: 10,
+        corpId: null,
+        name: null,
+        qwUserName: null,
+        type: null,
+        level: null,
+        gender: null,
+        transferStatus: null,
+        tagIds: null,
+        createTime: null,
+        status: 0
+      };
+      this.selectTags=[];
+      this.resetForm("queryForm");
+      this.queryParams.corpId = this.myQwCompanyList?.[0]?.dictValue || null
+      this.getList();
+    },
+    getList() {
+      this.loading = true
+      companyExtList(this.queryParams).then(response => {
+        this.externalContactList = response.rows;
+        this.total = response.total;
+        this.loading = false
+      })
+    },
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.multiple = !selection.length
+    },
+    updateCorpId(){
+      listTag({corpId:this.queryParams.corpId}).then(response => {
+        this.tagList = response.rows;
+      });
+      this.getList();
+    },
+    getQwCompanyList() {
+      getMyQwCompanyList().then(response => {
+        this.myQwCompanyList = response.data;
+        if(this.myQwCompanyList!=null){
+          this.queryParams.corpId=this.myQwCompanyList[0].dictValue;
+          listTag({corpId:this.queryParams.corpId}).then(response => {
+            this.tagList = response.rows;
+          });
+          this.getList();
+        }
+      });
+    },
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有企业微信客户数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        this.exportLoading = true;
+        return exportExternalContact(queryParams);
+      }).then(response => {
+        this.download(response.msg);
+        this.exportLoading = false;
+      }).catch(() => {});
+    },
+    reset() {
+      this.form = {
+        qwUserId: null,
+        nickName: null,
+        content: null
+      };
+      this.resetForm("form");
+    },
+    handleTransfer() {
+      this.reset()
+      if(!this.ids){
+        this.$message('请选择需要分配的客户')
+        return
+      }
+
+      this.submitLoading = false
+      this.title = "分配客户"
+      this.open = true
+    },
+    selectUser(selection) {
+      this.form.qwUserId = selection.id
+      this.form.nickName = selection.qwUserName
+    },
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+
+          if (this.submitLoading) {
+            return;
+          }
+          this.submitLoading = true
+
+          const params = {
+            ids: this.ids,
+            qwUserId: this.form.qwUserId,
+            content: this.form.content,
+            transferType: 1
+          }
+
+          companyTransfer(params).then(() => {
+            this.$message.success("审核已提交,等待管理员审核");
+            this.open = false;
+          })
+        }
+      });
+    }
+  },
+};
+</script>
+
+<style scoped>
+
+</style>

+ 144 - 0
src/views/qw/externalContactTransfer/companyTransferQwUserSelect.vue

@@ -0,0 +1,144 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
+      <el-form-item label="销售公司" prop="companyId">
+        <el-select v-model="queryParams.companyId" placeholder="销售公司"  size="small">
+          <el-option
+            v-for="dict in companyList"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="员工昵称" prop="nickName">
+        <el-input
+          v-model="queryParams.nickName"
+          placeholder="请输入员工昵称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">刷新/重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-table v-loading="loading" :data="userList" ref="userList" border>
+      <el-table-column label="归属销售公司" align="center" prop="companyName" />
+      <el-table-column label="企微员工账号" align="center" prop="qwUserId" />
+      <el-table-column label="员工昵称" align="center" prop="qwUserName" />
+      <el-table-column label="员工部门" align="center" prop="departmentName" />
+      <el-table-column label="员工昵称" align="center" prop="nickName"/>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="120px" >
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleSelectionChange(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="handlePaginationChange"
+    />
+  </div>
+</template>
+
+<script>
+import { companyQwUserlist, listUser } from '@/api/qw/user'
+import { getCompanyListByCorId } from '@/api/company/company'
+
+
+export default {
+  name: "companyTransferQwUserSelect",
+  props: {
+    corpId: {
+      type: String,
+      default: null
+    },
+    companyId: {
+      type: Number,
+      default: null
+    }
+  },
+  data() {
+    return {
+      // 遮罩层
+      loading: false,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 企微用户表格数据
+      userList: [],
+      companyList: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        companyId: null,
+        corpId: null,
+        nickName: null
+      },
+    };
+  },
+  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: {
+    /** 查询企微用户列表 */
+    getList() {
+      this.loading = true;
+      companyQwUserlist(this.queryParams).then(response => {
+        this.userList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    handlePaginationChange(row) {
+      this.queryParams.pageNum = row.page;
+      this.queryParams.pageSize = row.limit;
+      this.getList();
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.queryParams.corpId = this.corpId;
+      if (!Array.isArray(this.companyList) || this.companyList.length === 0) {
+        return
+      }
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      if (!Array.isArray(this.companyList) || this.companyList.length === 0) {
+        return
+      }
+      this.queryParams.companyId = this.companyList[0].dictValue;
+      this.handleQuery();
+    },
+    // 选中数据
+    handleSelectionChange(selection) {
+      this.$emit("selectUser",selection);
+    },
+  }
+};
+</script>

+ 48 - 3
src/views/qw/externalContactTransfer/deptTransferIndex.vue

@@ -164,10 +164,14 @@
       </el-table-column>
       <el-table-column label="备注" align="center" prop="remark" />
       <el-table-column label="描述信息" align="center" prop="description" />
-      <el-table-column label="标签" align="center" prop="tagIds" width="150px">
+      <el-table-column label="标签" align="center" prop="tagIds" width="300px">
         <template slot-scope="scope">
-          <div v-for="i in JSON.parse(scope.row.tagIds)" :key="i" style="display: inline;">
-          <el-tag type="success" v-for="ii in tagList" :key="ii.id" style="margin: 3px;" v-if="ii.tagId==i">{{ii.name}}</el-tag>
+          <div class="tag-container">
+            <div class="tag-list">
+              <div v-for="i in JSON.parse(scope.row.tagIds)" :key="i" style="display: inline;">
+                <el-tag type="success" v-for="ii in tagList" :key="ii.id" style="margin: 3px;" v-if="ii.tagId==i">{{ii.name}}</el-tag>
+              </div>
+            </div>
           </div>
         </template>
       </el-table-column>
@@ -522,3 +526,44 @@ export default {
   }
 };
 </script>
+<style  scoped>
+.tag-container {
+  max-height: 300px;
+  overflow-y: auto;
+  padding: 1px;
+  border: 1px solid #ebeef5;
+  border-radius: 1px;
+  background-color: #fafafa;
+}
+.tag-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+}
+.scroll-hint {
+  text-align: center;
+  color: #909399;
+  font-size: 12px;
+  padding: 1px 0;
+}
+.container {
+  max-width: 800px;
+  margin: 0 auto;
+  padding: 10px;
+}
+.title {
+  text-align: center;
+  color: #303133;
+  margin-bottom: 30px;
+}
+.demo-table {
+  width: 100%;
+  margin-bottom: 30px;
+}
+.instructions {
+  background-color: #f5f7fa;
+  padding: 15px;
+  border-radius: 1px;
+  margin-bottom: 20px;
+}
+</style>

+ 114 - 83
src/views/qw/externalContactTransfer/index.vue

@@ -30,20 +30,15 @@
           @keyup.enter.native="handleQuery"
         />
       </el-form-item>
-<el-form-item label="所属员工" prop="qwUserName">
-              <el-select @change="handleSelectChange" v-model="queryParams.qwUserName" remote filterable clearable reserve-keyword
-                         placeholder="请输入所属员工名称" :remote-method="qwUserMethod">
-                <el-option
-                  v-for="item in qwUserNameList"
-                  :key="item.id"
-                  :label="item.qwUserName"
-                  :value="item.qwUserName">
-                  <span style="float: left">{{ item.qwUserName }}</span>
-                </el-option>
-              </el-select>
-            </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="type">
         <el-select v-model="queryParams.type" placeholder="请选择用户类别" clearable size="small">
           <el-option
@@ -54,6 +49,16 @@
           />
         </el-select>
       </el-form-item>
+      <el-form-item label="客户等级" prop="level">
+        <el-select v-model="queryParams.level" placeholder="客户等级" clearable size="small">
+          <el-option
+            v-for="dict in ratingType"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
       <el-form-item label="性别" prop="gender">
         <el-input
           v-model="queryParams.gender"
@@ -82,6 +87,15 @@
           </el-option>
         </el-select>
       </el-form-item>
+      <el-form-item label="备注" prop="remark">
+        <el-input
+          v-model="queryParams.remark"
+          placeholder="请输入备注"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
       <el-form-item label="添加时间" prop="createTime">
         <el-date-picker clearable size="small"
                         v-model="queryParams.createTime"
@@ -126,15 +140,6 @@
           v-hasPermi="['qw:externalContact:transfer']"
         >分配客户</el-button>
       </el-col>
-      <el-col :span="1.5">
-        <el-button v-if="isQwUserISNull"
-          type="primary"
-          plain
-          size="mini"
-          @click="handleTransferAll"
-          v-hasPermi="['qw:externalContact:transfer']"
-        >分配此员工所有客户</el-button>
-      </el-col>
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
@@ -168,22 +173,20 @@
       </el-table-column>
       <el-table-column label="备注" align="center" prop="remark" />
       <el-table-column label="描述信息" align="center" prop="description" />
-      <el-table-column label="标签" align="center" prop="tagIds" width="150px">
+      <el-table-column label="标签" align="center" prop="tagIds" width="300px">
         <template slot-scope="scope">
-          <div v-for="i in JSON.parse(scope.row.tagIds)" :key="i" style="display: inline;">
-          <el-tag type="success" v-for="ii in tagList" :key="ii.id" style="margin: 3px;" v-if="ii.tagId==i">{{ii.name}}</el-tag>
+          <div class="tag-container">
+            <div class="tag-list">
+              <div v-for="i in JSON.parse(scope.row.tagIds)" :key="i" style="display: inline;">
+              <el-tag type="success" v-for="ii in tagList" :key="ii.id" style="margin: 3px;" v-if="ii.tagId==i">{{ii.name}}</el-tag>
+              </div>
+            </div>
           </div>
         </template>
       </el-table-column>
-      <el-table-column label="备注电话号码" align="center" prop="remarkMobiles" width="150px">
+      <el-table-column label="客户等级" align="center" prop="level" width="120px" >
         <template slot-scope="scope">
-          <span v-for="i in JSON.parse(scope.row.remarkMobiles)" :key="i">{{i}}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="备注企业名称" align="center" prop="remarkCorpName" />
-      <el-table-column label="来源" align="center" prop="addWay" width="100px">
-        <template slot-scope="scope">
-          <dict-tag :options="addWayOptions" :value="scope.row.addWay"/>
+          <dict-tag :options="ratingType" :value="scope.row.level"/>
         </template>
       </el-table-column>
       <el-table-column label="添加时间" align="center" prop="createTime" width="100px"/>
@@ -198,7 +201,17 @@
         </template>
       </el-table-column>
       <el-table-column label="企业id" align="center" prop="corpId" />
-
+      <el-table-column label="备注电话号码" align="center" prop="remarkMobiles" width="150px">
+        <template slot-scope="scope">
+          <span v-for="i in JSON.parse(scope.row.remarkMobiles)" :key="i">{{i}}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="备注企业名称" align="center" prop="remarkCorpName" />
+      <el-table-column label="来源" align="center" prop="addWay" width="100px">
+        <template slot-scope="scope">
+          <dict-tag :options="addWayOptions" :value="scope.row.addWay"/>
+        </template>
+      </el-table-column>
       <!-- <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
         <template slot-scope="scope">
           <el-button
@@ -217,7 +230,6 @@
       :total="total"
       :page.sync="queryParams.pageNum"
       :limit.sync="queryParams.pageSize"
-      :page-sizes="[100, 200, 300, 500]"
       @pagination="getList"
     />
 
@@ -245,6 +257,10 @@
         </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;">
+        <i class="el-icon-info"></i>
+        自定义转接的时候发给客户的消息内容(选填)ps:不填则是官方默认话术
+      </div>
 		</el-form-item>
 
         <el-card>
@@ -264,7 +280,7 @@
 <script>
 import { transfer,listExternalContact, getExternalContact, delExternalContact, addExternalContact, updateExternalContact, exportExternalContact } from "@/api/qw/externalContact";
 import { listTag, getTag, delTag, addTag, updateTag, exportTag } from "@/api/qw/tag";
-import { qwUserList,userList } from "@/api/qw/user";
+import { qwUserList } from "@/api/qw/user";
 import qwUserSelectOne from '@/views/qw/user/qwUserSelectOne.vue'
 import { getMyQwUserList,getMyQwCompanyList } from "@/api/qw/user";
 
@@ -294,8 +310,10 @@ export default {
       title: "",
       // 是否显示弹出层
       open: false,
+      filter: false,
       // 用户类别字典
       typeOptions: [],
+      ratingType: [],
       selectTags:[],
       // 性别字典
       genderOptions: [],
@@ -303,22 +321,16 @@ export default {
       addWayOptions: [],
       nickName:null,
       qwUserList:[],
-      isQwUserISNull:false,
-      qwUserNameList:[],
-      qwUserName:null,
-      type:'0',
-      qwUserNameParam:{
-        qwUserName:null
-      },
       // 查询参数
       queryParams: {
         pageNum: 1,
-        pageSize: 300,
+        pageSize: 10,
         userId: null,
         externalUserId: null,
         name: null,
         avatar: null,
         type: null,
+        remark:null,
         gender: null,
         description: null,
         tagIds: null,
@@ -367,6 +379,10 @@ export default {
     this.getDicts("sys_qw_externalContact_addWay").then(response => {
       this.addWayOptions = response.data;
     });
+    this.getDicts("sys_qw_sop_rating_type").then(response => {
+      this.ratingType = response.data;
+    });
+
 
     this.getDicts("sys_qw_external_contact_status").then(response => {
       this.statusOptions = response.data;
@@ -377,24 +393,6 @@ export default {
 
   },
   methods: {
-
-     qwUserMethod(query) {
-      if (query !== '') {
-        this.qwUserNameParam.qwUserName = query;
-        userList(this.qwUserNameParam).then(response => {
-          this.qwUserNameList = response.rows;
-        });
-      }
-    },
-     handleSelectChange(value) {
-    console.log('选中的值:', value);
-    if(value == ''){
-      this.qwUserNameList=null
-    this.isQwUserISNull = false;
-    }else{
-    this.isQwUserISNull = true;
-    }
-  },
     updateCorpId(){
       listTag({corpId:this.queryParams.corpId}).then(response => {
         this.tagList = response.rows;
@@ -454,8 +452,6 @@ export default {
     },
     /** 重置按钮操作 */
     resetQuery() {
-      this.qwUserNameList=null
-       this.isQwUserISNull = false;
       this.selectTags=[];
       this.resetForm("queryForm");
       this.handleQuery();
@@ -477,43 +473,38 @@ export default {
 
     handleTransfer(row) {
       this.reset();
-      this.type="0";
       if(this.ids==null||this.ids==""){
         return  this.$message('请选择需要分配的客户');
       }
-
       setTimeout(() => {
                     this.$refs.qwUserSelectOne.getDetails(this.queryParams.corpId);
        }, 1);
       this.open = true;
+      this.filter = false;
       this.title = "分配客户";
-
     },
 
-     handleTransferAll(row) {
-      this.reset();
-      this.qwUserName=this.queryParams.qwUserName;
-      this.type="1";
-      setTimeout(() => {
-                    this.$refs.qwUserSelectOne.getDetails(this.queryParams.corpId);
-       }, 1);
-      this.open = true;
-      this.title = "分配该员工所有客户";
-
-    },
     /** 提交按钮 */
     submitForm() {
       this.$refs["form"].validate(valid => {
 
 
+        let obj = JSON.parse(JSON.stringify(this.queryParams))
+        if(obj.tagIds == "" && obj.tagIds.length == 0){
+          obj.tagIds = null;
+        }
+        if(obj.tagIds !== null && obj.tagIds !== undefined){
+          obj.tagIds = obj.tagIds.split(",");
+        }
         if (valid) {
             var form={
               ids:this.ids,
-              qwUserName:this.qwUserName,
-              type:this.type,
+              addType: 0,
+              filter: this.filter,
+              param: obj,
               userId:this.form.userId,
               corpId:this.queryParams.corpId,
-              content:this.form.content,
+			        content:this.form.content,
             }
             transfer(form).then(response => {
               this.msgSuccess(response.msg);
@@ -523,7 +514,6 @@ export default {
 
         }
       });
-      this.qwUserName=null;
     },
     /** 删除按钮操作 */
     handleDelete(row) {
@@ -557,3 +547,44 @@ export default {
   }
 };
 </script>
+<style  scoped>
+.tag-container {
+  max-height: 300px;
+  overflow-y: auto;
+  padding: 1px;
+  border: 1px solid #ebeef5;
+  border-radius: 1px;
+  background-color: #fafafa;
+}
+.tag-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+}
+.scroll-hint {
+  text-align: center;
+  color: #909399;
+  font-size: 12px;
+  padding: 1px 0;
+}
+.container {
+  max-width: 800px;
+  margin: 0 auto;
+  padding: 10px;
+}
+.title {
+  text-align: center;
+  color: #303133;
+  margin-bottom: 30px;
+}
+.demo-table {
+  width: 100%;
+  margin-bottom: 30px;
+}
+.instructions {
+  background-color: #f5f7fa;
+  padding: 15px;
+  border-radius: 1px;
+  margin-bottom: 20px;
+}
+</style>

+ 604 - 0
src/views/qw/externalContactTransfer/index旧.vue

@@ -0,0 +1,604 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="110px">
+
+<!--      <el-form-item label="外部联系人账号" prop="externalUserId">-->
+<!--        <el-input-->
+<!--          v-model="queryParams.externalUserId"-->
+<!--          placeholder="外部联系人账号"-->
+<!--          clearable-->
+<!--          size="small"-->
+<!--          @keyup.enter.native="handleQuery"-->
+<!--        />-->
+<!--      </el-form-item>-->
+      <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="name">
+        <el-input
+          v-model="queryParams.name"
+          placeholder="请输入客户名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+<el-form-item label="所属员工" prop="qwUserName">
+              <el-select @change="handleSelectChange" v-model="queryParams.qwUserName" remote filterable clearable reserve-keyword
+                         placeholder="请输入所属员工名称" :remote-method="qwUserMethod">
+                <el-option
+                  v-for="item in qwUserNameList"
+                  :key="item.id"
+                  :label="item.qwUserName"
+                  :value="item.qwUserName">
+                  <span style="float: left">{{ item.qwUserName }}</span>
+                </el-option>
+              </el-select>
+            </el-form-item>
+
+
+      <el-form-item label="用户类别" prop="type">
+        <el-select v-model="queryParams.type" placeholder="请选择用户类别" clearable size="small">
+          <el-option
+            v-for="dict in typeOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="性别" prop="gender">
+        <el-input
+          v-model="queryParams.gender"
+          placeholder="请输入性别"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="转接状态" prop="addWay">
+        <el-select v-model="queryParams.transferStatus" placeholder="转接状态" clearable size="small">
+          <el-option
+            v-for="dict in transferStatusOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="标签" prop="tagIds">
+        <el-select v-model="selectTags" remote multiple placeholder="请选择" filterable  style="width: 100%;">
+          <el-option
+            v-for="dict in tagList"
+            :label="dict.name"
+            :value="dict.tagId">
+          </el-option>
+        </el-select>
+      </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>
+        <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
+          size="mini"
+          @click="handleAdd"
+          v-hasPermi="['qw:externalContact:add']"
+        >同步</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:externalContact:export']"
+        >导出</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          size="mini"
+          @click="handleTransfer"
+          v-hasPermi="['qw:externalContact:transfer']"
+        >分配客户</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button v-if="isQwUserISNull"
+          type="primary"
+          plain
+          size="mini"
+          @click="handleTransferAll"
+          v-hasPermi="['qw:externalContact:transfer']"
+        >分配此员工所有客户</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="externalContactList" @selection-change="handleSelectionChange" border>
+      <el-table-column type="selection" width="55" align="center" />
+
+      <el-table-column label="所属员工" align="center" prop="qwUserName" width="120px"/>
+      <el-table-column label="员工部门" align="center" prop="departmentName" width="120px"/>
+<!--      <el-table-column label="外部联系人账号" align="center" prop="externalUserId" width="120px"/>-->
+      <el-table-column label="客户名称" align="center" prop="name" />
+      <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="type">
+        <template slot-scope="scope">
+          <dict-tag :options="typeOptions" :value="scope.row.type"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="性别" align="center" prop="gender">
+        <template slot-scope="scope">
+          <dict-tag :options="genderOptions" :value="scope.row.gender"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="备注" align="center" prop="remark" />
+      <el-table-column label="描述信息" align="center" prop="description" />
+      <el-table-column label="标签" align="center" prop="tagIds" width="150px">
+        <template slot-scope="scope">
+          <div class="tag-container">
+            <div class="tag-list">
+              <div v-for="i in JSON.parse(scope.row.tagIds)" :key="i" style="display: inline;">
+              <el-tag type="success" v-for="ii in tagList" :key="ii.id" style="margin: 3px;" v-if="ii.tagId==i">{{ii.name}}</el-tag>
+              </div>
+            </div>
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column label="备注电话号码" align="center" prop="remarkMobiles" width="150px">
+        <template slot-scope="scope">
+          <span v-for="i in JSON.parse(scope.row.remarkMobiles)" :key="i">{{i}}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="备注企业名称" align="center" prop="remarkCorpName" />
+      <el-table-column label="来源" align="center" prop="addWay" width="100px">
+        <template slot-scope="scope">
+          <dict-tag :options="addWayOptions" :value="scope.row.addWay"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="添加时间" align="center" prop="createTime" width="100px"/>
+      <el-table-column label="状态" align="center" prop="status" width="100px">
+        <template slot-scope="scope">
+          <dict-tag :options="statusOptions" :value="scope.row.status"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="转接状态" align="center" prop="transferStatus" width="100px">
+        <template slot-scope="scope">
+          <dict-tag :options="transferStatusOptions" :value="scope.row.transferStatus"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="企业id" align="center" prop="corpId" />
+
+      <!-- <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="['qw:externalContact:edit']"
+          >修改</el-button>
+
+        </template>
+      </el-table-column> -->
+    </el-table>
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      :page-sizes="[100, 200, 300, 500]"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改企业微信客户对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <div style="background-color: rgb(239, 250, 255); margin: 10px;padding: 15px;">
+          <div>可将选中的客户转接给其他员工,进行后续服务</div>
+          <div>注意:90天内客户只能被转接一次,一个客户最多只能被转接两次</div>
+        </div>
+
+        <el-form-item label="接替员工" prop="userId">
+<!--          <el-button type="success" v-if="this.nickName">{{ nickName }}</el-button>-->
+          <el-input style="width: 150px" disabled>
+            <template slot="prefix">
+              <el-button
+                plain
+                size="small"
+                type="success"
+                v-if="this.nickName">
+                {{ nickName }}
+              </el-button>
+            </template>
+          </el-input>
+        </el-form-item>
+		<el-form-item label="消息内容" prop="content">
+		  <el-input v-model="form.content" placeholder="请输入内容" />
+		</el-form-item>
+
+        <el-card>
+          <qwUserSelectOne ref="qwUserSelectOne" @selectUser="selectUser"></qwUserSelectOne>
+        </el-card>
+
+
+      </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 { transfer,listExternalContact, getExternalContact, delExternalContact, addExternalContact, updateExternalContact, exportExternalContact } from "../../../api/qw/externalContact";
+import { listTag, getTag, delTag, addTag, updateTag, exportTag } from "@/api/qw/tag";
+import { qwUserList,userList } from "@/api/qw/user";
+import qwUserSelectOne from '@/views/qw/user/qwUserSelectOne.vue'
+import { getMyQwUserList,getMyQwCompanyList } from "@/api/qw/user";
+
+export default {
+  name: "ExternalContact",
+  components: { qwUserSelectOne },
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      myQwCompanyList:[],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 企业微信客户表格数据
+      externalContactList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 用户类别字典
+      typeOptions: [],
+      selectTags:[],
+      // 性别字典
+      genderOptions: [],
+      // 来源字典
+      addWayOptions: [],
+      nickName:null,
+      qwUserList:[],
+      isQwUserISNull:false,
+      qwUserNameList:[],
+      qwUserName:null,
+      type:'0',
+      qwUserNameParam:{
+        qwUserName:null
+      },
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 300,
+        userId: null,
+        externalUserId: null,
+        name: null,
+        avatar: null,
+        type: null,
+        gender: null,
+        description: null,
+        tagIds: null,
+        remarkMobiles: null,
+        remarkCorpName: null,
+        addWay: null,
+        operUserid: null,
+        corpId: null,
+        companyId: null,
+        status:0,
+        transferStatus:null
+      },
+      // 表单参数
+      form: {},
+      tagList:[],
+      transferStatusOptions:[],
+      statusOptions:[],
+      // 表单校验
+      rules: {
+        userId: [{ required: true, message: '请选择接替员工', trigger: 'blur' }],
+      }
+    };
+  },
+  created() {
+    getMyQwCompanyList().then(response => {
+            this.myQwCompanyList = response.data;
+            if(this.myQwCompanyList!=null){
+              this.queryParams.corpId=this.myQwCompanyList[0].dictValue;
+              listTag({corpId:this.queryParams.corpId}).then(response => {
+                this.tagList = response.rows;
+              });
+              qwUserList(this.queryParams.corpId).then(response => {
+                this.qwUserList = response.rows;
+              });
+
+              this.getList();
+            }
+    });
+
+    this.getDicts("sys_qw_externalContact_type").then(response => {
+      this.typeOptions = response.data;
+    });
+    this.getDicts("sys_user_sex").then(response => {
+      this.genderOptions = response.data;
+    });
+    this.getDicts("sys_qw_externalContact_addWay").then(response => {
+      this.addWayOptions = response.data;
+    });
+
+    this.getDicts("sys_qw_external_contact_status").then(response => {
+      this.statusOptions = response.data;
+    });
+    this.getDicts("sys_qw_transfer_status").then(response => {
+      this.transferStatusOptions = response.data;
+    });
+
+  },
+  methods: {
+
+     qwUserMethod(query) {
+      if (query !== '') {
+        this.qwUserNameParam.qwUserName = query;
+        userList(this.qwUserNameParam).then(response => {
+          this.qwUserNameList = response.rows;
+        });
+      }
+    },
+     handleSelectChange(value) {
+    console.log('选中的值:', value);
+    if(value == ''){
+      this.qwUserNameList=null
+    this.isQwUserISNull = false;
+    }else{
+    this.isQwUserISNull = true;
+    }
+  },
+    updateCorpId(){
+      listTag({corpId:this.queryParams.corpId}).then(response => {
+        this.tagList = response.rows;
+      });
+      qwUserList(this.queryParams.corpId).then(response => {
+        this.qwUserList = response.rows;
+      });
+      this.getList();
+     },
+    /** 查询企业微信客户列表 */
+    getList() {
+      this.loading = true;
+      listExternalContact(this.queryParams).then(response => {
+        this.externalContactList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        userId: null,
+        externalUserId: null,
+        name: null,
+        avatar: null,
+        type: null,
+        gender: null,
+        remark: null,
+        description: null,
+        tagIds: null,
+        remarkMobiles: null,
+        remarkCorpName: null,
+        addWay: null,
+        operUserid: null,
+        corpId: null,
+        companyId: null
+      };
+      this.resetForm("form");
+    },
+
+    selectUser(row){
+      this.form.userId=row.id
+	  console.log(row)
+      this.nickName=row.qwUserName
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.tagIds=this.selectTags.join(',')
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.qwUserNameList=null
+       this.isQwUserISNull = false;
+      this.selectTags=[];
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+     addExternalContact(this.form).then(response => {
+       this.msgSuccess("同步成功");
+       this.getList();
+     });
+
+    },
+
+    handleTransfer(row) {
+      this.reset();
+      this.type="0";
+      if(this.ids==null||this.ids==""){
+        return  this.$message('请选择需要分配的客户');
+      }
+
+      setTimeout(() => {
+                    this.$refs.qwUserSelectOne.getDetails(this.queryParams.corpId);
+       }, 1);
+      this.open = true;
+      this.title = "分配客户";
+
+    },
+
+     handleTransferAll(row) {
+      this.reset();
+      this.qwUserName=this.queryParams.qwUserName;
+      this.type="1";
+      setTimeout(() => {
+                    this.$refs.qwUserSelectOne.getDetails(this.queryParams.corpId);
+       }, 1);
+      this.open = true;
+      this.title = "分配该员工所有客户";
+
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+
+
+        if (valid) {
+            var form={
+              ids:this.ids,
+              qwUserName:this.qwUserName,
+              type:this.type,
+              userId:this.form.userId,
+              corpId:this.queryParams.corpId,
+              content:this.form.content,
+            }
+            transfer(form).then(response => {
+              this.msgSuccess(response.msg);
+              this.open = false;
+              this.getList();
+            });
+
+        }
+      });
+      this.qwUserName=null;
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除企业微信客户编号为"' + ids + '"的数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return delExternalContact(ids);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有企业微信客户数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportExternalContact(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    }
+  }
+};
+</script>
+<style  scoped>
+.tag-container {
+  max-height: 300px;
+  overflow-y: auto;
+  padding: 1px;
+  border: 1px solid #ebeef5;
+  border-radius: 1px;
+  background-color: #fafafa;
+}
+.tag-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+}
+.scroll-hint {
+  text-align: center;
+  color: #909399;
+  font-size: 12px;
+  padding: 1px 0;
+}
+.container {
+  max-width: 800px;
+  margin: 0 auto;
+  padding: 10px;
+}
+.title {
+  text-align: center;
+  color: #303133;
+  margin-bottom: 30px;
+}
+.demo-table {
+  width: 100%;
+  margin-bottom: 30px;
+}
+.instructions {
+  background-color: #f5f7fa;
+  padding: 15px;
+  border-radius: 1px;
+  margin-bottom: 20px;
+}
+</style>

+ 213 - 0
src/views/qw/externalContactTransferLog/companyTransferDetail.vue

@@ -0,0 +1,213 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <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="请输入企微主体名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="销售名称" prop="companyUserName">
+        <el-input
+          v-model="queryParams.companyUserName"
+          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 >
+          <el-option :value="0" label="全部"/>
+          <el-option
+            v-for="item in auditStatusOptions"
+            :key="item.dictValue"
+            :label="item.dictLabel"
+            :value="item.dictValue"
+          />
+        </el-select>
+      </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 v-loading="loading" :data="list" border>
+      <el-table-column label="主体名称" align="center" prop="corpName" />
+      <el-table-column label="接替公司名称" align="center" prop="companyName" />
+      <el-table-column label="接替销售名称" align="center" prop="companyUserName" />
+      <el-table-column label="接替企微用户名称" align="center" prop="qwUserName" />
+      <el-table-column label="审核状态" align="center" prop="status">
+        <template slot-scope="scope">
+          <dict-tag :options="auditStatusOptions" :value="scope.row.status"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="提交时间" align="center" prop="createTime" />
+      <el-table-column label="审核时间" align="center" prop="auditTime" />
+      <el-table-column label="被拒原因" align="center" prop="reason" />
+      <el-table-column label="提交人" align="center" prop="createBy" />
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            @click="handleView(scope.row)"
+            v-hasPermi="['qw:externalContactTransferCompanyAudit:detail']"
+          >详情</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-drawer title="详情" size="75%" :visible.sync="dialogViewVisible" append-to-body>
+      <el-row :gutter="10" class="mb8">
+        <el-col :span="1.5">
+          <el-button
+            style="margin-left: 10px"
+            type="primary"
+            plain
+            size="mini"
+            v-if="currentAudit.status === 2"
+            :loading="syncLoading"
+            @click="sync"
+            v-hasPermi="['qw:externalContactTransferCompanyAudit:sync']"
+          >同步</el-button>
+        </el-col>
+      </el-row>
+      <el-table v-loading="detailLoading" :data="userList" border>
+        <el-table-column label="客户名称" align="center" prop="externalUserName" />
+        <el-table-column label="原公司名称" align="center" prop="companyName" />
+        <el-table-column label="原销售名称" align="center" prop="companyUserName" />
+        <el-table-column label="原企微用户名称" align="center" prop="qwUserName" />
+        <el-table-column label="备注" align="center" prop="remark" />
+        <el-table-column label="接替状态" align="center" prop="status">
+          <template slot-scope="scope">
+            <dict-tag :options="replaceStatusOptions" :value="scope.row.status"/>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-drawer>
+  </div>
+</template>
+
+<script>
+import { detail, listExternalContactTransferAudit, sync } from '@/api/qw/externalContactTransferAudit'
+
+export default {
+  name: "companyTransferDetail",
+  data() {
+    return {
+      loading: false,
+      showSearch: true,
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        companyName: null,
+        corpName: null,
+        companyUserName: null,
+        status: 0,
+      },
+      total: 0,
+      list: [],
+      auditStatusOptions: [],
+      replaceStatusOptions: [],
+      dialogAuditVisible: false,
+      dialogViewVisible: false,
+      detailLoading: false,
+      userList: [],
+      currentAudit: {},
+      syncLoading: false,
+    }
+  },
+  created() {
+    this.getDicts("sys_qw_transfer_audit_status").then((response) => {
+      this.auditStatusOptions = response.data;
+    });
+    this.getDicts("sys_qw_transfer_status").then((response) => {
+      this.replaceStatusOptions = response.data;
+    });
+    this.handleQuery()
+  },
+  methods: {
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    resetQuery() {
+      this.queryParams = {
+        pageNum: 1,
+        pageSize: 10,
+        companyName: null,
+        corpName: null,
+        companyUserName: null,
+        status: 0,
+      };
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    getList() {
+      this.loading = true
+      const params = {
+        ...this.queryParams,
+        status: this.queryParams.status === 0 ? null : this.queryParams.status
+      }
+      listExternalContactTransferAudit(params).then(response => {
+        this.list = response.rows.map(item => {
+          return {
+            ...item,
+            createTime: item.createTime ? item.createTime.replace("T", " ") : null,
+            auditTime: item.auditTime ? item.auditTime.replace("T", " ") : null,
+          }
+        })
+        this.total = response.total
+        this.loading = false
+      })
+    },
+    handleView(row) {
+      this.currentAudit = row
+      this.detailLoading = true
+      detail(row.id).then(response => {
+        this.userList = response.data
+        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.syncLoading = false
+      })
+    },
+  }
+};
+</script>
+
+<style scoped>
+
+</style>

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

@@ -316,3 +316,45 @@ export default {
   }
 };
 </script>
+
+<style  scoped>
+.tag-container {
+  max-height: 300px;
+  overflow-y: auto;
+  padding: 1px;
+  border: 1px solid #ebeef5;
+  border-radius: 1px;
+  background-color: #fafafa;
+}
+.tag-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+}
+.scroll-hint {
+  text-align: center;
+  color: #909399;
+  font-size: 12px;
+  padding: 1px 0;
+}
+.container {
+  max-width: 800px;
+  margin: 0 auto;
+  padding: 10px;
+}
+.title {
+  text-align: center;
+  color: #303133;
+  margin-bottom: 30px;
+}
+.demo-table {
+  width: 100%;
+  margin-bottom: 30px;
+}
+.instructions {
+  background-color: #f5f7fa;
+  padding: 15px;
+  border-radius: 1px;
+  margin-bottom: 20px;
+}
+</style>

+ 464 - 0
src/views/qw/externalContactUnassigned/companyUnassigned.vue

@@ -0,0 +1,464 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="110px">
+      <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="qwUserName">
+        <el-select @change="handleSelectChange" v-model="queryParams.qwUserName" remote filterable clearable reserve-keyword
+                   placeholder="请输入原所属员工名称" :remote-method="qwUserMethod">
+          <el-option
+            v-for="item in qwUserNameList"
+            :key="item.id"
+            :label="item.qwUserName"
+            :value="item.qwUserName">
+            <span style="float: left">{{ item.qwUserName }}</span>
+          </el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="客户名称" prop="name">
+        <el-input
+          v-model="queryParams.name"
+          placeholder="请输入客户名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="用户类别" prop="type">
+        <el-select v-model="queryParams.type" placeholder="请选择用户类别" clearable size="small">
+          <el-option
+            v-for="dict in typeOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="性别" prop="gender">
+        <el-input
+          v-model="queryParams.gender"
+          placeholder="请输入性别"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="转接状态" prop="addWay">
+        <el-select v-model="queryParams.transferStatus" placeholder="转接状态" clearable size="small">
+          <el-option
+            v-for="dict in transferStatusOptions"
+            :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>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          size="mini"
+          @click="handleSync"
+          v-hasPermi="['qw:externalContact:addUnassigned']"
+        >同步</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:externalContact:export']"
+        >导出</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          size="mini"
+          @click="handleTransfer"
+          :disabled="multiple"
+          v-hasPermi="['qw:externalContact:companyTransfer']"
+        >分配客户</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          v-if="isQwUserISNull"
+          type="primary"
+          plain
+          size="mini"
+          @click="handleTransferAll"
+          v-hasPermi="['qw:externalContact:companyTransfer']"
+        >分配此员工所有客户</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="externalContactList" @selection-change="handleSelectionChange" border>
+      <el-table-column type="selection" width="55" align="center" />
+
+      <el-table-column label="原所属员工" align="center" prop="qwUserName" width="120px"/>
+      <el-table-column label="原员工部门" align="center" prop="departmentName" width="120px"/>
+      <el-table-column label="外部联系人账号" align="center" prop="externalUserId" width="120px"/>
+      <el-table-column label="客户名称" align="center" prop="name" />
+      <el-table-column label="头像" align="center" prop="avatar" width="120px">
+        <template slot-scope="scope">
+          <el-popover
+            placement="right"
+            title=""
+            trigger="hover">
+            <img slot="reference" :src="scope.row.avatar" width="100px">
+            <img :src="scope.row.avatar" style="max-width: 150px;">
+          </el-popover>
+        </template>
+      </el-table-column>
+      <el-table-column label="用户类别" align="center" prop="type">
+        <template slot-scope="scope">
+          <dict-tag :options="typeOptions" :value="scope.row.type"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="性别" align="center" prop="gender">
+        <template slot-scope="scope">
+          <dict-tag :options="genderOptions" :value="scope.row.gender"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="备注" align="center" prop="remark" />
+      <el-table-column label="描述信息" align="center" prop="description" />
+      <el-table-column label="标签" align="center" prop="tagIds" width="150px">
+        <template slot-scope="scope">
+          <div class="tag-container">
+            <div class="tag-list">
+              <div v-for="i in JSON.parse(scope.row.tagIds)" :key="i" style="display: inline;">
+                <el-tag type="success" v-for="ii in tagList" :key="ii.id" style="margin: 3px;" v-if="ii.tagId==i">{{ii.name}}</el-tag>
+              </div>
+            </div>
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column label="备注电话号码" align="center" prop="remarkMobiles" width="150px">
+        <template slot-scope="scope">
+          <span v-for="i in JSON.parse(scope.row.remarkMobiles)" :key="i">{{i}}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="备注企业名称" align="center" prop="remarkCorpName" />
+      <el-table-column label="来源" align="center" prop="addWay" width="100px">
+        <template slot-scope="scope">
+          <dict-tag :options="addWayOptions" :value="scope.row.addWay"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="状态" align="center" prop="status" width="100px">
+        <template slot-scope="scope">
+          <dict-tag :options="statusOptions" :value="scope.row.status"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="转接状态" align="center" prop="transferStatus" width="100px">
+        <template slot-scope="scope">
+          <dict-tag :options="transferStatusOptions" :value="scope.row.transferStatus"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="企业id" align="center" prop="corpId" />
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      :page-sizes="[100, 200, 300, 500]"
+      @pagination="getList"
+    />
+
+    <el-dialog :title="title" :visible.sync="open" width="1000px"  append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="100px" style="height: 100%">
+        <div style="background-color: rgb(239, 250, 255); margin: 10px;padding: 15px;">
+          <div>原跟进成员离职时间不能超过1年且离职前一年内至少登录过一次企业微信</div>
+          <div>接替成员最近一年内至少登陆过一次企业微信。</div>
+          <div>需等待总后台审核才会进行转接</div>
+        </div>
+        <el-form-item label="接替员工:" prop="nickName">
+
+          <el-input style="width: 150px" disabled>
+            <template slot="prefix">
+              <el-button
+                plain
+                size="small"
+                type="success"
+                v-if="form.nickName">
+                {{ form.nickName }}
+              </el-button>
+            </template>
+          </el-input>
+        </el-form-item>
+        <el-card>
+          <companyTransferQwUserSelect :corpId="queryParams.corpId" :companyId="companyId" @selectUser="selectUser"/>
+        </el-card>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary"
+                   @click="submitForm"
+                   :disabled="submitLoading"
+                   :loading="submitLoading">提交审核</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listTag } from '@/api/qw/tag'
+import { getMyQwCompanyList, userList } from '@/api/qw/user'
+import { addUnassigned, companyExtList, companyTransfer, exportExternalContact } from '@/api/qw/externalContact'
+import companyTransferQwUserSelect from '@/views/qw/externalContactTransfer/companyTransferQwUserSelect.vue'
+
+export default {
+  name: "companyUnassigned",
+  components: { companyTransferQwUserSelect },
+  data() {
+    return {
+      showSearch: true,
+      loading: false,
+      ids: [],
+      multiple: true,
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        corpId: null,
+        qwUserName: null,
+        name: null,
+        type: null,
+        gender: null,
+        transferStatus: null,
+        status: 1
+      },
+      total: 0,
+      externalContactList: [],
+      myQwCompanyList: [],
+      typeOptions: [],
+      genderOptions: [],
+      addWayOptions: [],
+      statusOptions: [],
+      transferStatusOptions: [],
+      qwUserNameParam: {
+        qwUserName: null,
+      },
+      qwUserNameList: [],
+      isQwUserISNull: false,
+      form: {
+        corpId: null,
+        qwUserId: null,
+        nickName: null,
+        content: null,
+        type: 0,
+        oldQwUserId: null
+      },
+      rules: {
+        nickName: [
+          { required: true, message: "接替员工不能为空", trigger: "blur" }
+        ]
+      },
+      exportLoading: false,
+      companyId: null,
+      submitLoading: false,
+      tagList: [],
+      open: false,
+      title: "",
+    };
+  },
+  created() {
+    // 从store中获取当前用户的companyId
+    this.companyId = this.$store.state.user.user?.companyId || null;
+
+    this.getQwCompanyList();
+    this.getDicts("sys_qw_externalContact_type").then(response => {
+      this.typeOptions = response.data;
+    });
+    this.getDicts("sys_sex").then(response => {
+      this.genderOptions = response.data;
+    });
+    this.getDicts("sys_qw_externalContact_addWay").then(response => {
+      this.addWayOptions = response.data;
+    });
+    this.getDicts("sys_qw_external_contact_status").then(response => {
+      this.statusOptions = response.data;
+    });
+    this.getDicts("sys_qw_transfer_status").then(response => {
+      this.transferStatusOptions = response.data;
+    });
+  },
+  methods: {
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    resetQuery() {
+      this.queryParams = {
+        pageNum: 1,
+        pageSize: 10,
+        corpId: null,
+        qwUserName: null,
+        name: null,
+        type: null,
+        gender: null,
+        transferStatus: null,
+        status: 1
+      };
+      this.qwUserNameList = null
+      this.isQwUserISNull = false
+      this.resetForm("queryForm")
+      this.queryParams.corpId = this.myQwCompanyList?.[0]?.dictValue || null
+      this.getList()
+    },
+    getList() {
+      this.loading = true
+      companyExtList(this.queryParams).then(response => {
+        this.externalContactList = response.rows;
+        this.total = response.total;
+        this.loading = false
+      })
+    },
+    getQwCompanyList() {
+      getMyQwCompanyList().then(response => {
+        this.myQwCompanyList = response.data;
+        if(this.myQwCompanyList!=null){
+          this.queryParams.corpId=this.myQwCompanyList[0].dictValue;
+          listTag({corpId:this.queryParams.corpId}).then(response => {
+            this.tagList = response.rows;
+          });
+          this.getList();
+        }
+      });
+    },
+    updateCorpId() {
+      listTag({corpId:this.queryParams.corpId}).then(response => {
+        this.tagList = response.rows;
+      });
+      this.getList();
+    },
+    handleSelectChange(value) {
+      if(!value){
+        this.qwUserNameList=null
+        this.isQwUserISNull = false;
+      }else{
+        this.isQwUserISNull = true;
+      }
+    },
+    qwUserMethod(query) {
+      if (query) {
+        this.qwUserNameParam.qwUserName = query;
+        userList(this.qwUserNameParam).then(response => {
+          this.qwUserNameList = response.rows;
+        });
+      }
+    },
+    handleSync() {
+      this.form.corpId=this.queryParams.corpId;
+      addUnassigned(this.form).then(() => {
+        this.msgSuccess("同步成功");
+        this.getList();
+      });
+    },
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有企业微信客户数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        this.exportLoading = true;
+        return exportExternalContact(queryParams);
+      }).then(response => {
+        this.download(response.msg);
+        this.exportLoading = false;
+      }).catch(() => {});
+    },
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.multiple = !selection.length
+    },
+    reset() {
+      this.form = {
+        corpId: null,
+        qwUserId: null,
+        nickName: null,
+        content: null,
+        type: 0,
+        oldQwUserId: null
+      };
+      this.resetForm("form");
+    },
+    handleTransfer() {
+      this.reset()
+      if(!this.ids){
+        this.$message('请选择需要分配的客户')
+        return
+      }
+
+      this.submitLoading = false
+      this.title = "分配客户"
+      this.open = true
+    },
+    handleTransferAll() {
+      this.reset()
+      this.form.type = 1
+      this.form.oldQwUserId = this.qwUserNameList.find(item => item.qwUserName === this.queryParams.qwUserName)?.id || null
+      this.submitLoading = false
+      this.title = "分配该员工所有客户"
+      this.open = true
+    },
+    selectUser(selection) {
+      this.form.qwUserId = selection.id
+      this.form.nickName = selection.qwUserName
+    },
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+
+          if (this.submitLoading) {
+            return;
+          }
+          this.submitLoading = true
+
+          const params = {
+            ids: this.ids,
+            qwUserId: this.form.qwUserId,
+            content: this.form.content,
+            type: this.form.type,
+            transferType: 2,
+            oldQwUserId: this.form.oldQwUserId
+          }
+
+          companyTransfer(params).then(() => {
+            this.$message.success("审核已提交,等待管理员审核");
+            this.open = false;
+          })
+        }
+      });
+    }
+  },
+};
+</script>
+
+<style scoped>
+
+</style>

+ 47 - 2
src/views/qw/externalContactUnassigned/deptUnassignedIndex.vue

@@ -131,8 +131,12 @@
       <el-table-column label="描述信息" align="center" prop="description" />
       <el-table-column label="标签" align="center" prop="tagIds" width="150px">
         <template slot-scope="scope">
-          <div v-for="i in JSON.parse(scope.row.tagIds)" :key="i" style="display: inline;">
-          <el-tag type="success" v-for="ii in tagList" :key="ii.id" style="margin: 3px;" v-if="ii.tagId==i">{{ii.name}}</el-tag>
+          <div class="tag-container">
+            <div class="tag-list">
+              <div v-for="i in JSON.parse(scope.row.tagIds)" :key="i" style="display: inline;">
+              <el-tag type="success" v-for="ii in tagList" :key="ii.id" style="margin: 3px;" v-if="ii.tagId==i">{{ii.name}}</el-tag>
+              </div>
+            </div>
           </div>
         </template>
       </el-table-column>
@@ -472,3 +476,44 @@ export default {
   }
 };
 </script>
+<style  scoped>
+.tag-container {
+  max-height: 300px;
+  overflow-y: auto;
+  padding: 1px;
+  border: 1px solid #ebeef5;
+  border-radius: 1px;
+  background-color: #fafafa;
+}
+.tag-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+}
+.scroll-hint {
+  text-align: center;
+  color: #909399;
+  font-size: 12px;
+  padding: 1px 0;
+}
+.container {
+  max-width: 800px;
+  margin: 0 auto;
+  padding: 10px;
+}
+.title {
+  text-align: center;
+  color: #303133;
+  margin-bottom: 30px;
+}
+.demo-table {
+  width: 100%;
+  margin-bottom: 30px;
+}
+.instructions {
+  background-color: #f5f7fa;
+  padding: 15px;
+  border-radius: 1px;
+  margin-bottom: 20px;
+}
+</style>

+ 48 - 2
src/views/qw/externalContactUnassigned/index.vue

@@ -144,8 +144,12 @@
       <el-table-column label="描述信息" align="center" prop="description" />
       <el-table-column label="标签" align="center" prop="tagIds" width="150px">
         <template slot-scope="scope">
-          <div v-for="i in JSON.parse(scope.row.tagIds)" :key="i" style="display: inline;">
-          <el-tag type="success" v-for="ii in tagList" :key="ii.id" style="margin: 3px;" v-if="ii.tagId==i">{{ii.name}}</el-tag>
+          <div class="tag-container">
+            <div class="tag-list">
+              <div v-for="i in JSON.parse(scope.row.tagIds)" :key="i" style="display: inline;">
+                <el-tag type="success" v-for="ii in tagList" :key="ii.id" style="margin: 3px;" v-if="ii.tagId==i">{{ii.name}}</el-tag>
+              </div>
+            </div>
           </div>
         </template>
       </el-table-column>
@@ -519,3 +523,45 @@ export default {
   }
 };
 </script>
+<style scoped>
+
+.tag-container {
+  max-height: 120px;
+  overflow-y: auto;
+  padding: 1px;
+  border: 1px solid #ebeef5;
+  border-radius: 1px;
+  background-color: #fafafa;
+}
+.tag-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+}
+.scroll-hint {
+  text-align: center;
+  color: #909399;
+  font-size: 12px;
+  padding: 1px 0;
+}
+.container {
+  max-width: 800px;
+  margin: 0 auto;
+  padding: 10px;
+}
+.title {
+  text-align: center;
+  color: #303133;
+  margin-bottom: 30px;
+}
+.demo-table {
+  width: 100%;
+  margin-bottom: 30px;
+}
+.instructions {
+  background-color: #f5f7fa;
+  padding: 15px;
+  border-radius: 1px;
+  margin-bottom: 20px;
+}
+</style>

+ 13 - 1
src/views/qw/friendWelcome/deptFriendWelcome.vue

@@ -66,6 +66,8 @@
 
 
     <el-table v-loading="loading" :data="friendWelcomeList" @selection-change="handleSelectionChange" border>
+      <el-table-column label="id" align="center" prop="id" width="180"/>
+      <el-table-column label="标题" align="center" prop="welcomeTitle"/>
       <el-table-column label="消息内容" align="left" prop="welcomeText"  width="400px" >
         <template slot-scope="scope">
           <span style="color:rgb(19, 154, 50);" v-if="scope.row.isDayparting==='1'">[共 {{JSON.parse(scope.row.daypartingItemlist).length+1}} 时段]</span>
@@ -160,6 +162,16 @@
               </el-tag>
             </div>
           </el-form-item>
+          <!-- 新增整体标题 -->
+          <el-form-item label="标题:" prop="welcomeTitle">
+            <el-input v-model="form.welcomeTitle" placeholder="请输入欢迎语标题"/>
+            <el-alert style="margin-top: 8px;height: 30px"
+                      title="此标题仅用于欢迎语的用途区分"
+                      type="info"
+                      :closable="false"
+                      show-icon>
+            </el-alert>
+          </el-form-item>
           <el-form-item label="是否发送欢迎语">
             <el-switch
               v-model="form.isSendMsg"
@@ -731,7 +743,7 @@ export default {
     //选择群发的企业成员账号
     handlelistUser(){
       setTimeout(() => {
-        this.$refs.QwUserList.getDetails(this.queryParams.corpId);
+        this.$refs.QwUserList.getDetails(this.queryParams.corpId,null,null,"2");
       }, 1);
       this.listUser.title="选择企业成员"
       this.listUser.open=true;

+ 12 - 0
src/views/qw/friendWelcome/indexNew.vue

@@ -66,6 +66,8 @@
 
 
     <el-table v-loading="loading" :data="friendWelcomeList" @selection-change="handleSelectionChange" border>
+      <el-table-column label="id" align="center" prop="id" width="180"/>
+      <el-table-column label="标题" align="center" prop="welcomeTitle"/>
       <el-table-column label="消息内容" align="left" prop="welcomeText"  width="400px" >
         <template slot-scope="scope">
           <span style="color:rgb(19, 154, 50);" v-if="scope.row.isDayparting==='1'">[共 {{JSON.parse(scope.row.daypartingItemlist).length+1}} 时段]</span>
@@ -161,6 +163,16 @@
               </el-tag>
             </div>
           </el-form-item>
+          <!-- 新增整体标题 -->
+          <el-form-item label="标题:" prop="welcomeTitle">
+            <el-input v-model="form.welcomeTitle" placeholder="请输入欢迎语标题"/>
+            <el-alert style="margin-top: 8px;height: 30px"
+                      title="此标题仅用于欢迎语的用途区分"
+                      type="info"
+                      :closable="false"
+                      show-icon>
+            </el-alert>
+          </el-form-item>
           <el-form-item label="是否发送欢迎语">
             <el-switch
               v-model="form.isSendMsg"

+ 13 - 2
src/views/qw/friendWelcome/myWelcome.vue

@@ -66,6 +66,8 @@
 
 
     <el-table v-loading="loading" :data="friendWelcomeList" @selection-change="handleSelectionChange" border>
+      <el-table-column label="id" align="center" prop="id" width="180"/>
+      <el-table-column label="标题" align="center" prop="welcomeTitle"/>
       <el-table-column label="消息内容" align="left" prop="welcomeText"  width="400px" >
         <template slot-scope="scope">
           <span style="color:rgb(19, 154, 50);" v-if="scope.row.isDayparting==='1'">[共 {{JSON.parse(scope.row.daypartingItemlist).length+1}} 时段]</span>
@@ -138,7 +140,6 @@
           </el-alert>
         </div>
         <el-form ref="form" :model="form" :rules="rules" label-width="120px">
-
           <el-form-item label="选择使用成员:" prop="qwUserIds" style="margin-top: 2%">
             <div>
               <el-button
@@ -160,6 +161,16 @@
               </el-tag>
             </div>
           </el-form-item>
+          <!-- 新增整体标题 -->
+          <el-form-item label="标题:" prop="welcomeTitle">
+            <el-input v-model="form.welcomeTitle" placeholder="请输入欢迎语标题"/>
+            <el-alert style="margin-top: 8px;height: 30px"
+                      title="此标题仅用于欢迎语的用途区分"
+                      type="info"
+                      :closable="false"
+                      show-icon>
+            </el-alert>
+          </el-form-item>
           <el-form-item label="是否发送欢迎语">
             <el-switch
               v-model="form.isSendMsg"
@@ -731,7 +742,7 @@ export default {
     //选择群发的企业成员账号
     handlelistUser(){
       setTimeout(() => {
-        this.$refs.QwUserList.getDetails(this.queryParams.corpId);
+        this.$refs.QwUserList.getDetails(this.queryParams.corpId,null,null,"1");
       }, 1);
       this.listUser.title="选择企业成员"
       this.listUser.open=true;

+ 336 - 3
src/views/qw/qwUserVoiceLog/index.vue

@@ -74,6 +74,22 @@
           @keyup.enter.native="handleQuery"
         />
       </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;">
+            <el-tag type="success"
+                    closable
+                    :disable-transitions="false"
+                    v-for="list in this.selectTags"
+                    :key="list.tagId"
+                    @close="handleCloseTags(list)"
+                    style="margin: 3px;"
+            >{{list.name}}
+            </el-tag>
+          </div>
+        </div>
+      </el-form-item>
       <el-form-item label="创建时间" prop="createTime">
         <el-date-picker v-model="createTime" size="small" style="width: 215px"
                         value-format="yyyy-MM-dd" type="daterange" range-separator="-"
@@ -147,6 +163,13 @@
           <dict-tag :options="statusOptions" :value="scope.row.status"/>
         </template>
       </el-table-column>
+      <el-table-column label="标签" align="center" prop="tagIdsName" width="250px">
+        <template slot-scope="scope">
+          <div v-for="name in scope.row.tagIdsName" style="display: inline;">
+            <el-tag type="success">{{ name }}</el-tag>
+          </div>
+        </template>
+      </el-table-column>
       <el-table-column label="企微id" align="center" width="80" prop="corpId" />
       <el-table-column label="公司名称" align="center" prop="company.companyName" />
       <el-table-column label="销售用户名称" align="center" prop="companyUser.userName" />
@@ -179,6 +202,46 @@
       @pagination="getList"
     />
 
+    <!--  搜索标签   -->
+    <el-dialog :title="changeTagDialog.title" :visible.sync="changeTagDialog.open" width="1000px"  append-to-body>
+
+      <div>搜索标签:
+        <el-input v-model="queryTagParams.name" placeholder="请输入标签名称" clearable size="small" style="width: 200px;margin-right: 10px" />
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleSearchTags(queryTagParams.name)">搜索</el-button>
+        <el-button type="primary" icon="el-icon-plus" size="mini" @click="cancelSearchTags">重置</el-button>
+      </div>
+      <div v-for="item in tagGroupList" :key="item.id"  >
+        <div style="font-size: 20px;margin-top: 20px;margin-bottom: 20px;">
+          <span class="name-background">{{ item.name }}</span>
+        </div>
+        <!-- 添加外层滚动容器 -->
+        <div class="scroll-wrapper">
+          <div class="tag-container">
+            <a
+              v-for="tagItem in item.tag"
+              class="tag-box"
+              @click="tagSelection(tagItem)"
+              :class="{ 'tag-selected': tagItem.isSelected }"
+            >
+              {{ tagItem.name }}
+            </a>
+          </div>
+        </div>
+      </div>
+
+      <pagination
+        v-show="tagTotal>0"
+        :total="tagTotal"
+        :page.sync="queryTagParams.pageNum"
+        :limit.sync="queryTagParams.pageSize"
+        @pagination="getPageListTagGroup"
+      />
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="tagSubmitForm()">确 定</el-button>
+        <el-button @click="tagCancel()">取消</el-button>
+      </div>
+    </el-dialog>
+
     <!-- 添加或修改企微用户通话记录对话框 -->
     <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
@@ -223,6 +286,8 @@
 
 <script>
 import { listQwUserVoiceLog, getQwUserVoiceLog, delQwUserVoiceLog, addQwUserVoiceLog, updateQwUserVoiceLog, exportQwUserVoiceLog } from "@/api/qw/qwUserVoiceLog";
+import {allListTagGroup} from "@/api/qw/tagGroup";
+import {searchTags} from "@/api/qw/tag";
 
 export default {
   name: "QwUserVoiceLog",
@@ -248,6 +313,29 @@ export default {
       total: 0,
       // 企微用户通话记录表格数据
       qwUserVoiceLogList: [],
+
+      selectTags:[],
+      queryTagParams:{
+        pageNum: 1,
+        pageSize: 5,
+        total:0,
+        name:null,
+        corpId:null,
+      },
+      //标签
+      changeTagDialog:{
+        title:"",
+        open:false,
+      },
+
+      tagTotal:0,
+
+      tagGroupList: [],
+
+      addTagForm:{
+        userIds:[],
+        tagIds:[]
+      },
       // 弹出层标题
       title: "",
       // 是否显示弹出层
@@ -273,6 +361,7 @@ export default {
         createTime:null,
         beginTime:null,
         endTime:null,
+        tagIds: null,
       },
       // 表单参数
       form: {},
@@ -288,10 +377,132 @@ export default {
     });
   },
   methods: {
+    tagSubmitForm(){
+
+      for (let i = 0; i < this.tagGroupList.length; i++) {
+        for (let x = 0; x < this.tagGroupList[i].tag.length; x++) {
+          if (this.tagGroupList[i].tag[x].isSelected === true) {
+
+            if (!this.selectTags) {
+              this.selectTags = [];
+            }
+
+            // 检查当前 tag 是否已经存在于 tagListFormIndex[index] 中
+            let tagExists = this.selectTags.some(
+              tag => tag.id === this.tagGroupList[i].tag[x].id
+            );
+
+            // 如果 tag 不存在于 tagListFormIndex[index] 中,则新增
+            if (!tagExists) {
+              this.selectTags.push(this.tagGroupList[i].tag[x]);
+            }
+          }
+        }
+      }
+      if (!this.selectTags || this.selectTags.length === 0) {
+        return this.$message('请选择标签');
+      }
+      this.changeTagDialog.open = false;
+    },
+
+    //取消选择标签
+    tagCancel(){
+      this.changeTagDialog.open = false;
+    },
+    tagSelection(row){
+
+      row.isSelected= !row.isSelected;
+      this.$forceUpdate();
+    },
+    handleSearchTags(name){
+
+      if (!name){
+        return this.$message.error("请输入要搜索的标签")
+      }
+      this.queryTagParams.name=name;
+      this.queryTagParams.corpId=this.queryParams.corpId;
+
+      searchTags(this.queryTagParams).then(response => {
+        this.tagGroupList = response.rows;
+      });
+
+      // searchTags({name:name,corpId:this.queryParams.corpId}).then(response => {
+      //   this.tagGroupList = response.rows;
+      // });
+
+    },
+
+    cancelSearchTags(){
+      this.resetSearchQueryTag()
+
+      this.getPageListTagGroup();
+    },
+    //删除一些选择的标签
+    handleCloseTags(list){
+      const ls = this.selectTags.findIndex(t => t.tagId === list.tagId);
+      if (ls !== -1) {
+        this.selectTags.splice(ls, 1);
+        this.selectTags = [...this.selectTags];
+      }
+
+      if (this.selectTags!=null && this.selectTags.length>0){
+        // 确保 this.form.tags 是数组
+        if (!this.queryParams.tagIds) {
+          this.queryParams.tagIds = []; // 如果未定义,初始化
+        } else {
+          this.queryParams.tagIds = []; // 清空已有数据
+        }
+
+        // 遍历并添加 tagId
+        this.selectTags.forEach(tag => {
+          if (tag.tagId) { // 确保 tagId 存在
+            this.queryParams.tagIds.push(tag.tagId);
+          }
+        });
+        console.log(this.queryParams.tagIds)
+        this.queryParams.tagIds=this.queryParams.tagIds.join(",");
+      }else {
+        this.queryParams.tagIds=null;
+      }
+
+    },
+    getPageListTagGroup(){
+      this.queryTagParams.corpId=this.queryParams.corpId
+      allListTagGroup(this.queryTagParams).then(response => {
+        this.tagGroupList = response.rows;
+        this.tagTotal = response.total;
+      });
+    },
+    //搜索的标签
+    hangleChangeTags(){
+
+      this.changeTagDialog.title="搜索的标签"
+      this.changeTagDialog.open=true;
+
+      // 获取 tagListFormIndex 中的所有 tagId,用于快速查找
+      const selectedTagIds = new Set(
+        (this.selectTags || []).map(tagItem => tagItem?.tagId)
+      );
+
+      this.queryTagParams.name=null;
+
+      this.getPageListTagGroup();
+
+      setTimeout(() => {
+        for (let i = 0; i < this.tagGroupList.length; i++) {
+          for (let x = 0; x < this.tagGroupList[i].tag.length; x++) {
+            this.tagGroupList[i].tag[x].isSelected = selectedTagIds.has(this.tagGroupList[i].tag[x].tagId);
+          }
+        }
+      }, 200);
+
+
+    },
     /** 查询企微用户通话记录列表 */
     getList() {
       this.loading = true;
-      listQwUserVoiceLog(this.queryParams).then(response => {
+      const { qwUserName, ...queryParams } = this.queryParams;
+      listQwUserVoiceLog(queryParams).then(response => {
         this.qwUserVoiceLogList = response.rows;
         this.total = response.total;
         this.loading = false;
@@ -331,15 +542,37 @@ export default {
     },
     /** 搜索按钮操作 */
     handleQuery() {
+      if (this.selectTags!=null && this.selectTags.length>0){
+        // 确保 this.form.tags 是数组
+        if (!this.queryParams.tagIds) {
+          this.queryParams.tagIds = []; // 如果未定义,初始化
+        } else {
+          this.queryParams.tagIds = []; // 清空已有数据
+        }
+
+        // 遍历并添加 tagId
+        this.selectTags.forEach(tag => {
+          if (tag.tagId) { // 确保 tagId 存在
+            this.queryParams.tagIds.push(tag.tagId);
+          }
+        });
+        //this.queryParams.tagIds=this.queryParams.tagIds.join(",");
+      }else {
+        this.queryParams.tagIds=null;
+      }
+
+
       this.queryParams.pageNum = 1;
       this.getList();
     },
     /** 重置按钮操作 */
     resetQuery() {
       this.resetForm("queryForm");
+      this.queryParams.corpId= this.myQwCompanyList[0].dictValue;
+      this.selectTags=[];
       this.createTime=null;
-      this.queryParams.beginTime = null;
-      this.queryParams.endTime = null;
+      this.queryParams.sTime=null;
+      this.queryParams.eTime=null;
       this.handleQuery();
     },
     // 多选框选中数据
@@ -416,3 +649,103 @@ export default {
   }
 };
 </script>
+
+<style scoped>
+/* CSS 样式 */
+.tag-container {
+  display: flex;
+  flex-wrap: wrap; /* 超出宽度时自动换行 */
+  gap: 8px; /* 设置标签之间的间距 */
+}
+.name-background {
+  display: inline-block;
+  background-color: #abece6; /* 背景颜色 */
+  padding: 4px 8px; /* 调整内边距,让背景包裹文字 */
+  border-radius: 4px; /* 可选:设置圆角 */
+}
+/* CSS 样式 */
+.tag-container {
+  display: flex;
+  flex-wrap: wrap; /* 超出宽度时自动换行 */
+  gap: 8px; /* 设置标签之间的间距 */
+}
+.name-background {
+  display: inline-block;
+  background-color: #abece6; /* 背景颜色 */
+  padding: 4px 8px; /* 调整内边距,让背景包裹文字 */
+  border-radius: 4px; /* 可选:设置圆角 */
+}
+.tag-box {
+  padding: 8px 12px;
+  border: 1px solid #989797;
+  border-radius: 4px;
+  cursor: pointer;
+  display: inline-block;
+}
+
+.tag-selected {
+  background-color: #00bc98;
+  color: #fff;
+  border-color: #00bc98;
+}
+
+.el-tag + .el-tag {
+  margin-left: 10px;
+}
+
+
+.suggestion-box {
+  position: absolute;
+  z-index: 999;
+  background: #fff;
+  border: 1px solid #ddd;
+  max-height: 200px;
+  overflow-y: auto;
+  width: 100%;
+}
+
+.suggestion-item {
+  padding: 10px;
+  cursor: pointer;
+}
+.suggestion-item:hover {
+  background-color: #f5f7fa;
+}
+
+.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;
+}
+
+.feedback-dialog {
+  width: 100%;
+  max-width: 1000px;
+  max-height: 80vh; /* 限制最大高度为视窗高度的 80% */
+  overflow-y: auto; /* 超出时显示垂直滚动条 */
+  padding: 20px;
+  box-sizing: border-box; /* 确保 padding 不影响总宽度 */
+}
+/* 新增的滚动容器样式(不影响原有样式) */
+.scroll-wrapper {
+  max-height: 130px; /* 大约三行的高度 */
+  overflow-y: auto;  /* 垂直滚动 */
+  padding-right: 5px; /* 为滚动条留出空间 */
+}
+
+/* 美化滚动条(可选) */
+.scroll-wrapper::-webkit-scrollbar {
+  width: 6px;
+}
+.scroll-wrapper::-webkit-scrollbar-thumb {
+  background: rgba(0, 0, 0, 0.2);
+  border-radius: 3px;
+}
+</style>

+ 339 - 4
src/views/qw/qwUserVoiceLogTotal/index.vue

@@ -19,9 +19,9 @@
                    :loading="companyUserOptionsLoading">
           <el-option
             v-for="item in companyUserOptions"
-            :key="item.value"
-            :label="item.label"
-            :value="item.value">
+            :key="item.dictValue"
+            :label="item.dictLabel"
+            :value="item.dictValue">
           </el-option>
         </el-select>
       </el-form-item>
@@ -81,6 +81,21 @@
                 @keyup.enter.native="handleQuery"
               />
             </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;">
+            <el-tag type="success"
+                    closable
+                    :disable-transitions="false"
+                    v-for="list in this.selectTags"
+                    :key="list.tagId"
+                    @close="handleCloseTags(list)"
+                    style="margin: 3px;"
+            >{{list.name}}
+            </el-tag>
+          </div>
+        </div>
+      </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="-"
@@ -169,6 +184,13 @@
       <el-table-column label="企微主体名称" align="center" prop="corpName" />
       <el-table-column label="企微用户id" align="center" prop="qwUser.qwUserId" />
       <el-table-column label="时长秒" align="center"  prop="duration" />
+      <el-table-column label="标签" align="center" prop="tagIdsName" width="250px">
+        <template slot-scope="scope">
+          <div v-for="name in scope.row.tagIdsName" style="display: inline;">
+            <el-tag type="success">{{ name }}</el-tag>
+          </div>
+        </template>
+      </el-table-column>
       <el-table-column label="接通数量" align="center"  prop="connectCount" />
       <el-table-column label="未接通数量" align="center"  prop="noConnectCount" />
       <!--      <el-table-column label="标题" align="center" width="80" prop="title" />-->
@@ -200,6 +222,47 @@
             </el-table-column>-->
     </el-table>
 
+
+    <!--  搜索标签   -->
+    <el-dialog :title="changeTagDialog.title" :visible.sync="changeTagDialog.open" width="1000px"  append-to-body>
+
+      <div>搜索标签:
+        <el-input v-model="queryTagParams.name" placeholder="请输入标签名称" clearable size="small" style="width: 200px;margin-right: 10px" />
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleSearchTags(queryTagParams.name)">搜索</el-button>
+        <el-button type="primary" icon="el-icon-plus" size="mini" @click="cancelSearchTags">重置</el-button>
+      </div>
+      <div v-for="item in tagGroupList" :key="item.id"  >
+        <div style="font-size: 20px;margin-top: 20px;margin-bottom: 20px;">
+          <span class="name-background">{{ item.name }}</span>
+        </div>
+        <!-- 添加外层滚动容器 -->
+        <div class="scroll-wrapper">
+          <div class="tag-container">
+            <a
+              v-for="tagItem in item.tag"
+              class="tag-box"
+              @click="tagSelection(tagItem)"
+              :class="{ 'tag-selected': tagItem.isSelected }"
+            >
+              {{ tagItem.name }}
+            </a>
+          </div>
+        </div>
+      </div>
+
+      <pagination
+        v-show="tagTotal>0"
+        :total="tagTotal"
+        :page.sync="queryTagParams.pageNum"
+        :limit.sync="queryTagParams.pageSize"
+        @pagination="getPageListTagGroup"
+      />
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="tagSubmitForm()">确 定</el-button>
+        <el-button @click="tagCancel()">取消</el-button>
+      </div>
+    </el-dialog>
+
     <pagination
       v-show="total>0"
       :total="total"
@@ -217,6 +280,8 @@ import {getCompanyUserListLikeName} from "@/api/company/companyUser";
 import Treeselect from "@riophae/vue-treeselect";
 import "@riophae/vue-treeselect/dist/vue-treeselect.css";
 import {treeselect} from "../../../api/company/companyDept";
+import {allListTagGroup} from "@/api/qw/tagGroup";
+import {searchTags} from "@/api/qw/tag";
 
 export default {
   name: "QwUserVoiceLog",
@@ -240,6 +305,30 @@ export default {
       createTime:null,
       beginTime:null,
       endTime:null,
+
+
+      selectTags:[],
+      queryTagParams:{
+        pageNum: 1,
+        pageSize: 5,
+        total:0,
+        name:null,
+        corpId:null,
+      },
+      //标签
+      changeTagDialog:{
+        title:"",
+        open:false,
+      },
+
+      tagTotal:0,
+
+      tagGroupList: [],
+
+      addTagForm:{
+        userIds:[],
+        tagIds:[]
+      },
       // 总条数
       total: 0,
       // 企微用户通话记录表格数据
@@ -270,6 +359,7 @@ export default {
         createTime:null,
         beginTime:null,
         endTime:null,
+        tagIds: null,
       },
       // 表单参数
       form: {},
@@ -294,11 +384,133 @@ export default {
     });
   },
   methods: {
+    tagSubmitForm(){
+
+      for (let i = 0; i < this.tagGroupList.length; i++) {
+        for (let x = 0; x < this.tagGroupList[i].tag.length; x++) {
+          if (this.tagGroupList[i].tag[x].isSelected === true) {
+
+            if (!this.selectTags) {
+              this.selectTags = [];
+            }
+
+            // 检查当前 tag 是否已经存在于 tagListFormIndex[index] 中
+            let tagExists = this.selectTags.some(
+              tag => tag.id === this.tagGroupList[i].tag[x].id
+            );
+
+            // 如果 tag 不存在于 tagListFormIndex[index] 中,则新增
+            if (!tagExists) {
+              this.selectTags.push(this.tagGroupList[i].tag[x]);
+            }
+          }
+        }
+      }
+      if (!this.selectTags || this.selectTags.length === 0) {
+        return this.$message('请选择标签');
+      }
+      this.changeTagDialog.open = false;
+    },
+
+    //取消选择标签
+    tagCancel(){
+      this.changeTagDialog.open = false;
+    },
+    tagSelection(row){
+
+      row.isSelected= !row.isSelected;
+      this.$forceUpdate();
+    },
+    handleSearchTags(name){
+
+      if (!name){
+        return this.$message.error("请输入要搜索的标签")
+      }
+      this.queryTagParams.name=name;
+      this.queryTagParams.corpId=this.queryParams.corpId;
+
+      searchTags(this.queryTagParams).then(response => {
+        this.tagGroupList = response.rows;
+      });
+
+      // searchTags({name:name,corpId:this.queryParams.corpId}).then(response => {
+      //   this.tagGroupList = response.rows;
+      // });
+
+    },
+
+    cancelSearchTags(){
+      this.resetSearchQueryTag()
+
+      this.getPageListTagGroup();
+    },
+    //删除一些选择的标签
+    handleCloseTags(list){
+      const ls = this.selectTags.findIndex(t => t.tagId === list.tagId);
+      if (ls !== -1) {
+        this.selectTags.splice(ls, 1);
+        this.selectTags = [...this.selectTags];
+      }
+
+      if (this.selectTags!=null && this.selectTags.length>0){
+        // 确保 this.form.tags 是数组
+        if (!this.queryParams.tagIds) {
+          this.queryParams.tagIds = []; // 如果未定义,初始化
+        } else {
+          this.queryParams.tagIds = []; // 清空已有数据
+        }
+
+        // 遍历并添加 tagId
+        this.selectTags.forEach(tag => {
+          if (tag.tagId) { // 确保 tagId 存在
+            this.queryParams.tagIds.push(tag.tagId);
+          }
+        });
+        console.log(this.queryParams.tagIds)
+        this.queryParams.tagIds=this.queryParams.tagIds.join(",");
+      }else {
+        this.queryParams.tagIds=null;
+      }
+
+    },
+    getPageListTagGroup(){
+      this.queryTagParams.corpId=this.queryParams.corpId
+      allListTagGroup(this.queryTagParams).then(response => {
+        this.tagGroupList = response.rows;
+        this.tagTotal = response.total;
+      });
+    },
+    //搜索的标签
+    hangleChangeTags(){
+
+      this.changeTagDialog.title="搜索的标签"
+      this.changeTagDialog.open=true;
+
+      // 获取 tagListFormIndex 中的所有 tagId,用于快速查找
+      const selectedTagIds = new Set(
+        (this.selectTags || []).map(tagItem => tagItem?.tagId)
+      );
+
+      this.queryTagParams.name=null;
+
+      this.getPageListTagGroup();
+
+      setTimeout(() => {
+        for (let i = 0; i < this.tagGroupList.length; i++) {
+          for (let x = 0; x < this.tagGroupList[i].tag.length; x++) {
+            this.tagGroupList[i].tag[x].isSelected = selectedTagIds.has(this.tagGroupList[i].tag[x].tagId);
+          }
+        }
+      }, 200);
+
+
+    },
     /** 查询企微用户通话记录列表 */
     getList() {
       this.loading = true;
       this.status = 0;
-      listQwUserVoiceLogTotal(this.queryParams).then(response => {
+      const { qwUserName, ...queryParams } = this.queryParams;
+      listQwUserVoiceLogTotal(queryParams).then(response => {
         this.qwUserVoiceLogList = response.rows;
         this.total = response.total;
         this.loading = false;
@@ -395,6 +607,24 @@ export default {
     },
     /** 搜索按钮操作 */
     handleQuery() {
+      if (this.selectTags!=null && this.selectTags.length>0){
+        // 确保 this.form.tags 是数组
+        if (!this.queryParams.tagIds) {
+          this.queryParams.tagIds = []; // 如果未定义,初始化
+        } else {
+          this.queryParams.tagIds = []; // 清空已有数据
+        }
+
+        // 遍历并添加 tagId
+        this.selectTags.forEach(tag => {
+          if (tag.tagId) { // 确保 tagId 存在
+            this.queryParams.tagIds.push(tag.tagId);
+          }
+        });
+        //this.queryParams.tagIds=this.queryParams.tagIds.join(",");
+      }else {
+        this.queryParams.tagIds=null;
+      }
       this.queryParams.pageNum = 1;
       this.handlePagination();
     },
@@ -404,6 +634,11 @@ export default {
       this.createTime=null;
       this.queryParams.beginTime = null;
       this.queryParams.endTime = null;
+      this.queryParams.corpId= this.myQwCompanyList[0].dictValue;
+      this.selectTags=[];
+      this.createTime=null;
+      this.queryParams.sTime=null;
+      this.queryParams.eTime=null;
       this.handleQuery();
     },
     // 多选框选中数据
@@ -485,3 +720,103 @@ export default {
   }
 };
 </script>
+
+<style scoped>
+/* CSS 样式 */
+.tag-container {
+  display: flex;
+  flex-wrap: wrap; /* 超出宽度时自动换行 */
+  gap: 8px; /* 设置标签之间的间距 */
+}
+.name-background {
+  display: inline-block;
+  background-color: #abece6; /* 背景颜色 */
+  padding: 4px 8px; /* 调整内边距,让背景包裹文字 */
+  border-radius: 4px; /* 可选:设置圆角 */
+}
+/* CSS 样式 */
+.tag-container {
+  display: flex;
+  flex-wrap: wrap; /* 超出宽度时自动换行 */
+  gap: 8px; /* 设置标签之间的间距 */
+}
+.name-background {
+  display: inline-block;
+  background-color: #abece6; /* 背景颜色 */
+  padding: 4px 8px; /* 调整内边距,让背景包裹文字 */
+  border-radius: 4px; /* 可选:设置圆角 */
+}
+.tag-box {
+  padding: 8px 12px;
+  border: 1px solid #989797;
+  border-radius: 4px;
+  cursor: pointer;
+  display: inline-block;
+}
+
+.tag-selected {
+  background-color: #00bc98;
+  color: #fff;
+  border-color: #00bc98;
+}
+
+.el-tag + .el-tag {
+  margin-left: 10px;
+}
+
+
+.suggestion-box {
+  position: absolute;
+  z-index: 999;
+  background: #fff;
+  border: 1px solid #ddd;
+  max-height: 200px;
+  overflow-y: auto;
+  width: 100%;
+}
+
+.suggestion-item {
+  padding: 10px;
+  cursor: pointer;
+}
+.suggestion-item:hover {
+  background-color: #f5f7fa;
+}
+
+.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;
+}
+
+.feedback-dialog {
+  width: 100%;
+  max-width: 1000px;
+  max-height: 80vh; /* 限制最大高度为视窗高度的 80% */
+  overflow-y: auto; /* 超出时显示垂直滚动条 */
+  padding: 20px;
+  box-sizing: border-box; /* 确保 padding 不影响总宽度 */
+}
+/* 新增的滚动容器样式(不影响原有样式) */
+.scroll-wrapper {
+  max-height: 130px; /* 大约三行的高度 */
+  overflow-y: auto;  /* 垂直滚动 */
+  padding-right: 5px; /* 为滚动条留出空间 */
+}
+
+/* 美化滚动条(可选) */
+.scroll-wrapper::-webkit-scrollbar {
+  width: 6px;
+}
+.scroll-wrapper::-webkit-scrollbar-thumb {
+  background: rgba(0, 0, 0, 0.2);
+  border-radius: 3px;
+}
+</style>

+ 8 - 8
src/views/qw/sop/addSop.vue

@@ -29,10 +29,10 @@
               :label="1"
             >标签
             </el-radio>
-<!--            <el-radio-->
-<!--              :label="2"-->
-<!--            >群聊-->
-<!--            </el-radio>-->
+            <el-radio
+              :label="2"
+            >群聊
+            </el-radio>
           </el-radio-group>
           <Tip :title="'标签:根据企微客户的标签筛选客户进入SOP'" />
         </el-form-item>
@@ -76,7 +76,7 @@
           </el-select>
           <Tip title="选择的企业微信员工下面的群聊" />
         </el-form-item>
-        <el-form-item label="客户评级" prop="isRating" style="margin-top: 2%">
+        <el-form-item label="客户评级" prop="isRating" style="margin-top: 2%" v-if="form.filterMode == 1">
           <el-switch
             v-model="form.isRating"
             active-color="#13ce66"
@@ -235,7 +235,7 @@
 
           </el-form-item>
         </div>
-        <el-form-item label="是否固定营期" prop="isFixed" v-if="form.type != 3">
+        <el-form-item label="是否固定营期" prop="isFixed" v-if="form.type != 3 && form.filterMode == 1">
           <el-radio-group v-model="form.isFixed">
             <el-radio
               :label="1"
@@ -265,7 +265,7 @@
           </el-row>
           <Tip title="任务生成的代发送消息,如果超过此设置的时间还未发送,自动作废" />
         </el-form-item>
-        <el-form-item v-if="(form.sendType==2 || form.sendType==4 || form.sendType==11) && form.type != 3 && form.isFixed == 0" label="自动添加SOP"
+        <el-form-item v-if="(form.sendType==2 || form.sendType==4 || form.sendType==11) && form.type != 3 && form.isFixed == 0 && form.filterMode == 1" label="自动添加SOP"
                       prop="autoSopTime.autoSopType">
           <el-radio-group v-model="form.autoSopTime.autoSopType">
             <el-radio
@@ -332,7 +332,7 @@
 <!--        </el-form-item>-->
 
 
-        <el-form-item label="是否只发送注册用户" prop="isRegister" v-if="form.type != 3">
+        <el-form-item label="是否只发送注册用户" prop="isRegister" v-if="form.type != 3 && form.filterMode == 1">
           <el-radio-group v-model="form.isRegister">
             <el-radio
               :label="1"

+ 3 - 1
src/views/qw/sop/deptSop.vue

@@ -1468,7 +1468,8 @@ export default {
         name: row.name,
         tempId: row.tempId,
         filterMode: row.filterMode,
-        corpId: row.corpId
+        corpId: row.corpId,
+        type:1,
       }
       // 使用 params 传递参数
       this.$router.push({
@@ -1484,6 +1485,7 @@ export default {
       this.sopLogsDialog.title = '规则执行详情';
       this.sopLogsDialog.open = true;
       this.sopLogsDialog.sopLogsForm = row;
+      this.$set(this.sopLogsDialog.sopLogsForm, 'filterSopType', 3);
     },
 
     handleAvatarSuccessFile(res, file, item) {

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä