瀏覽代碼

Merge remote-tracking branch 'origin/master'

yfh 1 月之前
父節點
當前提交
91210a72c4
共有 100 個文件被更改,包括 17340 次插入890 次删除
  1. 1 1
      .env.prod-bjzm
  2. 1 1
      .env.prod-hsyy
  3. 1 0
      package.json
  4. 62 0
      src/api/adv/advertiser.js
  5. 70 0
      src/api/adv/callbackAccount.js
  6. 29 0
      src/api/adv/channel.js
  7. 18 0
      src/api/adv/configuration.js
  8. 10 0
      src/api/adv/conversionLog.js
  9. 63 0
      src/api/adv/domain.js
  10. 73 0
      src/api/adv/landingPageTemplate.js
  11. 19 0
      src/api/adv/project.js
  12. 54 0
      src/api/adv/promotionAccount.js
  13. 60 0
      src/api/adv/site.js
  14. 34 0
      src/api/adv/siteStatistics.js
  15. 36 0
      src/api/adv/trackingLink.js
  16. 60 0
      src/api/company/schedule.js
  17. 16 0
      src/api/course/sopCourseLink.js
  18. 8 0
      src/api/course/userCoursePeriod.js
  19. 2 2
      src/api/live/liveData.js
  20. 53 0
      src/api/live/liveWatchLog.js
  21. 64 0
      src/api/live/mergedOrder.js
  22. 52 0
      src/api/qw/assignRule.js
  23. 61 0
      src/api/qw/customerLink.js
  24. 35 0
      src/api/qw/groupActual.js
  25. 44 0
      src/api/qw/groupLiveCode.js
  26. 41 0
      src/api/qw/luckyBag.js
  27. 53 0
      src/api/qw/luckyBagCollectRecord.js
  28. 8 0
      src/api/qw/sopTemp.js
  29. 二進制
      src/assets/images/image_de.png
  30. 二進制
      src/assets/images/link-button.gif
  31. 二進制
      src/assets/images/qr_code.png
  32. 二進制
      src/assets/logo/hsyy.jpg
  33. 36 4
      src/components/H5/FormWrapper.vue
  34. 1 12
      src/components/H5/config-item/common-config.vue
  35. 150 0
      src/components/H5/config-item/h5-add-wechat-button-config.vue
  36. 137 0
      src/components/H5/config-item/h5-form-config.vue
  37. 29 0
      src/components/H5/config-item/h5-image-config.vue
  38. 127 0
      src/components/H5/config-item/h5-link-button-config.vue
  39. 188 0
      src/components/H5/config-item/h5-qrcode-config.vue
  40. 3 2
      src/components/H5/css/base.css
  41. 102 0
      src/components/H5/h5-add-wechat-button.vue
  42. 233 0
      src/components/H5/h5-form.vue
  43. 10 1
      src/components/H5/h5-image.vue
  44. 102 0
      src/components/H5/h5-link-button.vue
  45. 127 0
      src/components/H5/h5-qrcode.vue
  46. 5 2
      src/components/H5/h5-sep.vue
  47. 4 0
      src/components/H5/h5-text.vue
  48. 1024 202
      src/components/H5Editor/index.vue
  49. 0 2
      src/router/index.js
  50. 1 0
      src/utils/errorCode.js
  51. 2 2
      src/utils/request.js
  52. 271 0
      src/views/adv/advertiser/index.vue
  53. 618 0
      src/views/adv/callbackAccount/index.vue
  54. 622 0
      src/views/adv/channel/index.vue
  55. 269 0
      src/views/adv/configuration/index.vue
  56. 163 0
      src/views/adv/conversionLog/index.vue
  57. 310 0
      src/views/adv/customPromotionAccount/index.vue
  58. 297 0
      src/views/adv/domain/index.vue
  59. 431 0
      src/views/adv/landingPageTemplate/index.vue
  60. 178 0
      src/views/adv/project/index.vue
  61. 736 0
      src/views/adv/promotionAccount/index.vue
  62. 1637 0
      src/views/adv/site/index.vue
  63. 602 0
      src/views/adv/statistics/index.vue
  64. 211 0
      src/views/adv/trackingLink/index.vue
  65. 2 1
      src/views/company/companyUser/index.vue
  66. 85 20
      src/views/components/course/userCourseCatalogDetails.vue
  67. 6 6
      src/views/course/courseFinishTemp/index.vue
  68. 5 5
      src/views/course/courseFinishTempParent/deptIndex.vue
  69. 6 6
      src/views/course/courseFinishTempParent/index.vue
  70. 6 6
      src/views/course/courseFinishTempParent/myIndex.vue
  71. 25 0
      src/views/course/courseWatchLog/index.vue
  72. 24 1
      src/views/course/courseWatchLog/watchLog.vue
  73. 50 0
      src/views/course/userCoursePeriod/index.vue
  74. 10 4
      src/views/hisStore/storeOrder/list.vue
  75. 51 9
      src/views/live/liveConsole/LiveConsole.vue
  76. 28 2
      src/views/live/liveConsole/LivePlayer.vue
  77. 31 10
      src/views/live/liveData/index.vue
  78. 9 9
      src/views/live/liveOrder/index-old.vue
  79. 28 28
      src/views/live/liveOrder/index.vue
  80. 468 0
      src/views/live/liveWatchLog/index.vue
  81. 1290 0
      src/views/live/order/index.vue
  82. 1118 0
      src/views/live/order/liveDetail.vue
  83. 880 0
      src/views/live/order/storeDetail.vue
  84. 880 0
      src/views/live/order/userDetail.vue
  85. 357 0
      src/views/qw/assignRule/index.vue
  86. 693 0
      src/views/qw/customerLink/index.vue
  87. 25 1
      src/views/qw/externalContactTransfer/index.vue
  88. 304 0
      src/views/qw/groupActual/index.vue
  89. 10 0
      src/views/qw/groupChatTransfer/index.vue
  90. 10 0
      src/views/qw/groupChatTransferOnJob/index.vue
  91. 263 0
      src/views/qw/groupLiveCode/index.vue
  92. 78 4
      src/views/qw/sop/sop.vue
  93. 1 1
      src/views/qw/sopLogs/sopLogsList.vue
  94. 3 3
      src/views/qw/sopTemp/addSopTemp.vue
  95. 87 2
      src/views/qw/sopTemp/index.vue
  96. 73 7
      src/views/qw/sopTemp/updateSopTemp.vue
  97. 1 0
      src/views/qw/sopUserLogs/sopUserLogsSchedule.vue
  98. 407 278
      src/views/qw/sopUserLogsInfo/sendMsgOpenTool.vue
  99. 316 242
      src/views/qw/sopUserLogsInfo/sendMsgSopOpenTool.vue
  100. 56 14
      src/views/qw/sopUserLogsInfo/sopUserLogsInfoDetails.vue

+ 1 - 1
.env.prod-bjzm

@@ -22,7 +22,7 @@ VUE_APP_COS_BUCKET = bjzmky-1323137866
 # 存储桶配置
 VUE_APP_COS_REGION = ap-chongqing
 # 线路一地址
-VUE_APP_VIDEO_LINE_1 = https://bjzmkytcpv.ylrzcloud.com
+VUE_APP_VIDEO_LINE_1 = http://bjzmkytcpv.ylrzcloud.com
 # 线路二地址
 VUE_APP_VIDEO_LINE_2 = https://bjzmobs.ylrztop.com
 

+ 1 - 1
.env.prod-hsyy

@@ -7,7 +7,7 @@ VUE_APP_ICP_RECORD =渝ICP备2024031984号-1
 # ICP网站访问地址
 VUE_APP_ICP_URL =https://beian.miit.gov.cn
 # 网站LOG
-VUE_APP_LOG_URL =@/assets/logo/kyt.jpg
+VUE_APP_LOG_URL =@/assets/logo/hsyy.jpg
 
 # 生产环境配置
 ENV = 'production'

+ 1 - 0
package.json

@@ -109,6 +109,7 @@
     "normalize.css": "7.0.0",
     "nprogress": "0.2.0",
     "path-to-regexp": "2.4.0",
+    "qrcode": "^1.5.4",
     "qrcode.vue": "^1.7.0",
     "qrcodejs2": "0.0.2",
     "quill": "1.3.7",

+ 62 - 0
src/api/adv/advertiser.js

@@ -0,0 +1,62 @@
+import request from '@/utils/request'
+
+// 分页查询广告商列表
+export function pageAdvertiser(query) {
+  return request({
+    url: '/adv/advertiser/page',
+    method: 'get',
+    params: query
+  })
+}
+
+// 根据ID查询广告商详情
+export function getAdvertiser(id) {
+  return request({
+    url: '/adv/advertiser/' + id,
+    method: 'get'
+  })
+}
+
+// 创建广告商
+export function addAdvertiser(data) {
+  return request({
+    url: '/adv/advertiser',
+    method: 'post',
+    data: data
+  })
+}
+
+// 更新广告商
+export function updateAdvertiser(id, data) {
+  return request({
+    url: '/adv/advertiser/' + id,
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除广告商
+export function delAdvertiser(id) {
+  return request({
+    url: '/adv/advertiser/' + id,
+    method: 'delete'
+  })
+}
+
+// 批量删除广告商
+export function batchDelAdvertiser(ids) {
+  return request({
+    url: '/adv/advertiser/batch',
+    method: 'delete',
+    data: ids
+  })
+}
+
+// 启用/停用广告商
+export function enableAdvertiser(id) {
+  return request({
+    url: '/adv/advertiser/enable/' + id,
+    method: 'post'
+  })
+}
+

+ 70 - 0
src/api/adv/callbackAccount.js

@@ -0,0 +1,70 @@
+import request from '@/utils/request'
+
+// 分页查询回传账号列表
+export function pageCallbackAccount(query) {
+  return request({
+    url: '/adv/callback-account/page',
+    method: 'get',
+    params: query
+  })
+}
+
+// 根据ID查询回传账号详情
+export function getCallbackAccount(id) {
+  return request({
+    url: '/adv/callback-account/' + id,
+    method: 'get'
+  })
+}
+
+// 创建回传账号
+export function addCallbackAccount(data) {
+  return request({
+    url: '/adv/callback-account',
+    method: 'post',
+    data: data
+  })
+}
+
+// 更新回传账号
+export function updateCallbackAccount(id, data) {
+  return request({
+    url: '/adv/callback-account/' + id,
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除回传账号
+export function delCallbackAccount(id) {
+  return request({
+    url: '/adv/callback-account/' + id,
+    method: 'delete'
+  })
+}
+
+// 批量删除回传账号
+export function batchDelCallbackAccount(ids) {
+  return request({
+    url: '/adv/callback-account/batch',
+    method: 'delete',
+    data: ids
+  })
+}
+
+// 查询事件类型
+export function queryEventType(advertiserId) {
+  return request({
+    url: '/adv/callback-account/queryEventType/' + advertiserId,
+    method: 'post'
+  })
+}
+
+// 保存转换事件
+export function saveEventType(id, data) {
+  return request({
+    url: '/adv/callback-account/saveEventType/' + id,
+    method: 'post',
+    data: data
+  })
+}

+ 29 - 0
src/api/adv/channel.js

@@ -0,0 +1,29 @@
+import request from '@/utils/request'
+
+// 查询分组列表
+export function pageProject(data) {
+  return request({
+    url: '/adv/channel/page',
+    method: 'get',
+    params: data
+  })
+}
+
+
+// 新增或更新渠道
+export function addOrUpdateChannel(data) {
+  return request({
+    url: '/adv/channel/addOrUpdate',
+    method: 'post',
+    data: data
+  })
+}
+
+// 批量复制渠道
+export function saveBatchChannel(data) {
+  return request({
+    url: '/adv/channel/saveBatch',
+    method: 'post',
+    data: data
+  })
+}

+ 18 - 0
src/api/adv/configuration.js

@@ -0,0 +1,18 @@
+import request from '@/utils/request'
+
+// 查询配置详情
+export function getConfigDetail() {
+    return request({
+        url: '/adv/config/detail',
+        method: 'get'
+    })
+}
+
+// 新增或修改配置
+export function addOrUpdateConfig(data) {
+    return request({
+        url: '/adv/config/addOrUpdate',
+        method: 'post',
+        data: data
+    })
+}

+ 10 - 0
src/api/adv/conversionLog.js

@@ -0,0 +1,10 @@
+import request from '@/utils/request'
+
+// 分页查询回传事件列表
+export function pageConversionLog(query) {
+  return request({
+    url: '/adv/conversion-log/page',
+    method: 'get',
+    params: query
+  })
+}

+ 63 - 0
src/api/adv/domain.js

@@ -0,0 +1,63 @@
+import request from '@/utils/request'
+
+// 分页查询域名列表
+export function pageDomain(query) {
+  return request({
+    url: '/adv/domains/page',
+    method: 'get',
+    params: query
+  })
+}
+
+// 根据ID查询域名详情
+export function getDomain(id) {
+  return request({
+    url: '/adv/domains/' + id,
+    method: 'get'
+  })
+}
+
+// 新增域名
+export function addDomain(data) {
+  return request({
+    url: '/adv/domains',
+    method: 'post',
+    data: data
+  })
+}
+
+// 更新域名
+export function updateDomain(id, data) {
+  return request({
+    url: '/adv/domains/' + id,
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除域名
+export function delDomain(id) {
+  return request({
+    url: '/adv/domains/' + id,
+    method: 'delete'
+  })
+}
+
+// 批量删除域名
+export function batchDelDomain(ids) {
+  return request({
+    url: '/adv/domains/batch',
+    method: 'delete',
+    data: ids
+  })
+}
+
+// 启用/禁用域名
+export function updateDomainStatus(id, status) {
+  return request({
+    url: '/adv/domains/' + id + '/status',
+    method: 'put',
+    params: { status }
+  })
+}
+

+ 73 - 0
src/api/adv/landingPageTemplate.js

@@ -0,0 +1,73 @@
+import request from '@/utils/request'
+
+// 分页查询模板列表
+export function pageTemplate(query) {
+  return request({
+    url: '/adv/landing-page-templates/page',
+    method: 'get',
+    params: query
+  })
+}
+
+
+
+// 根据ID查询模板详情
+export function getTemplate(id) {
+  return request({
+    url: '/adv/landing-page-templates/' + id,
+    method: 'get'
+  })
+}
+
+// 新增模板
+export function addTemplate(data) {
+  return request({
+    url: '/adv/landing-page-templates',
+    method: 'post',
+    data: data
+  })
+}
+
+// 更新模板
+export function updateTemplate(id, data) {
+  return request({
+    url: '/adv/landing-page-templates/' + id,
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除模板
+export function delTemplate(id) {
+  return request({
+    url: '/adv/landing-page-templates/' + id,
+    method: 'delete'
+  })
+}
+
+// 批量删除模板
+export function batchDelTemplate(ids) {
+  return request({
+    url: '/adv/landing-page-templates/batch',
+    method: 'delete',
+    data: ids
+  })
+}
+
+// 启用/禁用模板
+export function updateTemplateStatus(id, status) {
+  return request({
+    url: '/adv/landing-page-templates/enable/' + id,
+    method: 'post',
+    params: { status }
+  })
+}
+
+// 复制模板
+export function copyTemplate(id) {
+  return request({
+    url: '/adv/landing-page-templates/' + id + '/copy',
+    method: 'post'
+  })
+}
+

+ 19 - 0
src/api/adv/project.js

@@ -0,0 +1,19 @@
+import request from '@/utils/request'
+
+// 查询项目列表
+export function pageProject(data) {
+  return request({
+    url: '/adv/project/page',
+    method: 'get',
+    params: data
+  })
+}
+
+// 新增项目
+export function addProject(data) {
+  return request({
+    url: '/adv/project/add',
+    method: 'post',
+    data: data
+  })
+}

+ 54 - 0
src/api/adv/promotionAccount.js

@@ -0,0 +1,54 @@
+import request from '@/utils/request'
+
+// 分页查询推广账号列表
+export function pagePromotionAccount(query) {
+  return request({
+    url: '/adv/promotion-account/page',
+    method: 'get',
+    params: query
+  })
+}
+
+// 根据ID查询推广账号详情
+export function getPromotionAccount(id) {
+  return request({
+    url: '/adv/promotion-account/' + id,
+    method: 'get'
+  })
+}
+
+// 创建推广账号
+export function addPromotionAccount(data) {
+  return request({
+    url: '/adv/promotion-account',
+    method: 'post',
+    data: data
+  })
+}
+
+// 更新推广账号
+export function updatePromotionAccount(id, data) {
+  return request({
+    url: '/adv/promotion-account/' + id,
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除推广账号
+export function delPromotionAccount(id) {
+  return request({
+    url: '/adv/promotion-account/' + id,
+    method: 'delete'
+  })
+}
+
+// 批量删除推广账号
+export function batchDelPromotionAccount(ids) {
+  return request({
+    url: '/adv/promotion-account/batch',
+    method: 'delete',
+    data: ids
+  })
+}
+

+ 60 - 0
src/api/adv/site.js

@@ -0,0 +1,60 @@
+import request from '@/utils/request'
+
+// 查询站点列表
+export function listSite() {
+  return request({
+    url: '/adv/site/list',
+    method: 'get'
+  })
+}
+
+// 查询站点详情
+export function getSite(id) {
+  return request({
+    url: '/adv/site/' + id,
+    method: 'get'
+  })
+}
+
+// 创建站点
+export function addSite(data) {
+  return request({
+    url: '/adv/site',
+    method: 'post',
+    data: data
+  })
+}
+
+// 更新站点
+export function updateSite(id, data) {
+  return request({
+    url: '/adv/site/' + id,
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除站点
+export function delSite(id) {
+  return request({
+    url: '/adv/site/' + id,
+    method: 'delete'
+  })
+}
+
+// 查询站点统计数据
+export function getSiteStatistics(id) {
+  return request({
+    url: '/adv/site/' + id + '/statistics',
+    method: 'get'
+  })
+}
+
+// 启用/停用站点
+export function enableSite(id) {
+  return request({
+    url: '/adv/site/enable/' + id,
+    method: 'post'
+  })
+}
+

+ 34 - 0
src/api/adv/siteStatistics.js

@@ -0,0 +1,34 @@
+import request from '@/utils/request'
+
+// 查询站点统计列表
+export function pageSiteStatistics(query) {
+  return request({
+    url: '/adv/site-statistics/page',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询站点统计详情
+export function getSiteStatistics(id) {
+  return request({
+    url: '/adv/site-statistics/' + id,
+    method: 'get'
+  })
+}
+
+// 根据站点ID查询统计
+export function getSiteStatisticsBySiteId(siteId) {
+  return request({
+    url: '/adv/site-statistics/site/' + siteId,
+    method: 'get'
+  })
+}
+
+// 刷新站点统计数据
+export function refreshSiteStatistics(siteId) {
+  return request({
+    url: '/adv/site-statistics/refresh/' + siteId,
+    method: 'post'
+  })
+}

+ 36 - 0
src/api/adv/trackingLink.js

@@ -0,0 +1,36 @@
+import request from '@/utils/request'
+
+// 分页查询监测链接列表
+export function pageTrackingLink(query) {
+  return request({
+    url: '/adv/tracking-link/page',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询所有监测链接列表(不分页)
+export function listTrackingLink(query) {
+  return request({
+    url: '/adv/tracking-link/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 根据ID查询监测链接详情
+export function getTrackingLink(id) {
+  return request({
+    url: '/adv/tracking-link/' + id,
+    method: 'get'
+  })
+}
+
+// 根据广告商ID查询监测链接列表
+export function getTrackingLinkByAdvertiser(advertiserId) {
+  return request({
+    url: '/adv/tracking-link/advertiser/' + advertiserId,
+    method: 'get'
+  })
+}
+

+ 60 - 0
src/api/company/schedule.js

@@ -0,0 +1,60 @@
+import request from '@/utils/request'
+
+// 查询中医档期管理列表
+export function listSchedule(query) {
+  return request({
+    url: '/company/schedule/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询中医档期管理详细
+export function getSchedule(id) {
+  return request({
+    url: '/company/schedule/' + id,
+    method: 'get'
+  })
+}
+
+// 新增中医档期管理
+export function addSchedule(data) {
+  return request({
+    url: '/company/schedule',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改中医档期管理
+export function updateSchedule(data) {
+  return request({
+    url: '/company/schedule',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除中医档期管理
+export function delSchedule(id) {
+  return request({
+    url: '/company/schedule/' + id,
+    method: 'delete'
+  })
+}
+
+// 导出中医档期管理
+export function exportSchedule(query) {
+  return request({
+    url: '/company/schedule/export',
+    method: 'get',
+    params: query
+  })
+}
+
+export function getTcmScheduleList() {
+  return request({
+    url: '/company/schedule/getTcmScheduleList',
+    method: 'get'
+  })
+}

+ 16 - 0
src/api/course/sopCourseLink.js

@@ -8,3 +8,19 @@ export function createLinkUrl(data) {
     data: data
   })
 }
+
+
+export function queryQwIds() {
+  return request({
+    url: '/course/courseLink/queryQwIds',
+    method: 'get',
+  })
+}
+
+export function createRoomLinkUrl(data) {
+  return request({
+    url: '/course/courseLink/createRoomLink',
+    method: 'post',
+    data: data
+  })
+}

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

@@ -191,6 +191,14 @@ export function updatePeriod(data) {
   })
 }
 
+export function updatePeriodIsOpenRestReminder(data) {
+  return request({
+    url: '/course/period/updatePeriodIsOpenRestReminder',
+    method: 'put',
+    data: data
+  })
+}
+
 // 根据营期id获取公司红包金额列表
 export function getPeriodCompanyList(query) {
   return request({

+ 2 - 2
src/api/live/liveData.js

@@ -67,9 +67,9 @@ export function getLiveDataDetailBySql(liveId) {
   })
 }
 
-export function getLiveUserDetailListBySql(liveId) {
+export function getLiveUserDetailListBySql(liveId, pageNum, pageSize) {
   return request({
-    url: '/liveData/liveData/getLiveUserDetailListBySql?liveId=' + liveId,
+    url: '/liveData/liveData/getLiveUserDetailListBySql?liveId=' + liveId + '&pageNum=' + (pageNum || 1) + '&pageSize=' + (pageSize || 100),
     method: 'get'
   })
 }

+ 53 - 0
src/api/live/liveWatchLog.js

@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 查询直播看课记录列表
+export function listLiveWatchLog(query) {
+  return request({
+    url: '/live/liveWatchLog/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询直播看课记录详细
+export function getLiveWatchLog(logId) {
+  return request({
+    url: '/live/liveWatchLog/' + logId,
+    method: 'get'
+  })
+}
+
+// 新增直播看课记录
+export function addLiveWatchLog(data) {
+  return request({
+    url: '/live/liveWatchLog',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改直播看课记录
+export function updateLiveWatchLog(data) {
+  return request({
+    url: '/live/liveWatchLog',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除直播看课记录
+export function delLiveWatchLog(logId) {
+  return request({
+    url: '/live/liveWatchLog/' + logId,
+    method: 'delete'
+  })
+}
+
+// 导出直播看课记录
+export function exportLiveWatchLog(query) {
+  return request({
+    url: '/live/liveWatchLog/export',
+    method: 'get',
+    params: query
+  })
+}

+ 64 - 0
src/api/live/mergedOrder.js

@@ -0,0 +1,64 @@
+import request from '@/utils/request'
+
+// 查询合并订单列表
+export function listMergedOrder(query) {
+  return request({
+    url: '/order/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 导出合并订单
+export function exportMergedOrder(query) {
+  return request({
+    url: '/order/export',
+    method: 'get',
+    params: query
+  })
+}
+
+// 导出合并订单明细
+export function exportMergedOrderItems(query) {
+  return request({
+    url: '/order/exportItems',
+    method: 'get',
+    params: query
+  })
+}
+
+// 导出合并订单(明文)
+export function exportMergedOrderDetails(query) {
+  return request({
+    url: '/order/exportDetails',
+    method: 'get',
+    params: query
+  })
+}
+
+// 导出合并订单明细(明文)
+export function exportMergedOrderItemsDetails(query) {
+  return request({
+    url: '/order/exportItemsDetails',
+    method: 'get',
+    params: query
+  })
+}
+
+// 导出发货单
+export function exportMergedShipping(query) {
+  return request({
+    url: '/order/exportShipping',
+    method: 'get',
+    params: query
+  })
+}
+
+// 导入物流单号模板下载
+export function importDeliveryNoteExpressTemplate() {
+  return request({
+    url: '/order/importDeliveryNoteTemplate',
+    method: 'get'
+  })
+}
+

+ 52 - 0
src/api/qw/assignRule.js

@@ -0,0 +1,52 @@
+import request from '@/utils/request'
+
+// 查询分配规则列表
+export function listAssignRule(query) {
+  return request({
+    url: '/qwAssignRule/page',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询分配规则列表(指定参数版本)
+export function pageAssignRule(params) {
+  return request({
+    url: '/qwAssignRule/page',
+    method: 'get',
+    params: params
+  })
+}
+
+// 获取分配规则详细
+export function getAssignRule(id) {
+  return request({
+    url: '/qwAssignRule/' + id,
+    method: 'get'
+  })
+}
+
+// 新增或编辑分配规则
+export function addOrUpdateAssignRule(data) {
+  return request({
+    url: '/qwAssignRule/addOrUpdate',
+    method: 'post',
+    data: data
+  })
+}
+
+// 删除分配规则
+export function delAssignRule(id) {
+  return request({
+    url: '/qwAssignRule/' + id,
+    method: 'delete'
+  })
+}
+
+// 启用/停用分配规则
+export function enableAssignRule(id, status) {
+  return request({
+    url: '/qwAssignRule/enable/' + id + '/' + status,
+    method: 'post'
+  })
+}

+ 61 - 0
src/api/qw/customerLink.js

@@ -0,0 +1,61 @@
+import request from '@/utils/request'
+
+// 查询渠道链接列表
+export function pageCustomerLink(params) {
+  return request({
+    url: '/qwCustomerLink/page',
+    method: 'get',
+    params: params
+  })
+}
+
+// 获取渠道链接详情
+export function getCustomerLink(id) {
+  return request({
+    url: '/qwCustomerLink/' + id,
+    method: 'get'
+  })
+}
+
+// 新增或编辑渠道链接
+export function createOrUpdateCustomerLink(data) {
+  return request({
+    url: '/qwCustomerLink/createOrUpdate',
+    method: 'post',
+    data: data
+  })
+}
+
+// 删除渠道链接
+export function deleteCustomerLink(id) {
+  return request({
+    url: '/qwCustomerLink/delete/' + id,
+    method: 'post'
+  })
+}
+
+// 查询渠道链接的渠道列表
+export function pageCustomerLinkChannel(params) {
+  return request({
+    url: '/qwCustomerLink/channel/page',
+    method: 'get',
+    params: params
+  })
+}
+
+// 新增渠道链接的渠道
+export function createCustomerLinkChannel(data) {
+  return request({
+    url: '/qwCustomerLink/channel/create',
+    method: 'post',
+    data: data
+  })
+}
+
+// 删除渠道链接的渠道
+export function deleteCustomerLinkChannel(id) {
+  return request({
+    url: '/qwCustomerLink/channel/delete/' + id,
+    method: 'post'
+  })
+}

+ 35 - 0
src/api/qw/groupActual.js

@@ -0,0 +1,35 @@
+import request from '@/utils/request'
+
+// 查询群实际码列表
+export function listGroupActual(query) {
+  return request({
+    url: '/qwGroupActual/page',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询群实际码详细
+export function getGroupActual(id) {
+  return request({
+    url: '/qwGroupActual/' + id,
+    method: 'get'
+  })
+}
+
+// 新增或修改群实际码
+export function addOrUpdateGroupActual(data) {
+  return request({
+    url: '/qwGroupActual/addOrUpdate',
+    method: 'post',
+    data: data
+  })
+}
+
+// 删除群实际码
+export function delGroupActual(id) {
+  return request({
+    url: '/qwGroupActual/' + id,
+    method: 'delete'
+  })
+}

+ 44 - 0
src/api/qw/groupLiveCode.js

@@ -0,0 +1,44 @@
+import request from '@/utils/request'
+
+// 查询活码列表
+export function listGroupLiveCode(query) {
+  return request({
+    url: '/qwGroupLiveCode/page',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询活码列表(指定参数版本)
+export function pageGroupLiveCode(params) {
+  return request({
+    url: '/qwGroupLiveCode/page',
+    method: 'get',
+    params: params
+  })
+}
+
+// 查询活码详细
+export function getGroupLiveCode(id) {
+  return request({
+    url: '/qwGroupLiveCode/' + id,
+    method: 'get'
+  })
+}
+
+// 新增或修改活码
+export function addOrUpdateGroupLiveCode(data) {
+  return request({
+    url: '/qwGroupLiveCode/addOrUpdate',
+    method: 'post',
+    data: data
+  })
+}
+
+// 删除活码
+export function delGroupLiveCode(id) {
+  return request({
+    url: '/qwGroupLiveCode/' + id,
+    method: 'delete'
+  })
+}

+ 41 - 0
src/api/qw/luckyBag.js

@@ -0,0 +1,41 @@
+import request from '@/utils/request'
+export function listReward(query) {
+  return request({
+    url: '/qw/luckyBag/list',
+    method: 'get',
+    params: query
+  })
+}
+// 新增福袋配置
+export function addReward(data) {
+  return request({
+    url: '/qw/luckyBag/add',
+    method: 'post',
+    data: data
+  })
+}
+
+// 查询奖励配置详细
+export function getReward(id) {
+  return request({
+    url: '/qw/luckyBag/' + id,
+    method: 'get'
+  })
+}
+
+export function updateReward(data) {
+  return request({
+    url: '/qw/luckyBag',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除奖励配置
+export function delReward(id) {
+  return request({
+    url: '/qw/luckyBag/' + id,
+    method: 'delete'
+  })
+}
+

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

@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 查询福袋发放及领取记录列表
+export function listLuckyBagCollectRecord(query) {
+  return request({
+    url: '/qw/luckyBagCollectRecord/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询福袋发放及领取记录详细
+export function getLuckyBagCollectRecord(id) {
+  return request({
+    url: '/qw/luckyBagCollectRecord/' + id,
+    method: 'get'
+  })
+}
+
+// 新增福袋发放及领取记录
+export function addLuckyBagCollectRecord(data) {
+  return request({
+    url: '/qw/luckyBagCollectRecord',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改福袋发放及领取记录
+export function updateLuckyBagCollectRecord(data) {
+  return request({
+    url: '/qw/luckyBagCollectRecord',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除福袋发放及领取记录
+export function delLuckyBagCollectRecord(id) {
+  return request({
+    url: '/qw/luckyBagCollectRecord/' + id,
+    method: 'delete'
+  })
+}
+
+// 导出福袋发放及领取记录
+export function exportLuckyBagCollectRecord(query) {
+  return request({
+    url: '/qw/luckyBagCollectRecord/export',
+    method: 'get',
+    params: query
+  })
+}

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

@@ -166,3 +166,11 @@ export function getSelectableRange() {
     method: 'get'
   })
 }
+
+export function batchOpenOrCloseOfficial(data) {
+  return request({
+    url: '/qw/sopTemp/batchOpenOrCloseOfficial',
+    method: 'post',
+    data: data
+  })
+}

二進制
src/assets/images/image_de.png


二進制
src/assets/images/link-button.gif


二進制
src/assets/images/qr_code.png


二進制
src/assets/logo/hsyy.jpg


+ 36 - 4
src/components/H5/FormWrapper.vue

@@ -26,6 +26,34 @@
       @update:config="updateFormConfig"
     />
 
+    <!-- Form component form items -->
+    <form-form-items
+      v-if="form.type === 'h5-form'"
+      :config="form"
+      @update:config="updateFormConfig"
+    />
+
+    <!-- Link Button component form items -->
+    <link-button-form-items
+      v-if="form.type === 'h5-link-button'"
+      :config="form"
+      @update:config="updateFormConfig"
+    />
+
+    <!-- Add Wechat Button component form items -->
+    <add-wechat-button-form-items
+      v-if="form.type === 'h5-add-wechat-button'"
+      :config="form"
+      @update:config="updateFormConfig"
+    />
+
+    <!-- Qrcode component form items -->
+    <qrcode-form-items
+      v-if="form.type === 'h5-qrcode'"
+      :config="form"
+      @update:config="updateFormConfig"
+    />
+
     <chat-form-items
       v-if="form.type === 'h5-chat'"
       :config="form"
@@ -38,7 +66,6 @@
       :index="index"
       :list="list"
       @bottom-change="handleBottomChange"
-      @delete="handleDelete"
       @update:config="updateFormConfig"
     />
   </el-form>
@@ -50,6 +77,10 @@ import ImageFormItems from './config-item/h5-image-config.vue';
 import SepFormItems from './config-item/h5-sep-config.vue';
 import CommonFormItems from './config-item/common-config.vue';
 import CountdownFormItems from './config-item/h5-countdown-config.vue';
+import FormFormItems from './config-item/h5-form-config.vue';
+import LinkButtonFormItems from './config-item/h5-link-button-config.vue';
+import AddWechatButtonFormItems from './config-item/h5-add-wechat-button-config.vue';
+import QrcodeFormItems from './config-item/h5-qrcode-config.vue';
 import ChatFormItems from './config-item/h5-chat-config.vue'
 export default {
   name: 'FormWrapper',
@@ -59,6 +90,10 @@ export default {
     SepFormItems,
     CommonFormItems,
     CountdownFormItems,
+    FormFormItems,
+    LinkButtonFormItems,
+    AddWechatButtonFormItems,
+    QrcodeFormItems,
     ChatFormItems
   },
   props: {
@@ -108,9 +143,6 @@ export default {
     },
     removeClassFromItem(className) {
       this.form.classText = this.form.classText.filter(cls => cls !== className);
-    },
-    handleDelete() {
-      this.$emit('delete', this.index);
     }
   }
 }

+ 1 - 12
src/components/H5/config-item/common-config.vue

@@ -1,12 +1,7 @@
 // CommonFormItems.vue - Common form items shared across all components
 <template>
   <div id="h5-config-common" ref="commonForm">
-    <el-form-item label="是否底部">
-      <el-switch v-model="config.fixe" @change="handleBottom"></el-switch>
-    </el-form-item>
-    <el-form-item label="点击是否加微">
-      <el-switch v-model="config.addWxFun"/>
-    </el-form-item>
+
     <el-form-item label="获客链接" v-if="config.addWxFun">
       <el-select v-model="config.workUrl" filterable @change="val => updateConfig('workUrl', val)" placeholder="请选择">
         <el-option
@@ -18,9 +13,6 @@
       </el-select>
       <el-button type="primary" @click="copyLink">复制链接</el-button>
     </el-form-item>
-    <el-form-item>
-      <el-button type="danger" @click="handleDelete">删 除</el-button>
-    </el-form-item>
   </div>
 </template>
 
@@ -78,9 +70,6 @@ export default {
     },
     handleBottom() {
       this.$emit('bottom-change');
-    },
-    handleDelete() {
-      this.$emit('delete');
     }
   }
 }

+ 150 - 0
src/components/H5/config-item/h5-add-wechat-button-config.vue

@@ -0,0 +1,150 @@
+<template>
+  <div class="add-wechat-button-config">
+    <div class="config-block">
+      <h3>加微按钮配置</h3>
+
+      <!-- 按钮样式选择 -->
+      <el-form-item label="按钮样式">
+        <el-radio-group v-model="localConfig.buttonStyle" @change="handleStyleChange">
+          <el-radio label="text">文字</el-radio>
+          <el-radio label="image">图片</el-radio>
+        </el-radio-group>
+      </el-form-item>
+
+      <!-- 文字样式配置 -->
+      <template v-if="localConfig.buttonStyle === 'text'">
+        <el-form-item label="按钮文字">
+          <el-input 
+            v-model="localConfig.buttonText" 
+            placeholder="请输入按钮文字"
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+
+        <el-form-item label="按钮背景色">
+          <el-color-picker
+            v-model="localConfig.buttonColor"
+            show-alpha
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+
+        <el-form-item label="按钮文字色">
+          <el-color-picker
+            v-model="localConfig.buttonTextColor"
+            show-alpha
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+      </template>
+
+      <!-- 图片样式配置 -->
+      <template v-if="localConfig.buttonStyle === 'image'">
+        <el-form-item label="按钮图片">
+          <image-upload 
+            v-model="localConfig.buttonImage" 
+            :file-type='["png", "jpg", "jpeg", "gif"]' 
+            :limit="1"
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+      </template>
+
+      <!-- 其他设置 -->
+      <el-divider></el-divider>
+      <h4>其他设置</h4>
+      <el-form-item label="获客助手跳转">
+        <el-switch v-model="localConfig.enableGetCustomerAssistant" @change="handleConfigChange" />
+      </el-form-item>
+
+      <el-form-item label="跟随屏幕">
+        <el-switch v-model="localConfig.followScreen" @change="handleConfigChange" />
+      </el-form-item>
+
+      <el-form-item label="默认尝试拉起微信">
+        <el-switch v-model="localConfig.enableDefaultWechat" @change="handleConfigChange" />
+      </el-form-item>
+
+      <el-form-item label="启用小程序授权">
+        <el-switch v-model="localConfig.enableMiniProgram" @change="handleConfigChange" />
+      </el-form-item>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'AddWechatButtonConfigComponent',
+
+  props: {
+    config: {
+      type: Object,
+      required: false,
+      default: () => ({
+        buttonStyle: 'text',
+        buttonText: '提交',
+        buttonColor: '#FF5A5A',
+        buttonTextColor: '#ffffff',
+        buttonImage: '',
+        enableGetCustomerAssistant: true,
+        followScreen: true,
+        enableDefaultWechat: false,
+        enableMiniProgram: false
+      })
+    }
+  },
+
+  data() {
+    return {
+      localConfig: JSON.parse(JSON.stringify(this.config))
+    }
+  },
+
+  methods: {
+    handleStyleChange() {
+      // 切换样式时不清空图片,保持默认值
+      this.handleConfigChange()
+    },
+    handleConfigChange() {
+      this.$emit('update:config', JSON.parse(JSON.stringify(this.localConfig)))
+    }
+  },
+
+  watch: {
+    config: {
+      handler(newVal) {
+        this.localConfig = JSON.parse(JSON.stringify(newVal))
+      },
+      deep: true
+    },
+    'localConfig.buttonImage'(newVal) {
+      // 监听图片变化,实时更新父组件
+      this.handleConfigChange()
+    }
+  }
+}
+</script>
+
+<style scoped>
+.add-wechat-button-config {
+  width: 100%;
+  font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;
+}
+
+.config-block {
+  padding: 0px 15px;
+}
+
+h3 {
+  font-weight: 500;
+  margin: 15px 0 10px 0;
+  color: #303133;
+  font-size: 16px;
+  border-bottom: 1px solid #EBEEF5;
+  padding-bottom: 10px;
+}
+
+.el-form-item {
+  margin-bottom: 16px;
+}
+</style>

+ 137 - 0
src/components/H5/config-item/h5-form-config.vue

@@ -0,0 +1,137 @@
+<template>
+  <div class="form-config">
+    <div class="config-block">
+      <h3>表单配置</h3>
+
+      <!-- 第一行图片配置 -->
+      <el-form-item label="图片显示">
+        <el-switch v-model="localConfig.showImage" @change="handleConfigChange" />
+      </el-form-item>
+
+      <el-form-item v-if="localConfig.showImage" label="上传图片">
+        <image-upload 
+          v-model="localConfig.formImage" 
+          :file-type='["png", "jpg", "jpeg", "gif"]' 
+          :limit="1"
+          @change="handleConfigChange"
+        />
+      </el-form-item>
+
+      <!-- 提交按钮配置 -->
+      <el-form-item label="提交按钮显示">
+        <el-switch v-model="localConfig.showSubmitBtn" @change="handleConfigChange" />
+      </el-form-item>
+
+      <el-form-item v-if="localConfig.showSubmitBtn" label="按钮样式">
+        <el-radio-group v-model="localConfig.submitBtnStyle" @change="handleConfigChange">
+          <el-radio label="text">文字</el-radio>
+          <el-radio label="image">图片</el-radio>
+        </el-radio-group>
+      </el-form-item>
+
+      <!-- 文字+背景色样式配置 -->
+      <template v-if="localConfig.showSubmitBtn && localConfig.submitBtnStyle === 'text'">
+        <el-form-item label="按钮文字">
+          <el-input 
+            v-model="localConfig.submitBtnText" 
+            placeholder="请输入按钮文字"
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+
+        <el-form-item label="按钮颜色">
+          <el-color-picker
+            v-model="localConfig.submitBtnColor"
+            show-alpha
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+      </template>
+
+      <!-- 图片样式配置 -->
+      <el-form-item v-if="localConfig.showSubmitBtn && localConfig.submitBtnStyle === 'image'" label="按钮图片">
+        <image-upload 
+          v-model="localConfig.submitBtnImage" 
+          :file-type='["png", "jpg", "jpeg", "gif"]' 
+          :limit="1"
+          @change="handleConfigChange"
+        />
+      </el-form-item>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'FormConfigComponent',
+
+  props: {
+    config: {
+      type: Object,
+      required: false,
+      default: () => ({
+        showImage: true,
+        formImage: '',
+        showSubmitBtn: true,
+        submitBtnStyle: 'text',
+        submitBtnColor: '#409EFF',
+        submitBtnText: '提交',
+        submitBtnImage: ''
+      })
+    }
+  },
+
+  data() {
+    return {
+      localConfig: JSON.parse(JSON.stringify(this.config))
+    }
+  },
+
+  methods: {
+    handleConfigChange() {
+      this.$emit('update:config', JSON.parse(JSON.stringify(this.localConfig)))
+    }
+  },
+
+  watch: {
+    config: {
+      handler(newVal) {
+        this.localConfig = JSON.parse(JSON.stringify(newVal))
+      },
+      deep: true
+    },
+    'localConfig.formImage'(newVal) {
+      // 监听第一行图片变化,实时更新
+      this.handleConfigChange()
+    },
+    'localConfig.submitBtnImage'(newVal) {
+      // 监听提交按钮图片变化,实时更新
+      this.handleConfigChange()
+    }
+  }
+}
+</script>
+
+<style scoped>
+.form-config {
+  width: 100%;
+  font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;
+}
+
+.config-block {
+  padding: 0px 15px;
+}
+
+h3 {
+  font-weight: 500;
+  margin: 15px 0 10px 0;
+  color: #303133;
+  font-size: 16px;
+  border-bottom: 1px solid #EBEEF5;
+  padding-bottom: 10px;
+}
+
+.el-form-item {
+  margin-bottom: 16px;
+}
+</style>

+ 29 - 0
src/components/H5/config-item/h5-image-config.vue

@@ -4,6 +4,10 @@
     <el-form-item label="图片">
       <image-upload v-model="config.url" :file-type='["png", "jpg", "jpeg", "gif"]' :limit="1"/>
     </el-form-item>
+    <div class="image-tips">
+      <p>建议尺寸:宽750px,高不限;</p>
+      <p>支持格式:png/jpeg/gif,不大于1M尺寸。</p>
+    </div>
   </div>
 </template>
 
@@ -18,3 +22,28 @@ export default {
   }
 }
 </script>
+
+<style scoped>
+.image-tips {
+  margin-top: 12px;
+  padding: 12px;
+  background-color: #fef0e6;
+  border-left: 3px solid #ff9800;
+  border-radius: 4px;
+}
+
+.image-tips p {
+  margin: 4px 0;
+  color: #ff9800;
+  font-size: 12px;
+  line-height: 1.6;
+}
+
+.image-tips p:first-child {
+  margin-top: 0;
+}
+
+.image-tips p:last-child {
+  margin-bottom: 0;
+}
+</style>

+ 127 - 0
src/components/H5/config-item/h5-link-button-config.vue

@@ -0,0 +1,127 @@
+<template>
+  <div class="link-button-config">
+    <div class="config-block">
+      <h3>跳转按钮配置</h3>
+
+      <!-- 按钮样式选择 -->
+      <el-form-item label="按钮样式">
+        <el-radio-group v-model="localConfig.buttonStyle" @change="handleStyleChange">
+          <el-radio label="text">文字</el-radio>
+          <el-radio label="image">图片</el-radio>
+        </el-radio-group>
+      </el-form-item>
+
+      <!-- 文字样式配置 -->
+      <template v-if="localConfig.buttonStyle === 'text'">
+        <el-form-item label="按钮文字">
+          <el-input 
+            v-model="localConfig.buttonText" 
+            placeholder="请输入按钮文字"
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+
+        <el-form-item label="按钮背景色">
+          <el-color-picker
+            v-model="localConfig.buttonColor"
+            show-alpha
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+
+        <el-form-item label="按钮文字色">
+          <el-color-picker
+            v-model="localConfig.buttonTextColor"
+            show-alpha
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+      </template>
+
+      <!-- 图片样式配置 -->
+      <template v-if="localConfig.buttonStyle === 'image'">
+        <el-form-item label="按钮图片">
+          <image-upload 
+            v-model="localConfig.buttonImage" 
+            :file-type='["png", "jpg", "jpeg", "gif"]' 
+            :limit="1"
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+      </template>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'LinkButtonConfigComponent',
+
+  props: {
+    config: {
+      type: Object,
+      required: false,
+      default: () => ({
+        buttonStyle: 'text',
+        buttonText: '提交',
+        buttonColor: '#FF5A5A',
+        buttonTextColor: '#ffffff',
+        buttonImage: ''
+      })
+    }
+  },
+
+  data() {
+    return {
+      localConfig: JSON.parse(JSON.stringify(this.config))
+    }
+  },
+
+  methods: {
+    handleStyleChange() {
+      // 切换样式时不清空图片,保持默认值
+      this.handleConfigChange()
+    },
+    handleConfigChange() {
+      this.$emit('update:config', JSON.parse(JSON.stringify(this.localConfig)))
+    }
+  },
+
+  watch: {
+    config: {
+      handler(newVal) {
+        this.localConfig = JSON.parse(JSON.stringify(newVal))
+      },
+      deep: true
+    },
+    'localConfig.buttonImage'(newVal) {
+      // 监听图片变化,实时更新父组件
+      this.handleConfigChange()
+    }
+  }
+}
+</script>
+
+<style scoped>
+.link-button-config {
+  width: 100%;
+  font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;
+}
+
+.config-block {
+  padding: 0px 15px;
+}
+
+h3 {
+  font-weight: 500;
+  margin: 15px 0 10px 0;
+  color: #303133;
+  font-size: 16px;
+  border-bottom: 1px solid #EBEEF5;
+  padding-bottom: 10px;
+}
+
+.el-form-item {
+  margin-bottom: 16px;
+}
+</style>

+ 188 - 0
src/components/H5/config-item/h5-qrcode-config.vue

@@ -0,0 +1,188 @@
+<template>
+  <div class="qrcode-config">
+    <div class="config-block">
+      <h3>二维码配置</h3>
+
+      <!-- 获客助手跳转 -->
+      <el-form-item label="获客助手跳转">
+        <el-switch v-model="localConfig.enableGetCustomerAssistant" @change="handleConfigChange" />
+      </el-form-item>
+
+      <!-- 二维码尺寸 -->
+      <el-form-item label="二维码尺寸(px)">
+        <el-input-number 
+          v-model="localConfig.qrcodeSize" 
+          :min="80"
+          :max="300"
+          @change="handleConfigChange"
+        />
+      </el-form-item>
+
+      <!-- 复制微信号区域 -->
+      <el-divider></el-divider>
+      <h4>复制微信号按钮</h4>
+      <el-form-item label="是否启用">
+        <el-switch v-model="localConfig.showCopyBtn" @change="handleConfigChange" />
+      </el-form-item>
+
+      <template v-if="localConfig.showCopyBtn">
+        <el-form-item label="按钮文字">
+          <el-input 
+            v-model="localConfig.copyBtnText" 
+            placeholder="请输入按钮文字"
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+
+        <el-form-item label="文字颜色">
+          <el-color-picker
+            v-model="localConfig.copyBtnTextColor"
+            show-alpha
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+
+        <el-form-item label="填充颜色">
+          <el-color-picker
+            v-model="localConfig.copyBtnColor"
+            show-alpha
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+      </template>
+
+      <!-- 点击添加老师区域 -->
+      <el-divider></el-divider>
+      <h4>点击添加老师按钮</h4>
+      <el-form-item label="是否启用">
+        <el-switch v-model="localConfig.showClickBtn" @change="handleConfigChange" />
+      </el-form-item>
+
+      <template v-if="localConfig.showClickBtn">
+        <el-form-item label="按钮文字">
+          <el-input 
+            v-model="localConfig.clickBtnText" 
+            placeholder="请输入按钮文字"
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+
+        <el-form-item label="文字颜色">
+          <el-color-picker
+            v-model="localConfig.clickBtnTextColor"
+            show-alpha
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+
+        <el-form-item label="填充颜色">
+          <el-color-picker
+            v-model="localConfig.clickBtnColor"
+            show-alpha
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+
+        <el-form-item label="微信内跳转">
+          <el-switch v-model="localConfig.clickBtnWechatJump" @change="handleConfigChange" />
+        </el-form-item>
+      </template>
+
+      <!-- 其他选项 -->
+      <el-divider></el-divider>
+      <h4>其他设置</h4>
+      <el-form-item label="默认尝试拉起微信">
+        <el-switch v-model="localConfig.enableDefaultWechat" @change="handleConfigChange" />
+      </el-form-item>
+
+      <el-form-item label="启用小程序授权">
+        <el-switch v-model="localConfig.enableMiniProgram" @change="handleConfigChange" />
+      </el-form-item>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'QrcodeConfigComponent',
+
+  props: {
+    config: {
+      type: Object,
+      required: false,
+      default: () => ({
+        qrcodeImage: '',
+        qrcodeSize: 140,
+        showCopyBtn: true,
+        copyBtnText: '复制并添加老师',
+        copyBtnColor: '#FF9500',
+        copyBtnTextColor: '#ffffff',
+        showClickBtn: true,
+        clickBtnText: '点击添加老师',
+        clickBtnColor: '#FF9500',
+        clickBtnTextColor: '#ffffff',
+        clickBtnWechatJump: false,
+        enableGetCustomerAssistant: false,
+        enableDefaultWechat: false,
+        enableMiniProgram: false
+      })
+    }
+  },
+
+  data() {
+    return {
+      localConfig: JSON.parse(JSON.stringify(this.config))
+    }
+  },
+
+  methods: {
+    handleConfigChange() {
+      this.$emit('update:config', JSON.parse(JSON.stringify(this.localConfig)))
+    }
+  },
+
+  watch: {
+    config: {
+      handler(newVal) {
+        this.localConfig = JSON.parse(JSON.stringify(newVal))
+      },
+      deep: true
+    }
+  }
+}
+</script>
+
+<style scoped>
+.qrcode-config {
+  width: 100%;
+  font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;
+}
+
+.config-block {
+  padding: 0px 15px;
+}
+
+h3 {
+  font-weight: 500;
+  margin: 15px 0 10px 0;
+  color: #303133;
+  font-size: 16px;
+  border-bottom: 1px solid #EBEEF5;
+  padding-bottom: 10px;
+}
+
+h4 {
+  font-weight: 500;
+  margin: 10px 0 8px 0;
+  color: #606266;
+  font-size: 14px;
+}
+
+.el-form-item {
+  margin-bottom: 16px;
+}
+
+.el-divider {
+  margin: 15px 0;
+}
+</style>

+ 3 - 2
src/components/H5/css/base.css

@@ -1,9 +1,10 @@
 .parent-div{
-  border: 2px dashed transparent;
+  border: none;
   /*padding: 5px 0;*/
 }
 .parent-div.active{
-  border-color: #02ff9b;
+  outline: 2px dashed #02ff9b;
+  outline-offset: -2px;
 }
 .footer{
   position: absolute;

+ 102 - 0
src/components/H5/h5-add-wechat-button.vue

@@ -0,0 +1,102 @@
+<template>
+  <div :class="config.classText.join(' ')">
+    <div class="add-wechat-button-wrapper">
+      <!-- 文字样式按钮 -->
+      <button 
+        v-if="config.buttonStyle === 'text'"
+        class="add-wechat-button text-style"
+        :style="{ 
+          backgroundColor: config.buttonColor,
+          color: config.buttonTextColor
+        }"
+        @click="handleClick"
+      >
+        {{ config.buttonText }}
+      </button>
+
+      <!-- 图片样式按钮 -->
+      <img 
+        v-else-if="config.buttonStyle === 'image'"
+        :src="config.buttonImage || require('@/assets/images/default.jpg')"
+        class="add-wechat-button image-style"
+        @click="handleClick"
+      />
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'h5-add-wechat-button',
+  props: {
+    config: {
+      type: Object,
+      default: () => {
+        return {
+          classText: ['parent-div'],
+          buttonStyle: 'text',
+          buttonText: '提交',
+          buttonColor: '#FF5A5A',
+          buttonTextColor: '#ffffff',
+          buttonImage: ''
+        }
+      }
+    }
+  },
+  methods: {
+    handleClick() {
+      // 点击事件可在此处理
+    }
+  }
+}
+</script>
+
+<style src="./css/base.css"></style>
+<style lang="scss" scoped>
+.add-wechat-button-wrapper {
+  width: 100%;
+  display: flex;
+  justify-content: center;
+}
+
+.add-wechat-button {
+  border: none;
+  border-radius: 4px;
+  font-size: 16px;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  
+  &.text-style {
+    width: 100%;
+    padding: 12px 16px;
+    font-weight: 600;
+    
+    &:hover {
+      opacity: 0.9;
+      transform: translateY(-2px);
+      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+    }
+    
+    &:active {
+      opacity: 0.8;
+      transform: translateY(0);
+    }
+  }
+  
+  &.image-style {
+    max-width: 100%;
+    height: auto;
+    object-fit: contain;
+    
+    &:hover {
+      opacity: 0.9;
+      transform: scale(1.02);
+    }
+    
+    &:active {
+      opacity: 0.8;
+      transform: scale(1);
+    }
+  }
+}
+</style>

+ 233 - 0
src/components/H5/h5-form.vue

@@ -0,0 +1,233 @@
+<template>
+  <div :class="config.classText.join(' ')">
+    <div class="form-container">
+      <!-- 第一行:图片 -->
+      <div v-if="config.showImage" class="form-image-row">
+        <img v-if="config.formImage" :src="config.formImage" class="form-image" />
+      </div>
+
+      <!-- 第二行:手机号输入框 -->
+      <div class="form-input-row">
+        <input 
+          type="tel" 
+          class="form-input phone-input" 
+          placeholder="请输入手机号"
+          v-model="phoneNumber"
+        />
+      </div>
+
+      <!-- 第三行:验证码输入框 + 获取验证码按钮 -->
+      <div class="form-code-row">
+        <input 
+          type="text" 
+          class="form-input code-input" 
+          placeholder="请输入验证码"
+          v-model="verifyCode"
+        />
+        <button class="get-code-btn" @click="getVerifyCode">
+          {{ verifyCodeBtnText }}
+        </button>
+      </div>
+
+      <!-- 第四行:提交按钮 -->
+      <div v-if="config.showSubmitBtn" class="form-submit-row">
+        <!-- 文字+背景色样式 -->
+        <button 
+          v-if="config.submitBtnStyle === 'text'"
+          class="submit-btn text-style"
+          :style="{ backgroundColor: config.submitBtnColor }"
+        >
+          {{ config.submitBtnText }}
+        </button>
+        <!-- 图片样式 -->
+        <img 
+          v-else-if="config.submitBtnStyle === 'image'"
+          :src="config.submitBtnImage || require('@/assets/images/link-button.gif')"
+          class="submit-btn image-style"
+        />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'h5-form',
+  props: {
+    config: {
+      type: Object,
+      default: () => {
+        return {
+          classText: ['parent-div'],
+          showImage: true,
+          formImage: '',
+          showSubmitBtn: true,
+          submitBtnStyle: 'text', // 'text' 或 'image'
+          submitBtnColor: '#409EFF',
+          submitBtnText: '提交',
+          submitBtnImage: ''
+        }
+      }
+    }
+  },
+  data() {
+    return {
+      phoneNumber: '',
+      verifyCode: '',
+      countdownTime: 0,
+      verifyCodeBtnText: '获取验证码'
+    }
+  },
+  methods: {
+    getVerifyCode() {
+      if (!this.phoneNumber) {
+        this.$message.warning('请先输入手机号');
+        return;
+      }
+      // 启动倒计时
+      this.countdownTime = 60;
+      this.updateButtonText();
+    },
+    updateButtonText() {
+      if (this.countdownTime > 0) {
+        this.verifyCodeBtnText = `${this.countdownTime}s`;
+        this.countdownTime--;
+        setTimeout(() => {
+          this.updateButtonText();
+        }, 1000);
+      } else {
+        this.verifyCodeBtnText = '获取验证码';
+      }
+    }
+  }
+}
+</script>
+
+<style src="./css/base.css"></style>
+<style lang="scss" scoped>
+.form-container {
+  width: 100%;
+  padding: 16px;
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+}
+
+.active {
+  border-color: #02ff9b;
+}
+
+.form-image-row {
+  width: 100%;
+  display: flex;
+  justify-content: center;
+  margin-bottom: 8px;
+}
+
+.form-image {
+  max-width: 100%;
+  height: auto;
+  max-height: 150px;
+  border-radius: 4px;
+}
+
+.form-input-row,
+.form-code-row {
+  width: 100%;
+  display: flex;
+  gap: 8px;
+}
+
+.form-code-row {
+  justify-content: space-between;
+}
+
+.form-input {
+  border: 1px solid #DCDFE6;
+  border-radius: 20px;
+  padding: 10px 16px;
+  font-size: 14px;
+  color: #303133;
+  
+  &::placeholder {
+    color: #909399;
+  }
+
+  &:focus {
+    outline: none;
+    border-color: #409EFF;
+    box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
+  }
+}
+
+.phone-input {
+  width: 100%;
+}
+
+.code-input {
+  flex: 1;
+  min-width: 150px;
+}
+
+.get-code-btn {
+  padding: 10px 16px;
+  border: 1px solid #409EFF;
+  background-color: #fff;
+  color: #409EFF;
+  border-radius: 20px;
+  font-size: 14px;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  white-space: nowrap;
+
+  &:hover {
+    background-color: #F0F9FF;
+  }
+
+  &:active {
+    background-color: #E6F2FF;
+  }
+}
+
+.form-submit-row {
+  width: 100%;
+  display: flex;
+  justify-content: center;
+  margin-top: 8px;
+}
+
+.submit-btn {
+  border: none;
+  border-radius: 20px;
+  font-size: 16px;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  width: calc(100% - 32px);
+
+  &.text-style {
+    padding: 12px 48px;
+    color: #fff;
+    font-weight: 600;
+
+    &:hover {
+      opacity: 0.9;
+    }
+
+    &:active {
+      opacity: 0.8;
+    }
+  }
+
+  &.image-style {
+    max-width: 100%;
+    height: auto;
+    max-height: 60px;
+    object-fit: contain;
+    width: auto;
+
+    &:hover {
+      opacity: 0.9;
+    }
+  }
+}
+</style>

+ 10 - 1
src/components/H5/h5-image.vue

@@ -1,6 +1,6 @@
 <template>
   <div :class="config.classText.join(' ')">
-    <img :src="config.url || require('@/assets/images/default.jpg')" :style="config.style" />
+    <img :src="config.url || require('@/assets/images/image_de.png')" :style="config.style" />
   </div>
 <!--  <img  src="@/assets/images/default.jpg" width="200" height="200" />-->
 </template>
@@ -18,8 +18,17 @@ export default {
 </script>
 <style src="./css/base.css"></style>
 <style lang="scss" scoped>
+div {
+  margin: 0;
+  padding: 0;
+  width: 100%;
+  box-sizing: border-box;
+}
+
 img{
   width: 100% !important;
+  height: auto;
+  display: block;
 }
 .active{
   border-color: #02ff9b;

+ 102 - 0
src/components/H5/h5-link-button.vue

@@ -0,0 +1,102 @@
+<template>
+  <div :class="config.classText.join(' ')">
+    <div class="link-button-wrapper">
+      <!-- 文字样式按钮 -->
+      <button 
+        v-if="config.buttonStyle === 'text'"
+        class="link-button text-style"
+        :style="{ 
+          backgroundColor: config.buttonColor,
+          color: config.buttonTextColor
+        }"
+        @click="handleClick"
+      >
+        {{ config.buttonText }}
+      </button>
+
+      <!-- 图片样式按钮 -->
+      <img 
+        v-else-if="config.buttonStyle === 'image'"
+        :src="config.buttonImage || require('@/assets/images/link-button.gif')"
+        class="link-button image-style"
+        @click="handleClick"
+      />
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'h5-link-button',
+  props: {
+    config: {
+      type: Object,
+      default: () => {
+        return {
+          classText: ['parent-div'],
+          buttonStyle: 'text',
+          buttonText: '提交',
+          buttonColor: '#FF5A5A',
+          buttonTextColor: '#ffffff',
+          buttonImage: ''
+        }
+      }
+    }
+  },
+  methods: {
+    handleClick() {
+      // 点击事件可在此处理
+    }
+  }
+}
+</script>
+
+<style src="./css/base.css"></style>
+<style lang="scss" scoped>
+.link-button-wrapper {
+  width: 100%;
+  display: flex;
+  justify-content: center;
+}
+
+.link-button {
+  border: none;
+  border-radius: 4px;
+  font-size: 16px;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  
+  &.text-style {
+    width: 100%;
+    padding: 12px 16px;
+    font-weight: 600;
+    
+    &:hover {
+      opacity: 0.9;
+      transform: translateY(-2px);
+      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+    }
+    
+    &:active {
+      opacity: 0.8;
+      transform: translateY(0);
+    }
+  }
+  
+  &.image-style {
+    max-width: 100%;
+    height: auto;
+    object-fit: contain;
+    
+    &:hover {
+      opacity: 0.9;
+      transform: scale(1.02);
+    }
+    
+    &:active {
+      opacity: 0.8;
+      transform: scale(1);
+    }
+  }
+}
+</style>

+ 127 - 0
src/components/H5/h5-qrcode.vue

@@ -0,0 +1,127 @@
+<template>
+  <div :class="config.classText.join(' ')">
+    <div class="qrcode-container">
+      <div class="qrcode-image-row">
+        <img 
+          :src="config.qrcodeImage || require('@/assets/images/qr_code.png')"
+          class="qrcode-image"
+          :style="{ width: config.qrcodeSize + 'px', height: config.qrcodeSize + 'px' }"
+        />
+      </div>
+
+
+      <div v-if="config.showCopyBtn" class="copy-btn-row">
+        <button class="circle-btn copy-btn" :style="{ backgroundColor: config.copyBtnColor, color: config.copyBtnTextColor }">
+          {{ config.copyBtnText }}
+        </button>
+      </div>
+
+      <div v-if="config.showClickBtn" class="click-btn-row">
+        <button class="circle-btn click-btn" :style="{ backgroundColor: config.clickBtnColor, color: config.clickBtnTextColor }">
+          {{ config.clickBtnText }}
+        </button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'h5-qrcode',
+  props: {
+    config: {
+      type: Object,
+      default: () => ({
+        classText: ['parent-div'],
+        qrcodeImage: '',
+        qrcodeSize: 140,
+        showCopyBtn: true,
+        copyBtnText: '复制并添加老师',
+        copyBtnColor: '#FF9500',
+        copyBtnTextColor: '#ffffff',
+        showClickBtn: true,
+        clickBtnText: '点击添加老师',
+        clickBtnColor: '#FF9500',
+        clickBtnTextColor: '#ffffff'
+      })
+    }
+  }
+}
+</script>
+
+<style src="./css/base.css"></style>
+<style lang="scss" scoped>
+.qrcode-container {
+  width: 100%;
+  padding: 16px;
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+  align-items: center;
+}
+
+.qrcode-image-row {
+  width: 100%;
+  display: flex;
+  justify-content: center;
+  margin-bottom: 8px;
+}
+
+.qrcode-image {
+  max-width: 100%;
+  height: auto;
+  border-radius: 4px;
+}
+
+.wechat-number-row {
+  width: 100%;
+  display: flex;
+  justify-content: center;
+  margin-bottom: 8px;
+}
+
+.wechat-number-text {
+  font-size: 14px;
+  color: #303133;
+  text-align: center;
+}
+
+.copy-btn-row,
+.click-btn-row {
+  width: 100%;
+  display: flex;
+  justify-content: center;
+  margin-bottom: 8px;
+}
+
+.circle-btn {
+  border: none;
+  border-radius: 20px;
+  padding: 10px 20px;
+  font-size: 14px;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  white-space: nowrap;
+  font-weight: 500;
+
+  &:hover {
+    opacity: 0.9;
+  }
+
+  &:active {
+    opacity: 0.8;
+  }
+}
+
+.copy-btn {
+  min-width: 160px;
+}
+
+.click-btn {
+  min-width: 160px;
+}
+
+.active {
+  border-color: #02ff9b;
+}
+</style>

+ 5 - 2
src/components/H5/h5-sep.vue

@@ -19,8 +19,11 @@ export default {
 </script>
 <style src="./css/base.css"></style>
 <style lang="scss" scoped>
-p{
-  white-space: pre-line;
+div {
+  margin: 0;
+  padding: 0;
+  width: 100%;
+  box-sizing: border-box;
 }
 .active{
   border-color: #02ff9b;

+ 4 - 0
src/components/H5/h5-text.vue

@@ -20,6 +20,10 @@ export default {
 <style lang="scss" scoped>
 p{
   white-space: pre-line;
+  margin: 0;
+  padding: 0;
+  width: 100%;
+  box-sizing: border-box;
 }
 .active{
   border-color: #02ff9b;

+ 1024 - 202
src/components/H5Editor/index.vue

@@ -1,40 +1,122 @@
 <template>
   <div class="h5-editor-container">
-    <div class="h5-editor">
+    <!-- 标题编辑栏 -->
+    <div class="title-bar">
+      <el-input v-model="pageTitle" placeholder="输入落地页标题" class="title-input" />
     </div>
-    <el-row :gutter="24">
-      <el-col :span="2" class="button-body">
-        <div v-for="item in componentList">
-          <p/>
-          <div>
-            {{ item.groupName }}
+    <el-row :gutter="16" class="editor-wrapper">
+      <!-- 左上:页面选择面板 -->
+      <el-col :span="3" class="page-panel">
+        <div class="panel-header">
+          <h3>页面选择</h3>
+        </div>
+        <div class="page-list">
+          <div 
+            v-for="page in pageList" 
+            :key="page.pageStep"
+            class="page-card" 
+            :class="{active: currentPageStep === page.pageStep}"
+            @click="switchPage(page.pageStep)"
+          >
+            <div class="page-icon">
+              <i :class="page.icon"></i>
+            </div>
+            <div class="page-name">{{ page.name }}</div>
+          </div>
+        </div>
+      </el-col>
+
+      <!-- 左下:组件面板 -->
+      <el-col :span="3" class="left-panel">
+        <div class="panel-header">
+          <h3>组件库</h3>
+        </div>
+        <div class="components-list">
+          <div v-for="(group, idx) in componentList" :key="idx" class="component-group">
+            <div class="group-title">
+              <i class="el-icon-box"></i>
+              {{ group.groupName }}
+            </div>
+            <div class="buttons-wrapper">
+              <div 
+                v-for="comp in group.comps" 
+                :key="comp.type"
+                class="comp-button" 
+                draggable="true"
+                @dragstart="handleDragStart($event, comp)"
+                @dragend="handleDragEnd"
+              >
+                <i :class="comp.icon"></i>
+                <span>{{ comp.label }}</span>
+              </div>
+            </div>
           </div>
-          <el-button class="button-tag" @click="add(item)" v-for="item in item.comps">
-            <!-- 添加图标和文字 -->
-            <i :class="item.icon" style="margin-right: 5px"></i>
-            {{ item.label }}
-          </el-button>
         </div>
       </el-col>
-      <el-col :span="12">
+      
+      <!-- 中间:预览区域 -->
+      <el-col :span="10" class="center-panel">
+        <div class="panel-header">
+          <h3>页面预览</h3>
+          <span class="device-info">手机预览 (375px)</span>
+        </div>
         <div class="parent-container">
-          <div class="view-body">
-            <draggable v-model="list" @end="end">
-              <div v-for="(item, index) in list" :key="index" @click="select(index)" class="view-item">
-                <component :is="item.type" :config="item"/>
+          <!-- 模拟手机导航栏 -->
+          <div class="mobile-nav-bar">
+            <div class="nav-left">
+              <i class="el-icon-arrow-left"></i>
+            </div>
+            <div class="nav-title">{{ pageTitle || '落地页标题' }}</div>
+            <div class="nav-right">
+              <i class="el-icon-more"></i>
+            </div>
+          </div>
+          <div class="view-body" @dragover.prevent="handleDragOver" @drop="handleDrop" @dragleave="handleDragLeave" @mousemove="handleMouseMove">
+            <draggable 
+              v-model="list" 
+              @end="end" 
+              class="draggable-area"
+              animation="200"
+              group="components"
+              :disabled="false"
+              ghost-class="ghost"
+              drag-class="drag"
+            >
+              <div v-for="(item, index) in list" :key="'item-' + index" 
+                   class="draggable-wrapper"
+                   @dragenter="handleItemDragEnter($event, index)"
+                   @dragover="handleItemDragOver($event, index)"
+                   @dragleave="handleItemDragLeave($event, index)">
+                <div @click="select(index)" class="view-item" :class="{active: item.active}">
+                  <component :is="item.type" :config="item"/>
+                  <!-- 删除按钮 -->
+                  <div v-if="item.active" class="delete-button-wrapper" @click.stop="handleDelete(index)">
+                    <i class="el-icon-close"></i>
+                  </div>
+                </div>
               </div>
             </draggable>
+            <div v-if="list.length === 0" class="empty-state">
+              <i class="el-icon-picture"></i>
+              <p>从左侧拖拽或点击添加组件</p>
+            </div>
           </div>
         </div>
       </el-col>
-      <el-col :span="10" style="overflow-y: auto;height:80vh;">
-        <form-wrapper
-          :form="form"
-          :index="index"
-          :list="list"
-          @update:form="updateForm"
-          @delete="del"
-        />
+      
+      <!-- 右侧:属性面板 -->
+      <el-col :span="8" class="right-panel">
+        <div class="panel-header">
+          <h3>属性设置</h3>
+        </div>
+        <div class="properties-wrapper">
+          <form-wrapper
+            :form="form"
+            :index="index"
+            :list="list"
+            @update:form="updateForm"
+          />
+        </div>
       </el-col>
     </el-row>
   </div>
@@ -47,6 +129,10 @@ import H5Image from '@/components/H5/h5-image.vue'
 import H5Button from '@/components/H5/h5-button.vue'
 import H5Sep from '@/components/H5/h5-sep.vue'
 import H5Countdown from '@/components/H5/h5-countdown.vue'
+import H5Form from '@/components/H5/h5-form.vue'
+import H5LinkButton from '@/components/H5/h5-link-button.vue'
+import H5AddWechatButton from '@/components/H5/h5-add-wechat-button.vue'
+import H5Qrcode from '@/components/H5/h5-qrcode.vue'
 import FormWrapper from '@/components/H5/FormWrapper.vue'
 import H5Chat from '@/components/H5/h5-chat.vue'
 import AgentAvatar from '@/assets/images/customer.png?inline'
@@ -59,6 +145,10 @@ export default {
     H5Image,// 对应模板中的<h5-image>
     H5Sep,// 对应模板中的<h5-sep>
     H5Countdown,// 对应模板中的<h5-countdown>
+    H5Form,// 对应模板中的<h5-form>
+    H5LinkButton,// 对应模板中的<h5-link-button>
+    H5AddWechatButton,// 对应模板中的<h5-add-wechat-button>
+    H5Qrcode,// 对应模板中的<h5-qrcode>
     FormWrapper,
     H5Chat
   },
@@ -73,6 +163,14 @@ export default {
   },
   data() {
     return {
+      pageTitle: '',
+      currentPageStep: 'home',
+      pageList: [
+        { pageStep: 'home', name: '首页', icon: 'el-icon-house' },
+        { pageStep: 'appletLand', name: '小程序授权页', icon: 'el-icon-document' },
+        { pageStep: 'purchaseSuccess', name: '小程序出码页', icon: 'el-icon-success' }
+      ],
+      configList: [],
       componentList: [{
         groupName: '基础控件',
         comps: [
@@ -101,20 +199,159 @@ export default {
             icon: 'el-icon-timer'            // 计时器图标表示倒计时
           },
           {
-            type: 'h5-chat',
-            label: '互动问答',
-            icon: 'el-icon-chat-square'     // 购物袋表示购买相关
+            type: 'h5-form',
+            label: '表单',
+            icon: 'el-icon-document-copy'    // 表单图标
+          },
+          {
+            type: 'h5-link-button',
+            label: '跳转按钮',
+            icon: 'el-icon-connection'       // 跳转图标
+          },
+          {
+            type: 'h5-add-wechat-button',
+            label: '加微按钮',
+            icon: 'el-icon-connection'       // 加微图标
+          },
+          {
+            type: 'h5-qrcode',
+            label: '二维码',
+            icon: 'el-icon-picture'          // 二维码图标
           }
         ]
       }],
       list: [],
-      index: 0,
-      form: {}
+      index: -1,
+      form: {},
+      draggedComponent: null, // 存储被拖拽的组件信息
+      insertIndex: -1 // 存储插入位置
     }
   },
   methods: {
-    initData(json) {
-      this.list = JSON.parse(json)
+    initData(json, templateType) {
+      // 根据模板类型设置页面列表
+      if (templateType === '小程序表单类') {
+        this.pageList = [
+          { pageStep: 'home', name: '首页', icon: 'el-icon-house' },
+          { pageStep: 'appletLand', name: '小程序授权页', icon: 'el-icon-document' },
+          { pageStep: 'purchaseSuccess', name: '小程序出码页', icon: 'el-icon-success' }
+        ]
+      } else if (templateType === '小程序加粉类') {
+        this.pageList = [
+          { pageStep: 'home', name: '首页', icon: 'el-icon-house' },
+          { pageStep: 'purchaseSuccess', name: '小程序出码页', icon: 'el-icon-success' }
+        ]
+      } else if (templateType === '免费加粉类') {
+        this.pageList = [
+          { pageStep: 'home', name: '首页', icon: 'el-icon-house' }
+        ]
+      } else if (templateType === '免费表单类') {
+        this.pageList = [
+          { pageStep: 'home', name: '首页', icon: 'el-icon-house' },
+          { pageStep: 'receiveSuccess', name: '领取成功页', icon: 'el-icon-circle-check' }
+        ]
+      } else if (templateType === '一站式小程序表单类') {
+        this.pageList = [
+          { pageStep: 'home', name: '首页', icon: 'el-icon-house' },
+          { pageStep: 'purchaseSuccess', name: '小程序出码页', icon: 'el-icon-success' }
+        ]
+      } else {
+        // 默认显示首页
+        this.pageList = [
+          { pageStep: 'home', name: '首页', icon: 'el-icon-house' }
+        ]
+      }
+
+      // 防止switchPage将当前的空list保存到新加载的configList中
+      this.currentPageStep = ''
+      try {
+        const data = JSON.parse(json)
+        // 支持新格式
+        if (data.pageTitle !== undefined && Array.isArray(data.configList)) {
+          this.pageTitle = data.pageTitle
+          // 深拷贝接收到的configList
+          this.configList = JSON.parse(JSON.stringify(data.configList))
+          // 初始化configList,确保所有页面都存在
+          this.initializeConfigList()
+          // 加载首页数据
+          this.switchPage('home')
+        } else if (Array.isArray(data)) {
+          // 兼容旧格式(直接是数组)
+          this.configList = [{
+            pageStep: 'home',
+            moduleList: data
+          }, {
+            pageStep: 'appletLand',
+            moduleList: []
+          }, {
+            pageStep: 'purchaseSuccess',
+            moduleList: []
+          }]
+          this.switchPage('home')
+        } else {
+          // 其他格式
+          this.configList = [{
+            pageStep: 'home',
+            moduleList: Array.isArray(data) ? data : []
+          }, {
+            pageStep: 'appletLand',
+            moduleList: []
+          }, {
+            pageStep: 'purchaseSuccess',
+            moduleList: []
+          }]
+          this.switchPage('home')
+        }
+      } catch (e) {
+        // 初始化为新格式
+        this.pageTitle = ''
+        this.configList = this.pageList.map(page => ({
+          pageStep: page.pageStep,
+          moduleList: []
+        }))
+        this.switchPage('home')
+      }
+      // 重置选中状态和属性表单
+      this.form = {}
+      this.index = -1
+    },
+    initializeConfigList() {
+      // 根据当前的pageList初始化配置
+      this.pageList.forEach(page => {
+        if (!this.configList.find(config => config.pageStep === page.pageStep)) {
+          this.configList.push({
+            pageStep: page.pageStep,
+            moduleList: []
+          })
+        }
+      })
+    },
+    switchPage(pageStep) {
+      // 保存当前页面的数据
+      const currentConfig = this.configList.find(config => config.pageStep === this.currentPageStep)
+      if (currentConfig) {
+        currentConfig.moduleList = this.list
+      }
+      
+      // 切换到新页面
+      this.currentPageStep = pageStep
+      let newConfig = this.configList.find(config => config.pageStep === pageStep)
+      
+      // 如果找不到对应的配置,创建一个新的
+      if (!newConfig) {
+        newConfig = {
+          pageStep: pageStep,
+          moduleList: []
+        }
+        this.configList.push(newConfig)
+      }
+      
+      // 使用$set确保列表更新响应式追踪
+      this.$set(this, 'list', newConfig.moduleList || [])
+      
+      // 重置选中和表单
+      this.form = {}
+      this.index = -1
     },
     end() {
 
@@ -126,7 +363,6 @@ export default {
         name: item.label,
         fixe: false,
         classText: ['parent-div'],
-        addWxFun: false,
         workUrl: ''
       }
       if (item.type === 'h5-text') {
@@ -142,8 +378,6 @@ export default {
         }
       } else if (item.type === 'h5-image') {
         data.url = null
-      } else if (item.type === 'h5-button') {
-        data.content = '默认文本'
       } else if (item.type === 'h5-sep') {
         data.classText = [...data.classText, 'divider']
         data.style = {
@@ -156,101 +390,46 @@ export default {
         data.minutes = 0
         data.seconds = 0
         data.countdownMode = 1
-      } else if (item.type === 'h5-chat') {
-        data = {...data,
-          ...{
-            messages: [
-              {
-                id: 1,
-                sender: "agent",
-                text: "您好!欢迎报名【中老年健康养生大讲堂】,请仔细答一下问题,便于帮您分配专业的老师进行指导。",
-                welcome: true
-              },
-              {
-                id: 2,
-                sender: "agent",
-                text: "您的年龄?",
-                options: [
-                  { id: 1, text: "45-55岁" },
-                  { id: 2, text: "55-60岁" },
-                  { id: 3, text: "60-65岁" },
-                  { id: 4, text: "65岁以上" }
-                ],
-                userSelection: null
-              },
-              {
-                id: 3,
-                sender: "user",
-                text: '45-55岁',
-                userSelection: { id: 1, text: "45-55岁" }
-              },
-              {
-                id: 4,
-                sender: "agent",
-                text: "更想通过课程学习到哪些知识?",
-                options: [
-                  { id: 1, text: "食疗食补" },
-                  { id: 2, text: "经络疏通" },
-                  { id: 3, text: "脏腑调养" },
-                  { id: 4, text: "启蒙养生" },
-                  { id: 5, text: "以上所有" }
-                ],
-                userSelection: null
-              },
-              {
-                id: 5,
-                sender: "user",
-                text: '食疗食补',
-                userSelection: { id: 1, text: "食疗食补" }
-              }
-            ],
-            agentMsg:[{
-              id: 1,
-              sender: "agent",
-              text: "您好!欢迎报名【中老年健康养生大讲堂】,请仔细答一下问题,便于帮您分配专业的老师进行指导。",
-              welcome: true
-            },
-              {
-                id: 2,
-                sender: "agent",
-                text: "您的年龄?",
-                options: [
-                  { id: 1, text: "45-55岁" },
-                  { id: 2, text: "55-60岁" },
-                  { id: 3, text: "60-65岁" },
-                  { id: 4, text: "65岁以上" }
-                ],
-                userSelection: null
-              },
-              {
-                id: 4,
-                sender: "agent",
-                text: "更想通过课程学习到哪些知识?",
-                options: [
-                  { id: 1, text: "食疗食补" },
-                  { id: 2, text: "经络疏通" },
-                  { id: 3, text: "脏腑调养" },
-                  { id: 4, text: "启蒙养生" },
-                  { id: 5, text: "以上所有" }
-                ],
-                userSelection: null
-              }
-              ],
-            style: {
-              avatar: window.location.origin+AgentAvatar,
-              buttonColor: '#409EFF',
-              textColor: ''
-            }
-          }
-        }
-        console.log(data)
-        let h5chat = this.list.some(item => item.type && item.type.includes('h5-chat'))
-        if (h5chat) {
-          alert('全局只能允许有一个互动问答!')
-          return
-        }
+      } else if (item.type === 'h5-form') {
+        data.showImage = true
+        data.formImage = ''
+        data.showSubmitBtn = true
+        data.submitBtnStyle = 'text'
+        data.submitBtnColor = '#409EFF'
+        data.submitBtnText = '提交'
+        data.submitBtnImage = ''
+      } else if (item.type === 'h5-link-button') {
+        data.buttonStyle = 'text'
+        data.buttonText = '提交'
+        data.buttonColor = '#FF5A5A'
+        data.buttonTextColor = '#ffffff'
+        data.buttonImage = ''
+      } else if (item.type === 'h5-add-wechat-button') {
+        data.buttonStyle = 'text'
+        data.buttonText = '提交'
+        data.buttonColor = '#FF5A5A'
+        data.buttonTextColor = '#ffffff'
+        data.buttonImage = ''
+        data.enableGetCustomerAssistant = true
+        data.followScreen = true
+        data.enableDefaultWechat = false
+        data.enableMiniProgram = false
+      } else if (item.type === 'h5-qrcode') {
+        data.qrcodeImage = ''
+        data.qrcodeSize = 140
+        data.showCopyBtn = true
+        data.copyBtnText = '复制并添加老师'
+        data.copyBtnColor = '#FF9500'
+        data.copyBtnTextColor = '#ffffff'
+        data.showClickBtn = true
+        data.clickBtnText = '点击添加老师'
+        data.clickBtnColor = '#FF9500'
+        data.clickBtnTextColor = '#ffffff'
+        data.clickBtnWechatJump = false
+        data.enableGetCustomerAssistant = false
+        data.enableDefaultWechat = false
+        data.enableMiniProgram = false
       }
-      console.log(this.list)
 
       if (this.index !== null && this.index >= 0 && this.index < this.list.length) {
         this.list.splice(this.index + 1, 0, data)
@@ -260,35 +439,34 @@ export default {
     },
     select(index) {
       this.index = index
-      let className = 'active'
-      this.clearClass(className)
-      this.addClass(className)
-      this.$set(this.list[index], 'active', true)  // 确保响应式更新
-      // 直接绑定list中的对象(关键修改)
-      this.form = this.list[index]
-    },
-    clearClass(classText) {
-      this.list.forEach(item => {
-        // 推荐使用 $set
-        this.$set(item, 'classText', item.classText.filter(c => c !== classText))
+      // 更新所有元素的active状态
+      this.list.forEach((item, i) => {
+        this.$set(item, 'active', i === index)
       })
-    },
-    addClass(classText) {
-      this.list[this.index].classText.push(classText)
+      // 直接绑定list中的对象
+      this.form = this.list[index]
     },
 
     del() {
-      this.list.splice(this.index, 1)
+      if (this.index >= 0 && this.index < this.list.length) {
+        this.list.splice(this.index, 1)
+        this.form = {}
+        this.index = -1
+      }
+    },
+    /** 处理删除操作 */
+    handleDelete(index) {
+      this.index = index
+      this.list.splice(index, 1)
       this.form = {}
+      this.index = -1
     },
     updateForm(newForm) {
       // 更新表单数据
-      if (this.index === undefined || !this.list[this.index]) {
-        console.error('Invalid index or list item')
+      if (this.index < 0 || this.index >= this.list.length) {
         return
       }
 
-      console.log('newForm:', newForm) // 检查 newForm 数据
       Object.assign(this.form, newForm)
 
       // 更新列表中的数据
@@ -304,102 +482,746 @@ export default {
           }
         }
       }
+    },
+    /** 处理左侧组件拖拽开始 */
+    handleDragStart(event, comp) {
+      this.draggedComponent = comp
+      this.insertIndex = -1
+      event.dataTransfer.effectAllowed = 'copy'
+      event.dataTransfer.setData('text/html', event.target.innerHTML)
+    },
+    /** 处理拖拽结束 */
+    handleDragEnd() {
+      this.draggedComponent = null
+      this.insertIndex = -1
+    },
+    /** 处理控件池drag-enter */
+    handleItemDragEnter(event, index) {
+      // 仅根据是否来自左侧组件池
+      if (!this.draggedComponent) return
+      event.preventDefault()
+    },
+    /** 处理控件池drag-over */
+    handleItemDragOver(event, index) {
+      // 只有来自于左侧组件池的拖拽有效
+      if (!this.draggedComponent) return
+      event.preventDefault()
+      event.dataTransfer.dropEffect = 'copy'
+      
+      const wrapper = event.currentTarget
+      const rect = wrapper.getBoundingClientRect()
+      const midPoint = rect.top + rect.height / 2
+      
+      // 根据鼠标的纵位置判断是开始位置还是结数位置
+      wrapper.classList.remove('drag-before', 'drag-after')
+      if (event.clientY < midPoint) {
+        wrapper.classList.add('drag-before')
+        this.insertIndex = index
+      } else {
+        wrapper.classList.add('drag-after')
+        this.insertIndex = index + 1
+      }
+    },
+    /** 处理控件池drag-leave */
+    handleItemDragLeave(event, index) {
+      // 仅在来自左侧拖拽时事
+      if (!this.draggedComponent) return
+      const wrapper = event.currentTarget
+      if (event.target === wrapper) {
+        wrapper.classList.remove('drag-before', 'drag-after')
+      }
+    },
+    /** 根据鼠标位置计算插入索引 */
+    handleMouseMove(event) {
+      // 此方法已经不使用,位置计算变为了handleItemDragOver
+    },
+    /** 处理拖拽经过预览区域 */
+    handleDragOver(event) {
+      event.preventDefault()
+      event.dataTransfer.dropEffect = 'copy'
+      // 添加视觉反馈
+      this.$el.querySelector('.view-body').classList.add('drag-over')
 
-      console.log('Updated list:', this.list[this.index])
+      // 自动滚动逻辑
+      const container = this.$el.querySelector('.parent-container')
+      if (container) {
+        const rect = container.getBoundingClientRect()
+        const threshold = 60 // 边缘阈值
+        const scrollSpeed = 15 // 滚动速度
+
+        // 向下滚动
+        if (event.clientY > rect.bottom - threshold) {
+          container.scrollTop += scrollSpeed
+        }
+        // 向上滚动
+        else if (event.clientY < rect.top + threshold) {
+          container.scrollTop -= scrollSpeed
+        }
+      }
+    },
+    /** 处理拖拽离开预览区域 */
+    handleDragLeave(event) {
+      if (event.target === this.$el.querySelector('.view-body')) {
+        this.$el.querySelector('.view-body').classList.remove('drag-over')
+      }
+    },
+    /** 处理拖拽放下 */
+    handleDrop(event) {
+      event.preventDefault()
+      this.$el.querySelector('.view-body').classList.remove('drag-over')
+      
+      // 清除所有拖拽样式
+      document.querySelectorAll('.draggable-wrapper').forEach(el => {
+        el.classList.remove('drag-before', 'drag-after')
+      })
+      
+      // 处理新添加的组件(来自于左侧组件池)
+      if (this.draggedComponent) {
+        const newItem = this.createNewComponent(this.draggedComponent)
+        // 在插入位置插入新控件
+        if (this.insertIndex >= 0 && this.insertIndex <= this.list.length) {
+          this.list.splice(this.insertIndex, 0, newItem)
+        } else {
+          this.list.push(newItem)
+        }
+        this.draggedComponent = null
+        this.insertIndex = -1
+      }
+    },
+    /** 根据组件贫模对象创建新控件 */
+    createNewComponent(item) {
+      let data = {
+        type: item.type,
+        active: false,
+        name: item.label,
+        fixe: false,
+        classText: ['parent-div'],
+        workUrl: ''
+      }
+      if (item.type === 'h5-text') {
+        data.content = '默认文本'
+        data.style = {
+          textAlign: 'left',
+          fontSize: 20,
+          background: '#FFF',
+          color: '#000',
+          fontWeight: 'none',
+          fontStyle: 'none',
+          textDecoration: 'none'
+        }
+      } else if (item.type === 'h5-image') {
+        data.url = null
+      } else if (item.type === 'h5-sep') {
+        data.classText = [...data.classText, 'divider']
+        data.style = {
+          height: '10px',
+          background: 'rgb(228, 231, 237)'
+        }
+      } else if (item.type === 'h5-countdown') {
+        data.days = 0
+        data.hours = 0
+        data.minutes = 0
+        data.seconds = 0
+        data.countdownMode = 1
+      } else if (item.type === 'h5-form') {
+        data.showImage = true
+        data.formImage = ''
+        data.showSubmitBtn = true
+        data.submitBtnStyle = 'text'
+        data.submitBtnColor = '#409EFF'
+        data.submitBtnText = '提交'
+        data.submitBtnImage = ''
+      } else if (item.type === 'h5-link-button') {
+        data.buttonStyle = 'text'
+        data.buttonText = '提交'
+        data.buttonColor = '#FF5A5A'
+        data.buttonTextColor = '#ffffff'
+        data.buttonImage = ''
+      } else if (item.type === 'h5-add-wechat-button') {
+        data.buttonStyle = 'text'
+        data.buttonText = '提交'
+        data.buttonColor = '#FF5A5A'
+        data.buttonTextColor = '#ffffff'
+        data.buttonImage = ''
+        data.enableGetCustomerAssistant = true
+        data.followScreen = true
+        data.enableDefaultWechat = false
+        data.enableMiniProgram = false
+      } else if (item.type === 'h5-qrcode') {
+        data.qrcodeImage = ''
+        data.qrcodeSize = 140
+        data.showCopyBtn = true
+        data.copyBtnText = '复制并添加老师'
+        data.copyBtnColor = '#FF9500'
+        data.copyBtnTextColor = '#ffffff'
+        data.showClickBtn = true
+        data.clickBtnText = '点击添加老师'
+        data.clickBtnColor = '#FF9500'
+        data.clickBtnTextColor = '#ffffff'
+        data.clickBtnWechatJump = false
+        data.enableGetCustomerAssistant = false
+        data.enableDefaultWechat = false
+        data.enableMiniProgram = false
+      }
+      return data
+    },
+    /** 获取完整的页面数据(新格式JSON) */
+    getPageData() {
+      // 保存当前页面数据
+      const currentConfig = this.configList.find(config => config.pageStep === this.currentPageStep)
+      if (currentConfig) {
+        currentConfig.moduleList = this.list
+      }
+      
+      return {
+        pageTitle: this.pageTitle,
+        configList: this.configList
+      }
+    },
+    /** 获取JSON字符串 */
+    getJsonString() {
+      return JSON.stringify(this.getPageData())
     }
   }
 }
 </script>
 <style lang="scss" scoped>
-.button-body {
-  text-align: center;
-  margin: 0 auto;
+.h5-editor-container {
+  width: 100%;
+  height: 100%;
+  background: #f0f2f5;
+  display: flex;
+  flex-direction: column;
+  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
+}
+
+// 标题编辑栏
+.title-bar {
+  padding: 10px 20px;
+  background: #fff;
+  border-bottom: 1px solid #ebeef5;
+  flex-shrink: 0;
+  display: flex;
+  align-items: center;
+  box-shadow: 0 1px 4px rgba(0, 21, 41, 0.04);
+  z-index: 100;
 
-  .button-tag:first-child {
-    margin-top: 0 !important;
+  .title-input {
+    width: 300px;
+    
+    ::v-deep .el-input__inner {
+      border: 1px solid #dcdfe6;
+      border-radius: 4px;
+      &:focus {
+        border-color: #409eff;
+      }
+    }
   }
+}
 
-  .button-tag {
-    margin-left: 0;
-    margin-top: 10px;
+.editor-wrapper {
+  flex: 1;
+  padding: 0;
+  margin: 0 !important;
+  display: flex !important;
+  overflow: hidden;
+}
+
+// 面板公共样式
+.panel-header {
+  padding: 15px 20px;
+  border-bottom: 1px solid #f0f0f0;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  flex-shrink: 0;
+  background: #fff;
+
+  h3 {
+    margin: 0;
+    font-size: 15px;
+    font-weight: 600;
+    color: #1f2d3d;
+  }
+
+  .device-info {
+    font-size: 12px;
+    color: #909399;
+    background: #f4f4f5;
+    padding: 2px 8px;
+    border-radius: 10px;
   }
 }
 
-/* 外层容器 (父级div) */
-.parent-container {
-  background: #eff3f5;
-  padding: 40px 0;
-  width: 100%;
+// 左侧通用面板样式
+.page-panel,
+.left-panel {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  background: #fff;
+  border-right: 1px solid #ebeef5;
+  box-shadow: none;
+  z-index: 10;
+  border-radius: 0;
+}
+
+// 页面选择面板
+.page-panel {
+  .page-list {
+    flex: 1;
+    overflow-y: auto;
+    padding: 15px;
+    display: flex;
+    flex-direction: column;
+    gap: 12px;
+
+    &::-webkit-scrollbar {
+      width: 4px;
+    }
+    &::-webkit-scrollbar-thumb {
+      background: #e0e0e0;
+      border-radius: 2px;
+    }
+  }
+
+  .page-card {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    gap: 8px;
+    padding: 15px 10px;
+    border: 1px solid #ebeef5;
+    border-radius: 8px;
+    background: #fff;
+    cursor: pointer;
+    transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
+    position: relative;
+    overflow: hidden;
+
+    .page-icon {
+      font-size: 24px;
+      color: #606266;
+      transition: color 0.3s;
+    }
 
-  /* 关键设置 */
-  height: 80vh;
-  overflow-y: auto; /* 滚动条出在这里 */
-  border: 1px solid #DCDEE2;
+    .page-name {
+      font-size: 12px;
+      color: #606266;
+      font-weight: 500;
+      transition: color 0.3s;
+    }
+
+    &:hover {
+      border-color: #b3d8ff;
+      background: #ecf5ff;
+      transform: translateY(-2px);
+      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
+
+      .page-icon, .page-name {
+        color: #409eff;
+      }
+    }
+
+    &.active {
+      background: #409eff;
+      border-color: #409eff;
+      box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
+
+      .page-icon, .page-name {
+        color: #fff;
+      }
+    }
+  }
 }
-/* 定制滚动条样式 */
-.parent-container::-webkit-scrollbar {
-  width: 6px; /* 滚动条宽度 */
+
+// 左下组件面板
+.left-panel {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  background: #fff;
+  border-radius: 4px 0 0 4px;
+  overflow: hidden;
+  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+
+  .components-list {
+    flex: 1;
+    overflow-y: auto;
+    padding: 12px;
+
+    &::-webkit-scrollbar {
+      width: 6px;
+    }
+
+    &::-webkit-scrollbar-thumb {
+      background: #d0d0d0;
+      border-radius: 3px;
+
+      &:hover {
+        background: #aaa;
+      }
+    }
+  }
 }
 
-.parent-container::-webkit-scrollbar-track {
-  background: #f1f1f1; /* 滚动条轨道背景色 */
-  border-radius: 3px; /* 轨道圆角 */
+.component-group {
+  margin-bottom: 16px;
+
+  .group-title {
+    font-size: 12px;
+    font-weight: 600;
+    color: #606266;
+    padding: 8px 0;
+    margin-bottom: 12px;
+    display: flex;
+    align-items: center;
+    gap: 6px;
+    flex-shrink: 0;
+
+    i {
+      font-size: 14px;
+      color: #409eff;
+    }
+  }
+
+  .buttons-wrapper {
+    display: grid;
+    grid-template-columns: repeat(2, 1fr);
+    gap: 12px;
+  }
 }
 
-.parent-container::-webkit-scrollbar-thumb {
-  background: #888; /* 滚动条滑块颜色 */
-  border-radius: 3px; /* 滑块圆角 */
+.comp-button {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding: 15px 10px;
+  background: #fff;
+  border: 1px solid #ebeef5;
+  border-radius: 8px;
+  cursor: grab;
+  transition: all 0.2s ease;
+  min-height: 80px;
+
+  i {
+    font-size: 24px;
+    color: #606266;
+    margin-bottom: 8px;
+    transition: transform 0.2s;
+  }
+
+  span {
+    text-align: center;
+    word-break: break-word;
+    white-space: normal;
+  }
+
+  &:hover {
+    border-color: #409eff;
+    color: #409eff;
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
+    transform: translateY(-2px);
+
+    i {
+      color: #409eff;
+      transform: scale(1.1);
+    }
+    
+    span {
+      color: #409eff;
+    }
+  }
+
+  &:active {
+    background: #f0f9ff;
+    transform: scale(0.98);
+    cursor: grabbing;
+  }
 }
 
-.parent-container::-webkit-scrollbar-thumb:hover {
-  background: #555; /* 滑块悬停颜色 */
+// 中间预览面板
+.center-panel {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  background: #eaedf1;
+  border-left: 1px solid #ebeef5;
+  border-right: 1px solid #ebeef5;
+  box-shadow: inset 0 0 20px rgba(0,0,0,0.02);
+  
+  .panel-header {
+    background: #fff;
+    border-bottom: 1px solid #ebeef5;
+  }
 }
-.view-body {
-  width: 375px; /* 明确具体值(会覆盖下面的width:100%) */
-  height: auto; /* 改掉height:100%,否则会根据父级高度计算*/
 
-  /* 清除非必须设置 */
-  overflow-y: visible; /* 保持默认 */
+// 模拟手机外壳
+.mobile-nav-bar {
+  width: 375px;
+  background: #fff;
+  border-bottom: 1px solid #f2f2f2;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 0 12px;
+  flex-shrink: 0;
+  z-index: 20;
+  
+  border-top-left-radius: 30px; 
+  border-top-right-radius: 30px; 
+  padding-top: 10px; 
+  height: 54px; 
+  box-sizing: border-box;
+
+  box-shadow: 0 0 0 12px #333; 
+  margin: 0 auto;
+  
+  .nav-title {
+    font-size: 16px;
+    font-weight: 600;
+    color: #000;
+  }
+  
+  i {
+    font-size: 18px;
+    color: #333;
+  }
+    
+    .nav-left, .nav-right {
+      width: 40px;
+      display: flex;
+      align-items: center;
+      
+      i {
+        font-size: 18px;
+        color: #333;
+        cursor: pointer;
+      }
+    }
+
+    .nav-right {
+      justify-content: flex-end;
+    }
+}
 
-  /* 其他属性 */
-  margin: 0 auto; /* 代替外层flex的justify-content */
+.view-body {
+  width: 375px;
+  min-height: 667px;
   background: #fff;
+  padding: 0;
   position: relative;
+  
+  border-bottom-left-radius: 30px; 
+  border-bottom-right-radius: 30px; 
+  padding-bottom: 30px; 
+  
+  box-shadow: 0 0 0 12px #333, 0 20px 40px -10px rgba(0,0,0,0.4); 
+  margin: 0 auto;
+  
+  &.drag-over {
+    background-color: #f0f9ff;
+    
+    &::after {
+      content: '';
+      position: absolute;
+      top: 0;
+      left: 0;
+      right: 0;
+      bottom: 0;
+      border: 2px dashed #409eff;
+      border-radius: 0 0 30px 30px;
+      pointer-events: none;
+      z-index: 100;
+    }
+  }
+  
+  &:hover {
+    box-shadow: 0 0 0 12px #333, 0 25px 50px -12px rgba(0,0,0,0.5);
+  }
+}
+
+.parent-container {
+  flex: 1;
+  padding: 40px 0;
+  overflow-y: auto;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  
+  &::-webkit-scrollbar {
+    width: 0; 
+  }
+
+  &::-webkit-scrollbar-thumb {
+    background: #d0d0d0;
+    border-radius: 3px;
+
+    &:hover {
+      background: #aaa;
+    }
+  }
+}
+
+.draggable-area {
   padding: 0;
+  width: 100%;
+}
+
+.draggable-wrapper {
+  position: relative;
+  transition: border-color 0.2s ease, background-color 0.2s ease;
+  cursor: move;
+}
+
+.draggable-wrapper.drag-before {
+  border-top: 3px solid #409eff;
 }
 
-.icon.svg {
+.draggable-wrapper.drag-after {
+  border-bottom: 3px solid #409eff;
+}
+
+.view-item {
   cursor: pointer;
-  margin-left: 20px;
-  width: 20px;
-  height: 20px;
+  transition: background-color 0.2s ease;
+  border: none;
+  position: relative;
+  margin: 0;
+  padding: 0;
+  width: 100%;
+  box-sizing: border-box;
+  user-select: none;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+
+  &:hover {
+    background-color: #f0f2f5;
+  }
+
+  &.active {
+    outline: 2px solid #409eff;
+    outline-offset: -2px;
+    z-index: 2;
+    background-color: #ecf5ff;
+  }
 }
 
-.icon.svg.active {
-  color: #02ff9b;
+.ghost {
+  opacity: 0.5;
+  background: #f0f9ff;
 }
 
-.icon-span {
+.drag {
+  opacity: 0;
 }
 
-.divider {
+.insert-placeholder {
   width: 100%;
-  background: #DCDEE2;
-  height: 10px;
+  height: 3px;
+  background: linear-gradient(to right, #409eff 0%, #409eff 100%);
+  position: relative;
+  margin: 4px 0;
+  border-radius: 2px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+
+  span {
+    position: absolute;
+    background: #fff;
+    padding: 0 8px;
+    font-size: 10px;
+    color: #409eff;
+    font-weight: 600;
+  }
 }
 
-.button-tag {
-  /* 新增图标样式 */
-  .el-icon {
-    margin-right: 6px; /* 图标与文本间距 */
-    font-size: 16px; /* 统一图标大小 */
-    vertical-align: -2px; /* 垂直居中补偿 */
+.delete-button-wrapper {
+  position: absolute;
+  top: 8px;
+  right: 8px;
+  width: 28px;
+  height: 28px;
+  background: #ff4444;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+  z-index: 10;
+  transition: all 0.3s ease;
+  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
+
+  i {
+    color: #fff;
+    font-size: 16px;
+  }
+
+  &:hover {
+    background: #ff2222;
+    transform: scale(1.1);
+    box-shadow: 0 2px 8px rgba(255, 68, 68, 0.3);
+  }
+
+  &:active {
+    transform: scale(0.95);
   }
+}
 
-  &.is-plain .el-icon {
-    color: #666; /* 浅色主题下保持可视性 */
+.empty-state {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  height: 200px;
+  color: #909399;
+  font-size: 12px;
+
+  i {
+    font-size: 48px;
+    margin-bottom: 12px;
+    color: #d0d0d0;
   }
 }
 
+// 右侧属性面板
+.right-panel {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  background: #fff;
+  border-radius: 0 4px 4px 0;
+  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+}
+
+.properties-wrapper {
+  flex: 1;
+  overflow-y: auto;
+  padding: 12px;
+
+  &::-webkit-scrollbar {
+    width: 6px;
+  }
+
+  &::-webkit-scrollbar-thumb {
+    background: #d0d0d0;
+    border-radius: 3px;
+
+    &:hover {
+      background: #aaa;
+    }
+  }
+}
+
+// 分割线样式
+.divider {
+  width: 100%;
+  background: #DCDEE2;
+  height: 10px;
+}
 
-</style>
+</style>

+ 0 - 2
src/router/index.js

@@ -272,8 +272,6 @@ export const constantRoutes = [
     }
   },
 
-
-
 ]
 
 

+ 1 - 0
src/utils/errorCode.js

@@ -2,5 +2,6 @@ export default {
   '401': '认证失败,无法访问系统资源',
   '403': '当前操作没有权限',
   '404': '访问资源不存在',
+  '500': '服务器内部错误',
   'default': '系统未知错误,请反馈给管理员'
 }

+ 2 - 2
src/utils/request.js

@@ -59,8 +59,8 @@ service.interceptors.request.use(config => {
 service.interceptors.response.use(res => {
     // 未设置状态码则默认成功状态
     const code = res.data.code || 200;
-    // 获取错误信息
-    const msg = errorCode[code] || res.data.msg || errorCode['default']
+    // 获取错误信息 - 优先使用后端返回的 message 或 msg,兼容两种字段名
+    const msg = res.data.message || res.data.msg || errorCode[code] || errorCode['default']
     if (code === 401) {
       MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
           confirmButtonText: '重新登录',

+ 271 - 0
src/views/adv/advertiser/index.vue

@@ -0,0 +1,271 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch">
+      <el-form-item label="广告商名称" prop="advertiserName">
+        <el-input
+          v-model="queryParams.advertiserName"
+          placeholder="请输入广告商名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="类型" prop="custom">
+        <el-select v-model="queryParams.custom" placeholder="请选择类型" clearable size="small">
+          <el-option label="线上广告商" :value="1" />
+          <el-option label="自定义广告商" :value="2" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="状态" prop="enabled">
+        <el-select v-model="queryParams.enabled" placeholder="请选择状态" clearable size="small">
+          <el-option label="启用" :value="1" />
+          <el-option label="禁用" :value="0" />
+        </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-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          plain
+          type="primary"
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+        >新增</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table border v-loading="loading" :data="advertiserList">
+      <el-table-column label="广告商名称" align="left" prop="advertiserName" show-overflow-tooltip>
+        <template slot-scope="scope">
+          <el-switch
+            v-model="scope.row.enabled"
+            :active-value="1"
+            :inactive-value="0"
+            @change="handleStatusChange(scope.row)"
+            style="margin-right: 10px; vertical-align: middle;"
+          >
+          </el-switch>
+          <span>{{ scope.row.advertiserName }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="类型" align="center" prop="custom">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.custom === 1" type="success">线上广告商</el-tag>
+          <el-tag v-else type="danger">自定义广告商</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="是否支持API" align="center" prop="supportApi">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.supportApi === 1" type="success">是</el-tag>
+          <el-tag v-else type="info">否</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="是否支持回传" align="center" prop="supportCallback">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.supportCallback === 1" type="success">是</el-tag>
+          <el-tag v-else type="info">否</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="状态" align="center" prop="enabled">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.enabled === 1" type="success">启用</el-tag>
+          <el-tag v-else type="danger">禁用</el-tag>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.current"
+      :limit.sync="queryParams.size"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改广告商对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="100px">
+        <el-form-item label="广告商名称" prop="advertiserName">
+          <el-input v-model="form.advertiserName" placeholder="请输入广告商名称" />
+        </el-form-item>
+        <el-form-item label="类型" prop="custom">
+          <el-select v-model="form.custom" placeholder="请选择类型" style="width: 100%" disabled>
+            <el-option label="自定义广告商" :value="2" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="状态" prop="enabled">
+          <el-radio-group v-model="form.enabled">
+            <el-radio :label="1">启用</el-radio>
+            <el-radio :label="0">禁用</el-radio>
+          </el-radio-group>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { pageAdvertiser, getAdvertiser, addAdvertiser, updateAdvertiser, delAdvertiser, batchDelAdvertiser, enableAdvertiser } from "@/api/adv/advertiser";
+
+export default {
+  name: "Advertiser",
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 广告商表格数据
+      advertiserList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        current: 1,
+        size: 10,
+        advertiserName: undefined,
+        custom: undefined,
+        enabled: undefined
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        advertiserName: [
+          { required: true, message: "广告商名称不能为空", trigger: "blur" }
+        ],
+        custom: [
+          { required: true, message: "类型不能为空", trigger: "change" }
+        ],
+        enabled: [
+          { required: true, message: "状态不能为空", trigger: "change" }
+        ]
+      }
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询广告商列表 */
+    getList() {
+      this.loading = true;
+      pageAdvertiser(this.queryParams).then(response => {
+        this.advertiserList = response.data.records;
+        this.total = response.data.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: undefined,
+        advertiserName: undefined,
+        custom: 2,
+        enabled: 1
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.current = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id);
+      this.single = selection.length !== 1;
+      this.multiple = !selection.length;
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加广告商";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id || this.ids[0];
+      getAdvertiser(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改广告商";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != undefined) {
+            updateAdvertiser(this.form.id, this.form).then(response => {
+              if (response.code === 200) {
+                this.msgSuccess("修改成功");
+                this.open = false;
+                this.getList();
+              }
+            });
+          } else {
+            addAdvertiser(this.form).then(response => {
+              if (response.code === 200) {
+                this.msgSuccess("新增成功");
+                this.open = false;
+                this.getList();
+              }
+            });
+          }
+        }
+      });
+    },
+    /** 启用/停用状态切换 */
+    handleStatusChange(row) {
+      const statusText = row.enabled === 1 ? '启用' : '停用';
+      this.$confirm(`确认要${statusText}广告商“${row.advertiserName}”吗?`, "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        return enableAdvertiser(row.id);
+      }).then(() => {
+        this.msgSuccess(`${statusText}成功`);
+        this.getList();
+      }).catch(() => {
+        // 取消操作,恢复状态
+        row.enabled = row.enabled === 1 ? 0 : 1;
+      });
+    }
+  }
+};
+</script>
+

+ 618 - 0
src/views/adv/callbackAccount/index.vue

@@ -0,0 +1,618 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch">
+      <el-form-item label="账号名称" prop="accountName">
+        <el-input
+          v-model="queryParams.accountName"
+          placeholder="请输入账号名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="广告商" prop="advertiserId">
+        <el-select v-model="queryParams.advertiserId" placeholder="请选择广告商" clearable size="small" filterable>
+          <el-option
+            v-for="item in advertiserOptions"
+            :key="item.id"
+            :label="item.advertiserName"
+            :value="item.id"
+          />
+        </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-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          plain
+          type="primary"
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+        >新增</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          plain
+          type="danger"
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+        >删除</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table border v-loading="loading" :data="accountList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="ID" align="center" prop="id" width="80" />
+      <el-table-column label="回传账号" align="center" prop="accountName" min-width="150" show-overflow-tooltip />
+      <el-table-column label="广告商" align="center" prop="advertiserName" min-width="150" show-overflow-tooltip />
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="300">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-setting"
+            @click="handleConversionType(scope.row)"
+          >添加转换类型</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-s-data"
+            @click="handleConversionLog(scope.row)"
+          >回传结果</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+          >删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.current"
+      :limit.sync="queryParams.size"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改回传账号对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="120px">
+        <el-form-item label="广告商" prop="advertiserId">
+          <el-select 
+            v-model="form.advertiserId" 
+            placeholder="请选择广告商" 
+            style="width: 100%"
+            filterable
+            @change="handleAdvertiserChange"
+          >
+            <el-option
+              v-for="item in advertiserOptions"
+              :key="item.id"
+              :label="item.advertiserName"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+        
+        <el-form-item label="账号名称" prop="accountName">
+          <el-input v-model="form.accountName" placeholder="请输入账号名称" />
+        </el-form-item>
+        
+        <!-- advertiserId = 1 的字段 -->
+        <template v-if="form.advertiserId === 10001">
+          <el-form-item label="ocpcUid" prop="adAccountId">
+            <el-input v-model="form.adAccountId" placeholder="请输入ocpcUid" />
+          </el-form-item>
+          <el-form-item label="ocpcToken" prop="accessToken">
+            <el-input v-model="form.accessToken" placeholder="请输入ocpcToken" type="password" show-password />
+          </el-form-item>
+        </template>
+        
+        <!-- advertiserId = 3 的字段 -->
+        <template v-if="form.advertiserId === 10003">
+          <el-form-item label="广告账户id" prop="adAccountId">
+            <el-input v-model="form.adAccountId" placeholder="请输入广告账户id" />
+          </el-form-item>
+          <el-form-item label="数据源id" prop="scrId">
+            <el-input v-model="form.scrId" placeholder="请输入数据源id" />
+          </el-form-item>
+          <el-form-item label="数据源token" prop="accessToken">
+            <el-input v-model="form.accessToken" placeholder="请输入数据源token" type="password" show-password />
+          </el-form-item>
+        </template>
+        
+        <!-- advertiserId = 4 的字段 -->
+        <template v-if="form.advertiserId === 10004">
+          <el-form-item label="ownerId" prop="adAccountId">
+            <el-input v-model="form.adAccountId" placeholder="请输入ownerId" />
+          </el-form-item>
+          <el-form-item label="apiId" prop="appId">
+            <el-input v-model="form.appId" placeholder="请输入apiId" />
+          </el-form-item>
+          <el-form-item label="apiKey" prop="appSecret">
+            <el-input v-model="form.appSecret" placeholder="请输入apiKey" type="password" show-password />
+          </el-form-item>
+        </template>
+        
+        <!-- advertiserId = 5 的字段 -->
+        <template v-if="form.advertiserId === 10005">
+          <el-form-item label="账户id" prop="adAccountId">
+            <el-input v-model="form.adAccountId" placeholder="请输入账户id" />
+          </el-form-item>
+          <el-form-item label="应用id" prop="appId">
+            <el-input v-model="form.appId" placeholder="请输入应用id" />
+          </el-form-item>
+          <el-form-item label="应用秘钥" prop="appSecret">
+            <el-input v-model="form.appSecret" placeholder="请输入应用秘钥" type="password" show-password />
+          </el-form-item>
+          <el-form-item label="scr_id" prop="scrId">
+            <el-input v-model="form.scrId" placeholder="请输入scr_id" />
+          </el-form-item>
+        </template>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 转换类型配置对话框 -->
+    <el-dialog title="添加转换类型" :visible.sync="conversionOpen" width="700px" append-to-body>
+      <div style="margin-bottom: 15px;">
+        <el-button type="primary" size="small" icon="el-icon-plus" @click="addConversionEvent">添加事件</el-button>
+      </div>
+      
+      <div v-if="conversionEvents.length === 0" style="text-align: center; padding: 40px 0; color: #909399;">
+        暂无转换事件,请点击“添加事件”按钮添加
+      </div>
+      
+      <div v-for="(event, index) in conversionEvents" :key="index" class="conversion-event-item">
+        <div class="event-header">
+          <span class="event-title">事件{{ index + 1 }}</span>
+          <el-button 
+            type="danger" 
+            size="mini" 
+            icon="el-icon-delete" 
+            circle
+            @click="removeConversionEvent(index)"
+          ></el-button>
+        </div>
+        
+        <el-form label-width="140px" style="margin-top: 10px;">
+          <el-form-item label="广告商转化类型">
+            <el-select 
+              v-model="event.advertiserEventType" 
+              placeholder="请选择广告商转化类型" 
+              style="width: 100%"
+              @change="handleAdvertiserEventChange(index, $event)"
+            >
+              <el-option
+                v-for="item in advertiserEventOptions"
+                :key="item.eventType"
+                :label="item.eventName"
+                :value="item.eventType"
+              />
+            </el-select>
+          </el-form-item>
+          
+          <el-form-item label="回传数据类型">
+            <el-select 
+              v-model="event.systemEventType" 
+              placeholder="请选择回传数据类型" 
+              style="width: 100%"
+              @change="handleSystemEventChange(index, $event)"
+            >
+              <el-option
+                v-for="item in systemEventOptions"
+                :key="item.eventType"
+                :label="item.eventName"
+                :value="item.eventType"
+              />
+            </el-select>
+          </el-form-item>
+        </el-form>
+      </div>
+      
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="saveConversionEvents">保 存</el-button>
+        <el-button @click="conversionOpen = false">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { pageCallbackAccount, getCallbackAccount, addCallbackAccount, updateCallbackAccount, delCallbackAccount, batchDelCallbackAccount, queryEventType, saveEventType } from "@/api/adv/callbackAccount";
+import { pageAdvertiser } from "@/api/adv/advertiser";
+
+export default {
+  name: "CallbackAccount",
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 回传账号表格数据
+      accountList: [],
+      // 广告商选项
+      advertiserOptions: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 是否显示转换类型弹窗
+      conversionOpen: false,
+      // 当前操作的回传账号
+      currentAccount: null,
+      // 转换事件列表
+      conversionEvents: [],
+      // systemBuiltIn=0)
+      advertiserEventOptions: [],
+      // systemBuiltIn=1)
+      systemEventOptions: [],
+      // 查询参数
+      queryParams: {
+        current: 1,
+        size: 10,
+        accountName: undefined,
+        advertiserId: undefined
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        advertiserId: [
+          { required: true, message: "请选择广告商", trigger: "change" }
+        ],
+        accountName: [
+          { required: true, message: "账号名称不能为空", trigger: "blur" }
+        ],
+        adAccountId: [
+          { 
+            validator: (rule, value, callback) => {
+              if ([1, 3, 4, 5].includes(this.form.advertiserId) && !value) {
+                callback(new Error('此字段不能为空'));
+              } else {
+                callback();
+              }
+            }, 
+            trigger: "blur" 
+          }
+        ],
+        accessToken: [
+          { 
+            validator: (rule, value, callback) => {
+              if ([1, 3].includes(this.form.advertiserId) && !value) {
+                callback(new Error('此字段不能为空'));
+              } else {
+                callback();
+              }
+            }, 
+            trigger: "blur" 
+          }
+        ],
+        scrId: [
+          { 
+            validator: (rule, value, callback) => {
+              if ([3, 5].includes(this.form.advertiserId) && !value) {
+                callback(new Error('此字段不能为空'));
+              } else {
+                callback();
+              }
+            }, 
+            trigger: "blur" 
+          }
+        ],
+        appId: [
+          { 
+            validator: (rule, value, callback) => {
+              if ([4, 5].includes(this.form.advertiserId) && !value) {
+                callback(new Error('此字段不能为空'));
+              } else {
+                callback();
+              }
+            }, 
+            trigger: "blur" 
+          }
+        ],
+        appSecret: [
+          { 
+            validator: (rule, value, callback) => {
+              if ([4, 5].includes(this.form.advertiserId) && !value) {
+                callback(new Error('此字段不能为空'));
+              } else {
+                callback();
+              }
+            }, 
+            trigger: "blur" 
+          }
+        ]
+      }
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询广告商选项 */
+    getAdvertiserOptions() {
+      pageAdvertiser({ current: 1, size: 1000 }).then(response => {
+        this.advertiserOptions = response.data.records;
+      });
+    },
+    /** 查询回传账号列表 */
+    getList() {
+      this.loading = true;
+      pageCallbackAccount(this.queryParams).then(response => {
+        this.accountList = response.data.records;
+        this.total = response.data.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: undefined,
+        advertiserId: undefined,
+        advertiserName: undefined,
+        accountName: undefined,
+        adAccountId: undefined,
+        accessToken: undefined,
+        scrId: undefined,
+        appId: undefined,
+        appSecret: undefined
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.current = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id);
+      this.single = selection.length !== 1;
+      this.multiple = !selection.length;
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.getAdvertiserOptions();
+      this.open = true;
+      this.title = "添加回传账号";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      this.getAdvertiserOptions();
+      const id = row.id || this.ids[0];
+      getCallbackAccount(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改回传账号";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != undefined) {
+            updateCallbackAccount(this.form.id, this.form).then(response => {
+              if (response.code === 200) {
+                this.msgSuccess("修改成功");
+                this.open = false;
+                this.getList();
+              }
+            });
+          } else {
+            addCallbackAccount(this.form).then(response => {
+              if (response.code === 200) {
+                this.msgSuccess("新增成功");
+                this.open = false;
+                this.getList();
+              }
+            });
+          }
+        }
+      });
+    },
+    /** 广告商变化时 */
+    handleAdvertiserChange(advertiserId) {
+      this.form.advertiserName = "";
+      if (advertiserId) {
+        const advertiser = this.advertiserOptions.find(item => item.id === advertiserId);
+        if (advertiser) {
+          this.form.advertiserName = advertiser.advertiserName;
+        }
+      }
+      // 清空其他字段
+      this.form.adAccountId = undefined;
+      this.form.accessToken = undefined;
+      this.form.scrId = undefined;
+      this.form.appId = undefined;
+      this.form.appSecret = undefined;
+    },
+    /** 添加转换类型按钮操作 */
+    handleConversionType(row) {
+      this.currentAccount = row;
+      this.conversionOpen = true;
+      
+      // 加载事件类型选项
+      this.loadEventTypes(row.advertiserId);
+      
+      // 解析已有的转换事件
+      this.parseConversionEvents(row.conversionEvent);
+    },
+    /** 回传结果按钮操作 */
+    handleConversionLog(row) {
+      this.$router.push({
+        path: '/adv/new-adv/conversionLog',
+        query: { callbackAccountId: row.id }
+      });
+    },
+    /** 加载事件类型选项 */
+    loadEventTypes(advertiserId) {
+      queryEventType(advertiserId).then(response => {
+        const eventTypes = response.data || [];
+        // systemBuiltin='0' 为广告商事件
+        this.advertiserEventOptions = eventTypes.filter(item => item.systemBuiltin === '0');
+        // systemBuiltin='1' 为系统事件
+        this.systemEventOptions = eventTypes.filter(item => item.systemBuiltin === '1');
+      }).catch(error => {
+        console.error('加载事件类型失败:', error);
+        this.msgError('加载事件类型失败');
+      });
+    },
+    /** 解析转换事件 */
+    parseConversionEvents(conversionEvent) {
+      try {
+        if (conversionEvent && typeof conversionEvent === 'string') {
+          this.conversionEvents = JSON.parse(conversionEvent);
+        } else if (Array.isArray(conversionEvent)) {
+          this.conversionEvents = conversionEvent;
+        } else {
+          this.conversionEvents = [];
+        }
+      } catch (error) {
+        console.error('解析转换事件失败:', error);
+        this.conversionEvents = [];
+      }
+    },
+    /** 添加转换事件 */
+    addConversionEvent() {
+      this.conversionEvents.push({
+        systemEventType: '',
+        systemEventTypeName: '',
+        advertiserEventType: '',
+        advertiserEventName: ''
+      });
+    },
+    /** 删除转换事件 */
+    removeConversionEvent(index) {
+      this.conversionEvents.splice(index, 1);
+    },
+    /** 广告商事件变化 */
+    handleAdvertiserEventChange(index, eventType) {
+      const event = this.advertiserEventOptions.find(item => item.eventType === eventType);
+      if (event) {
+        this.conversionEvents[index].advertiserEventName = event.eventName;
+      }
+    },
+    /** 系统事件变化 */
+    handleSystemEventChange(index, eventType) {
+      const event = this.systemEventOptions.find(item => item.eventType === eventType);
+      if (event) {
+        this.conversionEvents[index].systemEventTypeName = event.eventName;
+      }
+    },
+    /** 保存转换事件 */
+    saveConversionEvents() {
+      // 校验数据
+      for (let i = 0; i < this.conversionEvents.length; i++) {
+        const event = this.conversionEvents[i];
+        if (!event.systemEventType || !event.advertiserEventType) {
+          this.msgError(`请完善事件${i + 1}的配置`);
+          return;
+        }
+      }
+      
+      // 调用保存接口
+      saveEventType(this.currentAccount.id, this.conversionEvents).then(response => {
+        if (response.code === 200) {
+          this.msgSuccess('保存成功');
+          this.conversionOpen = false;
+          this.getList();
+        }
+      }).catch(error => {
+        console.error('保存失败:', error);
+        this.msgError('保存失败');
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id ? [row.id] : this.ids;
+      this.$confirm('是否确认删除选中的回传账号?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        // 单条删除使用 delCallbackAccount,批量删除使用 batchDelCallbackAccount
+        if (row.id) {
+          return delCallbackAccount(row.id);
+        } else {
+          return batchDelCallbackAccount(ids);
+        }
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("删除成功");
+      }).catch(function() {});
+    }
+  }
+};
+</script>
+
+<style scoped>
+.conversion-event-item {
+  margin-bottom: 20px;
+  padding: 15px;
+  border: 1px solid #e8e8e8;
+  border-radius: 4px;
+  background-color: #fafafa;
+}
+
+.event-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 10px;
+}
+
+.event-title {
+  font-weight: 600;
+  font-size: 15px;
+  color: #303133;
+}
+</style>

+ 622 - 0
src/views/adv/channel/index.vue

@@ -0,0 +1,622 @@
+<template>
+  <div class="app-container channel-container">
+    <el-row :gutter="20" class="channel-wrapper">
+      <!-- 分组区域 -->
+      <el-col :xs="24" :sm="8" :md="6" class="group-section">
+        <div class="section-header">
+          <h3>分组管理</h3>
+          <el-button type="primary" size="mini" icon="el-icon-plus" @click="handleAddGroup">新增分组</el-button>
+        </div>
+        <div class="search-box">
+          <el-input 
+            v-model="queryParams.groupChannelName" 
+            placeholder="搜索分组"
+            clearable
+            size="small"
+            @input="getGroupList"
+          />
+        </div>
+        <div class="group-list">
+          <div 
+            :class="['group-item', 'all-group', { active: selectedGroupId === 'all' }]"
+            @click="selectAllGroup"
+          >
+            <span class="group-name">全部</span>
+          </div>
+          <div 
+            v-for="item in groupList" 
+            :key="item.id"
+            :class="['group-item', { active: selectedGroupId === item.id }]"
+            @click="selectGroup(item)"
+          >
+            <span class="group-name">{{ item.channelName }}</span>
+          </div>
+          <div v-if="groupList.length === 0" class="empty-tip">暂无分组</div>
+        </div>
+      </el-col>
+
+      <!-- 渠道区域 -->
+      <el-col :xs="24" :sm="16" :md="18" class="channel-section">
+        <div class="section-header">
+          <h3>{{ selectedGroupId ? '渠道管理' : '请选择分组' }}</h3>
+          <el-button 
+            v-if="selectedGroupId" 
+            type="primary" 
+            size="mini" 
+            icon="el-icon-plus" 
+            @click="handleAddChannel"
+          >新增渠道</el-button>
+        </div>
+        
+        <div v-if="selectedGroupId" class="search-box">
+          <el-input 
+            v-model="queryParams.channelChannelName" 
+            placeholder="搜索渠道"
+            clearable
+            size="small"
+            @input="getChannelList"
+          />
+        </div>
+        
+        <div v-if="!selectedGroupId" class="empty-state">
+          <p>请在左侧选择分组后查看渠道列表</p>
+        </div>
+
+        <el-table 
+          v-else
+          border 
+          v-loading="channelLoading" 
+          :data="channelList"
+          class="channel-table"
+        >
+          <el-table-column label="渠道名称" align="center" prop="channelName" min-width="150" show-overflow-tooltip />
+          <el-table-column label="更新时间" align="center" prop="updateTime" width="180">
+            <template slot-scope="scope">
+              <span>{{ parseTime(scope.row.updateTime) }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="160" fixed="right">
+            <template slot-scope="scope">
+              <el-button
+                size="mini"
+                type="text"
+                icon="el-icon-edit"
+                @click="handleEditChannel(scope.row)"
+              >编辑</el-button>
+              <el-button
+                size="mini"
+                type="text"
+                icon="el-icon-document-copy"
+                @click="handleCopyChannel(scope.row)"
+              >复制</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+        
+        <!-- 分页 -->
+        <el-pagination
+          v-if="channelList.length > 0"
+          :current-page="channelPageNum"
+          :page-size="channelPageSize"
+          :page-sizes="[10, 20, 50, 100]"
+          :total="channelTotal"
+          layout="total, sizes, prev, pager, next, jumper"
+          @size-change="handleChannelPageSizeChange"
+          @current-change="handleChannelPageChange"
+          style="text-align: right; margin-top: 10px;"
+        />
+      </el-col>
+    </el-row>
+
+    <!-- 分组对话框 -->
+    <el-dialog :title="groupDialogTitle" :visible.sync="groupDialogOpen" width="500px" append-to-body>
+      <el-form ref="groupForm" :model="groupForm" :rules="groupRules" label-width="100px">
+        <el-form-item label="分组名称" prop="channelName">
+          <el-input 
+            v-model="groupForm.channelName" 
+            placeholder="请输入分组名称"
+            clearable
+          />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="groupDialogOpen = false">取 消</el-button>
+        <el-button type="primary" @click="submitGroup">确 定</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 渠道对话框 -->
+    <el-dialog :title="channelDialogTitle" :visible.sync="channelDialogOpen" width="500px" append-to-body>
+      <el-form ref="channelForm" :model="channelForm" :rules="channelRules" label-width="100px">
+        <el-form-item label="所属分组" prop="parentId">
+          <el-select 
+            v-model="channelForm.parentId" 
+            placeholder="请选择分组"
+            clearable
+          >
+            <el-option
+              v-for="item in groupList"
+              :key="item.id"
+              :label="item.channelName"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="渠道名称" prop="channelName">
+          <el-input 
+            v-model="channelForm.channelName" 
+            placeholder="请输入渠道名称"
+            clearable
+          />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="channelDialogOpen = false">取 消</el-button>
+        <el-button type="primary" @click="submitChannel">确 定</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 复制渠道对话框 -->
+    <el-dialog title="复制渠道" :visible.sync="batchCopyDialogOpen" width="500px" append-to-body>
+      <el-form ref="batchCopyForm" :model="batchCopyForm" :rules="batchCopyRules" label-width="100px">
+        <el-form-item label="渠道名称" prop="channelName">
+          <el-input 
+            v-model="batchCopyForm.channelName" 
+            placeholder="请输入渠道名称"
+            clearable
+          />
+        </el-form-item>
+        <el-form-item label="复制数量" prop="num">
+          <el-input-number 
+            v-model="batchCopyForm.num" 
+            :min="1" 
+            :max="1000"
+            placeholder="请输入复制数量"
+          />
+        </el-form-item>
+        <el-form-item label="起始编号" prop="start">
+          <el-input 
+            v-model="batchCopyForm.start" 
+            placeholder="请输入起始编号(仅整数)"
+            clearable
+          />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="batchCopyDialogOpen = false">取 消</el-button>
+        <el-button type="primary" @click="submitBatchCopy">确 定</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { pageProject, addOrUpdateChannel, saveBatchChannel } from "@/api/adv/channel";
+
+export default {
+  name: "Channel",
+  data() {
+    return {
+      // 分组列表
+      groupList: [],
+      // 选中的分组ID
+      selectedGroupId: null,
+      // 渠道列表
+      channelList: [],
+      channelLoading: false,
+      // 渠道分页参数
+      channelPageNum: 1,
+      channelPageSize: 10,
+      channelTotal: 0,
+      // 查询参数
+      queryParams: {
+        groupChannelName: "",
+        channelChannelName: ""
+      },
+      // ... existing code ...
+      
+      // 分组对话框
+      groupDialogOpen: false,
+      groupDialogTitle: "新增分组",
+      groupForm: {
+        id: undefined,
+        channelName: ""
+      },
+      groupRules: {
+        channelName: [
+          { required: true, message: "分组名称不能为空", trigger: "blur" }
+        ]
+      },
+
+      // 渠道对话框
+      channelDialogOpen: false,
+      channelDialogTitle: "新增渠道",
+      channelForm: {
+        id: undefined,
+        channelName: "",
+        parentId: null
+      },
+      channelRules: {
+        parentId: [
+          { required: true, message: "请选择分组", trigger: "change" }
+        ],
+        channelName: [
+          { required: true, message: "渠道名称不能为空", trigger: "blur" }
+        ]
+      },
+
+      // 批量复制对话框
+      batchCopyDialogOpen: false,
+      batchCopyForm: {
+        channelName: "",
+        num: 1,
+        start: "",
+        parentId: null
+      },
+      batchCopyRules: {
+        channelName: [
+          { required: true, message: "渠道名称不能为空", trigger: "blur" }
+        ],
+        num: [
+          { required: true, message: "复制数量不能为空", trigger: "blur" },
+          { type: "number", message: "复制数量必须是整数", trigger: "blur" }
+        ],
+        start: [
+          { required: true, message: "起始编号不能为空", trigger: "blur" },
+          { pattern: /^\d+$/, message: "起始编号只能是整数", trigger: "blur" }
+        ]
+      }
+    };
+  },
+  created() {
+    this.getGroupList();
+    // 页面加载完成后默认选中“全部”
+    this.$nextTick(() => {
+      this.selectAllGroup();
+    });
+  },
+  methods: {
+    /** 获取分组列表 */
+    getGroupList() {
+      const params = { pageNum: 1, pageSize: 1000, parentId: 0 };
+      // 只有输入了名称才添加到参数中
+      if (this.queryParams.groupChannelName) {
+        params.channelName = this.queryParams.groupChannelName;
+      }
+      pageProject(params).then(response => {
+        this.groupList = response.data.records || [];
+      }).catch(error => {
+        console.error('加载分组列表失败:', error);
+        this.groupList = [];
+      });
+    },
+
+    /** 选择全部分组 */
+    selectAllGroup() {
+      this.selectedGroupId = 'all';
+      this.channelPageNum = 1;
+      this.getChannelList();
+    },
+
+    /** 选择分组 */
+    selectGroup(group) {
+      this.selectedGroupId = group.id;
+      this.channelPageNum = 1;
+      this.getChannelList();
+    },
+
+    /** 获取渠道列表 */
+    getChannelList() {
+      if (!this.selectedGroupId) return;
+      this.channelLoading = true;
+      const params = { pageNum: this.channelPageNum, pageSize: this.channelPageSize };
+      // 当不是选择"全部"时,添加 parentId 参数
+      if (this.selectedGroupId !== 'all') {
+        params.parentId = this.selectedGroupId;
+      }
+      // 只有输入了名称才添加到参数中
+      if (this.queryParams.channelChannelName) {
+        params.channelName = this.queryParams.channelChannelName;
+      }
+      pageProject(params).then(response => {
+        this.channelList = response.data.records || [];
+        this.channelTotal = response.data.total || 0;
+        this.channelLoading = false;
+      }).catch(error => {
+        console.error('加载渠道列表失败:', error);
+        this.channelList = [];
+        this.channelLoading = false;
+      });
+    },
+
+    /** 渠道分页变更 */
+    handleChannelPageChange(pageNum) {
+      this.channelPageNum = pageNum;
+      this.getChannelList();
+    },
+
+    /** 渠道与每页条数变更 */
+    handleChannelPageSizeChange(pageSize) {
+      this.channelPageSize = pageSize;
+      this.channelPageNum = 1;
+      this.getChannelList();
+    },
+
+    /** 新增分组 */
+    handleAddGroup() {
+      this.groupForm = { id: undefined, channelName: "" };
+      this.groupDialogTitle = "新增分组";
+      this.groupDialogOpen = true;
+    },
+
+    /** 提交分组 */
+    submitGroup() {
+      this.$refs["groupForm"].validate(valid => {
+        if (valid) {
+          const data = {
+            channelName: this.groupForm.channelName,
+            parentId: 0
+          };
+          addOrUpdateChannel(data).then(response => {
+            if (response.code === 200) {
+              this.msgSuccess("新增成功");
+              this.groupDialogOpen = false;
+              this.getGroupList();
+            }
+          }).catch(error => {
+            console.error('保存分组失败:', error);
+            this.msgError("保存失败");
+          });
+        }
+      });
+    },
+
+    /** 新增渠道 */
+    handleAddChannel() {
+      this.channelForm = { id: undefined, channelName: "", parentId: this.selectedGroupId };
+      this.channelDialogTitle = "新增渠道";
+      this.channelDialogOpen = true;
+    },
+
+    /** 编辑渠道 */
+    handleEditChannel(channel) {
+      this.channelForm = {
+        id: channel.id,
+        channelName: channel.channelName,
+        parentId: channel.parentId
+      };
+      this.channelDialogTitle = "编辑渠道";
+      this.channelDialogOpen = true;
+    },
+
+    /** 提交渠道 */
+    submitChannel() {
+      this.$refs["channelForm"].validate(valid => {
+        if (valid) {
+          const data = {
+            channelName: this.channelForm.channelName,
+            parentId: this.channelForm.parentId
+          };
+          if (this.channelForm.id) {
+            data.id = this.channelForm.id;
+          }
+          addOrUpdateChannel(data).then(response => {
+            if (response.code === 200) {
+              this.msgSuccess(this.channelForm.id ? "编辑成功" : "新增成功");
+              this.channelDialogOpen = false;
+              this.getChannelList();
+            }
+          }).catch(error => {
+            console.error('保存渠道失败:', error);
+            this.msgError("保存失败");
+          });
+        }
+      });
+    },
+
+    /** 复制渠道 */
+    handleCopyChannel(channel) {
+      this.batchCopyForm = { 
+        channelName: channel.channelName, 
+        num: 1, 
+        start: "",
+        parentId: channel.parentId
+      };
+      this.batchCopyDialogOpen = true;
+      this.$nextTick(() => {
+        this.$refs["batchCopyForm"].clearValidate();
+      });
+    },
+
+    /** 提交批量复制 */
+    submitBatchCopy() {
+      this.$refs["batchCopyForm"].validate(valid => {
+        if (valid) {
+          // 确保 start 是整数类型
+          const startNum = parseInt(this.batchCopyForm.start);
+          const data = {
+            channelName: this.batchCopyForm.channelName,
+            num: this.batchCopyForm.num,
+            start: startNum,
+            parentId: this.batchCopyForm.parentId
+          };
+          saveBatchChannel(data).then(response => {
+            if (response.code === 200) {
+              this.msgSuccess("复制成功");
+              this.batchCopyDialogOpen = false;
+              this.getChannelList();
+            }
+          }).catch(error => {
+            console.error('批量复制失败:', error);
+            this.msgError("批量复制失败");
+          });
+        }
+      });
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.channel-container {
+  padding: 20px;
+  background-color: #f5f7fa;
+  min-height: calc(100vh - 100px);
+}
+
+.channel-wrapper {
+  height: 100%;
+}
+
+.group-section,
+.channel-section {
+  background-color: #fff;
+  border-radius: 8px;
+  padding: 20px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+}
+
+.section-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 20px;
+  padding-bottom: 15px;
+  border-bottom: 2px solid #f0f0f0;
+
+  h3 {
+    margin: 0;
+    font-size: 16px;
+    font-weight: 600;
+    color: #303133;
+  }
+}
+
+.group-list {
+  max-height: 600px;
+  overflow-y: auto;
+}
+
+.search-box {
+  margin-bottom: 15px;
+  
+  ::v-deep .el-input__inner {
+    border-radius: 6px;
+    border: 1px solid #dcdfe6;
+    transition: all 0.3s ease;
+    
+    &:focus {
+      border-color: #667eea;
+      box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.1);
+    }
+  }
+}
+
+.group-item {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 12px 15px;
+  margin-bottom: 8px;
+  border: 1px solid #dcdfe6;
+  border-radius: 6px;
+  cursor: pointer;
+  transition: all 0.3s ease;
+
+  &:hover {
+    background-color: #f5f7fa;
+    border-color: #667eea;
+  }
+
+  &.active {
+    background-color: rgba(102, 126, 234, 0.1);
+    border-color: #667eea;
+    
+    .group-name {
+      color: #667eea;
+      font-weight: 600;
+    }
+  }
+
+  .group-name {
+    flex: 1;
+    font-size: 14px;
+    color: #606266;
+    word-break: break-all;
+  }
+
+  &.all-group {
+    margin-bottom: 15px;
+    padding: 14px 15px;
+    font-weight: 600;
+    border-bottom: 2px solid #e8e8e8;
+    
+    .group-name {
+      font-size: 15px;
+      font-weight: 600;
+    }
+  }
+
+  .group-actions {
+    display: flex;
+    gap: 5px;
+    margin-left: 10px;
+
+    .el-button--text {
+      color: #909399;
+
+      &:hover {
+        color: #667eea;
+      }
+    }
+  }
+}
+
+.empty-tip {
+  text-align: center;
+  padding: 40px 0;
+  color: #909399;
+  font-size: 14px;
+}
+
+.empty-state {
+  text-align: center;
+  padding: 60px 0;
+  color: #909399;
+  font-size: 14px;
+}
+
+.channel-table {
+  border-radius: 8px;
+  overflow: hidden;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+
+  ::v-deep .el-table__header {
+    th {
+      background-color: #f5f7fa;
+      color: #606266;
+      font-weight: 600;
+    }
+  }
+
+  ::v-deep .el-table__row {
+    transition: all 0.3s ease;
+
+    &:hover {
+      background-color: rgba(102, 126, 234, 0.05);
+    }
+  }
+}
+
+.dialog-footer {
+  text-align: right;
+}
+
+// 响应式调整
+@media (max-width: 992px) {
+  .group-section,
+  .channel-section {
+    margin-bottom: 20px;
+  }
+}
+</style>

+ 269 - 0
src/views/adv/configuration/index.vue

@@ -0,0 +1,269 @@
+<template>
+  <div class="app-container">
+    <el-row :gutter="20">
+      <!-- 站点重复报名设置 -->
+      <el-col :span="8">
+        <el-card shadow="hover" class="box-card" @click.native="openDialog('duplicate')">
+          <div slot="header" class="clearfix">
+            <span>站点重复报名设置</span>
+            <el-button style="float: right; padding: 3px 0" type="text">设置</el-button>
+          </div>
+          <div class="card-content">
+            <i class="el-icon-document-copy card-icon"></i>
+            <p class="card-desc">配置学员报名规则及重复报名限制天数</p>
+          </div>
+        </el-card>
+      </el-col>
+
+      <!-- 全局熟客设置 -->
+      <el-col :span="8">
+        <el-card shadow="hover" class="box-card" @click.native="openDialog('regular')">
+          <div slot="header" class="clearfix">
+            <span>全局熟客设置</span>
+            <el-button style="float: right; padding: 3px 0" type="text">设置</el-button>
+          </div>
+          <div class="card-content">
+            <i class="el-icon-user-solid card-icon"></i>
+            <p class="card-desc">配置全局熟客判断及小程序授权熟客逻辑</p>
+          </div>
+        </el-card>
+      </el-col>
+
+      <!-- 重复回传设置 -->
+      <el-col :span="8">
+        <el-card shadow="hover" class="box-card" @click.native="openDialog('repeat')">
+          <div slot="header" class="clearfix">
+            <span>重复回传设置</span>
+            <el-button style="float: right; padding: 3px 0" type="text">设置</el-button>
+          </div>
+          <div class="card-content">
+            <i class="el-icon-refresh card-icon"></i>
+            <p class="card-desc">配置线索去重策略及回传过滤规则</p>
+          </div>
+        </el-card>
+      </el-col>
+    </el-row>
+
+    <!-- 弹窗1: 站点重复报名设置 -->
+    <el-dialog title="站点重复报名设置" :visible.sync="dialogs.duplicate" width="600px" append-to-body>
+      <el-form ref="duplicateForm" :model="form.duplicateRegistrationSettings" label-width="120px">
+        <el-form-item label="报名逻辑">
+          <el-radio-group v-model="form.duplicateRegistrationSettings.signUpLogic">
+            <el-radio :label="1">学员可正常报名</el-radio>
+            <el-radio :label="2">学员不可重复报名</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="处理逻辑" v-if="form.duplicateRegistrationSettings.signUpLogic === 2">
+          <div>
+            客户站点报名后的
+            <el-input-number v-model="form.duplicateRegistrationSettings.day" :min="1" :max="9999" size="small" style="width: 100px; margin: 0 5px;" />
+            天内,不可重复报名相同项目站点
+          </div>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitConfig">确 定</el-button>
+        <el-button @click="dialogs.duplicate = false">取 消</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 弹窗2: 全局熟客设置 -->
+    <el-dialog title="全局熟客设置" :visible.sync="dialogs.regular" width="650px" append-to-body>
+      <el-form ref="regularForm" :model="form.regularCustomerSettings" label-width="200px">
+        <el-form-item label="全局熟客开关">
+          <el-switch
+            v-model="form.regularCustomerSettings.repeatSwitch"
+            :active-value="1"
+            :inactive-value="2"
+            active-text="开启"
+            inactive-text="关闭">
+          </el-switch>
+          <div class="form-tip">开启后,客户“站点投放报名”或“在线客服留言”时,将分配当前名片跟进人的二维码。特殊的,站点投放配置个微群活码或客服码,则在此模式下无法实现全局熟客,将按照分配规则进行分配出码</div>
+        </el-form-item>
+        <el-form-item label="小程序/加粉站点熟客判断">
+          <el-switch
+            v-model="form.regularCustomerSettings.authSwitch"
+            :active-value="1"
+            :inactive-value="2"
+            active-text="开启"
+            inactive-text="关闭">
+          </el-switch>
+          <div class="form-tip">小程序表单站点/加粉类站点可支持在微信授权后,根据微信身份判断熟客</div>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitConfig">确 定</el-button>
+        <el-button @click="dialogs.regular = false">取 消</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 弹窗3: 重复回传设置 -->
+    <el-dialog title="重复回传设置" :visible.sync="dialogs.repeat" width="600px" append-to-body>
+      <el-form ref="repeatForm" :model="form.repeatPostbackSettings" label-width="140px">
+        <el-form-item label="重复回传过滤">
+          <el-switch
+            v-model="form.repeatPostbackSettings.filter"
+            :active-value="1"
+            :inactive-value="2"
+            active-text="开启"
+            inactive-text="关闭">
+          </el-switch>
+          <div class="form-tip">开启后,若投放站点获取的线索与系统中已有客户重复,系统将不针对该线索进行回传。</div>
+        </el-form-item>
+        <el-form-item label="过滤方式" v-if="form.repeatPostbackSettings.filter === 1">
+          <el-radio-group v-model="form.repeatPostbackSettings.method">
+            <el-radio :label="1">按行为过滤</el-radio>
+            <el-radio :label="2">按手机号过滤</el-radio>
+          </el-radio-group>
+          <div class="form-tip" v-if="form.repeatPostbackSettings.method === 1">若同一个客户在不同站点,重复触发相同行为事件,则不会向广告商回传</div>
+          <div class="form-tip" v-if="form.repeatPostbackSettings.method === 2">若同一个客户在不同站点,重复提交相同手机号,则不会向广告商回传</div>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitConfig">确 定</el-button>
+        <el-button @click="dialogs.repeat = false">取 消</el-button>
+      </div>
+    </el-dialog>
+
+  </div>
+</template>
+
+<script>
+import { getConfigDetail, addOrUpdateConfig } from "@/api/adv/configuration";
+
+export default {
+  name: "AdvConfiguration",
+  data() {
+    return {
+      // 弹窗控制
+      dialogs: {
+        duplicate: false,
+        regular: false,
+        repeat: false
+      },
+      // 完整表单数据
+      form: {
+        duplicateRegistrationSettings: {
+          signUpLogic: 1,
+          day: undefined
+        },
+        regularCustomerSettings: {
+          repeatSwitch: 2,
+          authSwitch: 2
+        },
+        repeatPostbackSettings: {
+          filter: 2,
+          method: 1
+        }
+      }
+    };
+  },
+  created() {
+    this.getDetail();
+  },
+  methods: {
+    /** 获取配置详情 */
+    getDetail() {
+      getConfigDetail().then(response => {
+        if (response.data) {
+          // 合并数据,防止后端返回null导致结构丢失
+          const data = response.data;
+          if (data.duplicateRegistrationSettings) {
+            this.form.duplicateRegistrationSettings = { ...this.form.duplicateRegistrationSettings, ...data.duplicateRegistrationSettings };
+          }
+          if (data.regularCustomerSettings) {
+            this.form.regularCustomerSettings = { ...this.form.regularCustomerSettings, ...data.regularCustomerSettings };
+          }
+          if (data.repeatPostbackSettings) {
+            this.form.repeatPostbackSettings = { ...this.form.repeatPostbackSettings, ...data.repeatPostbackSettings };
+          }
+        }
+      });
+    },
+    /** 打开弹窗 */
+    openDialog(type) {
+      if (this.dialogs.hasOwnProperty(type)) {
+        this.dialogs[type] = true;
+      }
+    },
+    /** 提交保存 */
+    submitConfig() {
+      const loading = this.$loading({
+        lock: true,
+        text: '保存中...',
+        spinner: 'el-icon-loading',
+        background: 'rgba(0, 0, 0, 0.7)'
+      });
+      
+      addOrUpdateConfig(this.form).then(response => {
+        loading.close();
+        // 根据要求:返回1成功,0失败
+        // 通常 axios拦截器已处理code!=200的情况,但这里根据描述 data=1 成功
+        if (response.code === 200) {
+          if (response.data === 1 || response.data === '1' || response.data === true) {
+            this.$message.success("保存成功");
+            this.dialogs.duplicate = false;
+            this.dialogs.regular = false;
+            this.dialogs.repeat = false;
+            this.getDetail(); // 刷新数据
+          } else {
+            this.$message.error("保存失败");
+          }
+        } else {
+            this.$message.error(response.msg || "保存失败");
+        }
+      }).catch(() => {
+        loading.close();
+      });
+    }
+  }
+};
+</script>
+
+<style scoped lang="scss">
+.app-container {
+  padding: 20px;
+  background-color: #f0f2f5;
+  min-height: calc(100vh - 84px);
+}
+
+.box-card {
+  cursor: pointer;
+  height: 220px;
+  transition: all 0.3s;
+  
+  &:hover {
+    transform: translateY(-5px);
+    box-shadow: 0 12px 20px rgba(0, 0, 0, 0.1);
+  }
+
+  .card-content {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    height: 120px;
+    
+    .card-icon {
+      font-size: 48px;
+      color: #409EFF;
+      margin-bottom: 20px;
+    }
+    
+    .card-desc {
+      color: #909399;
+      font-size: 14px;
+      text-align: center;
+      line-height: 1.5;
+      padding: 0 10px;
+    }
+  }
+}
+
+.form-tip {
+  font-size: 12px;
+  color: #909399;
+  line-height: 1.5;
+  margin-top: 5px;
+}
+</style>

+ 163 - 0
src/views/adv/conversionLog/index.vue

@@ -0,0 +1,163 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch">
+      <el-form-item label="业务ID" prop="traceId">
+        <el-input
+          v-model="queryParams.traceId"
+          placeholder="请输入业务ID"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="账号ID" prop="callbackAccountId">
+        <el-input
+          v-model="queryParams.callbackAccountId"
+          placeholder="请输入账号ID"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="回传事件" prop="conversionType">
+        <el-input
+          v-model="queryParams.conversionType"
+          placeholder="请输入回传事件"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="回传状态" prop="callbackStatus">
+        <el-select v-model="queryParams.callbackStatus" placeholder="请选择回传状态" clearable size="small">
+          <el-option label="待回传" :value="0" />
+          <el-option label="成功" :value="1" />
+          <el-option label="失败" :value="2" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="广告商" prop="advertiserId">
+        <el-select v-model="queryParams.advertiserId" placeholder="请选择广告商" clearable size="small" filterable>
+          <el-option
+            v-for="item in advertiserOptions"
+            :key="item.id"
+            :label="item.advertiserName"
+            :value="item.id"
+          />
+        </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-row :gutter="10" class="mb8">
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table border v-loading="loading" :data="logList">
+      <el-table-column label="账号ID" align="center" prop="callbackAccountId" width="120" show-overflow-tooltip />
+      <el-table-column label="业务ID" align="center" prop="traceId" min-width="150" show-overflow-tooltip />
+      <el-table-column label="系统回传事件" align="center" prop="sysConversionEvent" min-width="150" show-overflow-tooltip />
+      <el-table-column label="广告商回传类型" align="center" prop="advConversionEvent" min-width="150" show-overflow-tooltip />
+      <el-table-column label="站点ID" align="center" prop="siteId" width="100" />
+      <el-table-column label="着陆页" align="center" prop="landingUrl" min-width="200" show-overflow-tooltip>
+        <template slot-scope="scope">
+          <el-link v-if="scope.row.landingUrl" :href="scope.row.landingUrl" target="_blank" type="primary">
+            {{ scope.row.landingUrl }}
+          </el-link>
+          <span v-else>-</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="回传时间" align="center" prop="updateTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.updateTime) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="回传状态" align="center" prop="callbackStatus" width="100">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.callbackStatus === 0" type="info">待回传</el-tag>
+          <el-tag v-else-if="scope.row.callbackStatus === 1" type="success">成功</el-tag>
+          <el-tag v-else-if="scope.row.callbackStatus === 2" type="danger">失败</el-tag>
+          <span v-else>-</span>
+        </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 { pageConversionLog } from "@/api/adv/conversionLog";
+import { pageAdvertiser } from "@/api/adv/advertiser";
+
+export default {
+  name: "ConversionLog",
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 回传事件表格数据
+      logList: [],
+      // 广告商选项
+      advertiserOptions: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        traceId: undefined,
+        callbackAccountId: undefined,
+        conversionType: undefined,
+        callbackStatus: undefined,
+        advertiserId: undefined
+      }
+    };
+  },
+  created() {
+    // 从 URL 参数中获取 callbackAccountId
+    if (this.$route.query.callbackAccountId) {
+      this.queryParams.callbackAccountId = this.$route.query.callbackAccountId;
+    }
+    this.getList();
+    this.getAdvertiserOptions();
+  },
+  methods: {
+    /** 查询广告商选项 */
+    getAdvertiserOptions() {
+      pageAdvertiser({ current: 1, size: 1000 }).then(response => {
+        this.advertiserOptions = response.data.records;
+      });
+    },
+    /** 查询回传事件列表 */
+    getList() {
+      this.loading = true;
+      pageConversionLog(this.queryParams).then(response => {
+        this.logList = response.data.records;
+        this.total = response.data.total;
+        this.loading = false;
+      });
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    }
+  }
+};
+</script>

+ 310 - 0
src/views/adv/customPromotionAccount/index.vue

@@ -0,0 +1,310 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch">
+      <el-form-item label="账号名称" prop="accountName">
+        <el-input
+          v-model="queryParams.accountName"
+          placeholder="请输入账号名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="广告商" prop="advertiserId">
+        <el-select v-model="queryParams.advertiserId" placeholder="请选择广告商" clearable size="small" filterable>
+          <el-option
+            v-for="item in advertiserOptions"
+            :key="item.id"
+            :label="item.advertiserName"
+            :value="item.id"
+          />
+        </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-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          plain
+          type="primary"
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+        >新增</el-button>
+      </el-col>
+      <!-- <el-col :span="1.5">
+        <el-button
+          plain
+          type="danger"
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+        >删除</el-button>
+      </el-col> -->
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table border v-loading="loading" :data="accountList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="推广账户名称" align="center" prop="accountName" show-overflow-tooltip />
+      <el-table-column label="广告商名称" align="center" prop="advertiserName" show-overflow-tooltip />
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime) }}</span>
+        </template>
+      </el-table-column>
+      <!-- <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180" fixed="right">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+          >删除</el-button>
+        </template>
+      </el-table-column> -->
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.current"
+      :limit.sync="queryParams.size"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改自定义推广账号对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="120px">
+        <el-form-item label="广告商" prop="advertiserId">
+          <el-select 
+            v-model="form.advertiserId" 
+            placeholder="请选择广告商" 
+            style="width: 100%" 
+            filterable
+            @change="handleAdvertiserChange"
+            :disabled="isEdit"
+          >
+            <el-option
+              v-for="item in advertiserOptions"
+              :key="item.id"
+              :label="item.advertiserName"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="推广账号名称" prop="accountName">
+          <el-input 
+            v-model="form.accountName" 
+            placeholder="请输入推广账号名称"
+            clearable
+            :disabled="isEdit"
+          />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="cancel">取 消</el-button>
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { pagePromotionAccount, getPromotionAccount, addPromotionAccount, updatePromotionAccount, delPromotionAccount, batchDelPromotionAccount } from "@/api/adv/promotionAccount";
+import { pageAdvertiser } from "@/api/adv/advertiser";
+
+export default {
+  name: "CustomPromotionAccount",
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 推广账号表格数据
+      accountList: [],
+      // 广告商选项
+      advertiserOptions: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 是否为编辑模式
+      isEdit: false,
+      // 查询参数
+      queryParams: {
+        current: 1,
+        size: 10,
+        custom: 2,
+        accountName: undefined,
+        advertiserId: undefined
+      },
+      // 表单参数
+      form: {
+
+      },
+      // 表单校验
+      rules: {
+        advertiserId: [
+          { required: true, message: "请选择广告商", trigger: "change" }
+        ],
+        accountName: [
+          { required: true, message: "推广账号名称不能为空", trigger: "blur" }
+        ]
+      }
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询广告商选项(只加载自定义广告商custom=2) */
+    getAdvertiserOptions() {
+      pageAdvertiser({ pageNum: 1, pageSize: 1000, enabled: 1, custom: 2 }).then(response => {
+        this.advertiserOptions = response.data.records;
+      });
+    },
+    /** 查询推广账号列表 */
+    getList() {
+      this.loading = true;
+      pagePromotionAccount(this.queryParams).then(response => {
+        this.accountList = response.data.records;
+        this.total = response.data.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: undefined,
+        promotionType: "展示类",
+        advertiserId: undefined,
+        advertiserName: undefined,
+        configMode: 1,
+        accountName: undefined,
+        accountShortName: undefined,
+        apiSwitch: 2,
+        custom: 2,
+        appId: undefined,
+        appSecret: undefined,
+        adAccountId: undefined,
+        callbackUrl: undefined,
+        authUrl: undefined
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.current = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id);
+      this.single = selection.length !== 1;
+      this.multiple = !selection.length;
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.isEdit = false;
+      // 新增时加载广告商列表(只加载自定义广告商custom=2)
+      this.getAdvertiserOptions();
+      this.open = true;
+      this.title = "添加自定义推广账号";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      this.isEdit = true;
+      const id = row.id || this.ids[0];
+      getPromotionAccount(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改自定义推广账号";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != undefined) {
+            updatePromotionAccount(this.form.id, this.form).then(response => {
+              if (response.code === 200) {
+                this.msgSuccess("修改成功");
+                this.open = false;
+                this.getList();
+              }
+            });
+          } else {
+            addPromotionAccount(this.form).then(response => {
+              if (response.code === 200) {
+                this.msgSuccess("新增成功");
+                this.open = false;
+                this.getList();
+              }
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id ? [row.id] : this.ids;
+      this.$confirm('是否确认删除选中的推广账号?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        return batchDelPromotionAccount(ids);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("删除成功");
+      }).catch(function() {});
+    },
+    /** 广告商变化时 */
+    handleAdvertiserChange(advertiserId) {
+      this.form.advertiserName = "";
+      if (advertiserId) {
+        const advertiser = this.advertiserOptions.find(item => item.id === advertiserId);
+        if (advertiser) {
+          this.form.advertiserName = advertiser.advertiserName;
+        }
+      }
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+</style>

+ 297 - 0
src/views/adv/domain/index.vue

@@ -0,0 +1,297 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch">
+      <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="domain">
+        <el-input
+          v-model="queryParams.domain"
+          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 label="启用" :value="1" />
+          <el-option label="禁用" :value="0" />
+        </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-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          plain
+          type="primary"
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+        >新增</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          plain
+          type="danger"
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+        >删除</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table border v-loading="loading" :data="domainList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="ID" align="center" prop="id" width="80" />
+      <el-table-column label="域名名称" align="center" prop="name" />
+      <el-table-column label="域名地址" align="center" prop="domain" show-overflow-tooltip />
+      <el-table-column label="状态" align="center" prop="status" width="100">
+        <template slot-scope="scope">
+          <el-switch
+            v-model="scope.row.status"
+            :active-value="1"
+            :inactive-value="0"
+            @change="handleStatusChange(scope.row)"
+          ></el-switch>
+        </template>
+      </el-table-column>
+      <el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip />
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+          >删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.page"
+      :limit.sync="queryParams.size"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改域名对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="100px">
+        <el-form-item label="域名名称" prop="name">
+          <el-input v-model="form.name" placeholder="请输入域名名称" />
+        </el-form-item>
+        <el-form-item label="域名地址" prop="domain">
+          <el-input v-model="form.domain" placeholder="请输入域名地址,如:www.example.com" />
+        </el-form-item>
+        <el-form-item label="状态" prop="status">
+          <el-radio-group v-model="form.status">
+            <el-radio :label="1">启用</el-radio>
+            <el-radio :label="0">禁用</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+          <el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { pageDomain, getDomain, addDomain, updateDomain, delDomain, batchDelDomain, updateDomainStatus } from "@/api/adv/domain";
+
+export default {
+  name: "Domain",
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 域名表格数据
+      domainList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        page: 1,
+        size: 10,
+        name: undefined,
+        domain: undefined,
+        status: undefined
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        name: [
+          { required: true, message: "域名名称不能为空", trigger: "blur" }
+        ],
+        domain: [
+          { required: true, message: "域名地址不能为空", trigger: "blur" }
+        ],
+        status: [
+          { required: true, message: "状态不能为空", trigger: "change" }
+        ]
+      }
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询域名列表 */
+    getList() {
+      this.loading = true;
+      pageDomain(this.queryParams).then(response => {
+        this.domainList = response.data.records;
+        this.total = response.data.total;
+        this.loading = false;
+      });
+    },
+    // 域名状态修改
+    handleStatusChange(row) {
+      let text = row.status === 1 ? "启用" : "禁用";
+      this.$confirm('确认要"' + text + '""' + row.name + '"域名吗?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        return updateDomainStatus(row.id, row.status);
+      }).then(() => {
+        this.msgSuccess(text + "成功");
+      }).catch(() => {
+        row.status = row.status === 0 ? 1 : 0;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: undefined,
+        name: undefined,
+        domain: undefined,
+        status: 1,
+        remark: undefined
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.page = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id);
+      this.single = selection.length !== 1;
+      this.multiple = !selection.length;
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加域名";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id || this.ids[0];
+      getDomain(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改域名";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != undefined) {
+            updateDomain(this.form.id, this.form).then(response => {
+              if (response.code === 200) {
+                this.msgSuccess("修改成功");
+                this.open = false;
+                this.getList();
+              }
+            });
+          } else {
+            addDomain(this.form).then(response => {
+              if (response.code === 200) {
+                this.msgSuccess("新增成功");
+                this.open = false;
+                this.getList();
+              }
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id ? [row.id] : this.ids;
+      this.$confirm('是否确认删除选中的域名?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        return batchDelDomain(ids);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("删除成功");
+      }).catch(function() {});
+    }
+  }
+};
+</script>
+

+ 431 - 0
src/views/adv/landingPageTemplate/index.vue

@@ -0,0 +1,431 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch">
+      <el-form-item label="模板名称" prop="templateName">
+        <el-input
+          v-model="queryParams.templateName"
+          placeholder="请输入模板名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="模板类型" prop="templateType">
+        <el-input
+          v-model="queryParams.templateType"
+          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 label="启用" :value="1" />
+          <el-option label="禁用" :value="0" />
+        </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-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          plain
+          type="primary"
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+        >新增</el-button>
+      </el-col>
+      <!-- <el-col :span="1.5">
+        <el-button
+          plain
+          type="danger"
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+        >删除</el-button>
+      </el-col> -->
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table border v-loading="loading" :data="templateList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="ID" align="center" prop="id" width="200" show-overflow-tooltip />
+      <el-table-column label="模板名称" align="center" prop="templateName" />
+      <el-table-column label="模板类型" align="center" prop="templateType" width="120" />
+      <el-table-column label="状态" align="center" prop="status" width="100">
+        <template slot-scope="scope">
+          <el-switch
+            v-model="scope.row.status"
+            :active-value="1"
+            :inactive-value="0"
+            @change="handleStatusChange(scope.row)"
+          ></el-switch>
+        </template>
+      </el-table-column>
+      <el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip />
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="210">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-document-copy"
+            @click="handleCopy(scope.row)"
+          >复制</el-button>
+          <!-- <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+          >删除</el-button> -->
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.page"
+      :limit.sync="queryParams.size"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改模板对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="100px">
+        <el-form-item label="模板名称" prop="templateName">
+          <el-input v-model="form.templateName" placeholder="请输入模板名称" />
+        </el-form-item>
+        <el-form-item label="模板类型" prop="templateType">
+          <el-select v-model="form.templateType" placeholder="请选择模板类型" style="width: 100%" @change="handleTemplateTypeChange" :disabled="form.id != undefined">
+            <el-option label="免费表单类" value="免费表单类" />
+            <el-option label="小程序表单类" value="小程序表单类" />
+            <el-option label="一站式小程序表单类" value="一站式小程序表单类" />
+            <el-option label="免费加粉类" value="免费加粉类" />
+            <el-option label="小程序加粉类" value="小程序加粉类" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="编辑页面">
+          <el-button type="primary" @click="openH5(form.templateData)" :disabled="!form.templateType">
+            编辑页面
+          </el-button>
+        </el-form-item>
+        <el-form-item label="状态" prop="status">
+          <el-radio-group v-model="form.status">
+            <el-radio :label="1">启用</el-radio>
+            <el-radio :label="0">禁用</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+          <el-input v-model="form.remark" type="textarea" 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>
+
+    <!-- H5编辑对话框 -->
+    <el-dialog title="编辑页面" :visible.sync="h5Open" append-to-body fullscreen class="h5-editor-dialog">
+      <div class="h5-editor-wrapper">
+        <H5Editor ref="h5Editor" />
+      </div>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="h5Ok">确 定</el-button>
+        <el-button @click="h5Cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { pageTemplate, getTemplate, addTemplate, updateTemplate, batchDelTemplate, updateTemplateStatus, copyTemplate } from "@/api/adv/landingPageTemplate";
+import H5Editor from "@/components/H5Editor/index.vue";
+
+export default {
+  name: "LandingPageTemplate",
+  components: {
+    H5Editor
+  },
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 模板表格数据
+      templateList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // H5编辑器对话框
+      h5Open: false,
+      queryParams: {
+        page: 1,
+        size: 10,
+        templateName: undefined,
+        templateType: undefined,
+        domainId: undefined,
+        status: undefined
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        templateName: [
+          { required: true, message: "模板名称不能为空", trigger: "blur" }
+        ],
+        status: [
+          { required: true, message: "状态不能为空", trigger: "change" }
+        ]
+      }
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询模板列表 */
+    getList() {
+      this.loading = true;
+      pageTemplate(this.queryParams).then(response => {
+        this.templateList = response.data.records;
+        this.total = response.data.total;
+        this.loading = false;
+      });
+    },
+    // 模板状态修改
+    handleStatusChange(row) {
+      let text = row.status === 1 ? "启用" : "禁用";
+      this.$confirm('确认要"' + text + '""' + row.templateName + '"模板吗?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        return updateTemplateStatus(row.id, row.status);
+      }).then(() => {
+        this.msgSuccess(text + "成功");
+      }).catch(() => {
+        row.status = row.status === 0 ? 1 : 0;
+      });
+    },
+    // 处理模板类型改变
+    handleTemplateTypeChange(val) {
+      let id = null;
+      switch (val) {
+        case '免费表单类':
+          id = 1;
+          break;
+        case '小程序表单类':
+          id = 2;
+          break;
+        case '一站式小程序表单类':
+          id = 3;
+          break;
+        case '免费加粉类':
+          id = 4;
+          break;
+        case '小程序加粉类':
+          id = 5;
+          break;
+      }
+      
+      if (id) {
+        getTemplate(id).then(response => {
+          if (response.data && response.data.templateData) {
+            this.form.templateData = response.data.templateData;
+            // 如果是在新增模式下且没有填写标题,可以考虑是否要同步其他信息,暂时只同步内容
+          }
+        });
+      }
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: undefined,
+        templateName: undefined,
+        templateData: undefined,
+        templateType: undefined,
+        domainId: undefined,
+        status: 0,
+        remark: undefined
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.page = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id);
+      this.single = selection.length !== 1;
+      this.multiple = !selection.length;
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      // 清空H5编辑器的缓存数据
+      if (this.$refs.h5Editor) {
+        this.$refs.h5Editor.initData('{"pageTitle":"","configList":[]}')
+      }
+      this.open = true;
+      this.title = "添加模板";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id || this.ids[0];
+      getTemplate(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改模板";
+      });
+    },
+    /** 复制按钮操作 */
+    /** 复制按钮操作 */
+    handleCopy(row) {
+      this.reset();
+      const id = row.id;
+      getTemplate(id).then(response => {
+        this.form = response.data;
+        this.form.id = undefined;
+        this.form.templateName = this.form.templateName + "_副本";
+        this.open = true;
+        this.title = "复制并添加模板";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          // 验证JSON格式
+          if (this.form.templateData) {
+            try {
+              JSON.parse(this.form.templateData);
+            } catch (e) {
+              this.msgError("模板数据格式不正确,必须是有效的JSON");
+              return;
+            }
+          }
+          
+          if (this.form.id != undefined) {
+            updateTemplate(this.form.id, this.form).then(response => {
+              if (response.code === 200) {
+                this.msgSuccess("修改成功");
+                this.open = false;
+                this.getList();
+              }
+            });
+          } else {
+            addTemplate(this.form).then(response => {
+              if (response.code === 200) {
+                this.msgSuccess("新增成功");
+                this.open = false;
+                this.getList();
+              }
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id ? [row.id] : this.ids;
+      this.$confirm('是否确认删除选中的模板?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        return batchDelTemplate(ids);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("删除成功");
+      }).catch(function() {});
+    },
+    /** 打开H5编辑器 */
+    openH5(templateData) {
+      this.h5Open = true
+      this.$nextTick(() => {
+        // 支持新旧数据格式
+        this.$refs.h5Editor.initData(templateData || '{"pageTitle":"","configList":[]}', this.form.templateType)
+      })
+    },
+    /** H5编辑器确定 */
+    h5Ok() {
+      // 通过ref获取子组件的完整数据(新格式JSON)
+      const pageData = this.$refs.h5Editor.getPageData()
+      this.form.templateData = JSON.stringify(pageData)
+      this.h5Open = false // 关闭对话框
+    },
+    /** H5编辑器取消 */
+    h5Cancel() {
+      this.h5Open = false;
+    }
+  }
+};
+</script>
+
+<style scoped>
+.h5-editor-dialog /deep/ .el-dialog {
+  display: flex;
+  flex-direction: column;
+}
+
+.h5-editor-dialog /deep/ .el-dialog__body {
+  flex: 1;
+  overflow: hidden;
+  padding: 0;
+}
+
+.h5-editor-wrapper {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+}
+
+.h5-editor-wrapper /deep/ .h5-editor-container {
+  width: 100%;
+  height: 100%;
+}
+</style>
+

+ 178 - 0
src/views/adv/project/index.vue

@@ -0,0 +1,178 @@
+<template>
+  <div class="app-container">
+    <!-- 搜索表单 -->
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch">
+      <el-form-item label="项目名称" prop="projectName">
+        <el-input
+          v-model="queryParams.projectName"
+          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-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          plain
+          type="primary"
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+        >新增</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <!-- 项目列表 -->
+    <el-table border v-loading="loading" :data="projectList">
+      <el-table-column label="项目名称" align="center" prop="projectName" min-width="150" show-overflow-tooltip />
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime) }}</span>
+        </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="100px">
+        <el-form-item label="项目名称" prop="projectName">
+          <el-input v-model="form.projectName" placeholder="请输入项目名称" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { pageProject, addProject } from "@/api/adv/project";
+
+export default {
+  name: "Project",
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 项目表格数据
+      projectList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        projectName: undefined
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        projectName: [
+          { required: true, message: "项目名称不能为空", trigger: "blur" }
+        ]
+      }
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询项目列表 */
+    getList() {
+      this.loading = true;
+      const params = {
+        pageNum: this.queryParams.pageNum,
+        pageSize: this.queryParams.pageSize
+      };
+      // 只有输入了名称才添加到参数中
+      if (this.queryParams.projectName) {
+        params.projectName = this.queryParams.projectName;
+      }
+      pageProject(params).then(response => {
+        this.projectList = response.data.records;
+        this.total = response.data.total;
+        this.loading = false;
+      }).catch(error => {
+        console.error('加载项目列表失败:', error);
+        this.projectList = [];
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        projectName: undefined
+      };
+      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 (valid) {
+          addProject(this.form).then(response => {
+            if (response.code === 200) {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            }
+          }).catch(error => {
+            console.error('新增项目失败:', error);
+            this.msgError("新增失败");
+          });
+        }
+      });
+    }
+  }
+};
+</script>
+
+<style scoped>
+</style>

+ 736 - 0
src/views/adv/promotionAccount/index.vue

@@ -0,0 +1,736 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch">
+      <el-form-item label="账号名称" prop="accountName">
+        <el-input
+          v-model="queryParams.accountName"
+          placeholder="请输入账号名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="广告商" prop="advertiserId">
+        <el-select v-model="queryParams.advertiserId" placeholder="请选择广告商" clearable size="small" filterable>
+          <el-option
+            v-for="item in advertiserOptions"
+            :key="item.id"
+            :label="item.advertiserName"
+            :value="item.id"
+          />
+        </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-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          plain
+          type="primary"
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+        >新增</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          plain
+          type="danger"
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+        >删除</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table border v-loading="loading" :data="accountList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="账户简称" align="center" prop="accountShortName" min-width="150" show-overflow-tooltip />
+      <el-table-column label="广告商名称" align="center" prop="advertiserName" min-width="150" show-overflow-tooltip />
+      <el-table-column label="读取方式" align="center" prop="apiSwitch" width="120">
+        <template slot-scope="scope">
+          <el-tag :type="scope.row.apiSwitch === 1 ? 'success' : 'info'" size="small">
+            {{ scope.row.apiSwitch === 1 ? 'API' : '手动录入' }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="广告主ID" align="center" prop="adAccountId" min-width="180" show-overflow-tooltip />
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="240" fixed="right">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-check"
+            @click="handleVerify(scope.row)"
+            :disabled="!scope.row.authUrl"
+          >验证</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+          >删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.current"
+      :limit.sync="queryParams.size"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改推广账号对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="680px" append-to-body class="promotion-dialog">
+      <el-form ref="form" :model="form" :rules="rules" label-width="140px" class="elegant-form">
+        <!-- 基础信息 -->
+        <div class="form-section">
+          <div class="section-title">
+            <i class="el-icon-info"></i>
+            <span>基础信息</span>
+          </div>
+          <el-form-item label="推广方式" prop="promotionType">
+            <el-input v-model="form.promotionType" disabled placeholder="展示类" />
+          </el-form-item>
+          <el-form-item label="广告商" prop="advertiserId">
+            <el-select 
+              v-model="form.advertiserId" 
+              placeholder="请选择广告商" 
+              style="width: 100%" 
+              filterable
+              @change="handleAdvertiserChange"
+              :disabled="isEdit"
+            >
+              <el-option
+                v-for="item in advertiserOptions"
+                :key="item.id"
+                :label="item.advertiserName"
+                :value="item.id"
+              />
+            </el-select>
+          </el-form-item>
+          <!-- 如何申请API按钮 -->
+          <el-form-item v-if="form.advertiserId" label=" " class="api-help-button">
+            <el-button 
+              type="text" 
+              @click="openApiHelp"
+              icon="el-icon-question"
+            >如何申请API</el-button>
+          </el-form-item>
+          <!-- <el-form-item label="配置方式" prop="configMode">
+            <el-radio-group v-model="form.configMode">
+              <el-radio :label="1" :border="true">服务模式</el-radio>
+              <el-radio :label="2" :border="true">广告主模式</el-radio>
+            </el-radio-group>
+          </el-form-item> -->
+        </div>
+
+        <!-- 账号信息 -->
+        <div class="form-section">
+          <div class="section-title">
+            <i class="el-icon-user"></i>
+            <span>账号信息</span>
+          </div>
+          <el-form-item label="推广账号名称" prop="accountName">
+            <el-input 
+              v-model="form.accountName" 
+              placeholder="请输入推广账号名称"
+              prefix-icon="el-icon-user-solid"
+              clearable
+              :disabled="isEdit"
+            />
+          </el-form-item>
+          <el-form-item label="推广账户简称" prop="accountShortName">
+            <el-input 
+              v-model="form.accountShortName" 
+              placeholder="请输入推广账户简称"
+              prefix-icon="el-icon-tickets"
+              clearable
+            />
+          </el-form-item>
+        </div>
+
+        <!-- API配置 -->
+        <div class="form-section">
+          <div class="section-title">
+            <i class="el-icon-setting"></i>
+            <span>API配置</span>
+          </div>
+          <el-form-item label="API读取账号信息" prop="apiSwitch">
+            <el-radio-group v-model="form.apiSwitch">
+              <el-radio :label="1" :border="true">开</el-radio>
+              <el-radio :label="2" :border="true">关</el-radio>
+            </el-radio-group>
+          </el-form-item>
+          
+          <template v-if="form.apiSwitch === 1">
+            <el-form-item label="AppID" prop="appId" class="slide-fade">
+              <el-input 
+                v-model="form.appId" 
+                placeholder="请输入AppID"
+                prefix-icon="el-icon-key"
+                clearable
+              />
+            </el-form-item>
+            <el-form-item label="Secret Key" prop="appSecret" class="slide-fade">
+              <el-input 
+                v-model="form.appSecret" 
+                placeholder="请输入Secret Key"
+                prefix-icon="el-icon-lock"
+                type="password"
+                show-password
+                clearable
+              />
+            </el-form-item>
+            <el-form-item label="广告主id" prop="adAccountId" class="slide-fade">
+              <el-input 
+                v-model="form.adAccountId" 
+                placeholder="请输入广告主id"
+                prefix-icon="el-icon-postcard"
+                clearable
+              />
+            </el-form-item>
+            <el-form-item label="回调地址" prop="callbackUrl" class="slide-fade">
+              <el-input 
+                v-model="form.callbackUrl" 
+                placeholder="请输入回调地址"
+                prefix-icon="el-icon-link"
+                clearable
+              />
+            </el-form-item>
+            <el-form-item v-if="form.advertiserId !== 10002" label="应用授权链接" prop="authUrl" class="slide-fade">
+              <el-input 
+                v-model="form.authUrl" 
+                placeholder="请输入应用授权链接"
+                prefix-icon="el-icon-connection"
+                clearable
+              />
+            </el-form-item>
+          </template>
+        </div>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="cancel">取 消</el-button>
+        <el-button type="primary" @click="submitForm" icon="el-icon-check">确 定</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { pagePromotionAccount, getPromotionAccount, addPromotionAccount, updatePromotionAccount, delPromotionAccount, batchDelPromotionAccount } from "@/api/adv/promotionAccount";
+import { pageAdvertiser } from "@/api/adv/advertiser";
+
+export default {
+  name: "PromotionAccount",
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 推广账号表格数据
+      accountList: [],
+      // 广告商选项
+      advertiserOptions: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 是否为编辑模式
+      isEdit: false,
+      // 查询参数
+      queryParams: {
+        current: 1,
+        size: 10,
+        custom:1,
+        accountName: undefined,
+        advertiserId: undefined
+      },
+      // 表单参数
+      form: {
+      },
+      // API文档URL映射
+      apiDocumentUrls: {
+        10001: 'https://probe.bjmantis.net/msp/prmt/help/api/BAIDU.pdf',
+        10002: 'https://probe.bjmantis.net/msp/prmt/help/api/TOUTIAO.pdf',
+        10003: 'https://probe.bjmantis.net/msp/prmt/help/api/GDT.pdf',
+        10004: 'https://probe.bjmantis.net/msp/prmt/help/api/OPPO.pdf',
+        10005: 'https://probe.bjmantis.net/msp/prmt/help/api/VIVO.pdf',
+        10006: 'https://probe.bjmantis.net/msp/prmt/help/api/AIQIYI.pdf'
+      },
+      // 表单校验
+      rules: {
+        promotionType: [
+          { required: true, message: "推广方式不能为空", trigger: "blur" }
+        ],
+        advertiserId: [
+          { required: true, message: "请选择广告商", trigger: "change" }
+        ],
+        configMode: [
+          { required: true, message: "请选择配置方式", trigger: "change" }
+        ],
+        accountName: [
+          { required: true, message: "推广账号名称不能为空", trigger: "blur" }
+        ],
+        accountShortName: [
+          { required: true, message: "推广账户简称不能为空", trigger: "blur" }
+        ],
+        apiSwitch: [
+          { required: true, message: "请选择API读取账号信息", trigger: "change" }
+        ],
+        appId: [
+          { 
+            validator: (rule, value, callback) => {
+              if (this.form.apiSwitch === 1 && !value) {
+                callback(new Error('请输入AppID'));
+              } else {
+                callback();
+              }
+            }, 
+            trigger: "blur" 
+          }
+        ],
+        appSecret: [
+          { 
+            validator: (rule, value, callback) => {
+              if (this.form.apiSwitch === 1 && !value) {
+                callback(new Error('请输入Secret Key'));
+              } else {
+                callback();
+              }
+            }, 
+            trigger: "blur" 
+          }
+        ],
+        adAccountId: [
+          { 
+            validator: (rule, value, callback) => {
+              if (this.form.apiSwitch === 1 && !value) {
+                callback(new Error('请输入广告主id'));
+              } else {
+                callback();
+              }
+            }, 
+            trigger: "blur" 
+          }
+        ]
+      }
+    };
+  },
+  created() {
+    this.getList();
+    // 移除自动加载广告商列表,改为新增时按需加载
+  },
+  methods: {
+    /** 查询广告商选项 */
+    getAdvertiserOptions() {
+      pageAdvertiser({ pageNum: 1, pageSize: 1000, enabled: 1, custom: 1 }).then(response => {
+        this.advertiserOptions = response.data.records;
+      });
+    },
+    /** 查询推广账号列表 */
+    getList() {
+      this.loading = true;
+      pagePromotionAccount(this.queryParams).then(response => {
+        this.accountList = response.data.records;
+        this.total = response.data.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: undefined,
+        promotionType: "展示类",
+        advertiserId: undefined,
+        advertiserName: undefined,
+        configMode: 1,
+        accountName: undefined,
+        accountShortName: undefined,
+        apiSwitch: 2,
+        custom: 1,
+        appId: undefined,
+        appSecret: undefined,
+        adAccountId: undefined,
+        callbackUrl: undefined,
+        authUrl: undefined
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.current = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id);
+      this.single = selection.length !== 1;
+      this.multiple = !selection.length;
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.isEdit = false;
+      // 新增时加载广告商列表(只加载线上广告商custom=1)
+      this.getAdvertiserOptions();
+      this.open = true;
+      this.title = "添加推广账号";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      this.isEdit = true;
+      const id = row.id || this.ids[0];
+      getPromotionAccount(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改推广账号";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != undefined) {
+            updatePromotionAccount(this.form.id, this.form).then(response => {
+              if (response.code === 200) {
+                this.msgSuccess("修改成功");
+                this.open = false;
+                this.getList();
+              }
+            });
+          } else {
+            addPromotionAccount(this.form).then(response => {
+              if (response.code === 200) {
+                this.msgSuccess("新增成功");
+                this.open = false;
+                this.getList();
+              }
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id ? [row.id] : this.ids;
+      this.$confirm('是否确认删除选中的推广账号?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        return batchDelPromotionAccount(ids);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("删除成功");
+      }).catch(function() {});
+    },
+    /** 广告商变化时 */
+    handleAdvertiserChange(advertiserId) {
+      this.form.advertiserName = "";
+      if (advertiserId) {
+        const advertiser = this.advertiserOptions.find(item => item.id === advertiserId);
+        if (advertiser) {
+          this.form.advertiserName = advertiser.advertiserName;
+        }
+      }
+    },
+    /** 打开API帮助文档 */
+    openApiHelp() {
+      const url = this.apiDocumentUrls[this.form.advertiserId];
+      if (url) {
+        window.open(url, '_blank');
+      } else {
+        this.$message.warning('此广告商暂无法理文档');
+      }
+    },
+    /** 验证按钮操作 */
+    handleVerify(row) {
+      if (!row.authUrl) {
+        this.$message.warning('此账户暂未配置应用授权链接');
+        return;
+      }
+      window.open(row.authUrl, '_blank');
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+// 对话框样式
+::v-deep .promotion-dialog {
+  .el-dialog__header {
+    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+    padding: 20px;
+    margin: 0;
+    border-radius: 4px 4px 0 0;
+    
+    .el-dialog__title {
+      color: #fff;
+      font-size: 18px;
+      font-weight: 500;
+    }
+    
+    .el-dialog__headerbtn .el-dialog__close {
+      color: #fff;
+      font-size: 20px;
+      
+      &:hover {
+        color: #f0f0f0;
+      }
+    }
+  }
+  
+  .el-dialog__body {
+    padding: 25px 30px;
+    background-color: #f8f9fa;
+    max-height: 70vh;
+    overflow-y: auto;
+  }
+  
+  .el-dialog__footer {
+    padding: 15px 30px;
+    border-top: 1px solid #e8e8e8;
+    background-color: #fff;
+  }
+}
+
+// 表单样式
+.elegant-form {
+  .form-section {
+    background: #fff;
+    border-radius: 8px;
+    padding: 20px;
+    margin-bottom: 20px;
+    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+    transition: all 0.3s ease;
+    
+    &:hover {
+      box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
+      transform: translateY(-2px);
+    }
+    
+    &:last-child {
+      margin-bottom: 0;
+    }
+  }
+  
+  .section-title {
+    display: flex;
+    align-items: center;
+    margin-bottom: 20px;
+    padding-bottom: 12px;
+    border-bottom: 2px solid #e8e8e8;
+    font-size: 15px;
+    font-weight: 600;
+    color: #303133;
+    
+    i {
+      font-size: 18px;
+      margin-right: 8px;
+      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+      -webkit-background-clip: text;
+      -webkit-text-fill-color: transparent;
+      background-clip: text;
+    }
+    
+    span {
+      position: relative;
+      
+      &::after {
+        content: '';
+        position: absolute;
+        left: 0;
+        bottom: -12px;
+        width: 0;
+        height: 2px;
+        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+        transition: width 0.3s ease;
+      }
+    }
+  }
+  
+  .form-section:hover .section-title span::after {
+    width: 100%;
+  }
+  
+  .el-form-item {
+    margin-bottom: 20px;
+    
+    &:last-child {
+      margin-bottom: 0;
+    }
+  }
+  
+  ::v-deep .el-form-item__label {
+    color: #606266;
+    font-weight: 500;
+    font-size: 14px;
+  }
+  
+  ::v-deep .el-input__inner,
+  ::v-deep .el-textarea__inner {
+    border-radius: 6px;
+    border: 1px solid #dcdfe6;
+    transition: all 0.3s ease;
+    
+    &:hover {
+      border-color: #c0c4cc;
+    }
+    
+    &:focus {
+      border-color: #667eea;
+      box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.1);
+    }
+    
+    &:disabled {
+      background-color: #f5f7fa;
+      color: #909399;
+    }
+  }
+  
+  ::v-deep .el-radio {
+    margin-right: 20px;
+    
+    &.is-bordered {
+      border-radius: 6px;
+      padding: 10px 20px;
+      transition: all 0.3s ease;
+      
+      &:hover {
+        border-color: #667eea;
+      }
+      
+      &.is-checked {
+        border-color: #667eea;
+        background-color: rgba(102, 126, 234, 0.05);
+      }
+    }
+  }
+  
+  // API字段显示动画
+  .slide-fade {
+    animation: slideDown 0.3s ease;
+  }
+}
+
+@keyframes slideDown {
+  from {
+    opacity: 0;
+    transform: translateY(-10px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+
+// API帮助按钮样式
+.api-help-button {
+  ::v-deep .el-form-item__content {
+    padding-top: 5px;
+  }
+  
+  .el-button {
+    color: #667eea;
+    font-size: 13px;
+    transition: all 0.3s ease;
+    padding: 0;
+    
+    &:hover {
+      color: #764ba2;
+      
+      .el-icon-question {
+        animation: bounce 0.5s ease;
+      }
+    }
+    
+    .el-icon-question {
+      margin-right: 6px;
+    }
+  }
+}
+
+@keyframes bounce {
+  0%, 100% {
+    transform: translateY(0);
+  }
+  50% {
+    transform: translateY(-3px);
+  }
+}
+
+// 按钮样式
+.dialog-footer {
+  text-align: right;
+  
+  .el-button {
+    padding: 10px 24px;
+    border-radius: 6px;
+    font-weight: 500;
+    transition: all 0.3s ease;
+    
+    &.el-button--primary {
+      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+      border: none;
+      
+      &:hover {
+        transform: translateY(-2px);
+        box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
+      }
+      
+      &:active {
+        transform: translateY(0);
+      }
+    }
+    
+    &.el-button--default {
+      &:hover {
+        color: #667eea;
+        border-color: #667eea;
+        background-color: rgba(102, 126, 234, 0.05);
+      }
+    }
+  }
+}
+</style>
+

+ 1637 - 0
src/views/adv/site/index.vue

@@ -0,0 +1,1637 @@
+<template>
+  <div class="app-container">
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          plain
+          type="primary"
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+        >新增</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          plain
+          type="success"
+          icon="el-icon-refresh"
+          size="mini"
+          @click="getList"
+        >刷新</el-button>
+      </el-col>
+    </el-row>
+
+    <el-table border v-loading="loading" :data="siteList">
+      <el-table-column label="ID" align="center" prop="id" width="80" />
+      <el-table-column label="站点名称" align="center" prop="siteName" min-width="150" show-overflow-tooltip />
+      <el-table-column label="投放类型" align="center" prop="launchType" width="100" />
+      <el-table-column label="配置回传" align="center" prop="configCallback" width="100">
+        <template slot-scope="scope">
+          <el-tag :type="scope.row.configCallback === 1 ? 'success' : 'info'" size="small">
+            {{ scope.row.configCallback === 1 ? '是' : '否' }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="站点状态" align="center" prop="status" width="100">
+        <template slot-scope="scope">
+          <el-tag :type="scope.row.status === 1 ? 'success' : 'info'" size="small">
+            {{ scope.row.status === 1 ? '启用' : '停用' }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="420" fixed="right">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-view"
+            @click="handleDetail(scope.row)"
+          >详情</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-data-line"
+            @click="handleStatistics(scope.row)"
+          >统计</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            :icon="scope.row.status === 1 ? 'el-icon-video-pause' : 'el-icon-video-play'"
+            @click="handleEnable(scope.row)"
+          >{{ scope.row.status === 1 ? '停用' : '启用' }}</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+          >删除</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-link"
+            @click="handleGenerateUrl(scope.row)"
+          >生成投放url</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 添加或修改站点对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="680px" append-to-body class="site-dialog">
+      <el-form ref="form" :model="form" :rules="rules" label-width="130px" class="elegant-form">
+        <!-- 基础信息 -->
+        <div class="form-section">
+          <div class="section-title">
+            <i class="el-icon-info"></i>
+            <span>基础信息</span>
+          </div>
+          <el-form-item label="站点名称" prop="siteName">
+            <el-input 
+              v-model="form.siteName" 
+              placeholder="请输入站点名称"
+              prefix-icon="el-icon-notebook-2"
+              clearable
+              :disabled="isDetail"
+            />
+          </el-form-item>
+          <el-form-item label="渠道" prop="channelId">
+            <el-select 
+              v-model="form.channelId" 
+              placeholder="请选择渠道" 
+              style="width: 100%"
+              filterable
+              @change="handleChannelChange"
+              :disabled="isDetail"
+            >
+              <el-option
+                v-for="item in channelList"
+                :key="item.id"
+                :label="item.channelName"
+                :value="item.id"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="项目" prop="projectId">
+            <el-select 
+              v-model="form.projectId" 
+              placeholder="请选择项目" 
+              style="width: 100%"
+              filterable
+              @change="handleProjectChange"
+              :disabled="isDetail"
+            >
+              <el-option
+                v-for="item in projectList"
+                :key="item.id"
+                :label="item.projectName"
+                :value="item.id"
+              />
+            </el-select>
+          </el-form-item>
+        </div>
+
+        <!-- 投放配置 -->
+        <div class="form-section">
+          <div class="section-title">
+            <i class="el-icon-setting"></i>
+            <span>投放配置</span>
+          </div>
+          <el-form-item label="投放类型" prop="launchType">
+            <el-select 
+              v-model="form.launchType" 
+              placeholder="请选择投放类型" 
+              style="width: 100%"
+              prefix-icon="el-icon-s-flag"
+              @change="handleLaunchTypeChange"
+              :disabled="isDetail"
+            >
+              <el-option
+                v-for="item in launchTypeOptions"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="广告类型" prop="adType">
+            <el-select 
+              v-model="form.adType" 
+              placeholder="请选择广告类型" 
+              style="width: 100%"
+              :disabled="isDetail"
+            >
+              <el-option
+                v-for="item in adTypeOptions"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+              />
+            </el-select>
+          </el-form-item>
+          
+          <!-- 企微分配规则 -->
+          <el-form-item label="企微分配规则" prop="allocationRule">
+            <el-radio-group v-model="form.allocationRule" @change="handleAllocationRuleChange" :disabled="!form.launchType || isDetail">
+              <el-radio 
+                label="1"
+                :border="true"
+              >个人码分配</el-radio>
+              <el-radio 
+                label="2"
+                :border="true"
+              >活码分配</el-radio>
+            </el-radio-group>
+          </el-form-item>
+          
+          <!-- 根据选择的分配规则显示不同的下拉框 -->
+          <el-form-item 
+            v-if="form.allocationRule === '1'" 
+            label="企业微信" 
+            prop="allocationRuleId"
+            class="slide-fade"
+          >
+            <el-select 
+              v-model="form.allocationRuleId" 
+              placeholder="请选择企业微信" 
+              style="width: 100%"
+              filterable
+              :disabled="isDetail"
+            >
+              <el-option
+                v-for="item in allocationRuleList"
+                :key="item.id"
+                :label="item.ruleName"
+                :value="item.id"
+              />
+            </el-select>
+          </el-form-item>
+          
+          <el-form-item 
+            v-if="form.allocationRule === '2'" 
+            :label="form.launchType === 1 ? '群活码' : '企业微信活码'" 
+            prop="allocationRuleId"
+            class="slide-fade"
+          >
+            <el-select 
+              v-model="form.allocationRuleId" 
+              :placeholder="form.launchType === 1 ? '请选择群活码' : '请选择企业微信活码'" 
+              style="width: 100%"
+              filterable
+              :disabled="isDetail"
+            >
+              <el-option
+                v-for="item in form.launchType === 1 ? groupActiveList : contactWayList"
+                :key="item.id"
+                :label="form.launchType === 1 ? item.groupName : item.name"
+                :value="item.id"
+              />
+            </el-select>
+          </el-form-item>
+        </div>
+
+        <!-- 广告商信息 -->
+        <div class="form-section">
+          <div class="section-title">
+            <i class="el-icon-office-building"></i>
+            <span>广告商信息</span>
+          </div>
+          <el-form-item label="投放广告商" prop="advertiserId">
+            <el-select 
+              v-model="form.advertiserId" 
+              placeholder="请先选择投放类型" 
+              style="width: 100%"
+              @change="handleAdvertiserChange"
+              filterable
+              :disabled="!form.launchType || isDetail"
+            >
+              <el-option
+                v-for="item in advertiserList"
+                :key="item.id"
+                :label="item.advertiserName"
+                :value="item.id"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="投放广告商账号" prop="promotionAccountId">
+            <el-select 
+              v-model="form.promotionAccountId" 
+              placeholder="请选择投放广告商账号" 
+              style="width: 100%"
+              @change="handlePromotionAccountChange"
+              filterable
+              :disabled="!form.advertiserId || isDetail"
+            >
+              <el-option
+                v-for="item in promotionAccountList"
+                :key="item.id"
+                :label="item.accountName"
+                :value="item.id"
+              />
+            </el-select>
+          </el-form-item>
+        </div>
+
+        <!-- 页面和来源 -->
+        <div class="form-section">
+          <div class="section-title">
+            <i class="el-icon-monitor"></i>
+            <span>页面和来源</span>
+          </div>
+          <el-form-item label="投放页面" prop="launchPageId">
+            <el-select 
+              v-model="form.launchPageId" 
+              placeholder="请选择投放页面" 
+              style="width: 100%"
+              @change="handleLaunchPageChange"
+              filterable
+              :disabled="isDetail"
+            >
+              <el-option
+                v-for="item in landingPageTemplateList"
+                :key="item.id"
+                :label="item.templateName"
+                :value="item.id"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="投放域名" prop="launchDomain">
+            <el-select 
+              v-model="form.launchDomain" 
+              placeholder="请选择投放域名" 
+              style="width: 100%"
+              filterable
+              :disabled="isDetail"
+            >
+              <el-option
+                v-for="item in launchDomainList"
+                :key="item.id"
+                :label="item.domain"
+                :value="item.domain"
+              />
+            </el-select>
+          </el-form-item>
+
+        </div>
+
+        <!-- 回传配置 -->
+        <div class="form-section">
+          <div class="section-title">
+            <i class="el-icon-share"></i>
+            <span>回传配置</span>
+          </div>
+          <el-form-item label="是否配置回传" prop="configCallback">
+            <el-radio-group v-model="form.configCallback" @change="handleConfigCallbackChange" :disabled="isDetail">
+              <el-radio 
+                v-for="item in configCallbackOptions"
+                :key="item.value"
+                :label="item.value"
+                :border="true"
+              >{{ item.label }}</el-radio>
+            </el-radio-group>
+          </el-form-item>
+          <el-form-item 
+            v-if="form.configCallback === 1" 
+            label="回传账号" 
+            prop="callbackAccountId"
+            class="slide-fade"
+          >
+            <el-select 
+              v-model="form.callbackAccountId" 
+              placeholder="请选择回传账号" 
+              style="width: 100%"
+              @change="handleCallbackAccountChange"
+              filterable
+              :disabled="isDetail"
+            >
+              <el-option
+                v-for="item in callbackAccountList"
+                :key="item.id"
+                :label="item.accountName"
+                :value="item.id"
+              />
+            </el-select>
+          </el-form-item>
+          
+          <!-- 转换类型配置区域 -->
+          <div v-if="form.configCallback === 1 && form.callbackAccountId" class="conversion-config-section">
+            <div class="config-header">
+              <span class="config-title">转换类型配置</span>
+              <el-button type="primary" size="small" icon="el-icon-plus" @click="addConversionEvent">添加事件</el-button>
+            </div>
+            
+            <div v-if="conversionEvents.length === 0" class="empty-tip">
+              暂无转换事件,请点击“添加事件”按钮添加
+            </div>
+            
+            <div v-for="(event, index) in conversionEvents" :key="index" class="conversion-event-item">
+              <div class="event-header">
+                <span class="event-title">事件{{ index + 1 }}</span>
+                <el-button 
+                  type="danger" 
+                  size="mini" 
+                  icon="el-icon-delete" 
+                  circle
+                  @click="removeConversionEvent(index)"
+                ></el-button>
+              </div>
+              
+              <el-form label-width="130px" style="margin-top: 10px;">
+                <el-form-item label="广告商转化类型">
+                  <el-select 
+                    v-model="event.advertiserEventType" 
+                    placeholder="请选择广告商转化类型" 
+                    style="width: 100%"
+                    @change="handleAdvertiserEventChange(index, $event)"
+                  >
+                    <el-option
+                      v-for="item in advertiserEventOptions"
+                      :key="item.eventType"
+                      :label="item.eventName"
+                      :value="item.eventType"
+                    />
+                  </el-select>
+                </el-form-item>
+                
+                <el-form-item label="回传数据类型">
+                  <el-select 
+                    v-model="event.systemEventType" 
+                    placeholder="请选择回传数据类型" 
+                    style="width: 100%"
+                    @change="handleSystemEventChange(index, $event)"
+                  >
+                    <el-option
+                      v-for="item in systemEventOptions"
+                      :key="item.eventType"
+                      :label="item.eventName"
+                      :value="item.eventType"
+                    />
+                  </el-select>
+                </el-form-item>
+              </el-form>
+            </div>
+          </div>
+        </div>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="cancel">取 消</el-button>
+        <el-button v-if="!isDetail" type="primary" @click="submitForm" icon="el-icon-check">确 定</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 站点详情对话框 -->
+    <el-dialog title="站点详情" :visible.sync="detailOpen" width="900px" append-to-body>
+      <el-descriptions :column="2" border>
+        <el-descriptions-item label="站点名称">{{ detail.siteName || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="站点URL" :span="2">{{ detail.siteUrl || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="投放类型">{{ detail.launchType || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="广告类型">{{ detail.adType || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="广告商名称" :span="2">{{ detail.advertiserName || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="推广账户名称" :span="2">{{ detail.promotionAccountName || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="投放页面名称" :span="2">{{ detail.launchPageName || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="投放域名" :span="2">{{ detail.launchDomain || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="一级分配规则">
+          <span v-if="detail.allocationRule === '1'">选择员工"个人码"分配规则</span>
+          <span v-else-if="detail.allocationRule === '2'">选择员工"活码"分配规则</span>
+          <span v-else>-</span>
+        </el-descriptions-item>
+        <el-descriptions-item label="二级分配汇总">{{ detail.allocationRuleId || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="项目名称">{{ detail.projectName || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="配置回传">
+          <el-tag :type="detail.configCallback === 1 ? 'success' : 'info'" size="small">
+            {{ detail.configCallback === 1 ? '是' : '否' }}
+          </el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="回传账号名称">{{ detail.callbackAccountName || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="站点状态">
+          <el-tag :type="detail.status === 1 ? 'success' : 'info'" size="small">
+            {{ detail.status === 1 ? '启用' : '停用' }}
+          </el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="创建人">{{ detail.creator || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="创建时间">{{ parseTime(detail.createTime) || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="更新人">{{ detail.updater || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="更新时间" :span="2">{{ parseTime(detail.updateTime) || '-' }}</el-descriptions-item>
+      </el-descriptions>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="detailOpen = false">关 闭</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 站点统计对话框 -->
+    <el-dialog title="站点统计" :visible.sync="statisticsOpen" width="800px" append-to-body>
+      <el-descriptions :column="2" border v-if="statistics">
+        <el-descriptions-item label="站点ID">{{ statistics.siteId }}</el-descriptions-item>
+        <el-descriptions-item label="访问量">{{ statistics.visitCount || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="访客数">{{ statistics.visitorCount || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="转化数">{{ statistics.conversionCount || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="转化率">
+          {{ statistics.conversionRate ? (statistics.conversionRate * 100).toFixed(2) + '%' : '0%' }}
+        </el-descriptions-item>
+        <el-descriptions-item label="统计时间">{{ parseTime(statistics.updateTime) }}</el-descriptions-item>
+      </el-descriptions>
+      <div v-else style="text-align: center; padding: 40px 0;">
+        <el-empty description="暂无统计数据"></el-empty>
+      </div>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="statisticsOpen = false">关 闭</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 生成投放url对话框 -->
+    <el-dialog title="生成投放url" :visible.sync="urlDialogOpen" width="500px" append-to-body>
+      <el-form label-width="100px">
+        <el-form-item label="投放链接">
+          <div class="url-container">
+            <el-input 
+              v-model="launchUrl" 
+              readonly
+              class="url-input"
+            />
+            <el-button 
+              type="primary" 
+              size="small"
+              icon="el-icon-document-copy"
+              @click="copyUrl"
+            >复制</el-button>
+          </div>
+        </el-form-item>
+        <el-form-item label="二维码">
+          <div id="qrcode" class="qrcode-container"></div>
+          <el-button 
+            type="primary" 
+            size="small"
+            icon="el-icon-download"
+            @click="downloadQrcode"
+            style="margin-top: 10px;"
+          >下载</el-button>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="urlDialogOpen = false">关 闭</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listSite, getSite, addSite, updateSite, delSite, getSiteStatistics, enableSite } from "@/api/adv/site";
+import { pageAdvertiser } from "@/api/adv/advertiser";
+import { pagePromotionAccount } from "@/api/adv/promotionAccount";
+import { pageTemplate } from "@/api/adv/landingPageTemplate";
+import QRCode from 'qrcode';
+
+import { pageCallbackAccount, getCallbackAccount, queryEventType, saveEventType } from "@/api/adv/callbackAccount";
+import { pageDomain } from "@/api/adv/domain";
+import { pageProject as pageChannel } from "@/api/adv/channel";
+import { pageProject } from "@/api/adv/project";
+import { pageAssignRule } from "@/api/qw/assignRule";
+import { pageGroupLiveCode } from "@/api/qw/groupLiveCode";
+import { listContactWay } from "@/api/qw/contactWay";
+
+export default {
+  name: "Site",
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 站点表格数据
+      siteList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 是否为详情模式(只读)
+      isDetail: false,
+      // 是否显示详情对话框
+      detailOpen: false,
+      // 详情数据
+      detail: {},
+      // 是否显示统计弹出层
+      statisticsOpen: false,
+      // 统计数据
+      statistics: null,
+      // 表单参数
+      form: {},
+      // 投放类型选项
+      launchTypeOptions: [
+        { label: "线上投放", value: 1 },
+        { label: "线下投放", value: 2 }
+      ],
+      // 广告类型选项
+      adTypeOptions: [
+        { label: "信息流广告", value: "信息流广告" }
+      ],
+      // 是否配置回传选项
+      configCallbackOptions: [
+        { label: "否", value: 0 },
+        { label: "是", value: 1 }
+      ],
+      // 广告商列表
+      advertiserList: [],
+      // 广告商账号列表
+      promotionAccountList: [],
+      // 落地页模板列表
+      landingPageTemplateList: [],
+      // 企业微信分配规则列表
+      allocationRuleList: [],
+      // 群活码列表
+      groupActiveList: [],
+      // 企业微信活码列表
+      contactWayList: [],
+      // 回传账号列表
+      callbackAccountList: [],
+      // 投放域名列表
+      launchDomainList: [],
+      // 渠道列表
+      channelList: [],
+      // 项目列表
+      projectList: [],
+      // 转换事件列表
+      conversionEvents: [],
+      // 广告商事件选项(systemBuiltin='0')
+      advertiserEventOptions: [],
+      // 系统事件选项(systemBuiltin='1')
+      systemEventOptions: [],
+      // 生成url对话框
+      urlDialogOpen: false,
+      // 投放链接
+      launchUrl: "",
+      // 表单校验
+      rules: {
+        siteName: [
+          { required: true, message: "站点名称不能为空", trigger: "blur" }
+        ],
+        siteUrl: [
+          { required: true, message: "站点地址不能为空", trigger: "blur" }
+        ],
+        launchType: [
+          { required: true, message: "请选择投放类型", trigger: "change" }
+        ],
+        adType: [
+          { required: true, message: "请选择广告类型", trigger: "change" }
+        ],
+        advertiserId: [
+          { required: true, message: "请选择投放广告商", trigger: "change" }
+        ],
+        promotionAccountId: [
+          { required: true, message: "请选择投放广告商账号", trigger: "change" }
+        ],
+        launchPageId: [
+          { required: true, message: "请选择投放页面", trigger: "change" }
+        ],
+
+        launchDomain: [
+          { required: true, message: "请选择投放域名", trigger: "change" }
+        ],
+        configCallback: [
+          { required: true, message: "请选择是否配置回传", trigger: "change" }
+        ],
+        callbackAccountId: [
+          { 
+            validator: (rule, value, callback) => {
+              if (this.form.configCallback === 1 && !value) {
+                callback(new Error('请选择回传账号'));
+              } else {
+                callback();
+              }
+            }, 
+            trigger: "change" 
+          }
+        ]
+      }
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询站点列表 */
+    getList() {
+      this.loading = true;
+      listSite().then(response => {
+        this.siteList = response.data;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: undefined,
+        siteName: undefined,
+        siteUrl: undefined,
+        launchType: undefined,
+        adType: undefined,
+        advertiserId: undefined,
+        advertiserName: undefined,
+        promotionAccountId: undefined,
+        promotionAccountName: undefined,
+        launchPageId: undefined,
+        launchPageName: undefined,
+        projectId: undefined,
+        projectName: undefined,
+        channelId: undefined,
+        channelName: undefined,
+        launchDomain: undefined,
+        configCallback: 0,
+        callbackAccountId: undefined,
+        callbackAccountName: undefined,
+        allocationRule: undefined,
+        allocationRuleId: undefined
+      };
+      this.promotionAccountList = [];
+      this.launchDomainList = [];
+      this.allocationRuleList = [];
+      this.groupActiveList = [];
+      this.contactWayList = [];
+      this.conversionEvents = [];
+      this.advertiserEventOptions = [];
+      this.systemEventOptions = [];
+      this.resetForm("form");
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      // 不再预加载广告商列表,等待选择投放类型后再加载
+      this.loadCommonSelectOptions();
+      this.isDetail = false;
+      this.open = true;
+      this.title = "添加站点";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      this.loadCommonSelectOptions();
+      this.isDetail = false;
+      getSite(row.id).then(response => {
+        this.form = response.data;
+        // 确保 allocationRule 是字符串类型
+        if (this.form.allocationRule !== undefined && this.form.allocationRule !== null) {
+          this.form.allocationRule = String(this.form.allocationRule);
+        }
+        // 根据投放类型加载广告商列表
+        if (this.form.launchType) {
+          this.loadAdvertiserList(this.form.launchType);
+        }
+        // 如果有广告商ID,加载对应的广告商账号列表
+        if (this.form.advertiserId) {
+          this.loadPromotionAccountList(this.form.advertiserId);
+          
+          // 如果配置了回传,加载回传账号列表
+          if (this.form.configCallback === 1) {
+            this.loadCallbackAccountList(this.form.advertiserId);
+            
+            // 如果有回传账号,加载事件类型和转换事件
+            if (this.form.callbackAccountId) {
+              this.loadEventTypesAndConversionEvents(this.form.advertiserId, this.form.callbackAccountId);
+            }
+          }
+        }
+        // 如果设置了企微分配规则,加载对应的数据
+        if (this.form.allocationRule) {
+          if (this.form.allocationRule === '1') {
+            // 加载企业微信分配规则列表
+            this.loadAllocationRuleList();
+          } else if (this.form.allocationRule === '2') {
+            // 加载活码列表(根据投放类型)
+            if (this.form.launchType === 1) {
+              this.loadGroupActiveList();
+            } else if (this.form.launchType === 2) {
+              this.loadContactWayList();
+            }
+          }
+        }
+        this.$nextTick(() => {
+          this.open = true;
+          this.title = "修改站点";
+        });
+      });
+    },
+    /** 查看统计按钮操作 */
+    handleStatistics(row) {
+      this.$router.push({
+        path: '/adv/new-adv/statistics',
+        query: { siteId: row.id }
+      });
+    },
+    /** 详情按钮操作 */
+    handleDetail(row) {
+      this.detail = {};
+      this.detailOpen = true;
+      getSite(row.id).then(response => {
+        this.detail = response.data;
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          // 如果配置了回传且有转换事件,校验转换事件
+          if (this.form.configCallback === 1 && this.form.callbackAccountId && this.conversionEvents.length > 0) {
+            for (let i = 0; i < this.conversionEvents.length; i++) {
+              const event = this.conversionEvents[i];
+              if (!event.systemEventType || !event.advertiserEventType) {
+                this.msgError(`请完善事件${i + 1}的配置`);
+                return;
+              }
+            }
+          }
+          
+          if (this.form.id != undefined) {
+            // 修改
+            updateSite(this.form.id, this.form).then(response => {
+              if (response.code === 200) {
+                // 如果配置了回传,保存转换事件
+                if (this.form.configCallback === 1 && this.form.callbackAccountId) {
+                  this.saveConversionEvents();
+                } else {
+                  this.msgSuccess("修改成功");
+                  this.open = false;
+                  this.getList();
+                }
+              }
+            });
+          } else {
+            // 新增
+            addSite(this.form).then(response => {
+              if (response.code === 200) {
+                // 如果配置了回传,保存转换事件
+                if (this.form.configCallback === 1 && this.form.callbackAccountId) {
+                  this.saveConversionEvents();
+                } else {
+                  this.msgSuccess("新增成功");
+                  this.open = false;
+                  this.getList();
+                }
+              }
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      this.$confirm('是否确认删除站点"' + row.siteName + '"?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        return delSite(row.id);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("删除成功");
+      }).catch(function() {});
+    },
+    /** 启用/停用按鑵操作 */
+    handleEnable(row) {
+      const statusText = row.status === 1 ? '停用' : '启用';
+      this.$confirm(`是否确认${statusText}站点"${row.siteName}"?`, "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        return enableSite(row.id);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess(`${statusText}成功`);
+      }).catch(function() {});
+    },
+    /** 生成投放url */
+    handleGenerateUrl(row) {
+      this.launchUrl = row.siteUrl || '';
+      this.urlDialogOpen = true;
+      this.$nextTick(() => {
+        this.generateQrcode();
+      });
+    },
+    /** 生成二维码 */
+    generateQrcode() {
+      const qrcodeElement = document.getElementById('qrcode');
+      if (qrcodeElement) {
+        qrcodeElement.innerHTML = '';
+        // 创建 canvas 元素
+        const canvas = document.createElement('canvas');
+        QRCode.toCanvas(canvas, this.launchUrl, {
+          errorCorrectionLevel: 'H',
+          type: 'image/jpeg',
+          quality: 0.95,
+          margin: 1,
+          width: 200
+        }).then(() => {
+          qrcodeElement.appendChild(canvas);
+        }).catch(error => {
+          console.error('生成二维码失败:', error);
+          this.msgError('生成二维码失败');
+        });
+      }
+    },
+    /** 复制url */
+    copyUrl() {
+      if (!this.launchUrl) {
+        this.msgError('投放链接为空');
+        return;
+      }
+      navigator.clipboard.writeText(this.launchUrl).then(() => {
+        this.msgSuccess('复制成功');
+      }).catch(() => {
+        // 平台不支持新API,改成传统方法
+        const textarea = document.createElement('textarea');
+        textarea.value = this.launchUrl;
+        document.body.appendChild(textarea);
+        textarea.select();
+        document.execCommand('copy');
+        document.body.removeChild(textarea);
+        this.msgSuccess('复制成功');
+      });
+    },
+    /** 下载二维码 */
+    downloadQrcode() {
+      const qrcodeCanvas = document.querySelector('#qrcode canvas');
+      if (qrcodeCanvas) {
+        const link = document.createElement('a');
+        link.href = qrcodeCanvas.toDataURL();
+        link.download = `qrcode_${new Date().getTime()}.png`;
+        link.click();
+        this.msgSuccess('下载成功');
+      } else {
+        this.msgError('没有找到二维码');
+      }
+    },
+    /** 加载下拉选项数据 */
+    loadSelectOptions() {
+      // 加载广告商列表(已废弃,改用loadAdvertiserList)
+      pageAdvertiser({ pageNum: 1, pageSize: 1000 }).then(response => {
+        this.advertiserList = response.data.records;
+      }).catch(error => {
+        console.error('加载广告商列表失败:', error);
+        this.advertiserList = [];
+      });
+      
+      // 加载落地页模板列表
+      pageTemplate({ pageNum: 1, pageSize: 1000, status: 1 }).then(response => {
+        this.landingPageTemplateList = response.data.records;
+      }).catch(error => {
+        console.error('加载落地页模板失败:', error);
+        this.landingPageTemplateList = [];
+      });
+      
+      // 加载域名列表
+      pageDomain({ pageNum: 1, pageSize: 1000, status: 1 }).then(response => {
+        this.launchDomainList = response.data.records;
+      }).catch(error => {
+        console.error('加载域名失败:', error);
+        this.launchDomainList = [];
+      });
+    },
+    /** 加载公共下拉选项数据(不包含广告商) */
+    loadCommonSelectOptions() {
+      // 加载落地页模板列表
+      pageTemplate({ pageNum: 1, pageSize: 1000, status: 1 }).then(response => {
+        this.landingPageTemplateList = response.data.records;
+      }).catch(error => {
+        console.error('加载落地页模板失败:', error);
+        this.landingPageTemplateList = [];
+      });
+      
+      // 加载域名列表
+      pageDomain({ pageNum: 1, pageSize: 1000, status: 1 }).then(response => {
+        this.launchDomainList = response.data.records;
+      }).catch(error => {
+        console.error('加载域名失败:', error);
+        this.launchDomainList = [];
+      });
+      
+      // 加载渠道列表
+      pageChannel({ pageNum: 1, pageSize: 1000 }).then(response => {
+        this.channelList = response.data.records || [];
+      }).catch(error => {
+        console.error('加载渠道列表失败:', error);
+        this.channelList = [];
+      });
+      
+      // 加载项目列表
+      pageProject({ pageNum: 1, pageSize: 1000 }).then(response => {
+        this.projectList = response.data.records || [];
+      }).catch(error => {
+        console.error('加载项目列表失败:', error);
+        this.projectList = [];
+      });
+    },
+    /** 投放类型变化时 */
+    handleLaunchTypeChange(launchType) {
+      // 重置广告商相关数据
+      this.form.advertiserId = undefined;
+      this.form.advertiserName = "";
+      this.form.promotionAccountId = undefined;
+      this.form.promotionAccountName = "";
+      this.advertiserList = [];
+      this.promotionAccountList = [];
+      
+      // 重置回传账号相关数据
+      this.form.callbackAccountId = undefined;
+      this.form.callbackAccountName = "";
+      this.callbackAccountList = [];
+      this.conversionEvents = [];
+      this.advertiserEventOptions = [];
+      this.systemEventOptions = [];
+      
+      // 重置企业微信分配规则相关数据
+      this.form.allocationRule = undefined;
+      this.form.allocationRuleId = undefined;
+      this.allocationRuleList = [];
+      this.groupActiveList = [];
+      this.contactWayList = [];
+      
+      // 根据投放类型加载广告商列表
+      if (launchType) {
+        this.loadAdvertiserList(launchType);
+      }
+      
+      // 根据投放类型重新加载活码数据(如果已选择活码分配)
+      if (this.form.allocationRule === '2') {
+        if (launchType === 1) {
+          this.loadGroupActiveList();
+        } else if (launchType === 2) {
+          this.loadContactWayList();
+        }
+      }
+    },
+    /** 加载广告商列表 */
+    loadAdvertiserList(launchType) {
+      if (!launchType) return;
+      // launchType: 1=线上投放, 2=线下投放
+      // custom: 1=线上广告商, 2=自定义广告商
+      const customValue = launchType; // launchType值直接对应custom值
+      pageAdvertiser({ 
+        pageNum: 1, 
+        pageSize: 1000, 
+        enabled: 1,
+        custom: customValue 
+      }).then(response => {
+        this.advertiserList = response.data.records;
+      }).catch(error => {
+        console.error('加载广告商列表失败:', error);
+        this.advertiserList = [];
+      });
+    },
+    /** 广告商变化时 */
+    handleAdvertiserChange(advertiserId) {
+      this.form.advertiserName = "";
+      this.form.promotionAccountId = undefined;
+      this.form.promotionAccountName = "";
+      this.promotionAccountList = [];
+      
+      // 重置回传账号相关数据
+      this.form.callbackAccountId = undefined;
+      this.form.callbackAccountName = "";
+      this.callbackAccountList = [];
+      
+      if (advertiserId) {
+        const advertiser = this.advertiserList.find(item => item.id === advertiserId);
+        if (advertiser) {
+          this.form.advertiserName = advertiser.advertiserName;
+        }
+        // 加载对应的广告商账号列表
+        this.loadPromotionAccountList(advertiserId);
+        
+        // 如果已选择配置回传,加载回传账号列表
+        if (this.form.configCallback === 1) {
+          this.loadCallbackAccountList(advertiserId);
+        }
+      }
+    },
+    /** 加载广告商账号列表 */
+    loadPromotionAccountList(advertiserId) {
+      if (!advertiserId) return;
+      pagePromotionAccount({ pageNum: 1, pageSize: 1000, advertiserId: advertiserId }).then(response => {
+        this.promotionAccountList = response.data.records;
+      }).catch(error => {
+        console.error('加载广告商账号失败:', error);
+        this.promotionAccountList = [];
+      });
+    },
+    /** 广告商账号变化时 */
+    handlePromotionAccountChange(promotionAccountId) {
+      this.form.promotionAccountName = "";
+      if (promotionAccountId) {
+        const account = this.promotionAccountList.find(item => item.id === promotionAccountId);
+        if (account) {
+          this.form.promotionAccountName = account.accountName;
+        }
+      }
+    },
+    /** 投放页面变化时 */
+    handleLaunchPageChange(launchPageId) {
+      this.form.launchPageName = "";
+      
+      if (launchPageId) {
+        const template = this.landingPageTemplateList.find(item => item.id === launchPageId);
+        if (template) {
+          this.form.launchPageName = template.templateName;
+        }
+      }
+    },
+    
+    /** 渠道变化时 */
+    handleChannelChange(channelId) {
+      this.form.channelName = "";
+      
+      if (channelId) {
+        const channel = this.channelList.find(item => item.id === channelId);
+        if (channel) {
+          this.form.channelName = channel.channelName;
+        }
+      }
+    },
+
+    /** 项目变化时 */
+    handleProjectChange(projectId) {
+      this.form.projectName = "";
+      
+      if (projectId) {
+        const project = this.projectList.find(item => item.id === projectId);
+        if (project) {
+          this.form.projectName = project.projectName;
+        }
+      }
+    },
+
+    /** 回传账号变化时 */
+    handleCallbackAccountChange(callbackAccountId) {
+      this.form.callbackAccountName = "";
+      this.conversionEvents = [];
+      this.advertiserEventOptions = [];
+      this.systemEventOptions = [];
+      
+      if (callbackAccountId) {
+        const account = this.callbackAccountList.find(item => item.id === callbackAccountId);
+        if (account) {
+          this.form.callbackAccountName = account.accountName;
+          // 加载事件类型和转换事件
+          this.loadEventTypesAndConversionEvents(this.form.advertiserId, callbackAccountId);
+        }
+      }
+    },
+    /** 配置回传变化时 */
+    handleConfigCallbackChange(value) {
+      // 重置回传账号
+      this.form.callbackAccountId = undefined;
+      this.form.callbackAccountName = "";
+      this.callbackAccountList = [];
+      this.conversionEvents = [];
+      this.advertiserEventOptions = [];
+      this.systemEventOptions = [];
+      
+      // 重置企微分配规则相关数据
+      this.form.allocationRule = undefined;
+      this.form.allocationRuleId = undefined;
+      this.allocationRuleList = [];
+      this.groupActiveList = [];
+      
+      // 如果选择配置回传,且已选择广告商,加载回传账号列表
+      if (value === 1 && this.form.advertiserId) {
+        this.loadCallbackAccountList(this.form.advertiserId);
+      }
+    },
+    /** 加载回传账号列表 */
+    loadCallbackAccountList(advertiserId) {
+      if (!advertiserId) return;
+      pageCallbackAccount({ pageNum: 1, pageSize: 1000, advertiserId: advertiserId }).then(response => {
+        this.callbackAccountList = response.data.records;
+      }).catch(error => {
+        console.error('加载回传账号列表失败:', error);
+        this.callbackAccountList = [];
+      });
+    },
+    /** 加载事件类型和转换事件 */
+    loadEventTypesAndConversionEvents(advertiserId, callbackAccountId) {
+      // 加载事件类型选项
+      queryEventType(advertiserId).then(response => {
+        const eventTypes = response.data || [];
+        // systemBuiltin='0' 为广告商事件
+        this.advertiserEventOptions = eventTypes.filter(item => item.systemBuiltin === '0');
+        // systemBuiltin='1' 为系统事件
+        this.systemEventOptions = eventTypes.filter(item => item.systemBuiltin === '1');
+      }).catch(error => {
+        console.error('加载事件类型失败:', error);
+      });
+      
+      // 加载已有的转换事件
+      getCallbackAccount(callbackAccountId).then(response => {
+        const account = response.data;
+        this.parseConversionEvents(account.conversionEvent);
+      }).catch(error => {
+        console.error('加载回传账号详情失败:', error);
+      });
+    },
+    /** 解析转换事件 */
+    parseConversionEvents(conversionEvent) {
+      try {
+        if (conversionEvent && typeof conversionEvent === 'string') {
+          this.conversionEvents = JSON.parse(conversionEvent);
+        } else if (Array.isArray(conversionEvent)) {
+          this.conversionEvents = conversionEvent;
+        } else {
+          this.conversionEvents = [];
+        }
+      } catch (error) {
+        console.error('解析转换事件失败:', error);
+        this.conversionEvents = [];
+      }
+    },
+    /** 添加转换事件 */
+    addConversionEvent() {
+      this.conversionEvents.push({
+        systemEventType: '',
+        systemEventTypeName: '',
+        advertiserEventType: '',
+        advertiserEventName: ''
+      });
+    },
+    /** 删除转换事件 */
+    removeConversionEvent(index) {
+      this.conversionEvents.splice(index, 1);
+    },
+    /** 广告商事件变化 */
+    handleAdvertiserEventChange(index, eventType) {
+      const event = this.advertiserEventOptions.find(item => item.eventType === eventType);
+      if (event) {
+        this.conversionEvents[index].advertiserEventName = event.eventName;
+      }
+    },
+    /** 系统事件变化 */
+    handleSystemEventChange(index, eventType) {
+      const event = this.systemEventOptions.find(item => item.eventType === eventType);
+      if (event) {
+        this.conversionEvents[index].systemEventTypeName = event.eventName;
+      }
+    },
+    /** 企微分配规则变化 */
+    handleAllocationRuleChange(allocationRule) {
+      // 重置需要加载的数据列表
+      this.form.allocationRuleId = undefined;
+      this.allocationRuleList = [];
+      this.groupActiveList = [];
+      this.contactWayList = [];
+      
+      // 根据不同的分配规则加载对应数据
+      if (allocationRule === '1') {
+        // 加载企业微信分配规则列表
+        this.loadAllocationRuleList();
+      } else if (allocationRule === '2') {
+        // 根据投放类型加载群活码或企业微信活码
+        if (this.form.launchType === 1) {
+          this.loadGroupActiveList();
+        } else if (this.form.launchType === 2) {
+          this.loadContactWayList();
+        }
+      }
+    },
+    /** 加载企业微信分配规则列表 */
+    loadAllocationRuleList() {
+      pageAssignRule({
+        pageNum: 1,
+        pageSize: 1000,
+        status: 1
+      }).then(response => {
+        this.allocationRuleList = response.data.records || [];
+      }).catch(error => {
+        console.error('加载企业微信分配规则列表失败:', error);
+        this.allocationRuleList = [];
+      });
+    },
+    /** 加载群活码列表 */
+    loadGroupActiveList() {
+      pageGroupLiveCode({
+        pageNum: 1,
+        pageSize: 1000,
+        status: 1
+      }).then(response => {
+        this.groupActiveList = response.data.records || [];
+      }).catch(error => {
+        console.error('加载群活码列表失败:', error);
+        this.groupActiveList = [];
+      });
+    },
+    /** 加载企业微信活码列表 */
+    loadContactWayList() {
+      // 调用/qw/contactWay/list接口获取企业微信活码数据
+      // 注意:此接口返回rows字段,不是data.records
+      listContactWay().then(response => {
+        this.contactWayList = response.rows || [];
+      }).catch(error => {
+        console.error('加载企业微信活码列表失败:', error);
+        this.contactWayList = [];
+      });
+    },
+    /** 保存转换事件 */
+    saveConversionEvents() {
+      // 调用保存接口
+      saveEventType(this.form.callbackAccountId, this.conversionEvents).then(response => {
+        if (response.code === 200) {
+          this.msgSuccess('保存成功');
+          this.open = false;
+          this.getList();
+        }
+      }).catch(error => {
+        console.error('保存失败:', error);
+        this.msgError('保存失败');
+      });
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.app-container {
+  padding: 20px;
+}
+
+// 对话框样式
+::v-deep .site-dialog {
+  .el-dialog__header {
+    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+    padding: 20px;
+    margin: 0;
+    border-radius: 4px 4px 0 0;
+    
+    .el-dialog__title {
+      color: #fff;
+      font-size: 18px;
+      font-weight: 500;
+    }
+    
+    .el-dialog__headerbtn .el-dialog__close {
+      color: #fff;
+      font-size: 20px;
+      
+      &:hover {
+        color: #f0f0f0;
+      }
+    }
+  }
+  
+  .el-dialog__body {
+    padding: 25px 30px;
+    background-color: #f8f9fa;
+  }
+  
+  .el-dialog__footer {
+    padding: 15px 30px;
+    border-top: 1px solid #e8e8e8;
+    background-color: #fff;
+  }
+}
+
+// 表单样式
+.elegant-form {
+  .form-section {
+    background: #fff;
+    border-radius: 8px;
+    padding: 20px;
+    margin-bottom: 20px;
+    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+    transition: all 0.3s ease;
+    
+    &:hover {
+      box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
+      transform: translateY(-2px);
+    }
+    
+    &:last-child {
+      margin-bottom: 0;
+    }
+  }
+  
+  .section-title {
+    display: flex;
+    align-items: center;
+    margin-bottom: 20px;
+    padding-bottom: 12px;
+    border-bottom: 2px solid #e8e8e8;
+    font-size: 15px;
+    font-weight: 600;
+    color: #303133;
+    
+    i {
+      font-size: 18px;
+      margin-right: 8px;
+      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+      -webkit-background-clip: text;
+      -webkit-text-fill-color: transparent;
+      background-clip: text;
+    }
+    
+    span {
+      position: relative;
+      
+      &::after {
+        content: '';
+        position: absolute;
+        left: 0;
+        bottom: -12px;
+        width: 0;
+        height: 2px;
+        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+        transition: width 0.3s ease;
+      }
+    }
+  }
+  
+  .form-section:hover .section-title span::after {
+    width: 100%;
+  }
+  
+  .el-form-item {
+    margin-bottom: 20px;
+    
+    &:last-child {
+      margin-bottom: 0;
+    }
+  }
+  
+  ::v-deep .el-form-item__label {
+    color: #606266;
+    font-weight: 500;
+    font-size: 14px;
+  }
+  
+  ::v-deep .el-input__inner,
+  ::v-deep .el-textarea__inner {
+    border-radius: 6px;
+    border: 1px solid #dcdfe6;
+    transition: all 0.3s ease;
+    
+    &:hover {
+      border-color: #c0c4cc;
+    }
+    
+    &:focus {
+      border-color: #667eea;
+      box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.1);
+    }
+  }
+  
+  ::v-deep .el-select {
+    .el-input__inner {
+      padding-left: 15px;
+    }
+  }
+  
+  ::v-deep .el-radio {
+    margin-right: 20px;
+    
+    &.is-bordered {
+      border-radius: 6px;
+      padding: 10px 20px;
+      transition: all 0.3s ease;
+      
+      &:hover {
+        border-color: #667eea;
+      }
+      
+      &.is-checked {
+        border-color: #667eea;
+        background-color: rgba(102, 126, 234, 0.05);
+      }
+    }
+  }
+  
+  // 回传账号显示动画
+  .slide-fade {
+    animation: slideDown 0.3s ease;
+  }
+}
+
+@keyframes slideDown {
+  from {
+    opacity: 0;
+    transform: translateY(-10px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+
+// 按钮样式优化
+.dialog-footer {
+  text-align: right;
+  
+  .el-button {
+    padding: 10px 24px;
+    border-radius: 6px;
+    font-weight: 500;
+    transition: all 0.3s ease;
+    
+    &.el-button--primary {
+      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+      border: none;
+      
+      &:hover {
+        transform: translateY(-2px);
+        box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
+      }
+      
+      &:active {
+        transform: translateY(0);
+      }
+    }
+    
+    &.el-button--default {
+      &:hover {
+        color: #667eea;
+        border-color: #667eea;
+        background-color: rgba(102, 126, 234, 0.05);
+      }
+    }
+  }
+}
+
+// 表格样式优化
+.el-table {
+  border-radius: 8px;
+  overflow: hidden;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+  
+  ::v-deep .el-table__header {
+    th {
+      background-color: #f5f7fa;
+      color: #606266;
+      font-weight: 600;
+      font-size: 14px;
+    }
+  }
+  
+  ::v-deep .el-table__row {
+    transition: all 0.3s ease;
+    
+    &:hover {
+      background-color: rgba(102, 126, 234, 0.05);
+    }
+  }
+}
+
+// 顶部按钮组样式
+.mb8 {
+  margin-bottom: 15px;
+  
+  .el-button {
+    border-radius: 6px;
+    padding: 10px 20px;
+    font-weight: 500;
+    transition: all 0.3s ease;
+    
+    &.el-button--primary {
+      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+      border: none;
+      
+      &:hover {
+        transform: translateY(-2px);
+        box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
+      }
+    }
+    
+    &.el-button--success {
+      &:hover {
+        transform: translateY(-2px);
+        box-shadow: 0 4px 12px rgba(103, 194, 58, 0.4);
+      }
+    }
+  }
+}
+
+// 转换类型配置区域样式
+.conversion-config-section {
+  margin-top: 20px;
+  padding: 20px;
+  background: #f8f9fa;
+  border-radius: 8px;
+  border: 1px solid #e8e8e8;
+  
+  .config-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 15px;
+    
+    .config-title {
+      font-size: 15px;
+      font-weight: 600;
+      color: #303133;
+    }
+  }
+  
+  .empty-tip {
+    text-align: center;
+    padding: 30px 0;
+    color: #909399;
+    font-size: 14px;
+  }
+}
+
+.conversion-event-item {
+  margin-bottom: 15px;
+  padding: 15px;
+  border: 1px solid #e8e8e8;
+  border-radius: 6px;
+  background-color: #fff;
+  transition: all 0.3s ease;
+  
+  &:hover {
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+  }
+  
+  .event-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 10px;
+    padding-bottom: 10px;
+    border-bottom: 1px solid #f0f0f0;
+    
+    .event-title {
+      font-weight: 600;
+      font-size: 14px;
+      color: #303133;
+    }
+  }
+}
+
+// 投放链接对话框样式
+.url-container {
+  display: flex;
+  gap: 10px;
+  align-items: center;
+  
+  .url-input {
+    flex: 1;
+  }
+}
+
+.qrcode-container {
+  display: flex;
+  justify-content: center;
+  padding: 20px 0;
+  
+  canvas {
+    border: 1px solid #e0e0e0;
+    border-radius: 4px;
+  }
+}
+</style>
+

+ 602 - 0
src/views/adv/statistics/index.vue

@@ -0,0 +1,602 @@
+<template>
+  <div class="app-container statistics-container">
+    <!-- 搜索筛选 -->
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" class="search-form">
+      <el-form-item label="站点" prop="siteId">
+        <el-select
+          v-model="queryParams.siteId"
+          placeholder="请选择站点"
+          clearable
+          size="small"
+          filterable
+        >
+          <el-option
+            v-for="item in siteList"
+            :key="item.id"
+            :label="item.siteName"
+            :value="item.id"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="广告商" prop="advertiserId">
+        <el-select
+          v-model="queryParams.advertiserId"
+          placeholder="请选择广告商"
+          clearable
+          size="small"
+          filterable
+        >
+          <el-option
+            v-for="item in advertiserList"
+            :key="item.id"
+            :label="item.advertiserName"
+            :value="item.id"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="日期范围" prop="dateRange">
+        <el-date-picker
+          v-model="queryParams.dateRange"
+          type="daterange"
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          format="yyyy-MM-dd"
+          value-format="yyyy-MM-dd"
+          size="small"
+          style="width: 280px"
+        />
+      </el-form-item>
+      <el-form-item>
+        <div class="date-quick-buttons">
+          <el-button type="primary" plain size="small" @click="setDateRange('today')">今日</el-button>
+          <el-button type="primary" plain size="small" @click="setDateRange('yesterday')">昨日</el-button>
+          <el-button type="primary" plain size="small" @click="setDateRange('week')">本周</el-button>
+          <el-button type="primary" plain size="small" @click="setDateRange('month')">本月</el-button>
+          <el-button type="primary" plain size="small" @click="setDateRange('30days')">30天</el-button>
+        </div>
+      </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
+          plain
+          type="success"
+          icon="el-icon-refresh"
+          size="mini"
+          @click="getList"
+        >刷新</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <!-- 数据表格 -->
+    <el-table border v-loading="loading" :data="statisticsList" class="stats-table">
+      <el-table-column label="站点ID" align="center" prop="siteId" width="80" />
+      <el-table-column label="站点名称" align="center" prop="siteName" min-width="150" show-overflow-tooltip />
+      
+      <!-- 流量数据 -->
+      <el-table-column label="流量数据" align="center">
+        <el-table-column label="PV" align="center" prop="pv" width="100">
+          <template slot-scope="scope">
+            <span class="number-text">{{ scope.row.pv || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="UV" align="center" prop="uv" width="100">
+          <template slot-scope="scope">
+            <span class="number-text">{{ scope.row.uv || 0 }}</span>
+          </template>
+        </el-table-column>
+      </el-table-column>
+
+      <!-- 广告数据 -->
+      <el-table-column label="广告数据" align="center">
+        <el-table-column label="展示数" align="center" prop="impressionCount" width="100">
+          <template slot-scope="scope">
+            <span class="number-text">{{ scope.row.impressionCount || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="点击数" align="center" prop="clickCount" width="100">
+          <template slot-scope="scope">
+            <span class="number-text">{{ scope.row.clickCount || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="点击率(%)" align="center" prop="clickRate" width="100">
+          <template slot-scope="scope">
+            <span class="rate-text">{{ scope.row.clickRate || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="平均点击单价" align="center" prop="avgClickPrice" width="120">
+          <template slot-scope="scope">
+            <span class="money-text">¥{{ scope.row.avgClickPrice || 0 }}</span>
+          </template>
+        </el-table-column>
+      </el-table-column>
+
+      <!-- 成本数据 -->
+      <el-table-column label="成本数据" align="center">
+        <el-table-column label="账面花费" align="center" prop="accountCost" width="120">
+          <template slot-scope="scope">
+            <span class="money-text">¥{{ scope.row.accountCost || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="实际花费" align="center" prop="actualCost" width="120">
+          <template slot-scope="scope">
+            <span class="money-text primary">¥{{ scope.row.actualCost || 0 }}</span>
+          </template>
+        </el-table-column>
+      </el-table-column>
+
+      <!-- 转化数据 -->
+      <el-table-column label="转化数据" align="center">
+        <el-table-column label="名片数" align="center" prop="cardCount" width="100">
+          <template slot-scope="scope">
+            <span class="number-text">{{ scope.row.cardCount || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="名片获取率(%)" align="center" prop="cardAcquireRate" width="120">
+          <template slot-scope="scope">
+            <span class="rate-text">{{ scope.row.cardAcquireRate || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="名片获取成本" align="center" prop="cardAcquireCost" width="120">
+          <template slot-scope="scope">
+            <span class="money-text">¥{{ scope.row.cardAcquireCost || 0 }}</span>
+          </template>
+        </el-table-column>
+      </el-table-column>
+
+      <!-- 企微数据 -->
+      <el-table-column label="企微数据" align="center">
+        <el-table-column label="企微添加人数" align="center" prop="wechatAddCount" width="120">
+          <template slot-scope="scope">
+            <span class="number-text">{{ scope.row.wechatAddCount || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="企微添加率(%)" align="center" prop="wechatAddRate" width="120">
+          <template slot-scope="scope">
+            <span class="rate-text">{{ scope.row.wechatAddRate || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="企微添加成本" align="center" prop="wechatAddCost" width="120">
+          <template slot-scope="scope">
+            <span class="money-text">¥{{ scope.row.wechatAddCost || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="企微加群人数" align="center" prop="wechatGroupCount" width="120">
+          <template slot-scope="scope">
+            <span class="number-text">{{ scope.row.wechatGroupCount || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="企微加群率(%)" align="center" prop="wechatGroupRate" width="120">
+          <template slot-scope="scope">
+            <span class="rate-text">{{ scope.row.wechatGroupRate || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="企微删除人数" align="center" prop="wechatDeleteCount" width="120">
+          <template slot-scope="scope">
+            <span class="number-text">{{ scope.row.wechatDeleteCount || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="企微删除率" align="center" prop="wechatDeleteCountRate" width="120">
+          <template slot-scope="scope">
+            <span class="rate-text">{{ scope.row.wechatDeleteCountRate || 0 }}</span>
+          </template>
+        </el-table-column>
+      </el-table-column>
+
+      <!-- 小程序数据 -->
+      <el-table-column label="小程序数据" align="center">
+        <el-table-column label="发起进入小程序人数" align="center" prop="miniLaunchIndexCount" width="140">
+          <template slot-scope="scope">
+            <span class="number-text">{{ scope.row.miniLaunchIndexCount || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="进入小程序落地页" align="center" prop="miniAuthIndexCount" width="140">
+          <template slot-scope="scope">
+            <span class="number-text">{{ scope.row.miniAuthIndexCount || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="小程序授权人数" align="center" prop="miniAuthCount" width="140">
+          <template slot-scope="scope">
+            <span class="number-text">{{ scope.row.miniAuthCount || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="进入出码页人数" align="center" prop="miniQrCodeIndexCount" width="140">
+          <template slot-scope="scope">
+            <span class="number-text">{{ scope.row.miniQrCodeIndexCount || 0 }}</span>
+          </template>
+        </el-table-column>
+      </el-table-column>
+
+      <!-- 下次转化数据 -->
+      <el-table-column label="下次转化数据" align="center">
+        <el-table-column label="报名成功人数" align="center" prop="registerSuccessCount" width="120">
+          <template slot-scope="scope">
+            <span class="number-text">{{ scope.row.registerSuccessCount || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="报名成功加微率(%)" align="center" prop="registerWechatRate" width="140">
+          <template slot-scope="scope">
+            <span class="rate-text">{{ scope.row.registerWechatRate || 0 }}</span>
+          </template>
+        </el-table-column>
+      </el-table-column>
+
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180" fixed="right">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime) }}</span>
+        </template>
+      </el-table-column>
+
+      <el-table-column label="操作" align="center" width="120" fixed="right">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-view"
+            @click="handleDetail(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="统计详情" :visible.sync="detailOpen" width="900px" append-to-body class="detail-dialog">
+      <el-descriptions :column="2" border>
+        <el-descriptions-item label="站点ID">{{ detail.siteId || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="站点名称">{{ detail.siteName || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="PV">{{ detail.pv || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="UV">{{ detail.uv || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="展示数">{{ detail.impressionCount || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="点击数">{{ detail.clickCount || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="点击率">{{ detail.clickRate || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="平均点击单价">¥{{ detail.avgClickPrice || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="账面花费">¥{{ detail.accountCost || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="实际花费">¥{{ detail.actualCost || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="名片数">{{ detail.cardCount || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="名片获取率">{{ detail.cardAcquireRate || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="名片获取成本">¥{{ detail.cardAcquireCost || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="企微添加人数">{{ detail.wechatAddCount || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="企微添加率">{{ detail.wechatAddRate || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="企微添加成本">¥{{ detail.wechatAddCost || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="企微加群人数">{{ detail.wechatGroupCount || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="企微加群率">{{ detail.wechatGroupRate || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="报名成功人数">{{ detail.registerSuccessCount || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="报名成功加微率">{{ detail.registerWechatRate || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="企微删除率">{{ detail.wechatDeleteCountRate || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="创建时间">{{ parseTime(detail.createTime) }}</el-descriptions-item>
+        <el-descriptions-item label="更新时间">{{ parseTime(detail.updateTime) }}</el-descriptions-item>
+      </el-descriptions>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="detailOpen = false">关 闭</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { pageSiteStatistics, getSiteStatistics } from "@/api/adv/siteStatistics";
+import { listSite } from "@/api/adv/site";
+import { pageAdvertiser } from "@/api/adv/advertiser";
+
+export default {
+  name: "SiteStatistics",
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 统计列表
+      statisticsList: [],
+      // 站点列表
+      siteList: [],
+      // 详情对话框
+      detailOpen: false,
+      // 详情数据
+      detail: {},
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        siteId: undefined,
+        advertiserId: undefined,
+        startDate: undefined,
+        endDate: undefined,
+        dateRange: []
+      },
+      // 广告商列表
+      advertiserList: []
+    };
+  },
+  created() {
+    // 从 URL参数获取站点ID
+    const siteId = this.$route.query.siteId;
+    if (siteId) {
+      this.queryParams.siteId = parseInt(siteId);
+    }
+    // 默认设置今天的日期范围
+    const today = new Date();
+    const dateStr = today.getFullYear() + '-' + String(today.getMonth() + 1).padStart(2, '0') + '-' + String(today.getDate()).padStart(2, '0');
+    this.queryParams.startDate = dateStr;
+    this.queryParams.endDate = dateStr;
+    this.queryParams.dateRange = [dateStr, dateStr];
+    this.getSiteList();
+    this.getAdvertiserList();
+    this.getList();
+  },
+  methods: {
+    /** 查询站点列表 */
+    getSiteList() {
+      listSite().then(response => {
+        this.siteList = response.data || [];
+      });
+    },
+    /** 查询广告商列表 */
+    getAdvertiserList() {
+      pageAdvertiser({ pageNum: 1, pageSize: 1000 }).then(response => {
+        this.advertiserList = response.data.records || [];
+      }).catch(error => {
+        console.error('查询广告商列表失败:', error);
+        this.advertiserList = [];
+      });
+    },
+    /** 设置日期范围 */
+    setDateRange(rangeType) {
+      const today = new Date();
+      let startDate, endDate;
+      
+      if (rangeType === 'today') {
+        startDate = new Date(today);
+        endDate = new Date(today);
+      } else if (rangeType === 'yesterday') {
+        startDate = new Date(today.getTime() - 24 * 60 * 60 * 1000);
+        endDate = new Date(today.getTime() - 24 * 60 * 60 * 1000);
+      } else if (rangeType === 'week') {
+        const dayOfWeek = today.getDay();
+        const diff = today.getDate() - dayOfWeek + (dayOfWeek === 0 ? -6 : 1);
+        startDate = new Date(today.getFullYear(), today.getMonth(), diff);
+        endDate = new Date(today);
+      } else if (rangeType === 'month') {
+        startDate = new Date(today.getFullYear(), today.getMonth(), 1);
+        endDate = new Date(today);
+      } else if (rangeType === '30days') {
+        startDate = new Date(today.getTime() - 29 * 24 * 60 * 60 * 1000);
+        endDate = new Date(today);
+      }
+      
+      const formatDate = (date) => {
+        return date.getFullYear() + '-' + String(date.getMonth() + 1).padStart(2, '0') + '-' + String(date.getDate()).padStart(2, '0');
+      };
+      
+      this.queryParams.startDate = formatDate(startDate);
+      this.queryParams.endDate = formatDate(endDate);
+      this.queryParams.dateRange = [this.queryParams.startDate, this.queryParams.endDate];
+    },
+    /** 查询统计列表 */
+    getList() {
+      this.loading = true;
+      const params = {
+        pageNum: this.queryParams.pageNum,
+        pageSize: this.queryParams.pageSize,
+        siteId: this.queryParams.siteId,
+        advertiserId: this.queryParams.advertiserId,
+        startDate: this.queryParams.startDate,
+        endDate: this.queryParams.endDate
+      };
+      pageSiteStatistics(params).then(response => {
+        this.statisticsList = response.data.records || [];
+        this.total = response.data.total || 0;
+        this.loading = false;
+      }).catch((error) => {
+        console.error('查询统计列表失败:', error);
+        this.loading = false;
+      });
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      // 不会会改版面pageNum,维持已有的pageNum
+      if (this.queryParams.dateRange && this.queryParams.dateRange.length === 2) {
+        this.queryParams.startDate = this.queryParams.dateRange[0];
+        this.queryParams.endDate = this.queryParams.dateRange[1];
+      }
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      // 重新设置为today是不是更好?这里应该是按照简易逻辑重新设置日期
+      const today = new Date();
+      const dateStr = today.getFullYear() + '-' + String(today.getMonth() + 1).padStart(2, '0') + '-' + String(today.getDate()).padStart(2, '0');
+      this.queryParams.startDate = dateStr;
+      this.queryParams.endDate = dateStr;
+      this.queryParams.dateRange = [dateStr, dateStr];
+      this.handleQuery();
+    },
+    /** 详情按钮操作 */
+    handleDetail(row) {
+      getSiteStatistics(row.id).then(response => {
+        this.detail = response.data;
+        this.detailOpen = true;
+      });
+    },
+    /** 获取ROI标签类型 */
+    getRoiType(roi) {
+      const value = parseFloat(roi || 0);
+      if (value >= 3) return 'success';
+      if (value >= 2) return 'warning';
+      if (value >= 1) return 'info';
+      return 'danger';
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.statistics-container {
+  padding: 20px;
+}
+
+// 搜索表单样式
+.search-form {
+  background: #fff;
+  padding: 20px;
+  border-radius: 8px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+  margin-bottom: 20px;
+}
+
+// 表格样式
+.stats-table {
+  border-radius: 8px;
+  overflow: hidden;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+  
+  .number-text {
+    color: #409eff;
+    font-weight: 500;
+  }
+  
+  .rate-text {
+    color: #e6a23c;
+    font-weight: 500;
+  }
+  
+  .money-text {
+    color: #f56c6c;
+    font-weight: 500;
+    
+    &.primary {
+      color: #409eff;
+    }
+    
+    &.success {
+      color: #67c23a;
+    }
+  }
+  
+  ::v-deep .el-table__header {
+    th {
+      background-color: #f5f7fa;
+      color: #606266;
+      font-weight: 600;
+      font-size: 13px;
+    }
+  }
+  
+  ::v-deep .el-table__row {
+    transition: all 0.3s ease;
+    
+    &:hover {
+      background-color: rgba(102, 126, 234, 0.05);
+    }
+  }
+}
+
+// 工具栏样式
+.mb8 {
+  margin-bottom: 15px;
+  
+  .el-button {
+    border-radius: 6px;
+    padding: 10px 20px;
+    font-weight: 500;
+    transition: all 0.3s ease;
+    
+    &.el-button--success {
+      &:hover {
+        transform: translateY(-2px);
+        box-shadow: 0 4px 12px rgba(103, 194, 58, 0.4);
+      }
+    }
+  }
+}
+
+// 日期快速设置按钮样式
+.date-quick-buttons {
+  display: flex;
+  gap: 12px;
+  flex-wrap: wrap;
+  
+  .el-button {
+    padding: 8px 18px;
+    font-size: 13px;
+    font-weight: 500;
+    border-radius: 6px;
+    transition: all 0.3s ease;
+    
+    &:hover {
+      transform: translateY(-2px);
+      box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
+    }
+    
+    &:active {
+      transform: translateY(0);
+    }
+  }
+}
+
+// 详情对话框样式
+::v-deep .detail-dialog {
+  .el-dialog__header {
+    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+    padding: 20px;
+    margin: 0;
+    border-radius: 4px 4px 0 0;
+    
+    .el-dialog__title {
+      color: #fff;
+      font-size: 18px;
+      font-weight: 500;
+    }
+    
+    .el-dialog__headerbtn .el-dialog__close {
+      color: #fff;
+      font-size: 20px;
+      
+      &:hover {
+        color: #f0f0f0;
+      }
+    }
+  }
+  
+  .el-dialog__body {
+    padding: 25px 30px;
+  }
+  
+  .el-descriptions {
+    ::v-deep .el-descriptions-item__label {
+      font-weight: 600;
+      color: #606266;
+      background-color: #fafafa;
+    }
+  }
+}
+
+.dialog-footer {
+  text-align: right;
+  
+  .el-button {
+    padding: 10px 24px;
+    border-radius: 6px;
+    font-weight: 500;
+  }
+}
+</style>

+ 211 - 0
src/views/adv/trackingLink/index.vue

@@ -0,0 +1,211 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch">
+      <el-form-item label="链接名称" prop="trackingName">
+        <el-input
+          v-model="queryParams.trackingName"
+          placeholder="请输入链接名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="广告商" prop="advertiserId">
+        <el-select v-model="queryParams.advertiserId" placeholder="请选择广告商" clearable size="small" filterable>
+          <el-option
+            v-for="item in advertiserOptions"
+            :key="item.id"
+            :label="item.advertiserName"
+            :value="item.id"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="推广类型" prop="promotionType">
+        <el-input
+          v-model="queryParams.promotionType"
+          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-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          plain
+          type="success"
+          icon="el-icon-refresh"
+          size="mini"
+          @click="getList"
+        >刷新</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table border v-loading="loading" :data="linkList">
+      <el-table-column label="ID" align="center" prop="id" width="80" />
+      <el-table-column label="链接名称" align="center" prop="trackingName" show-overflow-tooltip />
+      <el-table-column label="广告商" align="center" prop="advertiserName" width="200" show-overflow-tooltip />
+      <el-table-column label="推广类型" align="center" prop="promotionType" width="120" />
+      <el-table-column label="监测链接" align="center" prop="trackingUrl" show-overflow-tooltip min-width="200" />
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="150">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-view"
+            @click="handleView(scope.row)"
+          >详情</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-document-copy"
+            @click="handleCopy(scope.row)"
+          >复制链接</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.current"
+      :limit.sync="queryParams.size"
+      @pagination="getList"
+    />
+
+    <!-- 详情对话框 -->
+    <el-dialog title="监测链接详情" :visible.sync="open" width="700px" append-to-body>
+      <el-descriptions :column="1" border v-if="detail">
+        <el-descriptions-item label="链接ID">{{ detail.id }}</el-descriptions-item>
+        <el-descriptions-item label="链接名称">{{ detail.trackingName }}</el-descriptions-item>
+        <el-descriptions-item label="广告商">{{ detail.advertiserName || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="推广类型">{{ detail.promotionType }}</el-descriptions-item>
+        <el-descriptions-item label="监测链接">
+          <el-link :href="detail.trackingUrl" target="_blank" type="primary">{{ detail.trackingUrl }}</el-link>
+        </el-descriptions-item>
+        <el-descriptions-item label="创建时间">{{ detail.createTime }}</el-descriptions-item>
+        <el-descriptions-item label="更新时间">{{ detail.updateTime }}</el-descriptions-item>
+      </el-descriptions>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="handleCopy(detail)">复制链接</el-button>
+        <el-button @click="open = false">关 闭</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { pageTrackingLink, getTrackingLink } from "@/api/adv/trackingLink";
+import { pageAdvertiser } from "@/api/adv/advertiser";
+
+export default {
+  name: "TrackingLink",
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 监测链接表格数据
+      linkList: [],
+      // 广告商选项
+      advertiserOptions: [],
+      // 是否显示弹出层
+      open: false,
+      // 详情数据
+      detail: null,
+      // 查询参数
+      queryParams: {
+        current: 1,
+        size: 10,
+        trackingName: undefined,
+        advertiserId: undefined,
+        promotionType: undefined
+      }
+    };
+  },
+  created() {
+    this.getList();
+    this.getAdvertiserOptions();
+  },
+  methods: {
+    /** 查询广告商选项 */
+    getAdvertiserOptions() {
+      pageAdvertiser({ current: 1, size: 1000, enabled: 1 }).then(response => {
+        this.advertiserOptions = response.data.records;
+      });
+    },
+    /** 获取广告商名称 */
+    getAdvertiserName(advertiserId) {
+      const advertiser = this.advertiserOptions.find(item => item.id === advertiserId);
+      return advertiser ? advertiser.advertiserName : advertiserId;
+    },
+    /** 查询监测链接列表 */
+    getList() {
+      this.loading = true;
+      pageTrackingLink(this.queryParams).then(response => {
+        this.linkList = response.data.records;
+        this.total = response.data.total;
+        this.loading = false;
+      });
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.current = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    /** 查看详情按钮操作 */
+    handleView(row) {
+      this.detail = null;
+      this.open = true;
+      getTrackingLink(row.id).then(response => {
+        this.detail = response.data;
+      });
+    },
+    /** 复制链接按钮操作 */
+    handleCopy(row) {
+      const url = row.trackingUrl;
+      if (!url) {
+        this.msgError("链接地址为空");
+        return;
+      }
+      
+      // 创建临时input元素
+      const input = document.createElement('input');
+      input.value = url;
+      document.body.appendChild(input);
+      input.select();
+      
+      try {
+        // 执行复制
+        const successful = document.execCommand('copy');
+        if (successful) {
+          this.msgSuccess("链接已复制到剪贴板");
+        } else {
+          this.msgError("复制失败,请手动复制");
+        }
+      } catch (err) {
+        this.msgError("复制失败,请手动复制");
+      }
+      
+      // 移除临时元素
+      document.body.removeChild(input);
+    }
+  }
+};
+</script>
+

+ 2 - 1
src/views/company/companyUser/index.vue

@@ -1422,7 +1422,8 @@ export default {
     /** 删除按钮操作 */
     handleDelete(row) {
 
-      const userIds = row.userId || this.ids;
+      // 确保 userIds 始终是数组
+      const userIds = row.userId ? [row.userId] : this.ids;
 
       // 筛选出 userType 为 '00' 的 userId
       const excludedUserIds = this.userList

+ 85 - 20
src/views/components/course/userCourseCatalogDetails.vue

@@ -58,6 +58,10 @@
                      type="text" @click="openTagDialog(scope.row)">
             绑定看课标签
           </el-button>
+          <el-button size="mini"
+                     type="text" v-if="projectFrom === 'hzyy'" @click="openDialog(scope.row)">
+            复制看课链接
+          </el-button>
         </template>
       </el-table-column>
     </el-table>
@@ -92,20 +96,24 @@
       title="生成链接"
       :visible.sync="dialogVisible"
       width="400px"
-      @close="resetForm"
+      @close="resetRoomForm"
       append-to-body>
-      <div>
-        <p style="color: gray;">不传默认以系统参数为准</p>
-        <el-form :model="linkForm" label-width="120px">
-          <el-form-item label="链接有效时长(天)">
-            <el-input
-              v-model="linkForm.days"
-              placeholder="请输入有效时长"
-              type="number"
-            ></el-input>
-          </el-form-item>
-        </el-form>
-      </div>
+      <el-form :model="roomLinkForm" label-width="120px">
+        <!-- 新增下拉框 -->
+        <el-form-item label="销售企微选择">
+          <el-select
+            v-model="roomLinkForm.qwUserId"
+            placeholder="请选择销售企微"
+            style="width: 100%">
+            <el-option
+              v-for="item in qwUserList"
+              :key="item.id"
+              :label="formatOptionLabel(item)"
+              :value="item.id">
+            </el-option>
+          </el-select>
+        </el-form-item>
+      </el-form>
       <div slot="footer" class="dialog-footer">
         <el-button @click="dialogVisible = false">取消</el-button>
         <el-button type="primary" @click="confirm">确认</el-button>
@@ -125,7 +133,7 @@
 <script>
 import {getVideoListByCourseId, updatePacketMoney, updateUserCourseVideoUpdate} from "@/api/course/userCourseVideo";
 import userCourseVideoDetails from '../../components/course/userCourseVideoDetails.vue';
-import {createLinkUrl} from "@/api/course/sopCourseLink";
+import {createLinkUrl, createRoomLinkUrl, queryQwIds} from "@/api/course/sopCourseLink";
 import AutoTagDialog from "@/views/components/tag/AutoTagDialog.vue";
 import {addTag, updateTag} from "@/api/tag/api";
 
@@ -151,6 +159,7 @@ export default {
         tagDialogTitle: "",
         tagDialogFormData: null,
         currentVideoId:null,
+        projectFrom:process.env.VUE_APP_PROJECT_FROM,
         tagDialog: {
           videoId: null,
           groupId: null,
@@ -165,6 +174,14 @@ export default {
           courseId:null,
           videoId:null
         },
+        roomLinkForm:{
+          courseId:null,
+          videoId:null,
+          qwUserId:null,
+          qwUserName:null,
+          corpId:null,
+          title:null,
+        },
         dialogVisible: false, // 控制弹框显示
         //短链
         sortLink:'',
@@ -204,6 +221,9 @@ export default {
         fileId: '',
         courseName:null,
         userCourseVideoList:[],
+
+        fetchingQwIds: false,
+        qwUserList:[],
         total: 0,
         queryParams: {
           pageNum: 1,
@@ -242,7 +262,9 @@ export default {
       });
     },
     methods: {
-
+      formatOptionLabel(item) {
+        return item.corpName ? `${item.qwUserName} (${item.corpName})`  : item.qwUserName;
+      },
       closeTagDialog(){
         this.tagDialogVisible = false;
       },
@@ -274,17 +296,52 @@ export default {
       },
       // 打开弹框
       openDialog(row) {
-        this.linkForm.courseId = row.courseId;
-        this.linkForm.videoId = row.videoId;
+        if (!this.fetchingQwIds) {
+          this.fetchingQwIds = true;
+          queryQwIds().then(response => {
+            if (response.code === 200){
+              this.qwUserList = response.list;
+            }
+          }).finally(() => {
+            this.fetchingQwIds = false;
+            // 在请求完成后再显示弹框
+            this.showDialog(row);
+          });
+        } else {
+          // 如果已经在请求中,直接显示弹框
+          this.showDialog(row);
+        }
+      },
+      // 新增方法:显示弹框
+      showDialog(row) {
+        this.roomLinkForm.courseId = row.courseId;
+        this.roomLinkForm.videoId = row.videoId;
+        this.roomLinkForm.title = row.title;
+
         this.dialogVisible = true;
       },
       // 确认按钮操作
       confirm() {
-        if (!this.linkForm.days) {
-          this.$message.error("请输入有效时长!");
+        if (!this.roomLinkForm.qwUserId) {
+          this.$message.error("请选择销售企微!");
           return;
         }
-        this.handleCreateLink();
+        // 获取选中的用户信息
+        const selectedUser = this.qwUserList.find(user => user.id === this.roomLinkForm.qwUserId);
+        if (selectedUser) {
+          // 将用户信息填充到表单中
+          this.roomLinkForm.qwUserName = selectedUser.qwUserName;
+          this.roomLinkForm.corpId = selectedUser.corpId;
+          console.log(this.roomLinkForm);
+          // 调用创建链接的API
+          createRoomLinkUrl(this.roomLinkForm).then(response => {
+            if (response.code === 200){
+              this.msgSuccess("创建成功");
+              this.copyLink(response.link);
+              console.log(response.link);
+            }
+          });
+        }
 
         // 关闭弹框
         this.dialogVisible = false;
@@ -297,6 +354,14 @@ export default {
             videoId:null
         }
       },
+
+      resetRoomForm() {
+        this.roomLinkForm={
+          courseId:null,
+          videoId:null,
+          qwUserId:null,
+        }
+      },
       handleCreateLink(){
         createLinkUrl(this.linkForm).then(response => {
           if (response.code === 200){

+ 6 - 6
src/views/course/courseFinishTemp/index.vue

@@ -199,13 +199,13 @@
                     </el-radio-group>
                   </el-form-item>
                   <el-form-item label="内容" style="margin-bottom: 2%">
-                    <el-input v-if="item.contentType == 1 " v-model="item.value" type="textarea" :rows="3"
+                    <el-input v-if="item.contentType == 1  || item.contentType == 15" v-model="item.value" type="textarea" :rows="3"
                               placeholder="内容" style="width: 90%;margin-top: 10px;"/>
 
                     <ImageUpload v-if="item.contentType == 2 " v-model="item.imgUrl" type="image" :num="1" :width="150"
                                  :height="150"/>
 
-                    <div v-if="item.contentType == 3 ">
+                    <div v-if="item.contentType == 3 || item.contentType ==9">
                       <el-card class="box-card">
                         <el-form-item label="链接标题:" label-width="100px">
                           <el-input v-model="item.linkTitle" placeholder="请输入链接标题" style="width: 90%;"/>
@@ -307,7 +307,7 @@
                         </video>
                       </el-form-item>
                     </div>
-                    <div v-if="item.contentType == 7 ">
+                    <div v-if="item.contentType == 7  || item.contentType == 16">
                       <el-input
                         v-model="item.value"
                         type="textarea" :rows="3" maxlength="66" show-word-limit
@@ -381,13 +381,13 @@
                     </el-radio-group>
                   </el-form-item>
                   <el-form-item label="内容" style="margin-bottom: 2%">
-                    <el-input v-if="item.contentType == 1 " v-model="item.value" type="textarea" :rows="3"
+                    <el-input v-if="item.contentType == 1  || item.contentType == 15" v-model="item.value" type="textarea" :rows="3"
                               placeholder="内容" style="width: 90%;margin-top: 10px;"/>
 
                     <ImageUpload v-if="item.contentType == 2 " v-model="item.imgUrl" type="image" :num="1" :width="150"
                                  :height="150"/>
 
-                    <div v-if="item.contentType == 3 ">
+                    <div v-if="item.contentType == 3 || item.contentType ==9">
                       <el-card class="box-card">
                         <el-form-item label="链接标题:" label-width="100px">
                           <el-input v-model="item.linkTitle" placeholder="请输入链接标题" style="width: 90%;"/>
@@ -489,7 +489,7 @@
                         </video>
                       </el-form-item>
                     </div>
-                    <div v-if="item.contentType == 7 ">
+                    <div v-if="item.contentType == 7  || item.contentType == 16">
                       <el-input
                         v-model="item.value"
                         type="textarea" :rows="3" maxlength="66" show-word-limit

+ 5 - 5
src/views/course/courseFinishTempParent/deptIndex.vue

@@ -153,13 +153,13 @@
                     </el-radio-group>
                   </el-form-item>
                   <el-form-item label="内容" style="margin-bottom: 2%">
-                    <el-input v-if="item.contentType == 1 " v-model="item.value" type="textarea" :rows="3"
+                    <el-input v-if="item.contentType == 1 || item.contentType == 15" v-model="item.value" type="textarea" :rows="3"
                               placeholder="内容" style="width: 90%;margin-top: 10px;"/>
 
                     <ImageUpload v-if="item.contentType == 2 " v-model="item.imgUrl" type="image" :num="1" :width="150"
                                  :height="150"/>
 
-                    <div v-if="item.contentType == 3 ">
+                    <div v-if="item.contentType == 3 || item.contentType ==9">
                       <el-card class="box-card">
                         <el-form-item label="链接标题:" label-width="100px">
                           <el-input v-model="item.linkTitle" placeholder="请输入链接标题" style="width: 90%;"/>
@@ -335,13 +335,13 @@
                     </el-radio-group>
                   </el-form-item>
                   <el-form-item label="内容" style="margin-bottom: 2%">
-                    <el-input v-if="item.contentType == 1 " v-model="item.value" type="textarea" :rows="3"
+                    <el-input v-if="item.contentType == 1  || item.contentType == 15" v-model="item.value" type="textarea" :rows="3"
                               placeholder="内容" style="width: 90%;margin-top: 10px;"/>
 
                     <ImageUpload v-if="item.contentType == 2 " v-model="item.imgUrl" type="image" :num="1" :width="150"
                                  :height="150"/>
 
-                    <div v-if="item.contentType == 3 ">
+                    <div v-if="item.contentType == 3 || item.contentType ==9">
                       <el-card class="box-card">
                         <el-form-item label="链接标题:" label-width="100px">
                           <el-input v-model="item.linkTitle" placeholder="请输入链接标题" style="width: 90%;"/>
@@ -443,7 +443,7 @@
                         </video>
                       </el-form-item>
                     </div>
-                    <div v-if="item.contentType == 7 ">
+                    <div v-if="item.contentType == 7  || item.contentType == 16">
                       <el-input
                         v-model="item.value"
                         type="textarea" :rows="3" maxlength="66" show-word-limit

+ 6 - 6
src/views/course/courseFinishTempParent/index.vue

@@ -153,13 +153,13 @@
                     </el-radio-group>
                   </el-form-item>
                   <el-form-item label="内容" style="margin-bottom: 2%">
-                    <el-input v-if="item.contentType == 1 " v-model="item.value" type="textarea" :rows="3"
+                    <el-input v-if="item.contentType == 1 ||item.contentType == 15" v-model="item.value" type="textarea" :rows="3"
                               placeholder="内容" style="width: 90%;margin-top: 10px;"/>
 
                     <ImageUpload v-if="item.contentType == 2 " v-model="item.imgUrl" type="image" :num="1" :width="150"
                                  :height="150"/>
 
-                    <div v-if="item.contentType == 3 ">
+                    <div v-if="item.contentType == 3 || item.contentType ==9">
                       <el-card class="box-card">
                         <el-form-item label="链接标题:" label-width="100px">
                           <el-input v-model="item.linkTitle" placeholder="请输入链接标题" style="width: 90%;"/>
@@ -261,7 +261,7 @@
                         </video>
                       </el-form-item>
                     </div>
-                    <div v-if="item.contentType == 7 ">
+                    <div v-if="item.contentType == 7 || item.contentType == 16">
                       <el-input
                         v-model="item.value"
                         type="textarea" :rows="3" maxlength="66" show-word-limit
@@ -335,13 +335,13 @@
                     </el-radio-group>
                   </el-form-item>
                   <el-form-item label="内容" style="margin-bottom: 2%">
-                    <el-input v-if="item.contentType == 1 " v-model="item.value" type="textarea" :rows="3"
+                    <el-input v-if="item.contentType == 1 ||item.contentType == 15" v-model="item.value" type="textarea" :rows="3"
                               placeholder="内容" style="width: 90%;margin-top: 10px;"/>
 
                     <ImageUpload v-if="item.contentType == 2 " v-model="item.imgUrl" type="image" :num="1" :width="150"
                                  :height="150"/>
 
-                    <div v-if="item.contentType == 3 ">
+                    <div v-if="item.contentType == 3 || item.contentType ==9">
                       <el-card class="box-card">
                         <el-form-item label="链接标题:" label-width="100px">
                           <el-input v-model="item.linkTitle" placeholder="请输入链接标题" style="width: 90%;"/>
@@ -443,7 +443,7 @@
                         </video>
                       </el-form-item>
                     </div>
-                    <div v-if="item.contentType == 7 ">
+                    <div v-if="item.contentType == 7 || item.contentType == 16">
                       <el-input
                         v-model="item.value"
                         type="textarea" :rows="3" maxlength="66" show-word-limit

+ 6 - 6
src/views/course/courseFinishTempParent/myIndex.vue

@@ -153,13 +153,13 @@
                     </el-radio-group>
                   </el-form-item>
                   <el-form-item label="内容" style="margin-bottom: 2%">
-                    <el-input v-if="item.contentType == 1 " v-model="item.value" type="textarea" :rows="3"
+                    <el-input v-if="item.contentType == 1  || item.contentType == 15" v-model="item.value" type="textarea" :rows="3"
                               placeholder="内容" style="width: 90%;margin-top: 10px;"/>
 
                     <ImageUpload v-if="item.contentType == 2 " v-model="item.imgUrl" type="image" :num="1" :width="150"
                                  :height="150"/>
 
-                    <div v-if="item.contentType == 3 ">
+                    <div v-if="item.contentType == 3 || item.contentType ==9">
                       <el-card class="box-card">
                         <el-form-item label="链接标题:" label-width="100px">
                           <el-input v-model="item.linkTitle" placeholder="请输入链接标题" style="width: 90%;"/>
@@ -261,7 +261,7 @@
                         </video>
                       </el-form-item>
                     </div>
-                    <div v-if="item.contentType == 7 ">
+                    <div v-if="item.contentType == 7  || item.contentType == 16">
                       <el-input
                         v-model="item.value"
                         type="textarea" :rows="3" maxlength="66" show-word-limit
@@ -335,13 +335,13 @@
                     </el-radio-group>
                   </el-form-item>
                   <el-form-item label="内容" style="margin-bottom: 2%">
-                    <el-input v-if="item.contentType == 1 " v-model="item.value" type="textarea" :rows="3"
+                    <el-input v-if="item.contentType == 1  || item.contentType == 15" v-model="item.value" type="textarea" :rows="3"
                               placeholder="内容" style="width: 90%;margin-top: 10px;"/>
 
                     <ImageUpload v-if="item.contentType == 2 " v-model="item.imgUrl" type="image" :num="1" :width="150"
                                  :height="150"/>
 
-                    <div v-if="item.contentType == 3 ">
+                    <div v-if="item.contentType == 3 || item.contentType ==9">
                       <el-card class="box-card">
                         <el-form-item label="链接标题:" label-width="100px">
                           <el-input v-model="item.linkTitle" placeholder="请输入链接标题" style="width: 90%;"/>
@@ -443,7 +443,7 @@
                         </video>
                       </el-form-item>
                     </div>
-                    <div v-if="item.contentType == 7 ">
+                    <div v-if="item.contentType == 7  || item.contentType == 16">
                       <el-input
                         v-model="item.value"
                         type="textarea" :rows="3" maxlength="66" show-word-limit

+ 25 - 0
src/views/course/courseWatchLog/index.vue

@@ -148,6 +148,21 @@
       </el-autocomplete>
     </el-form-item>
 
+      <el-form-item label="看课类型" prop="watchType">
+        <el-select
+          filterable
+          v-model="queryParams.watchType"
+          placeholder="请选择看课类型"
+          clearable size="small">
+          <el-option
+            v-for="dict in watchTypeList"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+
       <!-- 营期时间 -->
       <!-- <el-form-item label="营期时间" prop="scheduleTime">
         <el-input
@@ -381,6 +396,11 @@
 <!--      <el-table-column label="所属公司" align="center" prop="companyName" />-->
       <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">
+        <template slot-scope="{ row }">
+          {{ row.watchType === 1 ? 'APP' : row.watchType === 2 ? '小程序' : '-' }}
+        </template>
+      </el-table-column>
       <el-table-column label="创建时间" align="center" prop="createTime" />
       <el-table-column label="更新时间" align="center" prop="updateTime" />
       <el-table-column label="完课时间" align="center" prop="finishTime" />
@@ -638,6 +658,10 @@ export default {
   components: {Treeselect },
   data() {
     return {
+      watchTypeList: [
+        { dictLabel: 'app', dictValue: 1 },
+        { dictLabel: '小程序', dictValue: 2 }
+      ],
       companyUserFirstLoad: true, // 首次加载标志
       qwUserFirstLoad: true,     // 首次加载标志
       companyUserDropdownVisible: false, // 下拉框显示状态
@@ -806,6 +830,7 @@ export default {
         sendType:process.env.VUE_APP_COURSE_DEFAULT,
         isVip: null,
         sopId: null, // sopId
+        watchType:null,
       },
       // 表单参数
       form: {},

+ 24 - 1
src/views/course/courseWatchLog/watchLog.vue

@@ -121,6 +121,20 @@
           </template>
         </el-autocomplete>
       </el-form-item>
+      <el-form-item label="看课类型" prop="watchType">
+        <el-select
+          filterable
+          v-model="queryParams.watchType"
+          placeholder="请选择看课类型"
+          clearable size="small">
+          <el-option
+            v-for="dict in watchTypeList"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
       <!-- 营期时间 -->
       <!-- <el-form-item label="营期时间" prop="scheduleTime">
         <el-input
@@ -390,6 +404,11 @@
         </template>
       </el-table-column>
       <el-table-column label="播放时长" align="center" prop="duration"/>
+      <el-table-column label="看课类型" align="center">
+        <template slot-scope="{ row }">
+          {{ row.watchType === 1 ? 'APP' : row.watchType === 2 ? '小程序' : '-' }}
+        </template>
+      </el-table-column>
 <!--      <el-table-column label="所属销售" align="center" prop="companyUserName"/>-->
 <!--      <el-table-column label="所属公司" align="center" prop="companyName"/>-->
 <!--      <el-table-column label="企微员工名称" align="center" prop="qwUserName"/>-->
@@ -698,6 +717,10 @@ export default {
   name: "CourseWatchLog",
   data() {
     return {
+      watchTypeList: [
+        { dictLabel: 'app', dictValue: 1 },
+        { dictLabel: '小程序', dictValue: 2 }
+      ],
       tableKey: 0,
       // 日历 key 控制刷新
       scheduleCalendarKey: 0,
@@ -861,6 +884,7 @@ export default {
         sendType:process.env.VUE_APP_COURSE_DEFAULT,
         isVip: null,
         sopId: null, // sopId
+        watchType:null,
       },
       // 表单参数
       form: {},
@@ -1502,7 +1526,6 @@ export default {
         this.resultDialogVisible = true; // 显示弹窗
         this.resultTitle = '批量添加标签结果';
 
-
         this.tagOpen = false;
         loadingRock.close();
         this.addTagFormByWatch={

+ 50 - 0
src/views/course/userCoursePeriod/index.vue

@@ -43,6 +43,12 @@
                        :formatter="periodStatusFormatter"/>
       <el-table-column label="开营开始时间" align="center" prop="periodStartingTime" width="180"/>
       <el-table-column label="开营结束时间" align="center" prop="periodEndTime" width="180"/>
+      <el-table-column label="看课休息开关" align="center" prop="isOpenRestReminder">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.isOpenRestReminder==1" type="success">开启</el-tag>
+          <el-tag v-if="scope.row.isOpenRestReminder==0" type="success">关闭</el-tag>
+        </template>
+      </el-table-column>
       <el-table-column label="创建时间" align="center" prop="createTime" width="180"/>
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
         <template slot-scope="scope">
@@ -78,6 +84,22 @@
             @click="handlePeriodUser(scope.row, '0')"
           >关闭单独注册会员
           </el-button>
+          <el-button
+            v-if="scope.row.isOpenRestReminder ===null||scope.row.isOpenRestReminder === undefined || scope.row.isOpenRestReminder===1 "
+            size="mini"
+            type="text"
+            icon="el-icon-open"
+            @click="handlePeriodUserOpenReminder(scope.row, '0')"
+          >关闭看课休息
+          </el-button>
+          <el-button
+            v-if="scope.row.isOpenRestReminder ===null||scope.row.isOpenRestReminder === undefined ||scope.row.isOpenRestReminder === 0"
+            size="mini"
+            type="text"
+            icon="el-icon-open"
+            @click="handlePeriodUserOpenReminder(scope.row, '1')"
+          >打开看课休息
+          </el-button>
 <!--          <el-button-->
 <!--          v-else-->
 <!--          size="mini"-->
@@ -301,6 +323,7 @@ import {
   pagePeriod,
   updatePeriod,
   updatePeriodIsNeedRegisterMember,
+  updatePeriodIsOpenRestReminder,
   getDays,
   addCourse,
   updateCourseTime,
@@ -580,6 +603,33 @@ export default {
         this.$message.info('已取消操作');
       });
     },
+    /** 处理单独注册会员开关 */
+    handlePeriodUserOpenReminder(data, open) {
+      const actionText = open === '1' ? '开启' : '关闭';
+      this.$confirm(`确定要${actionText}【${data.periodName}】的看课休息暂停功能吗?`, '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        var data1 = {
+          periodId:data.periodId,
+          isOpenRestReminder:open,
+        }
+        updatePeriodIsOpenRestReminder(data1).then(response => {
+          if (response.code === 200) {
+            this.msgSuccess(`${actionText}成功`);
+            this.getList();
+          } else {
+            this.msgError(response.msg || `${actionText}失败`);
+          }
+        }).catch(error => {
+          console.error(`${actionText}单独注册会员失败:`, error);
+          this.msgError(`${actionText}失败`);
+        });
+      }).catch(() => {
+        this.$message.info('已取消操作');
+      });
+    },
     /** 提交按钮 */
     submitForm() {
       this.$refs["form"].validate(valid => {

+ 10 - 4
src/views/hisStore/storeOrder/list.vue

@@ -322,9 +322,13 @@
         <el-form ref="form" :model="form" :rules="rules" label-width="120px">
            <el-form-item label="会员信息" >
                 <el-row  >
-                  <el-col >
-                      <el-input placeholder="请输入会员手机号" style="width:240px;cursor:pointer" v-model="phone">
+                  <el-col>
+                      <el-input placeholder="请输入会员手机号" style="width:200px;cursor:pointer;margin-left:5px;" v-model="phone">
                       </el-input>
+                    <el-input placeholder="请输入准确的会员ID" style="width:200px;cursor:pointer;margin-left:5px;" v-model="userId">
+                    </el-input>
+                    <el-input placeholder="请输入会员昵称" style="width:200px;cursor:pointer;margin-left:5px;" v-model="nickName">
+                    </el-input>
                       <el-button plain style="margin-left:10px;"    @click="searchUser()">查询</el-button>
                       <el-button plain style="margin-left:10px;" icon="el-icon-plus"  type="primary" @click="handleAddUser()">添加会员</el-button>
                   </el-col>
@@ -516,6 +520,8 @@ export default {
       tablekey:false,
       totalMoney:0.00,
       phone:null,
+      userId: null,
+      nickName: null,
       products:[],
       product:{
         open:false,
@@ -743,10 +749,10 @@ export default {
       this.product.open=true;
     },
     searchUser(){
-      if(this.phone==null||this.phone==""){
+      if((this.phone==null||this.phone==="") && (this.userId==null||this.userId==="") && (this.nickName==null||this.nickName==="")){
         return;
       }
-      var data={phone:this.phone}
+      var data={phone:this.phone, userId:this.userId, nickName: this.nickName}
       this.userloading = true;
       this.users=[];
       this.address=[];

+ 51 - 9
src/views/live/liveConsole/LiveConsole.vue

@@ -154,6 +154,8 @@
             type="primary"
             size="small"
             @click.stop="loadLatestMessages"
+            :disabled="isLoadingLatest"
+            :loading="isLoadingLatest"
             icon="el-icon-refresh">
             加载最新消息
           </el-button>
@@ -407,6 +409,8 @@ export default {
       isAutoScrollEnabled: true, // 是否启用自动滚动
       autoScrollTimer: null, // 自动滚动定时器
       showLoadLatestBtn: false, // 是否显示加载最新消息按钮
+      isLoadingLatest: false, // 是否正在加载最新消息
+      scrollDebounceTimer: null, // 滚动防抖定时器
       msgParams: {
         pageNum: 1,
         pageSize: 30,
@@ -479,10 +483,6 @@ export default {
     // 滚动到底部
     scrollToBottom(forceScroll = false) {
       // 如果自动滚动被禁用且不是强制滚动,则不执行
-      console.log("scrollToBottom")
-      console.log(!this.isAutoScrollEnabled && !forceScroll)
-      console.log(this.$refs.manageRightRef && this.$refs.manageRightRef.wrap)
-      console.log(forceScroll || this.isAutoScrollEnabled)
       if (!this.isAutoScrollEnabled && !forceScroll) {
         return;
       }
@@ -503,6 +503,11 @@ export default {
     },
     // 加载最新消息
     loadLatestMessages() {
+      // 如果正在加载,直接返回
+      if (this.isLoadingLatest) {
+        return;
+      }
+      this.isLoadingLatest = true;
       this.showLoadLatestBtn = false;
       // 恢复自动滚动
       this.isAutoScrollEnabled = true;
@@ -718,8 +723,6 @@ export default {
           }
           this.msgList.push(message)
           // 如果启用自动滚动,自动滚动到底部
-          console.log("handleWsMessage")
-          console.log(this.isAutoScrollEnabled)
           if (this.isAutoScrollEnabled) {
             this.$nextTick(() => {
               this.autoScrollTimer = setTimeout(() => {
@@ -803,7 +806,7 @@ export default {
             }
             if (res.data.liveType == 1) {
               this.videoParam.livingUrl = res.data.flvHlsUrl
-              this.videoParam.livingUrl = this.livingUrl.replace("flv","m3u8")
+              this.videoParam.livingUrl = this.videoParam.livingUrl.replace("flv","m3u8")
               // this.$nextTick(() => {
               //   this.initPlayer()
               // })
@@ -1182,6 +1185,9 @@ export default {
           // 所有消息加载完成后,根据自动滚动状态决定是否滚动
           this.$nextTick(() => {
             setTimeout(() => {
+              // 重置加载状态
+              this.isLoadingLatest = false;
+
               if (this.isAutoScrollEnabled) {
                 // 如果启用自动滚动,强制滚动到底部并隐藏按钮
                 this.scrollToBottom(true);
@@ -1195,7 +1201,7 @@ export default {
                   const maxScrollTop = scrollHeight - clientHeight;
 
                   if (currentScrollTop < maxScrollTop - 50) {
-                    // this.showLoadLatestBtn = true;
+                    this.showLoadLatestBtn = true;
                   } else {
                     this.showLoadLatestBtn = false;
                   }
@@ -1208,9 +1214,38 @@ export default {
 
       // 添加滚动监听
       this.$nextTick(() => {
-        this.$refs.manageRightRef.wrap.addEventListener("scroll", this.manageRightScroll)
+        if (this.$refs.manageRightRef && this.$refs.manageRightRef.wrap) {
+          this.$refs.manageRightRef.wrap.addEventListener("scroll", this.manageRightScroll)
+        }
       })
     },
+    // 消息滚动监听(带防抖)
+    manageRightScroll() {
+      // 清除之前的防抖定时器
+      if (this.scrollDebounceTimer) {
+        clearTimeout(this.scrollDebounceTimer);
+      }
+
+      // 设置防抖,300ms内只执行一次
+      this.scrollDebounceTimer = setTimeout(() => {
+        this.saveChatScrollPosition();
+
+        // 检查是否滚动到底部
+        if (this.$refs.manageRightRef && this.$refs.manageRightRef.wrap) {
+          const wrap = this.$refs.manageRightRef.wrap;
+          const scrollHeight = wrap.scrollHeight;
+          const clientHeight = wrap.clientHeight;
+          const currentScrollTop = wrap.scrollTop;
+          const maxScrollTop = scrollHeight - clientHeight;
+
+          // 如果滚动到底部(距离底部小于50px),隐藏按钮并恢复自动滚动
+          if (currentScrollTop >= maxScrollTop - 50) {
+            this.showLoadLatestBtn = false;
+            this.isAutoScrollEnabled = true;
+          }
+        }
+      }, 300);
+    },
     loadUserTotals() {
       if (!this.liveId) return;
       // 假设后端提供一个接口返回总人数(如果没有,可通过首次加载全量数据后统计)
@@ -1427,6 +1462,13 @@ export default {
     if (this.autoScrollTimer) {
       clearTimeout(this.autoScrollTimer);
     }
+    if (this.scrollDebounceTimer) {
+      clearTimeout(this.scrollDebounceTimer);
+    }
+    // 移除滚动监听器
+    if (this.$refs.manageRightRef && this.$refs.manageRightRef.wrap) {
+      this.$refs.manageRightRef.wrap.removeEventListener("scroll", this.manageRightScroll);
+    }
   },
   // 使用 deactivated 和 activated 钩子替代 beforeDestroy 和 destroyed
   deactivated() {

+ 28 - 2
src/views/live/liveConsole/LivePlayer.vue

@@ -1,8 +1,10 @@
 <template>
   <div class="live-player">
-    <video ref="videoPlayer" loop autoplay class="player">
+    <video v-if="videoParam.liveType == 2" ref="videoPlayer" loop autoplay class="player">
       <source :src="videoParam.videoUrl" type="application/x-mpegURL">
     </video>
+    <video v-if="videoParam.liveType == 1" ref="livePlayer" loop autoplay class="player">
+    </video>
   </div>
 </template>
 
@@ -69,6 +71,28 @@ export default {
         console.error('浏览器不支持 HLS')
       }
     },
+    livePlay(url) {
+      if (Hls.isSupported()) {
+        const videoElement = this.$refs.livePlayer
+        if (!videoElement) {
+          console.error('找不到 video 元素')
+          return
+        }
+        this.hls = new Hls();
+        this.hls.attachMedia(videoElement);
+        this.hls.on(Hls.Events.MEDIA_ATTACHED, () => {
+          this.hls.loadSource(url);
+          this.hls.on(Hls.Events.STREAM_LOADED, (event, data) => {
+            videoElement.play();
+          });
+        });
+        this.hls.on(Hls.Events.ERROR, (event, data) => {
+          console.error('HLS 错误:', data);
+        });
+      } else {
+        console.error('浏览器不支持 HLS')
+      }
+    },
     updateVideoPosition(video){
       const currentTime = new Date().getTime(); // 当前时间戳(毫秒)
       const elapsedTime = currentTime - this.startTime; // 已流逝时间(毫秒)
@@ -93,7 +117,9 @@ export default {
           console.error('直播地址为空,无法初始化播放器')
           return
         }
-        this.videoPlay(this.videoParam.livingUrl);
+        this.$nextTick(() => {
+          this.livePlay(this.videoParam.livingUrl);
+        })
         return;
       }
       const viUrl = this.videoParam.videoUrl === null || this.videoParam.videoUrl.trim() === ''

+ 31 - 10
src/views/live/liveData/index.vue

@@ -363,25 +363,25 @@
             </el-col>
             <el-col :span="12">
               <div class="detail-item">
-                <span class="detail-label">>20分钟人数(直播):</span>
+                <span class="detail-label">>=20分钟人数(直播):</span>
                 <span class="detail-value">{{ detailData.liveOver20Minutes || 0 }}</span>
               </div>
             </el-col>
             <el-col :span="12">
               <div class="detail-item">
-                <span class="detail-label">>30分钟人数(直播):</span>
+                <span class="detail-label">>=30分钟人数(直播):</span>
                 <span class="detail-value">{{ detailData.liveOver30Minutes || 0 }}</span>
               </div>
             </el-col>
             <el-col :span="12">
               <div class="detail-item">
-                <span class="detail-label">到课完课率直播(>20分钟):</span>
+                <span class="detail-label">到课完课率直播(>=20分钟):</span>
                 <span class="detail-value">{{ formatPercent(detailData.liveCompletionRate20) }}</span>
               </div>
             </el-col>
             <el-col :span="12">
               <div class="detail-item">
-                <span class="detail-label">到课完课率直播(>30分钟):</span>
+                <span class="detail-label">到课完课率直播(>=30分钟):</span>
                 <span class="detail-value">{{ formatPercent(detailData.liveCompletionRate30) }}</span>
               </div>
             </el-col>
@@ -399,25 +399,25 @@
             </el-col>
             <el-col :span="12">
               <div class="detail-item">
-                <span class="detail-label">>20分钟人数(回放):</span>
+                <span class="detail-label">>=20分钟人数(回放):</span>
                 <span class="detail-value">{{ detailData.playbackOver20Minutes || 0 }}</span>
               </div>
             </el-col>
             <el-col :span="12">
               <div class="detail-item">
-                <span class="detail-label">>30分钟人数(回放):</span>
+                <span class="detail-label">>=30分钟人数(回放):</span>
                 <span class="detail-value">{{ detailData.playbackOver30Minutes || 0 }}</span>
               </div>
             </el-col>
             <el-col :span="12">
               <div class="detail-item">
-                <span class="detail-label">到课完课率回放(>20分钟):</span>
+                <span class="detail-label">到课完课率回放(>=20分钟):</span>
                 <span class="detail-value">{{ formatPercent(detailData.playbackCompletionRate20) }}</span>
               </div>
             </el-col>
             <el-col :span="12">
               <div class="detail-item">
-                <span class="detail-label">到课完课率回放(>30分钟):</span>
+                <span class="detail-label">到课完课率回放(>=30分钟):</span>
                 <span class="detail-value">{{ formatPercent(detailData.playbackCompletionRate30) }}</span>
               </div>
             </el-col>
@@ -555,6 +555,16 @@
           <el-table-column prop="companyName" label="分公司" min-width="150"></el-table-column>
           <el-table-column prop="salesName" label="销售" min-width="120"></el-table-column>
         </el-table>
+        <!-- 用户详情分页组件 -->
+        <pagination
+          v-show="userDetailTotal > 0"
+          :total="userDetailTotal"
+          :page.sync="userDetailQueryParams.pageNum"
+          :limit.sync="userDetailQueryParams.pageSize"
+          :page-sizes="[10, 100, 1000]"
+          @pagination="handleViewUserDetail"
+          style="margin-top: 20px;"
+        />
       </div>
     </el-drawer>
   </div>
@@ -639,7 +649,12 @@ export default {
       userDetailList: [],
       userDetailLoading: false,
       showUserDetail: false,
-      userDetailExportLoading: false
+      userDetailExportLoading: false,
+      userDetailTotal: 0,
+      userDetailQueryParams: {
+        pageNum: 1,
+        pageSize: 10
+      }
     };
   },
   created() {
@@ -888,9 +903,10 @@ export default {
       if (!this.currentLiveId) return;
       this.showUserDetail = true;
       this.userDetailLoading = true;
-      getLiveUserDetailListBySql(this.currentLiveId).then(response => {
+      getLiveUserDetailListBySql(this.currentLiveId, this.userDetailQueryParams.pageNum, this.userDetailQueryParams.pageSize).then(response => {
         if (response.code === 200) {
           this.userDetailList = response.data || [];
+          this.userDetailTotal = response.total || 0;
         }
         this.userDetailLoading = false;
       }).catch(() => {
@@ -904,6 +920,11 @@ export default {
       this.detailData = {};
       this.userDetailList = [];
       this.currentLiveId = null;
+      this.userDetailTotal = 0;
+      this.userDetailQueryParams = {
+        pageNum: 1,
+        pageSize: 100
+      };
     },
     /** 格式化百分比 */
     formatPercent(value) {

+ 9 - 9
src/views/live/liveOrder/index-old.vue

@@ -46,15 +46,15 @@
         />
       </el-form-item>
 
-      <el-form-item label="销售价格" prop="price">
-        <el-input
-          v-model="queryParams.price"
-          placeholder="请输入销售价格"
-          clearable
-          size="small"
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
+<!--      <el-form-item label="销售价格" prop="price">-->
+<!--        <el-input-->
+<!--          v-model="queryParams.price"-->
+<!--          placeholder="请输入销售价格"-->
+<!--          clearable-->
+<!--          size="small"-->
+<!--          @keyup.enter.native="handleQuery"-->
+<!--        />-->
+<!--      </el-form-item>-->
 
       <el-form-item label="收货地址" prop="userAddress">
         <el-input

+ 28 - 28
src/views/live/liveOrder/index.vue

@@ -65,25 +65,25 @@
         />
       </el-form-item>
 
-      <el-form-item label="商品数量" prop="totalNum">
-        <el-input
-          v-model="queryParams.totalNum"
-          placeholder="请输入商品数量"
-          clearable
-          size="small"
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
+<!--      <el-form-item label="商品数量" prop="totalNum">-->
+<!--        <el-input-->
+<!--          v-model="queryParams.totalNum"-->
+<!--          placeholder="请输入商品数量"-->
+<!--          clearable-->
+<!--          size="small"-->
+<!--          @keyup.enter.native="handleQuery"-->
+<!--        />-->
+<!--      </el-form-item>-->
 
-      <el-form-item label="销售价格" prop="price">
-        <el-input
-          v-model="queryParams.price"
-          placeholder="请输入销售价格"
-          clearable
-          size="small"
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
+<!--      <el-form-item label="销售价格" prop="price">-->
+<!--        <el-input-->
+<!--          v-model="queryParams.price"-->
+<!--          placeholder="请输入销售价格"-->
+<!--          clearable-->
+<!--          size="small"-->
+<!--          @keyup.enter.native="handleQuery"-->
+<!--        />-->
+<!--      </el-form-item>-->
 
       <el-form-item label="收货地址" prop="userAddress">
         <el-input
@@ -115,15 +115,15 @@
         />
       </el-form-item>
 
-      <el-form-item label="供应商" prop="supplierName">
-        <el-input
-          v-model="queryParams.supplierName"
-          placeholder="请输入供应商名称"
-          clearable
-          size="small"
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
+<!--      <el-form-item label="供应商" prop="supplierName">-->
+<!--        <el-input-->
+<!--          v-model="queryParams.supplierName"-->
+<!--          placeholder="请输入供应商名称"-->
+<!--          clearable-->
+<!--          size="small"-->
+<!--          @keyup.enter.native="handleQuery"-->
+<!--        />-->
+<!--      </el-form-item>-->
 
       <!-- 下单时间范围选择 -->
       <el-form-item label="下单时间" prop="orderTimeRange">
@@ -212,7 +212,7 @@
       <el-table-column label="成交价" align="center" prop="payMoney"/>
 <!--      <el-table-column label="成本价格" align="center" prop="costPrice" />-->
       <el-table-column label="收货地址" align="center" prop="userAddress" width="200" />
-      <el-table-column label="对应供应商" align="center" prop="supplierName" width="120" />
+<!--      <el-table-column label="对应供应商" align="center" prop="supplierName" width="120" />-->
       <el-table-column label="下单时间" align="center" prop="createTime" width="180">
         <template slot-scope="scope">
           <span>{{ formatDateTime(scope.row.createTime) }}</span>

+ 468 - 0
src/views/live/liveWatchLog/index.vue

@@ -0,0 +1,468 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="用户Id" prop="userId">
+        <el-input
+          v-model="queryParams.userId"
+          placeholder="请输入用户userId"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="直播间id" prop="liveId">
+        <el-input
+          v-model="queryParams.liveId"
+          placeholder="请输入直播间id"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="记录类型" prop="logType">
+        <el-select v-model="queryParams.logType" placeholder="请选择记录类型" clearable size="small">
+          <el-option
+            v-for="dict in logTypeOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label-width="100px" label="外部联系人id" prop="externalContactId">
+        <el-input
+          v-model="queryParams.externalContactId"
+          placeholder="请输入外部联系人id"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="销售id" prop="companyUserId">
+        <el-input
+          v-model="queryParams.companyUserId"
+          placeholder="请输入销售id"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <!-- <el-form-item label="公司id" prop="companyId">
+        <el-input
+          v-model="queryParams.companyId"
+          placeholder="请输入公司id"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item> -->
+      <el-form-item label="完课时间" prop="finishTime">
+        <el-date-picker clearable size="small"
+          v-model="queryParams.finishTime"
+          type="date"
+          value-format="yyyy-MM-dd"
+          placeholder="选择完课时间">
+        </el-date-picker>
+      </el-form-item>
+      <!-- <el-form-item label="sop最后创建时间" prop="sopCreateTime">
+        <el-date-picker clearable size="small"
+          v-model="queryParams.sopCreateTime"
+          type="date"
+          value-format="yyyy-MM-dd"
+          placeholder="选择sop最后创建时间">
+        </el-date-picker>
+      </el-form-item> -->
+      <!-- <el-form-item label="发送小程序appid" prop="sendAppId">
+        <el-input
+          v-model="queryParams.sendAppId"
+          placeholder="请输入发送小程序appid"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item> -->
+      <!-- <el-form-item label="日志创建来源:1、个人sop,2、群聊sop,3、一键群发" prop="logSource">
+        <el-input
+          v-model="queryParams.logSource"
+          placeholder="请输入日志创建来源:1、个人sop,2、群聊sop,3、一键群发"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item> -->
+      <el-form-item label-width="110px" label="分享人企微id" prop="qwUserId">
+        <el-input
+          v-model="queryParams.qwUserId"
+          placeholder="请输入分享人企微id"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <!-- <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          v-hasPermi="['live:liveWatchLog:add']"
+        >新增</el-button>
+      </el-col> -->
+      <!-- <el-col :span="1.5">
+        <el-button
+          type="success"
+          plain
+          icon="el-icon-edit"
+          size="mini"
+          :disabled="single"
+          @click="handleUpdate"
+          v-hasPermi="['live:liveWatchLog:edit']"
+        >修改</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+          v-hasPermi="['live:liveWatchLog:remove']"
+        >删除</el-button>
+      </el-col> -->
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          :loading="exportLoading"
+          @click="handleExport"
+          v-hasPermi="['live:liveWatchLog:export']"
+        >导出</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table border v-loading="loading" :data="liveWatchLogList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="记录编号" align="center" prop="logId" />
+      <el-table-column label="会员id" align="center" prop="userId" />
+      <el-table-column label="会员名称" align="center" prop="userName" />
+      <el-table-column label="头像" width="150" align="center">
+        <template slot-scope="scope">
+          <img :src="scope.row.userAvatar" style="height: 80px">
+        </template>
+      </el-table-column>
+      <!-- <el-table-column label="直播间id" align="center" prop="liveId" /> -->
+      <el-table-column label="直播间" align="center" prop="liveName" />
+      <el-table-column label="记录类型" align="center" prop="logType">
+        <template slot-scope="scope">
+          <dict-tag :options="logTypeOptions" :value="scope.row.logType"/>
+        </template>
+      </el-table-column>
+      <!-- <el-table-column label="外部联系人id" align="center" prop="externalContactId" /> -->
+      <el-table-column label="外部联系人" align="center" prop="qwExternalName" />
+      <el-table-column label="销售" align="center" prop="companyUserName" />
+      <!-- <el-table-column label="公司id" align="center" prop="companyId" /> -->
+      <el-table-column label="完课时间" align="center" prop="finishTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.finishTime, '{y}-{m}-{d}') }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="sop最后创建时间" align="center" prop="sopCreateTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.sopCreateTime, '{y}-{m}-{d}') }}</span>
+        </template>
+      </el-table-column>
+      <!-- <el-table-column label="发送小程序appid" align="center" prop="sendAppId" /> -->
+      <!-- <el-table-column label="日志创建来源:1、个人sop,2、群聊sop,3、一键群发" align="center" prop="logSource" /> -->
+      <el-table-column label="企微" align="center" prop="qwUserName" />
+      <el-table-column label="是否直播时下单" align="center" prop="liveBuy" >
+         <template slot-scope="scope">
+            <span v-if="!!scope.row.liveBuy">是</span>
+            <span v-else>否</span>
+          </template>
+       </el-table-column>
+       <el-table-column label="是否回放时下单" align="center" prop="replayBuy" >
+         <template slot-scope="scope">
+            <span v-if="!!scope.row.replayBuy">是</span>
+            <span v-else>否</span>
+          </template>
+       </el-table-column>
+      <!-- <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['live:liveWatchLog:edit']"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['live:liveWatchLog:remove']"
+          >删除</el-button>
+        </template>
+      </el-table-column> -->
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改直播看课记录对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="用户userId" prop="userId">
+          <el-input v-model="form.userId" placeholder="请输入用户userId" />
+        </el-form-item>
+        <el-form-item label="直播间id" prop="liveId">
+          <el-input v-model="form.liveId" placeholder="请输入直播间id" />
+        </el-form-item>
+        <el-form-item label="记录类型" prop="logType">
+          <el-select v-model="form.logType" placeholder="请选择记录类型">
+            <el-option
+              v-for="dict in logTypeOptions"
+              :key="dict.dictValue"
+              :label="dict.dictLabel"
+              :value="parseInt(dict.dictValue)"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="外部联系人id" prop="externalContactId">
+          <el-input v-model="form.externalContactId" placeholder="请输入外部联系人id" />
+        </el-form-item>
+        <el-form-item label="销售id" prop="companyUserId">
+          <el-input v-model="form.companyUserId" placeholder="请输入销售id" />
+        </el-form-item>
+        <el-form-item label="公司id" prop="companyId">
+          <el-input v-model="form.companyId" placeholder="请输入公司id" />
+        </el-form-item>
+        <el-form-item label="完课时间" prop="finishTime">
+          <el-date-picker clearable size="small"
+            v-model="form.finishTime"
+            type="date"
+            value-format="yyyy-MM-dd"
+            placeholder="选择完课时间">
+          </el-date-picker>
+        </el-form-item>
+        <!-- <el-form-item label="sop最后创建时间" prop="sopCreateTime">
+          <el-date-picker clearable size="small"
+            v-model="form.sopCreateTime"
+            type="date"
+            value-format="yyyy-MM-dd"
+            placeholder="选择sop最后创建时间">
+          </el-date-picker>
+        </el-form-item> -->
+        <!-- <el-form-item label="发送小程序appid" prop="sendAppId">
+          <el-input v-model="form.sendAppId" placeholder="请输入发送小程序appid" />
+        </el-form-item>
+        <el-form-item label="日志创建来源:1、个人sop,2、群聊sop,3、一键群发" prop="logSource">
+          <el-input v-model="form.logSource" placeholder="请输入日志创建来源:1、个人sop,2、群聊sop,3、一键群发" />
+        </el-form-item> -->
+        <el-form-item label="分享人企微id" prop="qwUserId">
+          <el-input v-model="form.qwUserId" placeholder="请输入分享人企微id" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listLiveWatchLog, getLiveWatchLog, delLiveWatchLog, addLiveWatchLog, updateLiveWatchLog, exportLiveWatchLog } from "@/api/live/liveWatchLog";
+
+export default {
+  name: "LiveWatchLog",
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 直播看课记录表格数据
+      liveWatchLogList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 记录类型 1看课中 2完课 3待看课 4看课中断字典
+      logTypeOptions: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        userId: null,
+        liveId: null,
+        logType: null,
+        externalContactId: null,
+        companyUserId: null,
+        companyId: null,
+        finishTime: null,
+        sopCreateTime: null,
+        sendAppId: null,
+        logSource: null,
+        qwUserId: null
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      }
+    };
+  },
+  created() {
+    this.getList();
+    this.getDicts("live_watch_log_type").then(response => {
+      this.logTypeOptions = response.data;
+    });
+  },
+  methods: {
+    /** 查询直播看课记录列表 */
+    getList() {
+      this.loading = true;
+      listLiveWatchLog(this.queryParams).then(response => {
+        this.liveWatchLogList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        logId: null,
+        userId: null,
+        liveId: null,
+        logType: null,
+        createTime: null,
+        updateTime: null,
+        externalContactId: null,
+        companyUserId: null,
+        companyId: null,
+        finishTime: null,
+        createBy: null,
+        sopCreateTime: null,
+        sendAppId: null,
+        logSource: null,
+        qwUserId: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.logId)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加直播看课记录";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const logId = row.logId || this.ids
+      getLiveWatchLog(logId).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改直播看课记录";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.logId != null) {
+            updateLiveWatchLog(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addLiveWatchLog(this.form).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const logIds = row.logId || this.ids;
+      this.$confirm('是否确认删除直播看课记录编号为"' + logIds + '"的数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return delLiveWatchLog(logIds);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有直播看课记录数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportLiveWatchLog(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    }
+  }
+};
+</script>

+ 1290 - 0
src/views/live/order/index.vue

@@ -0,0 +1,1290 @@
+<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 filterable v-model="queryParams.companyId" placeholder="请选择公司名" @change="companyChange" 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-form-item>
+        <treeselect style="width: 220px" :clearable="false" v-model="queryParams.deptId" :options="deptOptions" :show-count="true" placeholder="请选择归属部门" />
+      </el-form-item>
+
+      <el-form-item label="订单类型" prop="orderTypeFilter">
+        <el-select v-model="queryParams.orderTypeFilter" placeholder="请选择订单类型" clearable size="small">
+          <el-option label="销售订单" :value="1" />
+          <el-option label="直播订单" :value="3" />
+        </el-select>
+      </el-form-item>
+
+      <el-form-item label="销售名称" prop="salesName">
+        <el-input
+          v-model="queryParams.salesName"
+          placeholder="请输入销售名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+
+      <el-form-item label="订单号" prop="orderCodes">
+        <div class="tag-input-container">
+          <div class="tags-wrapper" @click="focusInput">
+            <el-tag
+              v-for="(code, index) in queryParams.orderCodes"
+              :key="index"
+              closable
+              size="small"
+              @close="removeOrderCode(index)"
+              class="order-tag"
+            >
+              {{ code }}
+            </el-tag>
+            <el-input
+              ref="tagInput"
+              v-model="currentInput"
+              v-show="inputVisible || queryParams.orderCodes.length === 0"
+              :placeholder="queryParams.orderCodes.length === 0 ? '请输入订单号,按回车或逗号分隔' : '继续输入...'"
+              size="small"
+              class="tag-input"
+              @keydown.native="handleKeyDown"
+              @keyup.native="handleKeyUp"
+              @blur="handleInputConfirm"
+              @focus="inputVisible = true"
+              clearable
+            />
+            <el-button
+              v-if="!inputVisible && queryParams.orderCodes.length > 0"
+              class="button-new-tag"
+              size="small"
+              @click="showInput"
+              icon="el-icon-plus"
+              type="text"
+            >
+              添加订单号
+            </el-button>
+          </div>
+          <div class="input-tips">
+            <span class="tip-text">
+              支持:回车、逗号、空格分隔 |
+              已添加 {{ queryParams.orderCodes.length }} 个订单号
+            </span>
+          </div>
+        </div>
+      </el-form-item>
+
+      <el-form-item label="运单号" prop="deliveryId">
+        <el-input
+          v-model="queryParams.deliveryId"
+          placeholder="请输入运单号"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+
+      <el-form-item label="银行交易流水号" prop="bankTransactionId">
+        <el-input
+          v-model="queryParams.bankTransactionId"
+          placeholder="请输入银行交易流水号"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+
+      <el-form-item label="产品名称" prop="productName">
+        <el-input
+          v-model="queryParams.productName"
+          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="realName">
+        <el-input
+          v-model="queryParams.realName"
+          placeholder="请输入收件人姓名"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+
+
+      <el-form-item label="支付方式" prop="payType">
+        <el-select v-model="queryParams.payType" placeholder="请选择支付方式" clearable size="small">
+          <el-option
+            v-for="item in payTypeOptions"
+            :key="item.dictValue"
+            :label="item.dictLabel"
+            :value="item.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+
+<!--      <el-form-item label="上传凭证" prop="isUpload">-->
+<!--        <el-select v-model="queryParams.isUpload" placeholder="请选择" clearable size="small">-->
+<!--          <el-option key="0" label="未上传" value="0" />-->
+<!--          <el-option key="1" label="已上传" value="1" />-->
+<!--        </el-select>-->
+<!--      </el-form-item>-->
+
+<!--      <el-form-item label="物流状态" prop="deliveryStatus">-->
+<!--        <el-select v-model="queryParams.deliveryStatus" placeholder="请选择物流状态" clearable size="small">-->
+<!--          <el-option-->
+<!--            v-for="item in deliveryStatusOptions"-->
+<!--            :key="item.dictValue"-->
+<!--            :label="item.dictLabel"-->
+<!--            :value="item.dictValue"-->
+<!--          />-->
+<!--        </el-select>-->
+<!--      </el-form-item>-->
+
+<!--      <el-form-item label="结算状态" prop="deliveryPayStatus">-->
+<!--        <el-select v-model="queryParams.deliveryPayStatus" placeholder="请选择物流结算状态" clearable size="small">-->
+<!--          <el-option-->
+<!--            v-for="item in deliveryPayStatusOptions"-->
+<!--            :key="item.dictValue"-->
+<!--            :label="item.dictLabel"-->
+<!--            :value="item.dictValue"-->
+<!--          />-->
+<!--        </el-select>-->
+<!--      </el-form-item>-->
+
+<!--      <el-form-item label="小程序" prop="appId">-->
+<!--        <el-select v-model="queryParams.appId" placeholder="请选择所属小程序" clearable size="small">-->
+<!--          <el-option-->
+<!--            v-for="dict in appMallOptions"-->
+<!--            :key="dict.appid"-->
+<!--            :label="dict.name + '(' + dict.appid + ')'"-->
+<!--            :value="dict.appid"-->
+<!--          />-->
+<!--        </el-select>-->
+<!--      </el-form-item>-->
+
+<!--      <el-form-item label="商品规格" prop="productSpec">-->
+<!--        <el-input-->
+<!--          v-model="queryParams.productSpec"-->
+<!--          placeholder="请输入商品规格"-->
+<!--          clearable-->
+<!--          size="small"-->
+<!--          @keyup.enter.native="handleQuery"-->
+<!--        />-->
+<!--      </el-form-item>-->
+
+<!--      <el-form-item label="商品数量" prop="totalNum">-->
+<!--        <el-input-->
+<!--          v-model="queryParams.totalNum"-->
+<!--          placeholder="请输入商品数量"-->
+<!--          clearable-->
+<!--          size="small"-->
+<!--          @keyup.enter.native="handleQuery"-->
+<!--        />-->
+<!--      </el-form-item>-->
+
+<!--      <el-form-item label="销售价格" prop="price">-->
+<!--        <el-input-->
+<!--          v-model="queryParams.price"-->
+<!--          placeholder="请输入销售价格"-->
+<!--          clearable-->
+<!--          size="small"-->
+<!--          @keyup.enter.native="handleQuery"-->
+<!--        />-->
+<!--      </el-form-item>-->
+
+      <el-form-item label="收货地址" prop="userAddress">
+        <el-input
+          v-model="queryParams.userAddress"
+          placeholder="请输入收货地址"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+
+      <el-form-item label="商品ID" prop="productId">
+        <el-input
+          v-model="queryParams.productId"
+          placeholder="请输入商品ID"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+
+<!--      <el-form-item label="成本价格" prop="cost">-->
+<!--        <el-input-->
+<!--          v-model="queryParams.cost"-->
+<!--          placeholder="请输入成本价格"-->
+<!--          clearable-->
+<!--          size="small"-->
+<!--          @keyup.enter.native="handleQuery"-->
+<!--        />-->
+<!--      </el-form-item>-->
+
+<!--      <el-form-item label="供应商" prop="supplierName">-->
+<!--        <el-input-->
+<!--          v-model="queryParams.supplierName"-->
+<!--          placeholder="请输入供应商名称"-->
+<!--          clearable-->
+<!--          size="small"-->
+<!--          @keyup.enter.native="handleQuery"-->
+<!--        />-->
+<!--      </el-form-item>-->
+
+<!--      <el-form-item label="档期归属" prop="scheduleId">-->
+<!--        <el-select filterable style="width: 215px" v-model="queryParams.scheduleId" placeholder="请选择档期" clearable size="small">-->
+<!--          <el-option-->
+<!--            v-for="item in scheduleOptions"-->
+<!--            :key="item.id"-->
+<!--            :label="item.name"-->
+<!--            :value="item.id"-->
+<!--          />-->
+<!--        </el-select>-->
+<!--      </el-form-item>-->
+
+      <el-form-item label="代服账户" prop="erpAccount" v-if="SFDFopen">
+        <el-select v-model="queryParams.erpAccount" style="width: 215px" placeholder="ERP账户" clearable size="small">
+          <el-option
+            v-for="dict in erpAccountQueryList"
+            :key="dict"
+            :label="dict"
+            :value="dict"
+          />
+        </el-select>
+      </el-form-item>
+
+      <el-form-item label="代服电话" prop="erpPhoneNumber" v-if="SFDFopen">
+        <el-input
+          v-model="queryParams.erpPhoneNumber"
+          placeholder="ERP电话"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+
+      <el-form-item label="下单时间" prop="createTimeRange">
+        <el-date-picker
+          style="width:215px"
+          clearable
+          size="small"
+          v-model="createTimeRange"
+          type="datetimerange"
+          value-format="yyyy-MM-dd HH:mm:ss"
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          @change="handleOrderTimeChange"
+        />
+      </el-form-item>
+
+      <el-form-item label="支付时间" prop="payTimeRange">
+        <el-date-picker
+          style="width: 215px"
+          clearable
+          size="small"
+          v-model="payTimeRange"
+          type="datetimerange"
+          value-format="yyyy-MM-dd HH:mm:ss"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+        />
+      </el-form-item>
+
+<!--      <el-form-item label="发货时间" prop="deliverySendTimeRange">-->
+<!--        <el-date-picker-->
+<!--          style="width:215px"-->
+<!--          clearable-->
+<!--          size="small"-->
+<!--          v-model="deliverySendTimeRange"-->
+<!--          type="daterange"-->
+<!--          value-format="yyyy-MM-dd"-->
+<!--          start-placeholder="开始日期"-->
+<!--          end-placeholder="结束日期"-->
+<!--        />-->
+<!--      </el-form-item>-->
+
+<!--      <el-form-item label="回单时间" prop="deliveryImportTimeRange">-->
+<!--        <el-date-picker-->
+<!--          style="width:215px"-->
+<!--          clearable-->
+<!--          size="small"-->
+<!--          v-model="deliveryImportTimeRange"-->
+<!--          type="daterange"-->
+<!--          value-format="yyyy-MM-dd"-->
+<!--          start-placeholder="开始日期"-->
+<!--          end-placeholder="结束日期"-->
+<!--        />-->
+<!--      </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-->
+<!--          plain-->
+<!--          type="info"-->
+<!--          icon="el-icon-upload2"-->
+<!--          size="mini"-->
+<!--          @click="handleImport"-->
+<!--        >导入银行回单</el-button>-->
+<!--      </el-col>-->
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          icon="el-icon-download"
+          size="mini"
+          :loading="exportLoading"
+          @click="handleExport"
+          v-hasPermi="['live:order:export']"
+        >导出订单</el-button>
+      </el-col>
+<!--      <el-col :span="1.5">-->
+<!--        <el-button-->
+<!--          type="warning"-->
+<!--          icon="el-icon-download"-->
+<!--          size="mini"-->
+<!--          @click="handleExportItems"-->
+<!--        >导出订单明细</el-button>-->
+<!--      </el-col>-->
+<!--      <el-col :span="1.5">-->
+<!--        <el-button-->
+<!--          type="warning"-->
+<!--          icon="el-icon-s-order"-->
+<!--          size="mini"-->
+<!--          @click="openDeliveryNote"-->
+<!--        >批量导入物流单号</el-button>-->
+<!--      </el-col>-->
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          icon="el-icon-download"
+          size="mini"
+          @click="handleExportDetails"
+          v-hasPermi="['live:order:exportAll']"
+        >导出订单(明文)</el-button>
+      </el-col>
+<!--      <el-col :span="1.5">-->
+<!--        <el-button-->
+<!--          type="warning"-->
+<!--          icon="el-icon-download"-->
+<!--          size="mini"-->
+<!--          @click="handleExportItemsDetails"-->
+<!--        >导出订单明细(明文)</el-button>-->
+<!--      </el-col>-->
+      <el-col :span="1.5">
+        <el-button
+          icon="el-icon-tickets"
+          size="mini"
+          type="success"
+          :loading="exportShippingLoading"
+          @click="handleExportShippingOrder"
+        >导出发货单</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <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="-1"></el-tab-pane>
+      <el-tab-pane label="已退款" name="-2"></el-tab-pane>
+      <el-tab-pane label="已取消" name="-3"></el-tab-pane>
+    </el-tabs>
+
+    <el-table
+      ref="orderTable"
+      border
+      v-loading="loading"
+      :data="orderList"
+      @selection-change="handleSelectionChange"
+      :default-sort="{prop: 'createTime', order: 'descending'}"
+    >
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="订单类型" align="center" prop="orderTypeName" width="100px">
+        <template slot-scope="scope">
+          <el-tag :type="getOrderTypeTagType(scope.row.orderType)">
+            {{ scope.row.orderTypeName }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="订单号" align="center" prop="orderCode" width="200px" />
+      <el-table-column label="银行交易流水号" align="center" prop="bankTransactionId" width="200px" />
+      <el-table-column label="所属公司" align="center" prop="companyName" />
+      <el-table-column label="销售名称" align="center" prop="salesName" width="120px" />
+      <el-table-column label="员工昵称" align="center" prop="companyUserNickName" width="120px" />
+      <el-table-column label="用户昵称" align="center" prop="nickname" width="150px" />
+      <el-table-column label="收件人" align="center" prop="realName" width="120px" />
+      <el-table-column label="手机号" align="center" prop="userPhone" width="120px" />
+      <el-table-column label="商品规格" align="center" prop="productSpec" width="120px" />
+      <el-table-column label="商品数量" align="center" prop="totalNum" width="100px" />
+      <el-table-column label="订单金额" align="center" prop="totalPrice" width="100px">
+        <template slot-scope="scope">
+          <span v-if="scope.row.totalPrice != null">{{ scope.row.totalPrice.toFixed(2) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="应付金额" align="center" prop="payPrice" width="100px">
+        <template slot-scope="scope">
+          <span v-if="scope.row.payPrice != null">{{ scope.row.payPrice.toFixed(2) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="实付金额" align="center" prop="payMoney" width="100px">
+        <template slot-scope="scope">
+          <span v-if="scope.row.payMoney != null">{{ scope.row.payMoney.toFixed(2) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="下单时间" align="center" prop="createTime" width="180" />
+      <el-table-column label="支付时间" align="center" prop="payTime" width="180" />
+      <el-table-column label="支付方式" align="center" prop="payType" width="100px">
+        <template slot-scope="scope">
+          <el-tag v-for="(item, index) in payTypeOptions" :key="index" v-if="scope.row.payType == item.dictValue" size="mini">
+            {{ item.dictLabel }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="状态" align="center" prop="status" width="100px">
+        <template slot-scope="scope">
+          <el-tag v-for="(item, index) in statusOptions" :key="index" v-if="scope.row.status == item.dictValue" size="mini">
+            {{ item.dictLabel }}
+          </el-tag>
+        </template>
+      </el-table-column>
+<!--      <el-table-column label="物流状态" align="center" prop="deliveryStatus" width="100px">-->
+<!--        <template slot-scope="scope">-->
+<!--          <el-tag v-for="(item, index) in deliveryStatusOptions" :key="index" v-if="scope.row.deliveryStatus == item.dictValue" size="mini">-->
+<!--            {{ item.dictLabel }}-->
+<!--          </el-tag>-->
+<!--        </template>-->
+<!--      </el-table-column>-->
+      <el-table-column label="运单号" align="center" prop="deliveryId" width="150px" />
+      <el-table-column label="商品ID" align="center" prop="productId" width="120px" />
+      <el-table-column label="商品名称" align="center" prop="productName" width="150px" />
+      <el-table-column label="条码" align="center" prop="barCode" width="120px" />
+      <el-table-column label="分类" align="center" prop="cateName" width="120px" />
+      <el-table-column label="运费" align="center" prop="payDelivery" width="100px">
+        <template slot-scope="scope">
+          <span v-if="scope.row.payDelivery != null">{{ scope.row.payDelivery }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="成本价" align="center" prop="cost" width="100px">
+        <template slot-scope="scope">
+          <span v-if="scope.row.cost != null">{{ scope.row.cost }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" fixed="right" width="80px" align="center">
+        <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-drawer
+      :title="drawerTitle"
+      :visible.sync="drawerVisible"
+      direction="rtl"
+      size="75%"
+      :before-close="closeDrawer"
+    >
+      <div v-loading="drawerLoading" style="min-height: 200px;">
+        <!-- 直播订单详情 -->
+        <liveDetail
+          v-if="currentOrderType === 3 && drawerVisible"
+          ref="liveDetailRef"
+          :order-id="currentOrderId"
+        />
+        <!-- 销售订单详情 -->
+        <userDetail
+          v-if="currentOrderType === 1 && drawerVisible"
+          ref="userDetailRef"
+          :order-id="currentOrderId"
+        />
+        <!-- 商城订单详情 -->
+        <storeDetail
+          v-if="currentOrderType === 2 && drawerVisible"
+          ref="storeDetailRef"
+          :order-id="currentOrderId"
+        />
+      </div>
+    </el-drawer>
+
+    <!-- 批量导入物流单号对话框 -->
+    <el-dialog
+      :before-close="cancelResetDeliveryNote"
+      :visible.sync="deliveryNoteOpen"
+      center
+      title="批量发货"
+      width="35%"
+    >
+      <el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
+        <el-form-item label="小程序:" prop="miniAppId">
+          <el-select
+            v-model="ruleForm.miniAppId"
+            clearable
+            placeholder="请选择发货小程序"
+            style="width: 100%"
+          >
+            <el-option
+              v-for="item in miniAppList"
+              :key="item.appId"
+              :label="item.appName"
+              :value="item.appId"
+            />
+          </el-select>
+        </el-form-item>
+      </el-form>
+      <el-upload ref="upload" :action="uploadUrl" :auto-upload="false" :disabled="orderUpload.isUploading" :headers="orderUpload.headers"
+                 :limit="1" :on-progress="handleFileUploadProgress"
+                 :on-success="handleFileSuccess" accept=".xlsx, .xls" drag
+      >
+        <i class="el-icon-upload"></i>
+        <div class="el-upload__text">
+          将文件拖到此处,或
+          <em>点击上传</em>
+        </div>
+        <div slot="tip" class="el-upload__tip">
+          <el-link style="font-size:12px" type="info" @click="importDeliveryNoteTemplate">下载模板</el-link>
+        </div>
+        <div slot="tip" class="el-upload__tip" style="color:red">提示:仅允许导入"xls"或"xlsx"格式文件!</div>
+      </el-upload>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="cancelResetDeliveryNote">取 消</el-button>
+        <el-button type="primary" @click="submitDeliveryNote('ruleForm')">确 定</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 导入银行回单对话框 -->
+    <el-dialog :title="upload.title" :visible.sync="upload.open" append-to-body width="400px">
+      <el-upload ref="upload" :action="upload.url + '?updateSupport=' + upload.updateSupport" :auto-upload="false" :disabled="upload.isUploading"
+                 :headers="upload.headers" :limit="1"
+                 :on-progress="handleFileUploadProgress" :on-success="handleFileSuccess" accept=".xlsx, .xls" drag
+      >
+        <i class="el-icon-upload"></i>
+        <div class="el-upload__text">
+          将文件拖到此处,或
+          <em>点击上传</em>
+        </div>
+        <div slot="tip" class="el-upload__tip" style="color:red">提示:仅允许导入"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>
+  </div>
+</template>
+
+<script>
+import { listMergedOrder, exportMergedOrder, exportMergedShipping, exportMergedOrderItems, exportMergedOrderDetails, exportMergedOrderItemsDetails, importDeliveryNoteExpressTemplate } from '@/api/live/mergedOrder'
+import { getCompanyList } from '@/api/company/company'
+import { treeselect } from '@/api/company/companyDept'
+import Treeselect from '@riophae/vue-treeselect'
+import '@riophae/vue-treeselect/dist/vue-treeselect.css'
+import liveDetail from './liveDetail.vue'
+import userDetail from './userDetail.vue'
+import storeDetail from './storeDetail.vue'
+import { getToken } from '@/utils/auth'
+import { getTcmScheduleList } from '@/api/company/schedule'
+import { list as getAppMallOptions } from '@/api/course/coursePlaySourceConfig'
+import { getErpAccount } from '@/api/hisStore/storeOrder'
+import { getConfigByKey } from '@/api/system/config'
+
+export default {
+  name: 'MergedOrder',
+  components: {Treeselect, liveDetail, userDetail, storeDetail },
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      exportLoading: false,
+      exportShippingLoading: false,
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 订单表格数据
+      orderList: [],
+      // 公司列表
+      companys: [],
+      companyId: null,
+      // 部门树选项
+      deptOptions: [],
+      // 字典
+      payTypeOptions: [],
+      deliveryStatusOptions: [],
+      deliveryPayStatusOptions: [],
+      statusOptions: [],
+      orderTypeOptions: [],
+      appMallOptions: [],
+      scheduleOptions: [],
+      erpAccountQueryList: [],
+      SFDFopen: false,
+      // 订单号标签输入相关
+      maxOrderCodes: 50,
+      inputVisible: false,
+      currentInput: '',
+      // 弹出层标题
+      title: '',
+      // 是否显示弹出层
+      open: false,
+      createTimeRange: [],
+      payTimeRange: [],
+      deliverySendTimeRange: [],
+      deliveryImportTimeRange: [],
+      activeName: '00',
+      // 侧边栏相关
+      drawerVisible: false,
+      drawerTitle: '订单详情',
+      currentOrderType: null,
+      currentOrderId: null,
+      drawerLoading: false,
+      // 上传相关
+      upload: {
+        open: false,
+        title: '',
+        isUploading: false,
+        updateSupport: 0,
+        headers: { Authorization: 'Bearer ' + getToken() },
+        url: process.env.VUE_APP_BASE_API + '/order/importExpress'
+      },
+      orderUpload: {
+        open: false,
+        title: '',
+        isUploading: false,
+        updateSupport: 0,
+        headers: { Authorization: 'Bearer ' + getToken() }
+      },
+      deliveryNoteOpen: false,
+      miniAppList: [],
+      ruleForm: {
+        miniAppId: null
+      },
+      rules: {
+        miniAppId: [
+          { required: true, message: '发货小程序不能为空' }
+        ]
+      },
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        companyId: null,
+        deptId: null,
+        orderTypeFilter: 1,
+        salesName: null,
+        orderCodes: [],
+        orderCode: null,
+        orderCodeList: null,
+        deliveryId: null,
+        userPhone: null,
+        realName: null,
+        productName: null,
+        bankTransactionId: null,
+        productSpec: null,
+        productId: null,
+        totalNum: null,
+        price: null,
+        userAddress: null,
+        cost: null,
+        supplierName: null,
+        companyUserNickName: null,
+        orderType: null,
+        isUpload: null,
+        payType: null,
+        deliveryStatus: null,
+        deliveryPayStatus: null,
+        appId: null,
+        scheduleId: null,
+        erpAccount: null,
+        erpPhoneNumber: null,
+        status: null,
+        createTimeStart: null,
+        createTimeEnd: null,
+        createTimeRange: null,
+        payTimeRange: null,
+        deliverySendTimeRange: null,
+        deliveryImportTimeRange: null
+      }
+    }
+  },
+  created() {
+    // 初始化公司列表
+    getCompanyList().then(response => {
+      this.companys = response.data || []
+      if (this.companys != null && this.companys.length > 0) {
+        this.companyId = this.companys[0].companyId
+        this.getTreeselect()
+      }
+    }).catch(error => {
+      console.error('获取公司列表失败:', error)
+      this.companys = []
+    })
+
+    // 初始化支付方式字典
+    this.getDicts('store_pay_type').then((response) => {
+      this.payTypeOptions = response.data || []
+    }).catch(error => {
+      console.error('获取支付方式字典失败:', error)
+      this.payTypeOptions = []
+    })
+
+    // 初始化订单状态字典
+    this.getDicts('store_order_status').then((response) => {
+      this.statusOptions = response.data || []
+    }).catch(error => {
+      console.error('获取订单状态字典失败:', error)
+      this.statusOptions = []
+    })
+
+    // 初始化物流状态字典
+    this.getDicts('store_order_delivery_status').then((response) => {
+      this.deliveryStatusOptions = response.data || []
+    }).catch(error => {
+      console.error('获取物流状态字典失败:', error)
+      this.deliveryStatusOptions = []
+    })
+
+    // 初始化物流结算状态字典
+    this.getDicts('store_delivery_pay_status').then((response) => {
+      this.deliveryPayStatusOptions = response.data || []
+    }).catch(error => {
+      console.error('获取物流结算状态字典失败:', error)
+      this.deliveryPayStatusOptions = []
+    })
+
+    // // 初始化订单类型字典
+    // this.getDicts('store_order_type').then((response) => {
+    //   this.orderTypeOptions = response.data || []
+    // }).catch(error => {
+    //   console.error('获取订单类型字典失败:', error)
+    //   this.orderTypeOptions = []
+    // })
+
+    // 初始化档期归属列表
+    // getTcmScheduleList().then(response => {
+    //   this.scheduleOptions = response.data || []
+    // }).catch(error => {
+    //   console.error('获取档期归属列表失败:', error)
+    //   this.scheduleOptions = []
+    // })
+
+    // 初始化小程序列表
+    this.getAppMallOptions()
+
+    // 初始化ERP账户列表
+    this.getErpAccountList()
+
+    // 加载订单列表
+    this.getList()
+  },
+  computed: {
+    uploadUrl() {
+      return process.env.VUE_APP_BASE_API +
+        '/order/importDeliveryNoteExpress?miniAppId=' +
+        this.ruleForm.miniAppId
+    }
+  },
+  methods: {
+    /** 查询订单列表 */
+    getList() {
+      this.loading = true
+      if (this.queryParams.status == '00') {
+        this.queryParams.status = null
+      }
+
+      // 处理订单号数组
+      if (this.queryParams.orderCodes && this.queryParams.orderCodes.length > 0) {
+        this.queryParams.orderCodeList = this.queryParams.orderCodes.join(',')
+      } else {
+        this.queryParams.orderCodeList = 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.deliverySendTimeRange != null && this.deliverySendTimeRange.length === 2) {
+        this.queryParams.deliverySendTimeRange = this.deliverySendTimeRange[0] + '--' + this.deliverySendTimeRange[1]
+      } else {
+        this.queryParams.deliverySendTimeRange = null
+      }
+
+      if (this.deliveryImportTimeRange != null && this.deliveryImportTimeRange.length === 2) {
+        this.queryParams.deliveryImportTimeRange = this.deliveryImportTimeRange[0] + '--' + this.deliveryImportTimeRange[1]
+      } else {
+        this.queryParams.deliveryImportTimeRange = null
+      }
+
+      listMergedOrder(this.queryParams).then(response => {
+        this.orderList = response.rows
+        this.total = response.total
+        this.loading = false
+        if (response.msg == 'knt') {
+          this.SFDFopen = true
+        } else {
+          this.SFDFopen = false
+        }
+      }).catch(error => {
+        console.error('查询失败:', error)
+        this.loading = false
+        this.$message.error('查询数据失败')
+      })
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1
+      this.getList()
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.createTimeRange = []
+      this.payTimeRange = []
+      this.deliverySendTimeRange = []
+      this.deliveryImportTimeRange = []
+      this.queryParams.orderCodes = []
+      this.currentInput = ''
+      this.inputVisible = false
+      this.resetForm('queryForm')
+      this.handleQuery()
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id || item.orderId)
+      this.single = selection.length !== 1
+      this.multiple = !selection.length
+    },
+    handleClick(tab, event) {
+      this.activeName = tab.name
+      this.queryParams.status = tab.name
+      this.getList()
+    },
+    handleOrderTimeChange(value) {
+      if (value && value.length === 2) {
+        this.queryParams.createTimeStart = value[0]
+        this.queryParams.createTimeEnd = value[1]
+      } else {
+        this.queryParams.createTimeStart = null
+        this.queryParams.createTimeEnd = null
+      }
+    },
+    /** 导出订单 */
+    handleExport() {
+      this.prepareExportParams()
+      this.$confirm('是否确认导出所有订单数据项?', '警告', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        this.exportLoading = true
+        return exportMergedOrder(this.queryParams)
+      }).then(response => {
+        this.download(response.msg)
+        this.exportLoading = false
+      }).catch(() => {
+        this.exportLoading = false
+      })
+    },
+    /** 导出发货单 */
+    handleExportShippingOrder() {
+      this.prepareExportParams()
+      this.$confirm('是否确认导出所有订单明细数据项?', '警告', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        this.exportShippingLoading = true
+        return exportMergedShipping(this.queryParams)
+      }).then(response => {
+        this.download(response.msg)
+        this.exportShippingLoading = false
+      }).catch(() => {
+        this.exportShippingLoading = false
+      })
+    },
+    handleDetails(row) {
+      // 根据订单类型打开对应的侧边栏详情
+      this.currentOrderType = row.orderType
+
+      if (row.orderType === 1) {
+        // 销售订单
+        this.currentOrderId = row.id
+        this.drawerTitle = '销售订单详情'
+      } else if (row.orderType === 2) {
+        // 商城订单
+        this.currentOrderId = row.id
+        this.drawerTitle = '商城订单详情'
+      } else if (row.orderType === 3) {
+        // 直播订单
+        this.currentOrderId = row.orderId
+        this.drawerTitle = '直播订单详情'
+      }
+
+      this.drawerVisible = true
+    },
+    closeDrawer() {
+      this.drawerVisible = false
+      this.currentOrderType = null
+      this.currentOrderId = null
+    },
+    getOrderTypeTagType(orderType) {
+      const typeMap = {
+        1: 'success', // 销售订单
+        2: 'warning', // 商城订单
+        3: 'info' // 直播订单
+      }
+      return typeMap[orderType] || ''
+    },
+    /** 查询部门下拉树结构 */
+    getTreeselect() {
+      var param = { companyId: this.companyId }
+      treeselect(param).then((response) => {
+        this.deptOptions = response.data
+      })
+    },
+    companyChange(val) {
+      this.companyId = val
+      this.getTreeselect()
+    },
+    // 订单号标签输入相关方法
+    handleKeyDown(event) {
+      const { key, target } = event
+      if (key === 'Backspace' && !target.value && this.queryParams.orderCodes.length > 0) {
+        event.preventDefault()
+        this.removeOrderCode(this.queryParams.orderCodes.length - 1)
+      }
+      if ([',', ',', ' ', 'Enter'].includes(key)) {
+        event.preventDefault()
+        this.handleInputConfirm()
+      }
+    },
+    handleKeyUp(event) {
+      const value = event.target.value
+      if (/[,,\s]/.test(value)) {
+        this.handleInputConfirm()
+      }
+    },
+    handleInputConfirm() {
+      const inputValue = this.currentInput.trim()
+      if (inputValue) {
+        const codes = inputValue.split(/[,,\s]+/).filter(code => code.trim())
+        codes.forEach(code => {
+          this.addOrderCode(code.trim())
+        })
+      }
+      this.currentInput = ''
+    },
+    addOrderCode(code) {
+      if (!code) return
+      if (this.maxOrderCodes > 0 && this.queryParams.orderCodes.length >= this.maxOrderCodes) {
+        this.$message.warning(`最多只能添加 ${this.maxOrderCodes} 个订单号`)
+        return
+      }
+      if (this.queryParams.orderCodes.includes(code)) {
+        this.$message.warning(`订单号 "${code}" 已存在`)
+        return
+      }
+      this.queryParams.orderCodes.push(code)
+    },
+    removeOrderCode(index) {
+      this.queryParams.orderCodes.splice(index, 1)
+    },
+    showInput() {
+      this.inputVisible = true
+      this.$nextTick(() => {
+        if (this.$refs.tagInput) {
+          this.$refs.tagInput.focus()
+        }
+      })
+    },
+    focusInput() {
+      if (!this.inputVisible) {
+        this.showInput()
+      }
+    },
+    // 导出相关方法
+    handleExportItems() {
+      this.prepareExportParams()
+      this.$confirm('是否确认导出所有订单明细数据项?', '警告', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        return exportMergedOrderItems(this.queryParams)
+      }).then(response => {
+        this.download(response.msg)
+      }).catch(() => {})
+    },
+    handleExportDetails() {
+      this.prepareExportParams()
+      this.$confirm('是否确认导出所有订单数据项(明文)?', '警告', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        return exportMergedOrderDetails(this.queryParams)
+      }).then(response => {
+        this.download(response.msg)
+      }).catch(() => {})
+    },
+    handleExportItemsDetails() {
+      this.prepareExportParams()
+      this.$confirm('是否确认导出所有订单明细数据项(明文)?', '警告', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        return exportMergedOrderItemsDetails(this.queryParams)
+      }).then(response => {
+        this.download(response.msg)
+      }).catch(() => {})
+    },
+    prepareExportParams() {
+      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.deliverySendTimeRange != null && this.deliverySendTimeRange.length === 2) {
+        this.queryParams.deliverySendTimeRange = this.deliverySendTimeRange[0] + '--' + this.deliverySendTimeRange[1]
+      } else {
+        this.queryParams.deliverySendTimeRange = null
+      }
+      if (this.deliveryImportTimeRange != null && this.deliveryImportTimeRange.length === 2) {
+        this.queryParams.deliveryImportTimeRange = this.deliveryImportTimeRange[0] + '--' + this.deliveryImportTimeRange[1]
+      } else {
+        this.queryParams.deliveryImportTimeRange = null
+      }
+      if (this.queryParams.orderCodes && this.queryParams.orderCodes.length > 0) {
+        this.queryParams.orderCodeList = this.queryParams.orderCodes.join(',')
+      } else {
+        this.queryParams.orderCodeList = null
+      }
+    },
+    // 导入相关方法
+    handleImport() {
+      this.upload.title = '导入银行回单'
+      this.upload.open = true
+    },
+    submitFileForm() {
+      this.$refs.upload.submit()
+    },
+    handleFileUploadProgress(event, file, fileList) {
+      this.upload.isUploading = true
+    },
+    handleFileSuccess(response, file, fileList) {
+      this.upload.open = false
+      this.upload.isUploading = false
+      if (this.$refs.upload) {
+        this.$refs.upload.clearFiles()
+      }
+      this.$message.success('导入成功')
+      this.getList()
+    },
+    // 批量导入物流单号
+    openDeliveryNote() {
+      this.deliveryNoteOpen = true
+      this.getAppList()
+    },
+    getAppList() {
+      this.miniAppList = []
+      const key = 'courseMa.config'
+      getConfigByKey(key).then(response => {
+        const { code, data } = response
+        if (code === 200) {
+          let value = data?.configValue
+          if (value) {
+            try {
+              const appList = JSON.parse(value)
+              this.miniAppList = appList.filter(v => v.type === '1').map(v => {
+                return { appId: v.appid, appName: v.name }
+              })
+            } catch (parseError) {
+              console.error('解析小程序配置失败:', parseError)
+              this.miniAppList = []
+            }
+          }
+        }
+      }).catch(error => {
+        console.error('获取小程序配置失败:', error)
+        this.miniAppList = []
+      })
+    },
+    importDeliveryNoteTemplate() {
+      importDeliveryNoteExpressTemplate().then((response) => {
+        this.download(response.msg)
+      })
+    },
+    submitDeliveryNote(formName) {
+      this.$refs[formName].validate((valid) => {
+        if (valid) {
+          const uploadFiles = this.$refs.upload.uploadFiles
+          if (uploadFiles.length === 0) {
+            this.$message.error('请选择要上传的文件')
+            return
+          }
+          this.$refs.upload.submit()
+        } else {
+          return false
+        }
+      })
+    },
+    cancelResetDeliveryNote() {
+      this.deliveryNoteOpen = false
+      this.resetForm('ruleForm')
+    },
+    // 获取小程序选项列表
+    getAppMallOptions() {
+      getAppMallOptions({ pageNum: 1, pageSize: 100, isMall: 1 }).then(response => {
+        this.appMallOptions = response.rows || []
+      }).catch(error => {
+        console.error('获取小程序选项列表失败:', error)
+        this.appMallOptions = []
+      })
+    },
+    // 获取ERP账户列表
+    async getErpAccountList() {
+      try {
+        const response = await getErpAccount()
+        if (response.code === 200) {
+          const list = response.data || []
+          this.erpAccountQueryList = [...list, '未分拣']
+        } else {
+          this.erpAccountQueryList = []
+        }
+      } catch (error) {
+        console.error('获取ERP账户列表失败:', error)
+        this.erpAccountQueryList = []
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+.tag-input-container {
+  min-width: 445px;
+}
+
+.tags-wrapper {
+  min-height: 32px;
+  padding: 4px 8px;
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+  cursor: text;
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  gap: 4px;
+  transition: border-color 0.2s;
+}
+
+.tags-wrapper:hover {
+  border-color: #c0c4cc;
+}
+
+.tags-wrapper:focus-within {
+  border-color: #409eff;
+  box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
+}
+
+.order-tag {
+  margin: 2px;
+  flex-shrink: 0;
+}
+
+.tag-input {
+  border: none;
+  outline: none;
+  flex: 1;
+  min-width: 120px;
+}
+
+.tag-input >>> .el-input__inner {
+  border: none;
+  padding: 0;
+  height: 24px;
+  line-height: 24px;
+}
+
+.button-new-tag {
+  height: 24px;
+  line-height: 22px;
+  padding: 0 8px;
+  margin: 2px;
+}
+
+.input-tips {
+  margin-top: 4px;
+  font-size: 12px;
+  color: #909399;
+}
+
+.tip-text {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+</style>
+

+ 1118 - 0
src/views/live/order/liveDetail.vue

@@ -0,0 +1,1118 @@
+<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" >
+        <el-steps  :active="item.status+1" align-center>
+          <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 orderOptions"    v-if="item.status==ite.dictValue">{{ite.dictLabel}}</el-tag>
+               </span>
+          <div class="operate-button-container" v-if="item.status==3">
+            <el-button size="mini" @click="finishOrder()" v-hasPermi="['his:liveOrder:good']">确认收货</el-button>
+          </div>
+          <div class="operate-button-container"  v-if="item.status==2&&item.orderType==1">
+            <el-button size="mini" @click="sendVisible=true" v-hasPermi="['his:liveOrder:sendGoods']">发货</el-button>
+          </div>
+          <div class="operate-button-container"  v-if="item.status==2&&item.orderType==2">
+            <el-button size="mini" @click="tuiOrder()" v-hasPermi="['his:liveOrder:sendHisGoods']" >推送订单</el-button>
+          </div>
+          <div class="operate-button-container"   v-if="item.deliverySn!=null" v-hasPermi="['his:liveOrder:express']">
+            <el-button size="mini" @click="showExpress()" >查看物流</el-button>
+          </div>
+          <div class="operate-button-container" >
+            <el-button size="mini" @click="editDelivery()"  v-hasPermi="['his:liveOrder:updateDelivery']" >修改物流单号</el-button>
+          </div>
+
+          <div class="operate-button-container" >
+            <el-button size="mini" @click="editOrder()"  v-hasPermi="['his:liveOrder:edit']" >修改订单</el-button>
+          </div>
+          <div class="operate-button-container" v-if="item.extendOrderId == null"  >
+            <el-button size="mini" @click="addErpOrder()" >创建ERP订单信息</el-button>
+          </div>
+          <div class="operate-button-container" v-if="item.extendOrderId!=null"  >
+            <el-button size="mini" @click="showErpOrder()" >ERP订单信息</el-button>
+          </div>
+          <div class="operate-button-container" v-if="item.status>1">
+            <el-button size="mini" @click="refund()" v-hasPermi="['his:liveOrder:refundOrderMoney']">退款</el-button>
+          </div>
+        </div>
+        <div class="desct">
+          基本信息
+        </div>
+        <el-descriptions :column="3" border  >
+          <el-descriptions-item label="订单编号"  ><span v-if="item!=null">{{item.orderCode}}</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="支付方式"  ><dict-tag :options="PayOptions" :value="item.payType"/></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:liveOrder:queryPhone']"></el-button>
+          </el-descriptions-item>
+          <el-descriptions-item label="收货地址" > <span>{{item.userAddress}}</span>  </el-descriptions-item>
+          <el-descriptions-item label="所属公司"><span v-if="item!=null">{{item.companyName}}</span></el-descriptions-item>
+          <el-descriptions-item label="员工"><span v-if="item!=null">{{item.companyUserName}}</span></el-descriptions-item>
+          <el-descriptions-item label="推广佣金" ><span v-if="item.tuiMoney!=null">{{item.tuiMoney.toFixed(2)}}</span></el-descriptions-item>
+          <el-descriptions-item label="推广佣金状态" ><span v-if="item!=null"><dict-tag :options="tuiOptions" :value="item.tuiMoneyStatus"/> </span></el-descriptions-item>
+          <el-descriptions-item label="ERP编号" ><span v-if="item!=null">{{item.extendOrderId}}</span></el-descriptions-item>
+          <el-descriptions-item label="用户备注" ><span v-if="item!=null">{{item.remark}}</span></el-descriptions-item>
+          <el-descriptions-item label="档期归属" >
+            <el-tag prop="scheduleId" v-for="(schedule, index) in scheduleOptions"    v-if="item!=null&&item.scheduleId==schedule.id">{{schedule.name}}
+            </el-tag>
+          </el-descriptions-item>
+          <el-descriptions-item label="订单购买类型" ><span v-if="item!=null"><dict-tag :options="orderBuyTypeOptions" :value="item.orderBuyType"/></span></el-descriptions-item>
+          <el-descriptions-item label=" 公众号/渠道" ><span v-if="item!=null">{{item.channel}}</span></el-descriptions-item>
+          <el-descriptions-item label=" 渠道" ><span v-if="item!=null"><dict-tag :options="channelOptions" :value="item.orderChannel"/></span></el-descriptions-item>
+          <el-descriptions-item label=" 企微主体" ><span v-if="item!=null"><dict-tag :options="qwSubjectOptions" :value="item.qwSubject"/></span></el-descriptions-item>
+        </el-descriptions>
+      </el-card>
+    </div>
+<!--    <div class="contentx" v-if="item!=null" style="padding-bottom: 70px;">-->
+<!--      <div style="margin-top: 20px">-->
+<!--        <div class="desct">-->
+<!--          物流信息-->
+<!--        </div>-->
+<!--        &nbsp;-->
+<!--        <el-link  type="primary" @click="editDelivery(null)">添加物流信息</el-link>-->
+<!--      </div>-->
+<!--      <el-table-->
+<!--        border-->
+<!--        :data="deliverList"-->
+<!--        size="small"-->
+<!--        style="width: 100%;margin-top: 20px" >-->
+<!--        <el-table-column label="物流公司编码" width="150" align="center">-->
+<!--          <template slot-scope="scope">-->
+<!--            <p>{{scope.row.deliverSn}}</p>-->
+<!--          </template>-->
+<!--        </el-table-column>-->
+<!--        <el-table-column label="物流公司名称" width="300" align="center">-->
+<!--          <template slot-scope="scope">-->
+<!--            <p>{{scope.row.deliverName}}</p>-->
+<!--          </template>-->
+<!--        </el-table-column>-->
+<!--        <el-table-column label="物流单号" width="300" align="center">-->
+<!--          <template slot-scope="scope">-->
+<!--            <p>{{scope.row.deliverId}}</p>-->
+<!--          </template>-->
+<!--        </el-table-column>-->
+<!--        <el-table-column label="物流状态" width="300" align="center">-->
+<!--          <template slot-scope="scope">-->
+<!--              <span>-->
+<!--              <el-tag v-for="(item, index) in deliveryStatusOptions"    v-if="scope.row!=null&&scope.row.status==item.dictValue">-->
+<!--              {{item.dictLabel}}-->
+<!--              </el-tag>-->
+<!--              </span>-->
+<!--          </template>-->
+<!--        </el-table-column>-->
+<!--        <el-table-column label="发货时间" width="240" align="center">-->
+<!--          <template slot-scope="scope">-->
+<!--            {{scope.row.deliverySendTime}}-->
+<!--          </template>-->
+<!--        </el-table-column>-->
+<!--        <el-table-column label="操作" width="240" align="center">-->
+<!--          <template slot-scope="scope">-->
+<!--            <el-link  type="primary" @click="showExpress(scope)">查看物流跟踪</el-link>-->
+<!--            &nbsp;&nbsp;-->
+<!--            <el-link  type="primary" @click="editDelivery(scope)">修改物流</el-link>-->
+<!--          </template>-->
+<!--        </el-table-column>-->
+<!--      </el-table>-->
+<!--    </div>-->
+    <div class="contentx" v-if="item!=null" style="padding-bottom: 70px;">
+      <div class="desct">
+        商品信息
+      </div>
+      <el-tooltip class="item" effect="dark" :content="showList ? '显示全部' : '隐藏'" placement="top" style="float: right;">
+        <el-button size="mini" circle icon="el-icon-search" @click="showListD()" />
+      </el-tooltip>
+      <el-table border v-if="showProd!=null" :data="showProd" size="small" style="width: 100%;margin-top: 20px" >
+        <el-table-column label="商品图片" width="150" align="center">
+          <template slot-scope="scope">
+            <img :src="scope.row.imgUrl" style="height: 80px">
+          </template>
+        </el-table-column>
+        <el-table-column label="商品编码" width="300" align="center">
+          <template slot-scope="scope">
+            <p>{{scope.row.barCode}}</p>
+          </template>
+        </el-table-column>
+        <el-table-column label="仓库代码" width="300" align="center">
+          <template slot-scope="scope">
+            <p>{{scope.row.warehouseCode}}</p>
+          </template>
+        </el-table-column>
+        <el-table-column label="商品名称" width="300" align="center">
+          <template slot-scope="scope">
+            <p>{{scope.row.productName}}</p>
+          </template>
+        </el-table-column>
+        <el-table-column label="单价" width="240" align="center">
+          <template slot-scope="scope">
+            <p>¥{{scope.row.price.toFixed(2)}}</p>
+          </template>
+        </el-table-column>
+        <el-table-column label="市场价" width="240" align="center">
+          <template slot-scope="scope">
+            {{scope.row.otPrice}}
+          </template>
+        </el-table-column>
+      </el-table>
+      <div style="float: right;margin: 20px" v-if="item.totalPrice!=null">
+        合计:<span class="color-danger">¥{{item.totalPrice.toFixed(2)}}</span>
+      </div>
+    </div>
+    <div class="contentx" v-if="item!=null" style="padding-bottom: 70px;">
+      <div class="desct">
+        费用信息
+      </div>
+      <el-tooltip class="item" effect="dark" :content="showList ? '显示全部' : '隐藏'" placement="top" style="float: right;">
+        <el-button size="mini" circle icon="el-icon-search" @click="showListD()" />
+      </el-tooltip>
+      <el-descriptions :column="3" border  >
+        <el-descriptions-item label="商品合计"  ><span v-if="item!=null">{{item.totalPrice}}</span></el-descriptions-item>
+        <el-descriptions-item label="应付金额"><span v-if="item.totalPrice!=null">{{item.totalPrice}}</span></el-descriptions-item>
+        <el-descriptions-item label="运费"><span v-if="item.payDelivery!=null">{{item.payDelivery}}</span></el-descriptions-item>
+        <el-descriptions-item label="优惠券"  ><span v-if="item.discountMoney!=null"/>{{item.discountMoney}}</el-descriptions-item>
+        <el-descriptions-item label="积分抵扣" >  <span v-if="item!=null">{{item.payIntegral}}</span>  </el-descriptions-item>
+        <el-descriptions-item label="实付金额" >  <span v-if="item!=null">{{item.payMoney}}</span>  </el-descriptions-item>
+        <el-descriptions-item label="代收金额" >  <span v-if="item!=null">{{item.payRemain}}</span>  </el-descriptions-item>
+        <el-descriptions-item label="服务费" >  <span v-if="item!=null">{{0.00}}</span>  </el-descriptions-item>
+      </el-descriptions>
+      <div style="float: right;margin: 20px" v-if="item.totalPrice!=null">
+        合计:<span class="color-danger">¥{{item.totalPrice.toFixed(2)}}</span>
+      </div>
+    </div>
+
+    <div class="contentx" v-if="payments!=null" style="padding-bottom: 70px;">
+      <div class="desct">
+        支付信息
+      </div>
+    <el-table
+      border
+      :data="payments"
+      size="small"
+      style="width: 100%;margin-top: 20px" >
+      <el-table-column label="支付单号" align="center" prop="payCode" width="120px" />
+      <el-table-column label="支付金额" align="center" prop="payMoney" />
+      <el-table-column label="类型" align="center" prop="payTypeCode" />
+      <el-table-column label="交易单号" align="center" prop="bankTransactionId" />
+      <el-table-column label="银行单号" align="center" prop="bankSerialNo" />
+      <el-table-column label="创建时间" align="center" prop="createTime" />
+      <el-table-column label="支付时间" align="center" prop="payTime" />
+    </el-table>
+    </div>
+
+    <div class="contentx" v-if="logs!=null" style="padding-bottom: 70px;">
+
+      <div class="desct">
+        操作信息
+      </div>
+    <el-table style="margin-top: 20px;width: 100%"
+              ref="orderHistoryTable"
+              :data="logs" border>
+      <el-table-column label="操作时间"  width="160" align="center">
+        <template slot-scope="scope">
+          {{scope.row.changeTime}}
+        </template>
+      </el-table-column>
+      <el-table-column label="备注" align="center">
+        <template slot-scope="scope">
+          {{scope.row.changeMessage}}
+        </template>
+      </el-table-column>
+    </el-table>
+</div>
+
+    <div class="contentx" v-if="tuiMoneyLogs!=null" style="padding-bottom: 70px;">
+
+      <div class="desct">
+        分佣信息
+      </div>
+    <el-table
+      border
+      :data="tuiMoneyLogs"
+      size="small"
+      style="width: 100%;margin-top: 20px" >
+      <el-table-column label="公司名称" align="center" prop="companyName" width="120px" />
+      <el-table-column label="金额" align="center" prop="money" />
+      <el-table-column label="余额" align="center" prop="balance" />
+      <el-table-column label="创建时间" align="center" prop="createTime" />
+      <el-table-column label="备注" align="center" prop="remark" />
+    </el-table>
+    </div>
+
+    <el-dialog :title="edit.title" :visible.sync="edit.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 orderOptions "
+              :key="dict.dictValue"
+              :label="dict.dictLabel"
+              :value="dict.dictValue"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="物流状态" prop="deliveryStatus" >
+          <el-select v-model="editForm.deliveryStatus" placeholder="请选择物流状态" clearable size="small" filterable>
+            <el-option
+              v-for="dict in deliveryStatusOptions "
+              :key="dict.dictValue"
+              :label="dict.dictLabel"
+              :value="dict.dictValue"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="物流跟踪状态" prop="deliveryType" >
+          <el-select v-model="editForm.deliveryType" placeholder="请选择状态" clearable size="small" filterable>
+            <el-option
+              v-for="dict in deliveryTypeOptions "
+              :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>
+
+
+    <el-dialog :title="erpDialog.title" :visible.sync="erpDialog.open" width="600px" append-to-body>
+      <div v-if="order!=null&&order.extendOrderId!=null&&order.status==1"  v-hasPermi="['store:storeOrder:updateExpress']"  >
+        <el-button size="mini" @click="updateExpress()" >同步物流发货</el-button>
+      </div>
+      <div class="table-layout"  v-if="erpOrder!=null">
+        <el-row>
+          <el-col :span="6" class="table-cell-title">订单编号</el-col>
+          <el-col :span="6" class="table-cell-title">是否代收</el-col>
+          <el-col :span="6" class="table-cell-title">快递编号</el-col>
+          <el-col :span="6" class="table-cell-title">快递名称</el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="6" class="table-cell">
+            {{erpOrder.code}}
+          </el-col>
+          <el-col :span="6" class="table-cell">
+            {{erpOrder.cod?'是':'否'}}
+          </el-col>
+          <el-col :span="6" class="table-cell">
+            {{erpOrder.express_code}}
+          </el-col>
+          <el-col :span="6" class="table-cell">
+            {{erpOrder.express_name}}
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="6" class="table-cell-title">收货人</el-col>
+          <el-col :span="6" class="table-cell-title">电话</el-col>
+          <el-col :span="6" class="table-cell-title">地址</el-col>
+          <el-col :span="6" class="table-cell-title">运单号</el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="6" class="table-cell">
+            {{erpOrder.receiver_name}}
+          </el-col>
+          <el-col :span="6" class="table-cell">
+            {{erpOrder.receiver_mobile}}
+          </el-col>
+          <el-col :span="6" class="table-cell">
+            {{erpOrder.receiver_address}}
+          </el-col>
+          <el-col :span="6" class="table-cell">
+            {{erpOrder.deliverys!=null&&erpOrder.deliverys.length>0?erpOrder.deliverys[0].mail_no:''}}
+          </el-col>
+
+        </el-row>
+      </div>
+    </el-dialog>
+
+    <el-dialog :title="editDy.title" :visible.sync="editDy.open" width="600px" append-to-body>
+      <el-form ref="editDyForm" :model="editDyForm" :rules="editDyRules" label-width="100px">
+        <div  v-hasPermi="['his:liveOrder:updateErpOrder']"  style="margin-bottom: 20px;" >
+          <el-button size="mini" @click="updateErpOrder" >同步物流单号信息</el-button>
+        </div>
+        <el-form-item label="物流公司" prop="deliveryCode" >
+          <el-select style="width:220px" v-model="editDyForm.deliveryCode" placeholder="请选择" clearable size="small">
+            <el-option key="SF"  label="顺丰" value="SF" />
+            <el-option key="EMS"  label="邮政" value="EMS" />
+            <el-option key="ZTO"  label="中通" value="ZTO" />
+            <el-option key="STO"  label="申通" value="STO" />
+            <el-option key="JD"  label="京东" value="JD" />
+            <el-option key="DBL"  label="德邦" value="DBL" />
+            <el-option key="JTSD"  label="极兔" value="JTSD" />
+            <el-option key="YD"  label="韵达" value="YD" />
+            <el-option key="YTO"  label="圆通" value="YTO" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="物流单号" prop="deliverySn"  >
+          <el-input v-model="editDyForm.deliverySn" placeholder="请输入物流单号" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitEditDyForm">确 定</el-button>
+        <el-button @click="editDy.open = false">取 消</el-button>
+      </div>
+    </el-dialog>
+
+    <el-dialog :title="expressDialog.title" :visible.sync="expressDialog.open" width="600px" append-to-body>
+      <div  v-hasPermi="['his:storeOrder:syncExpress']"  >
+        <el-button size="mini" @click="syncExpress()" >同步快递鸟物流状态</el-button>
+      </div>
+      <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>
+
+
+  </div>
+</template>
+
+
+<script>
+import {
+  getLiveOrder,
+  syncExpress,
+  updateLiveOrder,
+  updateErp,
+  getExpress,
+  listOrderitem,
+  tuiOrder,
+  refundOrderMoney,
+  editDeliveryId,
+  finishOrder,
+  getOrderExpress,
+  getOrderPayments,
+  getOrderLog,
+  tuiMoneyLogs,
+  updateExpress,
+  createErpOrder, getEroOrder
+} from "@/api/live/liveOrder";
+import {getStoreOrderLive} from "@/api/store/storeOrder";
+
+
+export default {
+  props:{
+    data: [Object, Array, String, Number, Boolean],
+    orderId: {
+      type: [String, Number],
+      default: null
+    }
+  },
+  watch: {
+    orderId: {
+      immediate: true,
+      handler(val) {
+        if (val) {
+          this.getDetails(val, null, null)
+        }
+      }
+    }
+  },
+  data() {
+    return {
+      expressDialog:{
+        title:"物流信息",
+        open:false,
+      },
+      editDy:{
+        title:"修改物流单号",
+        open:false,
+      },
+      sourceOptions:[],
+      payments: [],
+      followDialogVisible:false,
+      dialogVisible:false,
+      total: 0,
+      msgForm:{
+        pageNum: 1,
+        pageSize: 10,
+        userId:null,
+        followDoctorId:null,
+      },
+      editDyForm:{
+        orderId:null,
+        deliveryId:null,
+        deliveryCode:null,
+      },
+      showList:true,
+      edit:{
+        title:"修改订单",
+        open:false,
+      },
+      erpDialog:{
+        title:"ERP订单信息",
+        open:false,
+      },
+      show:{
+        title:"问诊详情",
+        open:false,
+      },
+      pack:{
+        title:"套餐包",
+        open:false,
+      },
+      followList:[],
+      money:null,
+      moneyVisible:false,
+      rules:{},
+      sendVisible:false,
+      logs:[],
+      pay:[],
+      nickName:null,
+      storeName:null,
+      PayOptions:[],
+      orderOptions:[],
+      payStatusOptions:[],
+      express:null,
+      traces:[],
+      msg:[],
+      deliverList: [],
+      sexOptions:[],
+      refundOptions:[],
+      channelOptions:[],
+      qwSubjectOptions:[],
+      tuiOptions:[],
+      orOptions:[],
+      storeOPtions:[],
+      deliveryStatusOptions:[],
+      deliveryPayStatusOptions:[],
+      deliveryTypeOptions:[],
+      orderTypeOptions:[],
+      orderBuyTypeOptions:[],
+      scheduleOptions:[],
+      item:null,
+      tuiMoneyLogs:[],
+      erpOrder:null,
+      prod:null,
+      showProd:null,
+      editForm:{
+        orderId:null,
+        status:null,
+        userAddress:null,
+        deliveryStatus:null,
+        deliveryType:null,
+        remark:"",
+      },
+      editDyRules:{
+        deliverySn: [
+          { required: true, message: "物流单号不能为空", trigger: "blur" }
+        ],
+        deliveryCode: [
+          { required: true, message: "物流公司不能为空", trigger: "blur" }
+        ],
+      },
+      editRules:{
+        userAddress: [
+          { required: true, message: "收货地址不能为空", trigger: "blur" }
+        ],
+      },
+      mrules:{
+      },
+      form: {
+        deliveryCode: null,
+        deliveryName:null,
+        deliverySn:null,
+        orderId:null,
+      }
+    }
+  },
+
+  created() {
+    this.getDicts("sys_store_pay_type").then(response => {
+      this.PayOptions = response.data;
+    });
+    this.getDicts("sys_live_order_status").then(response => {
+      this.orderOptions = response.data;
+    });
+    this.getDicts("sys_order_pay").then(response => {
+      this.payStatusOptions = response.data;
+    });
+    this.getDicts("sys_order_source").then(response => {
+      this.sourceOptions = response.data;
+    });
+    this.getDicts("sys_store_order_type").then(response => {
+      this.orderTypeOptions = response.data;
+    });
+    this.getDicts("sys_store_order_buy_type").then(response => {
+      this.orderBuyTypeOptions = response.data;
+    });
+    this.getDicts("sys_refund_status").then(response => {
+      this.refundOptions = response.data;
+    });
+    this.getDicts("sys_store_channel").then(response => {
+      this.channelOptions = response.data;
+    });
+    this.getDicts("sys_store_qw_subject").then(response => {
+      this.qwSubjectOptions = response.data;
+    });
+    this.getDicts("sys_tui_money_status").then(response => {
+      this.tuiOptions = response.data;
+    });
+    this.getDicts("sys_company_or").then(response => {
+      this.orOptions = response.data;
+    });
+    this.getDicts("sys_patient_sex").then(response => {
+      this.sexOptions = response.data;
+    });
+    this.getDicts("sys_store_delivery_pay_status").then(response => {
+      this.deliveryPayStatusOptions = response.data;
+    });
+    this.getDicts("store_order_delivery_status").then(response => {
+      this.deliveryStatusOptions = response.data;
+    });
+    this.getDicts("logistics_status").then(response => {
+      this.deliveryTypeOptions = response.data;
+    });
+
+  },
+  methods: {
+
+    getOrderLive(orderId){
+      getLiveOrder(orderId).then(response => {
+        this.item=response.data
+      });
+      this.getlistOrderitem(orderId);
+    },
+    followMsg(row){
+      const userId = this.item.userId;
+      const followDoctorId =this.item.followDoctorId;
+      const doctorName = this.item.doctorName;
+      const patientName = this.item.patientName;
+
+      setTimeout(() => {
+        this.$refs.msgDetails.getDetails(userId,followDoctorId,doctorName,patientName);
+      }, 500);
+      this.dialogVisible = true;
+    },
+    msgDialogClose(){
+      this.dialogVisible = false;
+    },
+    handlePhone(){
+      const orderId = this.item.orderId;
+      getOrderUserPhone(orderId).then(response =>{
+        this.item.userPhone = response.userPhone;
+      })
+    },
+    editDelivery(){
+      this.editDy.open = true;
+      this.editDyForm.orderId = this.item.orderId;
+
+    },
+    showListD(){
+      if(this.showList){
+        this.showProd=this.prod
+      }else{
+        this.showProd=[this.prod[0]]
+      }
+      this.showList=this.showList?false:true;
+    },
+
+    showExpress(){
+      this.expressDialog.open=true;
+      getExpress({
+        orderId: this.item.orderId,
+        deliverId: this.item.deliverySn,
+        deliverSn: this.item.deliveryCode,
+      }).then(response => {
+        this.express = response.data;
+        if(this.express!=null&&this.express.Traces!=null){
+          this.traces=this.express.Traces
+        }
+      });
+    },
+    updateErpOrder(){
+      var that=this;
+      this.$confirm('确定同步物流单号信息吗', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function() {
+        var data={orderId:that.item.orderId}
+        return updateErp(data);
+      }).then(() => {
+        this.msgSuccess("操作成功");
+        getLiveOrder(this.item.orderId).then(response => {
+          this.item=response.data
+          that.getlogList(this.item.orderId);
+          that.$parent.$parent.getList();
+        });
+        this.editDy.open = false
+      }).catch(function() {});
+
+    },
+    sendFollowMsg(){
+      var that=this;
+      this.$confirm('是否确认发送消息?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function() {
+        return sendMsg(that.item.orderId);
+      }).then(() => {
+        this.msgSuccess("操作成功");
+      }).catch(function() {});
+    },
+    handleClose1(){
+      this.dialogVisible=false;
+    },
+    handleClose2(){
+      this.followDialogVisible=false;
+    },
+    follow(row){
+      getMsgFollow(row).then(response => {
+        if(response.data.formJson!=null&&response.data.formJson!=''&&response.data.writeStatus==1){
+          this.messageFollowList=JSON.parse(response.data.formJson );
+          this.followDialogVisible=true;
+        }else{
+          this.$message({
+            message: '未填写随访单',
+            type: 'info'
+          });
+          return
+        }
+      });
+    },
+
+    //修改订单状态
+    submitEditForm(){
+      this.$refs["editForm"].validate(valid => {
+        if (valid) {
+          updateLiveOrder(this.editForm).then(response => {
+            if (response.code === 200) {
+              this.msgSuccess("操作成功");
+              this.edit.open = false;
+              getLiveOrder(this.item.orderId).then(response => {
+                this.item=response.data
+                that.getlogList(this.item.orderId);
+                that.$parent.$parent.getList();
+              });
+            }
+          });
+        }
+      });
+    },
+    editOrder(){
+      this.edit.open=true;
+      this.editForm.orderId=this.item.orderId;
+      this.editForm.remark=this.item.remark;
+      this.editForm.userAddress = this.item.userAddress == null ? '' : this.item.userAddress.toString();
+      this.editForm.status = this.item.status == null ? '' : this.item.status.toString();
+      this.editForm.deliveryType = this.item.deliveryType == null ? '' : this.item.deliveryType.toString();
+      this.editForm.deliveryStatus = this.item.deliveryStatus == null ? '' : this.item.deliveryStatus.toString();
+
+    },
+    updateExpress(){
+      var that=this;
+      this.$confirm('确定同步物流信息吗,同步后将自动发货?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function() {
+        var data=that.item.orderId
+        return updateExpress(data);
+      }).then(() => {
+        this.msgSuccess("操作成功");
+        getLiveOrder(this.item.orderId).then(response => {
+          this.item=response.data
+          that.getlogList(this.item.orderId);
+          that.$parent.$parent.getList();
+        });
+      }).catch(function() {});
+    },
+    addErpOrder(){
+      var that=this;
+      this.$confirm('是否确认推送管易?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function() {
+        var data=that.item.orderCode
+        return createErpOrder({
+          orderCode: data
+        });
+      }).then(() => {
+        this.msgSuccess("操作成功");
+        getLiveOrder(this.item.orderId).then(response => {
+          this.item=response.data
+          that.getlogList(this.item.orderId);
+          that.$parent.$parent.getList();
+        });
+      }).catch(function() {});
+    },
+    showErpOrder(){
+      this.erpDialog.open=true;
+      var data=this.item.extendOrderId;
+      getEroOrder({
+        extendOrderId: data
+      }).then(response => {
+        this.erp = response.data;
+        if(response.data.orders!=null&&response.data.orders.length==1){
+          this.erpOrder=response.data.orders[0]
+        }
+      });
+    },
+
+    editTuiMoney1(){
+      var that=this;
+      this.$confirm('是否解冻此订单推广佣金吗?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function() {
+        var data=that.item.orderId;
+        return editTuiMoney(data);
+      }).then(() => {
+        this.msgSuccess("操作成功");
+        getLiveOrder(this.item.orderId).then(response => {
+          this.item=response.data
+          that.getlogList(this.item.orderId);
+          that.$parent.$parent.getList();
+        });
+      }).catch(function() {});
+    },
+    editTuiMoney2(){
+      var that=this;
+      this.$confirm('是否冻结此订单推广佣金吗?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function() {
+        var data=that.item.orderId;
+        return editTuiMoney(data);
+      }).then(() => {
+        this.msgSuccess("操作成功");
+        getLiveOrder(this.item.orderId).then(response => {
+          this.item=response.data
+          that.getlogList(this.item.orderId);
+          that.$parent.$parent.getList();
+        });
+      }).catch(function() {});
+    },
+    returnCost(){
+      var that=this;
+      this.$confirm('是否退还此订单成本吗?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function() {
+        var data=that.item.orderId;
+        return returnCost(data);
+      }).then(() => {
+        this.msgSuccess("操作成功");
+        this.getDetails(this.item.orderId,null,null)
+      }).catch(function() {});
+    },
+    moneyCancel(){
+      this.money=null;
+      this.moneyVisible=false;
+    },
+    refund(){
+      var that=this;
+      this.$confirm('是否确认申请退款?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function() {
+        var data={
+          orderId:that.item.orderId
+        }
+        return refundOrderMoney(data);
+      }).then(() => {
+        this.msgSuccess("操作成功");
+        getLiveOrder(this.item.orderId).then(response => {
+          this.item = response.data;
+          // this.getlogList(this.item.orderId);
+          this.$parent.$parent.getList();
+        });
+      }).catch(function() {});
+    },
+    finishOrder(){
+      var that=this;
+      this.$confirm('是否确认客户已收货?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function() {
+        var data={
+          orderId:that.item.orderId
+        }
+        return finishOrder(data);
+      }).then(() => {
+        this.msgSuccess("操作成功");
+        getLiveOrder(this.item.orderId).then(response => {
+          this.item = response.data;
+          this.getlistOrderitem(this.item.orderId);
+          this.$parent.$parent.getList();
+        });
+      }).catch(function() {});
+    },
+
+
+    syncExpress(){
+      var that=this;
+      this.$confirm('确定同步物流状态吗', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function() {
+        var data=that.item.orderId
+        return syncExpress(data);
+      }).then(() => {
+        this.msgSuccess("操作成功");
+        this.expressDialog.open=false
+        getLiveOrder(this.item.orderId).then(response => {
+          this.item=response.data
+          that.getlogList(this.item.orderId);
+          that.$parent.$parent.getList();
+        });
+      }).catch(function() {});
+    },
+    getInquiryOrder(){
+
+      this.show.open=true;
+      setTimeout(() => {
+        this.$refs.Details.getDetails(this.item.inquiryOrderId);
+      }, 1);
+    },
+    getPackageOrder(){
+      this.pack.open=true;
+      console.log(this.item.packageOrderId)
+      setTimeout(() => {
+        this.$refs.packDetails.getDetails(this.item.packageOrderId);
+      }, 1);
+    },
+    tuiOrder(){
+      var that=this;
+      this.$confirm('是否确认推送订单?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function() {
+        var data={
+          orderId:that.item.orderId
+        }
+        return tuiOrder(data);
+      }).then(() => {
+        this.msgSuccess("操作成功");
+        getLiveOrder(this.item.orderId).then(response => {
+          this.item = response.data;
+          this.getlogList(this.item.orderId);
+          this.$parent.$parent.getList();
+        });
+      }).catch(function() {});
+    },
+    getDetails(orderId,nickName,storeName) {
+      this.nickName=nickName;
+      this.storeName=storeName;
+      this.item=null;
+      this.tuiMoneyLogs=null;
+      getLiveOrder(orderId).then(response => {
+        this.item = response.data;
+        console.log(this.tuiMoneyLogs)
+        this.tuiMoneyLogs = response.tuiMoneyLogs;
+        this.msgForm.userId=response.data.userId;
+        this.msgForm.followDoctorId=response.data.followDoctorId;
+        this.getlistOrderitem(this.item.orderId);
+        getOrderExpress(orderId).then(response => {
+          this.deliverList = response.data;
+          console.log(this.deliverList)
+        });
+
+        getOrderPayments(this.item.orderId).then(response => {
+          this.payments = response.payments;
+        });
+
+        getOrderLog(this.item.orderId).then(response => {
+          this.logs = response.logs
+        });
+
+        tuiMoneyLogs(this.item.orderId).then(response => {
+          this.tuiMoneyLogs = response.data
+        })
+      });
+
+    },
+    getOrder(){
+      getLiveOrder(this.item.orderId).then(response => {
+        this.item = response.data;
+        this.getlistOrderitem(this.item.orderId);
+      });
+    },
+    submitEditDyForm(){
+      this.$refs["editDyForm"].validate(valid => {
+        if (valid) {
+          editDeliveryId({orderId: this.editDyForm.orderId,
+            deliverSn: this.editDyForm.deliveryCode,
+            deliverId: this.editDyForm.deliverySn,type: 1}).then(response => {
+            if (response.code === 200) {
+              this.msgSuccess("操作成功");
+              this.editDy.open = false;
+              getLiveOrder(this.item.orderId).then(response => {
+                this.item = response.data;
+                this.$parent.$parent.getList();
+              });
+            }
+          })
+        }
+      });
+    },
+    getlistOrderitem(orderId){
+      this.prod = null
+      this.showProd=[]
+      this.showList = true
+      listOrderitem(orderId).then(response => {
+        this.prod = response.rows;
+        if (this.prod.length > 0) {
+          this.showProd=[this.prod[0]];
+        }
+      });
+    },
+    // getlogList(orderId){
+    //   logList(orderId).then(response => {
+    //     this.logs = response.rows;
+    //   });
+    // },
+    // getPayment(orderId){
+    //   payment(orderId).then(response => {
+    //     console.log(response)
+    //     this.pay = response.data;
+    //   });
+    // }
+  }
+}
+
+</script>
+
+
+<style scoped>
+.content{
+  height: 100%;
+  background-color: #fff;
+  padding: 0px 20px;
+
+}
+.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;
+}
+
+.order-content{
+  margin: 10px;
+
+}
+
+.operate-container {
+  background: #F2F6FC;
+  height: 60px;
+  margin: -20px -20px 0;
+  line-height: 60px;
+}
+
+.operate-button-container {
+  float: right;
+  margin-right: 20px
+}
+</style>
+
+<style scoped>
+.chat-records {
+
+  overflow-y: auto;
+}
+.timestamp {
+  font-size: 12px;
+  color: #A9A9A9;
+}
+.chat-record {
+  margin: 10px;
+  flex-direction: column;
+  align-items: flex-start;
+}
+.sent {
+  background-color: #fbfdff;
+  color: #000839;
+}
+.sent .timestamp {
+  float: right;
+}
+.right{
+  float: right;
+}
+.received {
+  background-color: #fbfdff;
+  color: #000000;
+}
+
+.el-descriptions-item__content {
+  max-width: 150px;
+  min-width: 100px;
+}
+.order-content{
+  margin: 10px;
+
+}
+
+.operate-container {
+  background: #F2F6FC;
+  height: 60px;
+  margin: -20px -20px 0;
+  line-height: 60px;
+}
+
+.operate-button-container {
+  float: right;
+  margin-right: 20px
+}
+</style>

+ 880 - 0
src/views/live/order/storeDetail.vue

@@ -0,0 +1,880 @@
+<template>
+  <div class="order-content">
+      <div class="order-status" v-if="order!=null" >
+          <el-steps  :active="order.status==3?order.status+1:order.status" align-center>
+            <el-step title="待支付"></el-step>
+            <el-step title="待发货"></el-step>
+            <el-step title="待收货"></el-step>
+            <el-step title="交易完成"></el-step>
+          </el-steps>
+      </div>
+      <div>
+      <el-card shadow="never" style="margin-top: 15px">
+      <div class="operate-container"  v-if="order!=null">
+        <span style="margin-left: 20px" class="color-danger">订单状态:
+           <el-tag prop="status" v-for="(item, index) in statusOptions"   v-if="order.status==item.dictValue">{{item.dictLabel}}</el-tag>
+        </span>
+        <div class="operate-button-container" >
+          <el-button size="mini" @click="editOrder()"  v-hasPermi="['store:storeOrder:edit']" >修改订单</el-button>
+        </div>
+        <div class="operate-button-container"  v-hasPermi="['store:storeOrder:finishOrder']" v-if="order.status===2">
+          <el-button size="mini" @click="finishOrder()" >确认收货</el-button>
+        </div>
+        <div class="operate-button-container" v-if="order.status==1||order.status==2"  v-hasPermi="['store:storeOrder:refundOrderMoney']"  >
+          <el-button size="mini" @click="refundOrderMoney()" >退款</el-button>
+        </div>
+        <div class="operate-button-container"  v-hasPermi="['store:storeOrder:express']"  >
+          <el-button size="mini" @click="showExpress()">查看物流</el-button>
+        </div>
+        <div class="operate-button-container" v-if="order.tuiMoneyStatus==0"  v-hasPermi="['store:storeOrder:editTuiMoney']"  >
+          <el-button size="mini" @click="editTuiMoney1()" >解冻</el-button>
+        </div>
+        <div class="operate-button-container" v-if="order.tuiMoneyStatus==1"  v-hasPermi="['store:storeOrder:editTuiMoney']"  >
+          <el-button size="mini" @click="editTuiMoney2()" >冻结</el-button>
+        </div>
+
+        <div class="operate-button-container" v-if="order.status==3&&order.tuiMoneyStatus==null"  v-hasPermi="['store:storeOrder:addTuiMoney']"  >
+          <el-button size="mini" @click="addTuiMoney()" >分佣</el-button>
+        </div>
+
+<!--        <div class="operate-button-container" v-if="order.extendOrderId!=null"  v-hasPermi="['store:storeOrder:getEroOrder']"  >
+          <el-button size="mini" @click="showErpOrder()" >查看ERP订单信息</el-button>
+        </div>-->
+        <div class="operate-button-container" v-if="order.isPayRemain!=null&&order.isPayRemain==1"  v-hasPermi="['store:storeOrder:auditPayRemain']"  >
+          <el-button size="mini" @click="auditPayRemain()" >尾款审核</el-button>
+        </div>
+        <div class="operate-button-container" >
+          <el-button size="mini" @click="editDelivery()"  v-hasPermi="['store:storeOrder:editDeliveryId']" >修改物流</el-button>
+        </div>
+        <div class="operate-button-container" v-if="order.status >=1 && order.extendOrderId == null && order.deliveryId == null"  v-hasPermi="['store:storeOrder:createErpOrder']"  >
+          <el-button size="mini" @click="addErpOrder()" >创建ERP订单信息</el-button>
+        </div>
+        <!-- <div class="operate-button-container" v-show="order.status===2">
+          <el-button size="mini"  >去发货</el-button>
+          <el-button size="mini" >备注订单</el-button>
+        </div>
+        <div class="operate-button-container" v-show="order.status===4">
+          <el-button size="mini"  >订单跟踪</el-button>
+          <el-button size="mini"  disabled>备注订单</el-button>
+        </div> -->
+      </div>
+      <div style="margin: 20px 0px">
+        <span class="font-small">基本信息</span>
+      </div>
+      <el-descriptions :column="4" border  >
+            <el-descriptions-item label="订单编号"  >
+                <span v-if="order!=null">
+                  {{order.orderCode}}
+                </span>
+                <el-tag  v-for="(item, index) in createTypeOptions"    v-if="order!=null&&order.orderCreateType==item.dictValue">{{item.dictLabel}}
+                </el-tag>
+            </el-descriptions-item>
+            <el-descriptions-item label="会员"  >
+                <span v-if="user!=null">
+                  {{user.nickname}}({{user.phone}})
+                </span>
+            </el-descriptions-item>
+            <el-descriptions-item label="支付方式"  >
+              <el-tag prop="payType" v-for="(item, index) in payTypeOptions"    v-if="order!=null&&order.payType==item.dictValue">{{item.dictLabel}}
+              </el-tag>
+            </el-descriptions-item>
+<!--            <el-descriptions-item label="订单类型"  >
+              <el-tag prop="orderType" v-for="(item, index) in orderTypeOptions"    v-if="order!=null&&order.orderType==item.dictValue">{{item.dictLabel}}</el-tag>
+            </el-descriptions-item>-->
+            <el-descriptions-item label="物流公司编号"  >
+                <span v-if="order!=null">
+                  {{order.deliverySn}}
+                </span>
+            </el-descriptions-item>
+            <el-descriptions-item label="物流公司名称"  >
+                <span v-if="order!=null">
+                  {{order.deliveryName}}
+                </span>
+            </el-descriptions-item>
+            <el-descriptions-item label="物流单号"  >
+                <span v-if="order!=null">
+                  {{order.deliveryId}}
+                </span>
+            </el-descriptions-item>
+            <el-descriptions-item label="物流状态"  >
+                <span v-if="order!=null">
+                  <el-tag prop="deliveryId" v-for="(item, index) in deliveryStatusOptions"    v-if="order!=null&&order.deliveryStatus==item.dictValue">{{item.dictLabel}}</el-tag>
+                </span>
+            </el-descriptions-item>
+            <el-descriptions-item label="物流跟踪状态"  >
+                <span v-if="order!=null">
+                  <el-tag prop="deliveryId" v-for="(item, index) in deliveryTypeOptions"    v-if="order!=null&&order.deliveryType==item.dictValue">{{item.dictLabel}}</el-tag>
+                </span>
+            </el-descriptions-item>
+            <el-descriptions-item label="物流结算费用"  >
+                <span v-if="order!=null&&order.deliveryPayMoney!=null ">
+                  {{order.deliveryPayMoney.toFixed(2) }}
+                </span>
+            </el-descriptions-item>
+            <el-descriptions-item label="快递帐单日期"  >
+                <span v-if="order!=null ">
+                  {{order.deliveryTime }}
+                </span>
+            </el-descriptions-item>
+            <el-descriptions-item label="快递结算日期"  >
+                <span v-if="order!=null ">
+                  {{order.deliveryPayTime }}
+                </span>
+            </el-descriptions-item>
+            <el-descriptions-item label="推广佣金"  >
+                <span v-if="order!=null ">
+                  {{order.tuiMoney }}
+                </span>
+            </el-descriptions-item>
+            <el-descriptions-item label="收货人"  >
+                <span v-if="order!=null ">
+                  {{order.realName }}
+                </span>
+            </el-descriptions-item>
+            <el-descriptions-item label="手机号码"  >
+                <span v-if="order!=null ">
+                  {{order.userPhone }}
+                </span>
+                <el-button icon="el-icon-search" size="mini" @click="handlePhone()" style="margin-left: 20px;" circle v-hasPermi="['store:storeOrder:queryPhone']"></el-button>
+            </el-descriptions-item>
+            <el-descriptions-item label="收货地址"  >
+              <el-popover
+                v-if="order!=null"
+                placement="top-start"
+                title="收货地址"
+                width="300"
+                trigger="hover"
+                :content="order.userAddress">
+                <span slot="reference">{{order.userAddress}}</span>
+                <el-button icon="el-icon-search" size="mini" @click="handleAddress()" style="margin-left: 20px;" circle v-hasPermi="['store:storeOrder:queryAddress']"></el-button>
+              </el-popover>
+            </el-descriptions-item>
+            <el-descriptions-item label="档期归属"  >
+              <el-tag prop="scheduleId" v-for="(item, index) in scheduleOptions"    v-if="order!=null&&order.scheduleId==item.id">{{item.name}}
+              </el-tag>
+            </el-descriptions-item>
+            <el-descriptions-item label="用户备注"  >
+                <span v-if="order!=null">
+                  {{order.mark}}
+                </span>
+            </el-descriptions-item>
+      </el-descriptions>
+      <div style="margin: 20px 0px"  v-if="order!=null">
+        <span class="font-small">
+          凭证信息
+        </span>
+      </div>
+      <el-image
+          v-if="certificates != null"
+          :src="certificates"
+          :preview-src-list="[certificates]"
+          :style="{ width: '100px', height: '100px' }"
+          @click.native="showImageDialog"
+        ></el-image>
+        <el-dialog :visible.sync="dialogVisibleImage" width="10%">
+          <img :src="certificates" style="width: 100%" alt="">
+        </el-dialog>
+      <div style="margin-top: 20px">
+        <span class="font-small">商品信息</span>
+      </div>
+      <el-table
+        border
+        v-if="items!=null"
+        :data="items"
+        size="small"
+        style="width: 100%;margin-top: 20px" >
+        <el-table-column label="商品图片" width="150" align="center">
+          <template slot-scope="scope">
+            <img :src="JSON.parse(scope.row.jsonInfo).image" style="height: 80px">
+          </template>
+        </el-table-column>
+        <el-table-column label="商品编码" width="300" align="center">
+          <template slot-scope="scope">
+            <p>{{JSON.parse(scope.row.jsonInfo).barCode}}</p>
+          </template>
+        </el-table-column>
+        <el-table-column label="商品组合编码" width="300" align="center">
+          <template slot-scope="scope">
+            <p>{{JSON.parse(scope.row.jsonInfo).groupBarCode}}</p>
+          </template>
+        </el-table-column>
+        <el-table-column label="商品名称" width="300" align="center">
+          <template slot-scope="scope">
+            <p>{{JSON.parse(scope.row.jsonInfo).productName}}</p>
+          </template>
+        </el-table-column>
+        <el-table-column label="单价" width="240" align="center">
+          <template slot-scope="scope">
+            <p>¥{{JSON.parse(scope.row.jsonInfo).price.toFixed(2)}}</p>
+          </template>
+        </el-table-column>
+        <el-table-column label="规格" width="240" align="center">
+          <template slot-scope="scope">
+            {{JSON.parse(scope.row.jsonInfo).sku}}
+          </template>
+        </el-table-column>
+        <el-table-column label="数量" width="180" align="center">
+          <template slot-scope="scope">
+            {{scope.row.num}}
+          </template>
+        </el-table-column>
+        <el-table-column label="处方药" width="240" align="center">
+          <template slot-scope="scope">
+            {{scope.row.isPrescribe!=null&&scope.row.isPrescribe==1?'是':'否'}}
+          </template>
+        </el-table-column>
+        <el-table-column label="小计"  align="center">
+          <template slot-scope="scope" >
+            ¥{{scope.row.num*JSON.parse(scope.row.jsonInfo).price.toFixed(2)}}
+          </template>
+        </el-table-column>
+      </el-table>
+      <div style="float: right;margin: 20px" v-if="order!=null">
+        合计:<span class="color-danger">¥{{order.totalPrice.toFixed(2)}}</span>
+      </div>
+      <div style="margin: 60px 0px 20px 0px">
+        <span class="font-small">费用信息</span>
+      </div>
+      <el-descriptions   :column="4" border  >
+          <el-descriptions-item label="商品合计"  >
+              <span v-if="order!=null">
+                ¥{{order.totalPrice.toFixed(2)}}
+              </span>
+          </el-descriptions-item>
+          <el-descriptions-item label="应付金额"  >
+              <span v-if="order!=null">
+                ¥{{order.payPrice.toFixed(2)}}
+              </span>
+          </el-descriptions-item>
+          <el-descriptions-item label="运费"  >
+              <span v-if="order!=null">
+                ¥{{order.payPostage.toFixed(2)}}
+              </span>
+          </el-descriptions-item>
+          <el-descriptions-item label="优惠券"  >
+              <span v-if="order!=null">
+                ¥{{order.couponPrice.toFixed(2)}}
+              </span>
+          </el-descriptions-item>
+          <el-descriptions-item label="积分抵扣"  >
+              <span v-if="order!=null">
+                ¥{{order.deductionPrice.toFixed(2)}}
+              </span>
+          </el-descriptions-item>
+          <el-descriptions-item label="实付金额"  >
+              <span v-if="order!=null">
+                ¥{{order.payMoney.toFixed(2)}}
+              </span>
+          </el-descriptions-item>
+          <el-descriptions-item label="代收金额"  >
+              <span v-if="order!=null">
+                ¥{{order.payDelivery.toFixed(2)}}
+              </span>
+          </el-descriptions-item>
+      </el-descriptions>
+
+      <div style="margin-top: 20px">
+        <span class="font-small">支付信息</span>
+      </div>
+      <el-table
+        border
+        :data="payments"
+        size="small"
+        style="width: 100%;margin-top: 20px" >
+          <el-table-column label="支付单号" align="center" prop="payCode" width="120px" />
+          <el-table-column label="支付金额" align="center" prop="payMoney" />
+          <el-table-column label="类型" align="center" prop="payTypeCode" />
+          <el-table-column label="交易单号" align="center" prop="bankTransactionId" />
+          <el-table-column label="银行单号" align="center" prop="bankSerialNo" />
+          <el-table-column label="创建时间" align="center" prop="createTime" />
+          <el-table-column label="支付时间" align="center" prop="payTime" />
+      </el-table>
+
+
+      <div style="margin-top: 20px">
+        <span class="font-small">操作信息</span>
+      </div>
+      <el-table style="margin-top: 20px;width: 100%"
+                ref="orderHistoryTable"
+                :data="logs" border>
+        <el-table-column label="操作时间"  width="160" align="center">
+          <template slot-scope="scope">
+            {{scope.row.changeTime}}
+          </template>
+        </el-table-column>
+        <el-table-column label="备注" align="center">
+          <template slot-scope="scope">
+            {{scope.row.changeMessage}}
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <div style="margin-top: 20px">
+        <!-- <svg-icon icon-class="marker" style="color: #606266"></svg-icon> -->
+        <span class="font-small">分佣信息</span>
+      </div>
+      <el-table
+        border
+        :data="tuiMoneyLogs"
+        size="small"
+                style="width: 100%;margin-top: 20px" >
+          <el-table-column label="公司名称" align="center" prop="companyName" width="120px" />
+          <el-table-column label="金额" align="center" prop="money" />
+          <el-table-column label="余额" align="center" prop="balance" />
+          <el-table-column label="创建时间" align="center" prop="createTime" />
+          <el-table-column label="备注" align="center" prop="remark" />
+      </el-table>
+
+        <div style="margin-top: 20px">
+          <span class="font-small">审批信息</span>
+        </div>
+        <el-table style="margin-top: 20px;width: 100%"
+                  :data="auditLogs" border>
+          <el-table-column label="操作时间"  width="160" align="center">
+            <template slot-scope="scope">
+              {{scope.row.createTime}}
+            </template>
+          </el-table-column>
+          <el-table-column label="备注" align="center">
+            <template slot-scope="scope">
+              {{scope.row.content}}
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-card>
+    </div>
+
+    <el-dialog :title="edit.title" :visible.sync="edit.open" width="600px" append-to-body>
+      <el-form ref="editForm" :model="editForm" :rules="editRules" label-width="100px">
+<!--        <el-form-item label="订单类型" prop="orderType"  >
+            <el-select style="width: 200px" v-model="editForm.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-form-item label="状态" prop="status" >
+           <el-select style="width:220px" v-model="editForm.status" placeholder="请选择状态" clearable size="small">
+            <el-option key="0"  label="待支付" value="0" />
+            <el-option key="1"  label="待发货" value="1" />
+             <el-option key="2"  label="待收货" value="2" />
+             <el-option key="3"  label="交易成功" value="3" />
+             <el-option key="-1"  label="退款中" value="-1" />
+             <el-option key="-2"  label="已退款" value="-2" />
+             <el-option key="-3"  label="已取消" value="-3" />
+           </el-select>
+         </el-form-item>
+<!--         <el-form-item label="档期归属" prop="scheduleId"  >
+            <el-select filterable style="width: 200px" v-model="editForm.scheduleId" placeholder="请选择档期" clearable size="small" >
+              <el-option
+                      v-for="item in scheduleOptions"
+                      :key="item.id"
+                      :label="item.name"
+                      :value="item.id"
+                    />
+            </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="erp单号" prop="extendOrderId"  >
+          <el-input v-model="editForm.extendOrderId" placeholder="请输入" />
+        </el-form-item> -->
+        <el-form-item label="备注" prop="mark"  >
+          <el-input v-model="editForm.mark" placeholder="请输入备注" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitEditForm">确 定</el-button>
+      </div>
+    </el-dialog>
+
+    <el-dialog :title="editDy.title" :visible.sync="editDy.open" width="600px" append-to-body>
+      <el-form ref="editDyForm" :model="editDyForm" :rules="editDyRules" label-width="120px">
+<!--      <div  v-hasPermi="['store:storeOrder:updateErpOrder']"  style="margin-bottom: 20px;" >
+        <el-button size="mini" @click="updateErpOrder" >同步物流单号信息</el-button>
+      </div>-->
+        <el-form-item label="物流公司" prop="deliverySn" >
+           <el-select style="width:220px" v-model="editDyForm.deliverySn" placeholder="请选择" clearable size="small">
+            <el-option key="SF"  label="顺丰" value="SF" />
+            <el-option key="EMS"  label="邮政" value="EMS" />
+             <el-option key="ZTO"  label="中通" value="ZTO" />
+             <el-option key="STO"  label="申通" value="STO" />
+             <el-option key="JD"  label="京东" value="JD" />
+             <el-option key="DBL"  label="德邦" value="DBL" />
+             <el-option key="JTSD"  label="极兔" value="JTSD" />
+             <el-option key="YD"  label="韵达" value="YD" />
+             <el-option key="YTO"  label="圆通" value="YTO" />
+           </el-select>
+         </el-form-item>
+        <el-form-item label="物流单号" prop="deliveryId"  >
+          <el-input v-model="editDyForm.deliveryId" placeholder="请输入物流单号" />
+        </el-form-item>
+
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitEditDyForm">确 定</el-button>
+        <el-button @click="editDy.open = false">取 消</el-button>
+      </div>
+    </el-dialog>
+
+    <el-dialog :title="expressDialog.title" :visible.sync="expressDialog.open" width="600px" append-to-body>
+<!--      <div  v-hasPermi="['store:storeOrder:syncExpress']"  >
+        <el-button size="mini" @click="syncExpress()" >同步快递鸟物流状态</el-button>
+      </div>-->
+      <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="erpDialog.title" :visible.sync="erpDialog.open" width="600px" append-to-body>
+      <div v-if="order!=null&&order.extendOrderId!=null&&order.status==1"  v-hasPermi="['store:storeOrder:updateExpress']"  >
+        <el-button size="mini" @click="updateExpress()" >同步物流发货</el-button>
+      </div>
+      <div class="table-layout"  v-if="erpOrder!=null">
+        <el-row>
+          <el-col :span="6" class="table-cell-title">订单编号</el-col>
+          <el-col :span="6" class="table-cell-title">是否代收</el-col>
+          <el-col :span="6" class="table-cell-title">快递编号</el-col>
+          <el-col :span="6" class="table-cell-title">快递名称</el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="6" class="table-cell">
+            {{erpOrder.code}}
+          </el-col>
+          <el-col :span="6" class="table-cell">
+            {{erpOrder.cod?'是':'否'}}
+          </el-col>
+          <el-col :span="6" class="table-cell">
+            {{erpOrder.express_code}}
+          </el-col>
+          <el-col :span="6" class="table-cell">
+            {{erpOrder.express_name}}
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="6" class="table-cell-title">收货人</el-col>
+          <el-col :span="6" class="table-cell-title">电话</el-col>
+          <el-col :span="6" class="table-cell-title">地址</el-col>
+          <el-col :span="6" class="table-cell-title">运单号</el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="6" class="table-cell">
+            {{erpOrder.receiver_name}}
+          </el-col>
+          <el-col :span="6" class="table-cell">
+            {{erpOrder.receiver_mobile}}
+          </el-col>
+          <el-col :span="6" class="table-cell">
+            {{erpOrder.receiver_address}}
+          </el-col>
+          <el-col :span="6" class="table-cell">
+            {{erpOrder.deliverys!=null&&erpOrder.deliverys.length>0?erpOrder.deliverys[0].mail_no:''}}
+          </el-col>
+
+        </el-row>
+      </div>
+    </el-dialog>
+
+  </div>
+</template>
+
+<script>
+import {auditPayRemain,addTuiMoney,syncExpress,updateExpress,getEroOrder,refundOrderMoney, editTuiMoney,getExpress,finishOrder,listStoreOrder, getStoreOrder, delStoreOrder, addStoreOrder, updateStoreOrder, exportStoreOrder,updateDeliveryId, createErpOrder,updateErp,getStoreOrderAddress,getStoreOrderPhone} from "@/api/hisStore/storeOrder";
+import { getTcmScheduleList } from "@/api/company/schedule";
+export default {
+  name: "StoreDetail",
+  props: {
+    orderId: {
+      type: [String, Number],
+      default: null
+    }
+  },
+  watch: {
+    orderId: {
+      immediate: true,
+      handler(val) {
+        if (val) {
+          this.getOrder(val);
+        }
+      }
+    }
+  },
+  data() {
+    return {
+      dialogVisibleImage: false,
+      createTypeOptions:[],
+      deliveryStatusOptions:[],
+      deliveryTypeOptions:[],
+      scheduleOptions:[],
+      schedules:[],
+      orderId:null,
+      erpDialog:{
+        title:"ERP订单信息",
+        open:false,
+      },
+      expressDialog:{
+        title:"物流信息",
+        open:false,
+      },
+      edit:{
+        title:"",
+        open:false,
+      },
+      editDy:{
+        title:"修改物流单号",
+        open:false,
+      },
+      editDyForm:{
+        deliverySn:null,
+        deliveryId:null,
+      },
+      editForm:{
+        orderType:null,
+        status:null,
+        userAddress:null,
+        // extendOrderId:null,
+        scheduleId:null,
+        mark:"",
+      },
+
+      editDyRules:{
+        deliverySn: [
+          { required: true, message: "物流公司不能为空", trigger: "blur" }
+        ],
+        deliveryId: [
+          { required: true, message: "物流单号不能为空", trigger: "blur" }
+        ],
+      },
+      editRules:{
+        userAddress: [
+          { required: true, message: "收货地址不能为空", trigger: "blur" }
+        ],
+
+      },
+      orderTypeOptions:[],
+      payTypeOptions:[],
+      statusOptions:[],
+      certificates:null,
+      orderStatus:null,
+      order:null,
+      user:{},
+      logs:[],
+      items:[],
+      express:null,
+      traces:[],
+      payments:[],
+      tuiMoneyLogs:[],
+      erpOrder:null,
+      auditLogs: [],
+    };
+  },
+  created() {
+    this.getDicts("store_order_delivery_status").then((response) => {
+      this.deliveryStatusOptions = response.data;
+    });
+    this.getDicts("store_order_delivery_type").then((response) => {
+      this.deliveryTypeOptions = response.data;
+    });
+
+    this.getDicts("store_order_type").then((response) => {
+      this.orderTypeOptions = response.data;
+    });
+    this.getDicts("store_order_status").then((response) => {
+      this.statusOptions = response.data;
+    });
+    this.getDicts("store_pay_type").then((response) => {
+      this.payTypeOptions = response.data;
+    });
+    this.getDicts("store_order_create_type").then((response) => {
+      this.createTypeOptions = response.data;
+    });
+    getTcmScheduleList().then(response => {
+      this.scheduleOptions = response.data;
+    });
+  },
+  methods: {
+    handleAddress(){
+        const id = this.order.id;
+        getStoreOrderAddress(id).then(response =>{
+            this.order.userAddress = response.address;
+        })
+      },
+      handlePhone(){
+        const id = this.order.id;
+        getStoreOrderPhone(id).then(response =>{
+            this.order.userPhone = response.userPhone;
+        })
+      },
+    showImageDialog() {
+      this.dialogVisible = true;
+    },
+    syncExpress(){
+      var that=this;
+        this.$confirm('确定同步物流状态吗', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          var data={orderId:that.order.id}
+          return syncExpress(data);
+        }).then(() => {
+          this.msgSuccess("操作成功");
+          this.getOrder(this.order.id);
+        }).catch(function() {});
+    },
+    updateErpOrder(){
+      var that=this;
+        this.$confirm('确定同步物流单号信息吗', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          var data={orderId:that.order.id}
+          return updateErp(data);
+        }).then(() => {
+          this.msgSuccess("操作成功");
+          this.getOrder(this.order.id);
+          this.editDy.open = false
+        }).catch(function() {});
+    },
+    updateExpress(){
+      var that=this;
+        this.$confirm('确定同步物流信息吗,同步后将自动发货', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          var data={orderId:that.order.id}
+          return updateExpress(data);
+        }).then(() => {
+          this.msgSuccess("操作成功");
+          this.getOrder(this.order.id);
+        }).catch(function() {});
+    },
+    showErpOrder(){
+      this.erpDialog.open=true;
+      var data={extendOrderId:this.order.extendOrderId};
+      getEroOrder(data).then(response => {
+          this.erp = response.data;
+          if(response.data.orders!=null&&response.data.orders.length==1){
+            this.erpOrder=response.data.orders[0]
+          }
+          console.log(this.erpOrder)
+
+      });
+    },
+    showExpress(){
+      this.expressDialog.open=true;
+      getExpress(this.orderId).then(response => {
+          this.express = response.data;
+          if(this.express!=null&&this.express.Traces!=null){
+              this.traces=this.express.Traces
+          }
+
+      });
+
+    },
+    submitEditForm(){
+        this.$refs["editForm"].validate(valid => {
+        if (valid) {
+          updateStoreOrder(this.editForm).then(response => {
+            if (response.code === 200) {
+              this.msgSuccess("操作成功");
+              this.edit.open = false;
+              this.getOrder(this.order.id);
+            }
+          });
+
+        }
+      });
+    },
+    submitEditDyForm(){
+        this.$refs["editDyForm"].validate(valid => {
+        if (valid) {
+          updateDeliveryId(this.editDyForm).then(response => {
+            if (response.code === 200) {
+              this.msgSuccess("操作成功");
+              this.editDy.open = false;
+              this.getOrder(this.order.id);
+            }
+          });
+
+        }
+      });
+    },
+    editTuiMoney1(){
+      var that=this;
+        this.$confirm('是否冻结此订单推广佣金吗?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          var data={orderId:that.order.id}
+          return editTuiMoney(data);
+        }).then(() => {
+          this.msgSuccess("操作成功");
+          this.getOrder(this.order.id);
+        }).catch(function() {});
+    },
+    editTuiMoney2(){
+      var that=this;
+        this.$confirm('是否解冻此订单推广佣金吗?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          var data={orderId:that.order.id}
+          return editTuiMoney(data);
+        }).then(() => {
+          this.msgSuccess("操作成功");
+          this.getOrder(this.order.id);
+        }).catch(function() {});
+    },
+    addTuiMoney(){
+      var that=this;
+        this.$confirm('确定手动分佣金吗?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          var data={orderId:that.order.id}
+          return addTuiMoney(data);
+        }).then(() => {
+          this.msgSuccess("操作成功");
+          this.getOrder(this.order.id);
+        }).catch(function() {});
+    },
+    refundOrderMoney(){
+        var that=this;
+        this.$confirm('确认退款吗', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          var data={orderId:that.order.id}
+          return refundOrderMoney(data);
+        }).then(() => {
+          this.msgSuccess("操作成功");
+          this.getOrder(this.order.id);
+        }).catch(function() {});
+    },
+    finishOrder(){
+        var that=this;
+        this.$confirm('是否确认客户已收货?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          var data={orderId:that.order.id}
+          return finishOrder(data);
+        }).then(() => {
+          this.msgSuccess("操作成功");
+          this.getOrder(this.order.id);
+        }).catch(function() {});
+    },
+    auditPayRemain(){
+        var that=this;
+        this.$confirm('是否确认已收尾款?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          var data={orderId:that.order.id}
+          return auditPayRemain(data);
+        }).then(() => {
+          this.msgSuccess("操作成功");
+          this.getOrder(this.order.id);
+        }).catch(function() {});
+    },
+    editOrder(){
+        this.edit.open=true;
+        this.editForm.id=this.order.id;
+        this.editForm.mark=this.order.mark
+        this.editForm.orderType=this.order.orderType.toString();
+        this.editForm.status = this.order.status.toString();
+        this.editForm.userAddress = this.order.userAddress.toString();
+        this.editForm.scheduleId = this.order.scheduleId;
+        // this.editForm.extendOrderId = this.order.extendOrderId.toString();
+
+    },
+    //推送管易按钮
+    addErpOrder(){
+        var that=this;
+        this.$confirm('是否确认推送erp?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          var data={orderCode:that.order.orderCode}
+          return createErpOrder(data);
+        }).then(() => {
+          this.msgSuccess("操作成功");
+          this.getOrder(this.order.id);
+        }).catch(function() {});
+    },
+    editDelivery(){
+      this.editDy.open = true;
+      this.editDyForm.id = this.order.id;
+      this.editDyForm.deliveryId = this.order.deliveryId;
+      this.editDyForm.deliverySn = this.order.deliverySn;
+    },
+    getOrder(orderId){
+        this.orderId=orderId;
+        this.certificates = null;
+        getStoreOrder(orderId).then(response => {
+            this.order = response.order;
+            if(response.order.certificates != null){
+              this.certificates = response.order.certificates;
+            }
+            if(response.order.status != null){
+              this.orderStatus = response.order.status;
+            }
+            this.user = response.user;
+            this.logs = response.logs;
+            this.items = response.items;
+            this.payments=response.payments;
+            this.tuiMoneyLogs=response.tuiMoneyLogs;
+            this.auditLogs = response.auditLogs;
+        });
+     }
+  }
+};
+</script>
+<style scoped>
+.order-content{
+  margin: 10px;
+
+}
+
+.operate-container {
+  background: #F2F6FC;
+  height: 60px;
+  margin: -20px -20px 0;
+  line-height: 60px;
+}
+
+.operate-button-container {
+  float: right;
+  margin-right: 20px
+}
+</style>

+ 880 - 0
src/views/live/order/userDetail.vue

@@ -0,0 +1,880 @@
+<template>
+  <div class="order-content">
+      <div class="order-status" v-if="order!=null" >
+          <el-steps  :active="order.status==3?order.status+1:order.status" align-center>
+            <el-step title="待支付"></el-step>
+            <el-step title="待发货"></el-step>
+            <el-step title="待收货"></el-step>
+            <el-step title="交易完成"></el-step>
+          </el-steps>
+      </div>
+      <div>
+      <el-card shadow="never" style="margin-top: 15px">
+      <div class="operate-container"  v-if="order!=null">
+        <span style="margin-left: 20px" class="color-danger">订单状态:
+           <el-tag prop="status" v-for="(item, index) in statusOptions"   v-if="order.status==item.dictValue">{{item.dictLabel}}</el-tag>
+        </span>
+        <div class="operate-button-container" >
+          <el-button size="mini" @click="editOrder()"  v-hasPermi="['store:storeOrder:edit']" >修改订单</el-button>
+        </div>
+        <div class="operate-button-container"  v-hasPermi="['store:storeOrder:finishOrder']" v-if="order.status===2">
+          <el-button size="mini" @click="finishOrder()" >确认收货</el-button>
+        </div>
+        <div class="operate-button-container" v-if="order.status==1||order.status==2"  v-hasPermi="['store:storeOrder:refundOrderMoney']"  >
+          <el-button size="mini" @click="refundOrderMoney()" >退款</el-button>
+        </div>
+        <div class="operate-button-container"  v-hasPermi="['store:storeOrder:express']"  >
+          <el-button size="mini" @click="showExpress()">查看物流</el-button>
+        </div>
+        <div class="operate-button-container" v-if="order.tuiMoneyStatus==0"  v-hasPermi="['store:storeOrder:editTuiMoney']"  >
+          <el-button size="mini" @click="editTuiMoney1()" >解冻</el-button>
+        </div>
+        <div class="operate-button-container" v-if="order.tuiMoneyStatus==1"  v-hasPermi="['store:storeOrder:editTuiMoney']"  >
+          <el-button size="mini" @click="editTuiMoney2()" >冻结</el-button>
+        </div>
+
+        <div class="operate-button-container" v-if="order.status==3&&order.tuiMoneyStatus==null"  v-hasPermi="['store:storeOrder:addTuiMoney']"  >
+          <el-button size="mini" @click="addTuiMoney()" >分佣</el-button>
+        </div>
+
+<!--        <div class="operate-button-container" v-if="order.extendOrderId!=null"  v-hasPermi="['store:storeOrder:getEroOrder']"  >
+          <el-button size="mini" @click="showErpOrder()" >查看ERP订单信息</el-button>
+        </div>-->
+        <div class="operate-button-container" v-if="order.isPayRemain!=null&&order.isPayRemain==1"  v-hasPermi="['store:storeOrder:auditPayRemain']"  >
+          <el-button size="mini" @click="auditPayRemain()" >尾款审核</el-button>
+        </div>
+        <div class="operate-button-container" >
+          <el-button size="mini" @click="editDelivery()"  v-hasPermi="['store:storeOrder:editDeliveryId']" >修改物流</el-button>
+        </div>
+        <div class="operate-button-container" v-if="order.status >=1 && order.extendOrderId == null && order.deliveryId == null"  v-hasPermi="['store:storeOrder:createErpOrder']"  >
+          <el-button size="mini" @click="addErpOrder()" >创建ERP订单信息</el-button>
+        </div>
+        <!-- <div class="operate-button-container" v-show="order.status===2">
+          <el-button size="mini"  >去发货</el-button>
+          <el-button size="mini" >备注订单</el-button>
+        </div>
+        <div class="operate-button-container" v-show="order.status===4">
+          <el-button size="mini"  >订单跟踪</el-button>
+          <el-button size="mini"  disabled>备注订单</el-button>
+        </div> -->
+      </div>
+      <div style="margin: 20px 0px">
+        <span class="font-small">基本信息</span>
+      </div>
+      <el-descriptions :column="4" border  >
+            <el-descriptions-item label="订单编号"  >
+                <span v-if="order!=null">
+                  {{order.orderCode}}
+                </span>
+                <el-tag  v-for="(item, index) in createTypeOptions"    v-if="order!=null&&order.orderCreateType==item.dictValue">{{item.dictLabel}}
+                </el-tag>
+            </el-descriptions-item>
+            <el-descriptions-item label="会员"  >
+                <span v-if="user!=null">
+                  {{user.nickname}}({{user.phone}})
+                </span>
+            </el-descriptions-item>
+            <el-descriptions-item label="支付方式"  >
+              <el-tag prop="payType" v-for="(item, index) in payTypeOptions"    v-if="order!=null&&order.payType==item.dictValue">{{item.dictLabel}}
+              </el-tag>
+            </el-descriptions-item>
+<!--            <el-descriptions-item label="订单类型"  >
+              <el-tag prop="orderType" v-for="(item, index) in orderTypeOptions"    v-if="order!=null&&order.orderType==item.dictValue">{{item.dictLabel}}</el-tag>
+            </el-descriptions-item>-->
+            <el-descriptions-item label="物流公司编号"  >
+                <span v-if="order!=null">
+                  {{order.deliverySn}}
+                </span>
+            </el-descriptions-item>
+            <el-descriptions-item label="物流公司名称"  >
+                <span v-if="order!=null">
+                  {{order.deliveryName}}
+                </span>
+            </el-descriptions-item>
+            <el-descriptions-item label="物流单号"  >
+                <span v-if="order!=null">
+                  {{order.deliveryId}}
+                </span>
+            </el-descriptions-item>
+            <el-descriptions-item label="物流状态"  >
+                <span v-if="order!=null">
+                  <el-tag prop="deliveryId" v-for="(item, index) in deliveryStatusOptions"    v-if="order!=null&&order.deliveryStatus==item.dictValue">{{item.dictLabel}}</el-tag>
+                </span>
+            </el-descriptions-item>
+            <el-descriptions-item label="物流跟踪状态"  >
+                <span v-if="order!=null">
+                  <el-tag prop="deliveryId" v-for="(item, index) in deliveryTypeOptions"    v-if="order!=null&&order.deliveryType==item.dictValue">{{item.dictLabel}}</el-tag>
+                </span>
+            </el-descriptions-item>
+            <el-descriptions-item label="物流结算费用"  >
+                <span v-if="order!=null&&order.deliveryPayMoney!=null ">
+                  {{order.deliveryPayMoney.toFixed(2) }}
+                </span>
+            </el-descriptions-item>
+            <el-descriptions-item label="快递帐单日期"  >
+                <span v-if="order!=null ">
+                  {{order.deliveryTime }}
+                </span>
+            </el-descriptions-item>
+            <el-descriptions-item label="快递结算日期"  >
+                <span v-if="order!=null ">
+                  {{order.deliveryPayTime }}
+                </span>
+            </el-descriptions-item>
+            <el-descriptions-item label="推广佣金"  >
+                <span v-if="order!=null ">
+                  {{order.tuiMoney }}
+                </span>
+            </el-descriptions-item>
+            <el-descriptions-item label="收货人"  >
+                <span v-if="order!=null ">
+                  {{order.realName }}
+                </span>
+            </el-descriptions-item>
+            <el-descriptions-item label="手机号码"  >
+                <span v-if="order!=null ">
+                  {{order.userPhone }}
+                </span>
+                <el-button icon="el-icon-search" size="mini" @click="handlePhone()" style="margin-left: 20px;" circle v-hasPermi="['store:storeOrder:queryPhone']"></el-button>
+            </el-descriptions-item>
+            <el-descriptions-item label="收货地址"  >
+              <el-popover
+                v-if="order!=null"
+                placement="top-start"
+                title="收货地址"
+                width="300"
+                trigger="hover"
+                :content="order.userAddress">
+                <span slot="reference">{{order.userAddress}}</span>
+                <el-button icon="el-icon-search" size="mini" @click="handleAddress()" style="margin-left: 20px;" circle v-hasPermi="['store:storeOrder:queryAddress']"></el-button>
+              </el-popover>
+            </el-descriptions-item>
+            <el-descriptions-item label="档期归属"  >
+              <el-tag prop="scheduleId" v-for="(item, index) in scheduleOptions"    v-if="order!=null&&order.scheduleId==item.id">{{item.name}}
+              </el-tag>
+            </el-descriptions-item>
+            <el-descriptions-item label="用户备注"  >
+                <span v-if="order!=null">
+                  {{order.mark}}
+                </span>
+            </el-descriptions-item>
+      </el-descriptions>
+      <div style="margin: 20px 0px"  v-if="order!=null">
+        <span class="font-small">
+          凭证信息
+        </span>
+      </div>
+      <el-image
+          v-if="certificates != null"
+          :src="certificates"
+          :preview-src-list="[certificates]"
+          :style="{ width: '100px', height: '100px' }"
+          @click.native="showImageDialog"
+        ></el-image>
+        <el-dialog :visible.sync="dialogVisibleImage" width="10%">
+          <img :src="certificates" style="width: 100%" alt="">
+        </el-dialog>
+      <div style="margin-top: 20px">
+        <span class="font-small">商品信息</span>
+      </div>
+      <el-table
+        border
+        v-if="items!=null"
+        :data="items"
+        size="small"
+        style="width: 100%;margin-top: 20px" >
+        <el-table-column label="商品图片" width="150" align="center">
+          <template slot-scope="scope">
+            <img :src="JSON.parse(scope.row.jsonInfo).image" style="height: 80px">
+          </template>
+        </el-table-column>
+        <el-table-column label="商品编码" width="300" align="center">
+          <template slot-scope="scope">
+            <p>{{JSON.parse(scope.row.jsonInfo).barCode}}</p>
+          </template>
+        </el-table-column>
+        <el-table-column label="商品组合编码" width="300" align="center">
+          <template slot-scope="scope">
+            <p>{{JSON.parse(scope.row.jsonInfo).groupBarCode}}</p>
+          </template>
+        </el-table-column>
+        <el-table-column label="商品名称" width="300" align="center">
+          <template slot-scope="scope">
+            <p>{{JSON.parse(scope.row.jsonInfo).productName}}</p>
+          </template>
+        </el-table-column>
+        <el-table-column label="单价" width="240" align="center">
+          <template slot-scope="scope">
+            <p>¥{{JSON.parse(scope.row.jsonInfo).price.toFixed(2)}}</p>
+          </template>
+        </el-table-column>
+        <el-table-column label="规格" width="240" align="center">
+          <template slot-scope="scope">
+            {{JSON.parse(scope.row.jsonInfo).sku}}
+          </template>
+        </el-table-column>
+        <el-table-column label="数量" width="180" align="center">
+          <template slot-scope="scope">
+            {{scope.row.num}}
+          </template>
+        </el-table-column>
+        <el-table-column label="处方药" width="240" align="center">
+          <template slot-scope="scope">
+            {{scope.row.isPrescribe!=null&&scope.row.isPrescribe==1?'是':'否'}}
+          </template>
+        </el-table-column>
+        <el-table-column label="小计"  align="center">
+          <template slot-scope="scope" >
+            ¥{{scope.row.num*JSON.parse(scope.row.jsonInfo).price.toFixed(2)}}
+          </template>
+        </el-table-column>
+      </el-table>
+      <div style="float: right;margin: 20px" v-if="order!=null">
+        合计:<span class="color-danger">¥{{order.totalPrice.toFixed(2)}}</span>
+      </div>
+      <div style="margin: 60px 0px 20px 0px">
+        <span class="font-small">费用信息</span>
+      </div>
+      <el-descriptions   :column="4" border  >
+          <el-descriptions-item label="商品合计"  >
+              <span v-if="order!=null">
+                ¥{{order.totalPrice.toFixed(2)}}
+              </span>
+          </el-descriptions-item>
+          <el-descriptions-item label="应付金额"  >
+              <span v-if="order!=null">
+                ¥{{order.payPrice.toFixed(2)}}
+              </span>
+          </el-descriptions-item>
+          <el-descriptions-item label="运费"  >
+              <span v-if="order!=null">
+                ¥{{order.payPostage.toFixed(2)}}
+              </span>
+          </el-descriptions-item>
+          <el-descriptions-item label="优惠券"  >
+              <span v-if="order!=null">
+                ¥{{order.couponPrice.toFixed(2)}}
+              </span>
+          </el-descriptions-item>
+          <el-descriptions-item label="积分抵扣"  >
+              <span v-if="order!=null">
+                ¥{{order.deductionPrice.toFixed(2)}}
+              </span>
+          </el-descriptions-item>
+          <el-descriptions-item label="实付金额"  >
+              <span v-if="order!=null">
+                ¥{{order.payMoney.toFixed(2)}}
+              </span>
+          </el-descriptions-item>
+          <el-descriptions-item label="代收金额"  >
+              <span v-if="order!=null">
+                ¥{{order.payDelivery.toFixed(2)}}
+              </span>
+          </el-descriptions-item>
+      </el-descriptions>
+
+      <div style="margin-top: 20px">
+        <span class="font-small">支付信息</span>
+      </div>
+      <el-table
+        border
+        :data="payments"
+        size="small"
+        style="width: 100%;margin-top: 20px" >
+          <el-table-column label="支付单号" align="center" prop="payCode" width="120px" />
+          <el-table-column label="支付金额" align="center" prop="payMoney" />
+          <el-table-column label="类型" align="center" prop="payTypeCode" />
+          <el-table-column label="交易单号" align="center" prop="bankTransactionId" />
+          <el-table-column label="银行单号" align="center" prop="bankSerialNo" />
+          <el-table-column label="创建时间" align="center" prop="createTime" />
+          <el-table-column label="支付时间" align="center" prop="payTime" />
+      </el-table>
+
+
+      <div style="margin-top: 20px">
+        <span class="font-small">操作信息</span>
+      </div>
+      <el-table style="margin-top: 20px;width: 100%"
+                ref="orderHistoryTable"
+                :data="logs" border>
+        <el-table-column label="操作时间"  width="160" align="center">
+          <template slot-scope="scope">
+            {{scope.row.changeTime}}
+          </template>
+        </el-table-column>
+        <el-table-column label="备注" align="center">
+          <template slot-scope="scope">
+            {{scope.row.changeMessage}}
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <div style="margin-top: 20px">
+        <!-- <svg-icon icon-class="marker" style="color: #606266"></svg-icon> -->
+        <span class="font-small">分佣信息</span>
+      </div>
+      <el-table
+        border
+        :data="tuiMoneyLogs"
+        size="small"
+                style="width: 100%;margin-top: 20px" >
+          <el-table-column label="公司名称" align="center" prop="companyName" width="120px" />
+          <el-table-column label="金额" align="center" prop="money" />
+          <el-table-column label="余额" align="center" prop="balance" />
+          <el-table-column label="创建时间" align="center" prop="createTime" />
+          <el-table-column label="备注" align="center" prop="remark" />
+      </el-table>
+
+        <div style="margin-top: 20px">
+          <span class="font-small">审批信息</span>
+        </div>
+        <el-table style="margin-top: 20px;width: 100%"
+                  :data="auditLogs" border>
+          <el-table-column label="操作时间"  width="160" align="center">
+            <template slot-scope="scope">
+              {{scope.row.createTime}}
+            </template>
+          </el-table-column>
+          <el-table-column label="备注" align="center">
+            <template slot-scope="scope">
+              {{scope.row.content}}
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-card>
+    </div>
+
+    <el-dialog :title="edit.title" :visible.sync="edit.open" width="600px" append-to-body>
+      <el-form ref="editForm" :model="editForm" :rules="editRules" label-width="100px">
+<!--        <el-form-item label="订单类型" prop="orderType"  >
+            <el-select style="width: 200px" v-model="editForm.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-form-item label="状态" prop="status" >
+           <el-select style="width:220px" v-model="editForm.status" placeholder="请选择状态" clearable size="small">
+            <el-option key="0"  label="待支付" value="0" />
+            <el-option key="1"  label="待发货" value="1" />
+             <el-option key="2"  label="待收货" value="2" />
+             <el-option key="3"  label="交易成功" value="3" />
+             <el-option key="-1"  label="退款中" value="-1" />
+             <el-option key="-2"  label="已退款" value="-2" />
+             <el-option key="-3"  label="已取消" value="-3" />
+           </el-select>
+         </el-form-item>
+<!--         <el-form-item label="档期归属" prop="scheduleId"  >
+            <el-select filterable style="width: 200px" v-model="editForm.scheduleId" placeholder="请选择档期" clearable size="small" >
+              <el-option
+                      v-for="item in scheduleOptions"
+                      :key="item.id"
+                      :label="item.name"
+                      :value="item.id"
+                    />
+            </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="erp单号" prop="extendOrderId"  >
+          <el-input v-model="editForm.extendOrderId" placeholder="请输入" />
+        </el-form-item> -->
+        <el-form-item label="备注" prop="mark"  >
+          <el-input v-model="editForm.mark" placeholder="请输入备注" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitEditForm">确 定</el-button>
+      </div>
+    </el-dialog>
+
+    <el-dialog :title="editDy.title" :visible.sync="editDy.open" width="600px" append-to-body>
+      <el-form ref="editDyForm" :model="editDyForm" :rules="editDyRules" label-width="120px">
+<!--      <div  v-hasPermi="['store:storeOrder:updateErpOrder']"  style="margin-bottom: 20px;" >
+        <el-button size="mini" @click="updateErpOrder" >同步物流单号信息</el-button>
+      </div>-->
+        <el-form-item label="物流公司" prop="deliverySn" >
+           <el-select style="width:220px" v-model="editDyForm.deliverySn" placeholder="请选择" clearable size="small">
+            <el-option key="SF"  label="顺丰" value="SF" />
+            <el-option key="EMS"  label="邮政" value="EMS" />
+             <el-option key="ZTO"  label="中通" value="ZTO" />
+             <el-option key="STO"  label="申通" value="STO" />
+             <el-option key="JD"  label="京东" value="JD" />
+             <el-option key="DBL"  label="德邦" value="DBL" />
+             <el-option key="JTSD"  label="极兔" value="JTSD" />
+             <el-option key="YD"  label="韵达" value="YD" />
+             <el-option key="YTO"  label="圆通" value="YTO" />
+           </el-select>
+         </el-form-item>
+        <el-form-item label="物流单号" prop="deliveryId"  >
+          <el-input v-model="editDyForm.deliveryId" placeholder="请输入物流单号" />
+        </el-form-item>
+
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitEditDyForm">确 定</el-button>
+        <el-button @click="editDy.open = false">取 消</el-button>
+      </div>
+    </el-dialog>
+
+    <el-dialog :title="expressDialog.title" :visible.sync="expressDialog.open" width="600px" append-to-body>
+<!--      <div  v-hasPermi="['store:storeOrder:syncExpress']"  >
+        <el-button size="mini" @click="syncExpress()" >同步快递鸟物流状态</el-button>
+      </div>-->
+      <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="erpDialog.title" :visible.sync="erpDialog.open" width="600px" append-to-body>
+      <div v-if="order!=null&&order.extendOrderId!=null&&order.status==1"  v-hasPermi="['store:storeOrder:updateExpress']"  >
+        <el-button size="mini" @click="updateExpress()" >同步物流发货</el-button>
+      </div>
+      <div class="table-layout"  v-if="erpOrder!=null">
+        <el-row>
+          <el-col :span="6" class="table-cell-title">订单编号</el-col>
+          <el-col :span="6" class="table-cell-title">是否代收</el-col>
+          <el-col :span="6" class="table-cell-title">快递编号</el-col>
+          <el-col :span="6" class="table-cell-title">快递名称</el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="6" class="table-cell">
+            {{erpOrder.code}}
+          </el-col>
+          <el-col :span="6" class="table-cell">
+            {{erpOrder.cod?'是':'否'}}
+          </el-col>
+          <el-col :span="6" class="table-cell">
+            {{erpOrder.express_code}}
+          </el-col>
+          <el-col :span="6" class="table-cell">
+            {{erpOrder.express_name}}
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="6" class="table-cell-title">收货人</el-col>
+          <el-col :span="6" class="table-cell-title">电话</el-col>
+          <el-col :span="6" class="table-cell-title">地址</el-col>
+          <el-col :span="6" class="table-cell-title">运单号</el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="6" class="table-cell">
+            {{erpOrder.receiver_name}}
+          </el-col>
+          <el-col :span="6" class="table-cell">
+            {{erpOrder.receiver_mobile}}
+          </el-col>
+          <el-col :span="6" class="table-cell">
+            {{erpOrder.receiver_address}}
+          </el-col>
+          <el-col :span="6" class="table-cell">
+            {{erpOrder.deliverys!=null&&erpOrder.deliverys.length>0?erpOrder.deliverys[0].mail_no:''}}
+          </el-col>
+
+        </el-row>
+      </div>
+    </el-dialog>
+
+  </div>
+</template>
+
+<script>
+import {auditPayRemain,addTuiMoney,syncExpress,updateExpress,getEroOrder,refundOrderMoney, editTuiMoney,getExpress,finishOrder,listStoreOrder, getStoreOrder, delStoreOrder, addStoreOrder, updateStoreOrder, exportStoreOrder,updateDeliveryId, createErpOrder,updateErp,getStoreOrderAddress,getStoreOrderPhone} from "@/api/hisStore/storeOrder";
+import { getTcmScheduleList } from "@/api/company/schedule";
+export default {
+  name: "UserDetail",
+  props: {
+    orderId: {
+      type: [String, Number],
+      default: null
+    }
+  },
+  watch: {
+    orderId: {
+      immediate: true,
+      handler(val) {
+        if (val) {
+          this.getOrder(val);
+        }
+      }
+    }
+  },
+  data() {
+    return {
+      dialogVisibleImage: false,
+      createTypeOptions:[],
+      deliveryStatusOptions:[],
+      deliveryTypeOptions:[],
+      scheduleOptions:[],
+      schedules:[],
+      orderId:null,
+      erpDialog:{
+        title:"ERP订单信息",
+        open:false,
+      },
+      expressDialog:{
+        title:"物流信息",
+        open:false,
+      },
+      edit:{
+        title:"",
+        open:false,
+      },
+      editDy:{
+        title:"修改物流单号",
+        open:false,
+      },
+      editDyForm:{
+        deliverySn:null,
+        deliveryId:null,
+      },
+      editForm:{
+        orderType:null,
+        status:null,
+        userAddress:null,
+        // extendOrderId:null,
+        scheduleId:null,
+        mark:"",
+      },
+
+      editDyRules:{
+        deliverySn: [
+          { required: true, message: "物流公司不能为空", trigger: "blur" }
+        ],
+        deliveryId: [
+          { required: true, message: "物流单号不能为空", trigger: "blur" }
+        ],
+      },
+      editRules:{
+        userAddress: [
+          { required: true, message: "收货地址不能为空", trigger: "blur" }
+        ],
+
+      },
+      orderTypeOptions:[],
+      payTypeOptions:[],
+      statusOptions:[],
+      certificates:null,
+      orderStatus:null,
+      order:null,
+      user:{},
+      logs:[],
+      items:[],
+      express:null,
+      traces:[],
+      payments:[],
+      tuiMoneyLogs:[],
+      erpOrder:null,
+      auditLogs: [],
+    };
+  },
+  created() {
+    this.getDicts("store_order_delivery_status").then((response) => {
+      this.deliveryStatusOptions = response.data;
+    });
+    this.getDicts("store_order_delivery_type").then((response) => {
+      this.deliveryTypeOptions = response.data;
+    });
+
+    this.getDicts("store_order_type").then((response) => {
+      this.orderTypeOptions = response.data;
+    });
+    this.getDicts("store_order_status").then((response) => {
+      this.statusOptions = response.data;
+    });
+    this.getDicts("store_pay_type").then((response) => {
+      this.payTypeOptions = response.data;
+    });
+    this.getDicts("store_order_create_type").then((response) => {
+      this.createTypeOptions = response.data;
+    });
+    getTcmScheduleList().then(response => {
+      this.scheduleOptions = response.data;
+    });
+  },
+  methods: {
+    handleAddress(){
+        const id = this.order.id;
+        getStoreOrderAddress(id).then(response =>{
+            this.order.userAddress = response.address;
+        })
+      },
+      handlePhone(){
+        const id = this.order.id;
+        getStoreOrderPhone(id).then(response =>{
+            this.order.userPhone = response.userPhone;
+        })
+      },
+    showImageDialog() {
+      this.dialogVisible = true;
+    },
+    syncExpress(){
+      var that=this;
+        this.$confirm('确定同步物流状态吗', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          var data={orderId:that.order.id}
+          return syncExpress(data);
+        }).then(() => {
+          this.msgSuccess("操作成功");
+          this.getOrder(this.order.id);
+        }).catch(function() {});
+    },
+    updateErpOrder(){
+      var that=this;
+        this.$confirm('确定同步物流单号信息吗', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          var data={orderId:that.order.id}
+          return updateErp(data);
+        }).then(() => {
+          this.msgSuccess("操作成功");
+          this.getOrder(this.order.id);
+          this.editDy.open = false
+        }).catch(function() {});
+    },
+    updateExpress(){
+      var that=this;
+        this.$confirm('确定同步物流信息吗,同步后将自动发货', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          var data={orderId:that.order.id}
+          return updateExpress(data);
+        }).then(() => {
+          this.msgSuccess("操作成功");
+          this.getOrder(this.order.id);
+        }).catch(function() {});
+    },
+    showErpOrder(){
+      this.erpDialog.open=true;
+      var data={extendOrderId:this.order.extendOrderId};
+      getEroOrder(data).then(response => {
+          this.erp = response.data;
+          if(response.data.orders!=null&&response.data.orders.length==1){
+            this.erpOrder=response.data.orders[0]
+          }
+          console.log(this.erpOrder)
+
+      });
+    },
+    showExpress(){
+      this.expressDialog.open=true;
+      getExpress(this.orderId).then(response => {
+          this.express = response.data;
+          if(this.express!=null&&this.express.Traces!=null){
+              this.traces=this.express.Traces
+          }
+
+      });
+
+    },
+    submitEditForm(){
+        this.$refs["editForm"].validate(valid => {
+        if (valid) {
+          updateStoreOrder(this.editForm).then(response => {
+            if (response.code === 200) {
+              this.msgSuccess("操作成功");
+              this.edit.open = false;
+              this.getOrder(this.order.id);
+            }
+          });
+
+        }
+      });
+    },
+    submitEditDyForm(){
+        this.$refs["editDyForm"].validate(valid => {
+        if (valid) {
+          updateDeliveryId(this.editDyForm).then(response => {
+            if (response.code === 200) {
+              this.msgSuccess("操作成功");
+              this.editDy.open = false;
+              this.getOrder(this.order.id);
+            }
+          });
+
+        }
+      });
+    },
+    editTuiMoney1(){
+      var that=this;
+        this.$confirm('是否冻结此订单推广佣金吗?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          var data={orderId:that.order.id}
+          return editTuiMoney(data);
+        }).then(() => {
+          this.msgSuccess("操作成功");
+          this.getOrder(this.order.id);
+        }).catch(function() {});
+    },
+    editTuiMoney2(){
+      var that=this;
+        this.$confirm('是否解冻此订单推广佣金吗?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          var data={orderId:that.order.id}
+          return editTuiMoney(data);
+        }).then(() => {
+          this.msgSuccess("操作成功");
+          this.getOrder(this.order.id);
+        }).catch(function() {});
+    },
+    addTuiMoney(){
+      var that=this;
+        this.$confirm('确定手动分佣金吗?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          var data={orderId:that.order.id}
+          return addTuiMoney(data);
+        }).then(() => {
+          this.msgSuccess("操作成功");
+          this.getOrder(this.order.id);
+        }).catch(function() {});
+    },
+    refundOrderMoney(){
+        var that=this;
+        this.$confirm('确认退款吗', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          var data={orderId:that.order.id}
+          return refundOrderMoney(data);
+        }).then(() => {
+          this.msgSuccess("操作成功");
+          this.getOrder(this.order.id);
+        }).catch(function() {});
+    },
+    finishOrder(){
+        var that=this;
+        this.$confirm('是否确认客户已收货?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          var data={orderId:that.order.id}
+          return finishOrder(data);
+        }).then(() => {
+          this.msgSuccess("操作成功");
+          this.getOrder(this.order.id);
+        }).catch(function() {});
+    },
+    auditPayRemain(){
+        var that=this;
+        this.$confirm('是否确认已收尾款?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          var data={orderId:that.order.id}
+          return auditPayRemain(data);
+        }).then(() => {
+          this.msgSuccess("操作成功");
+          this.getOrder(this.order.id);
+        }).catch(function() {});
+    },
+    editOrder(){
+        this.edit.open=true;
+        this.editForm.id=this.order.id;
+        this.editForm.mark=this.order.mark
+        this.editForm.orderType=this.order.orderType.toString();
+        this.editForm.status = this.order.status.toString();
+        this.editForm.userAddress = this.order.userAddress.toString();
+        this.editForm.scheduleId = this.order.scheduleId;
+        // this.editForm.extendOrderId = this.order.extendOrderId.toString();
+
+    },
+    //推送管易按钮
+    addErpOrder(){
+        var that=this;
+        this.$confirm('是否确认推送erp?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          var data={orderCode:that.order.orderCode}
+          return createErpOrder(data);
+        }).then(() => {
+          this.msgSuccess("操作成功");
+          this.getOrder(this.order.id);
+        }).catch(function() {});
+    },
+    editDelivery(){
+      this.editDy.open = true;
+      this.editDyForm.id = this.order.id;
+      this.editDyForm.deliveryId = this.order.deliveryId;
+      this.editDyForm.deliverySn = this.order.deliverySn;
+    },
+    getOrder(orderId){
+        this.orderId=orderId;
+        this.certificates = null;
+        getStoreOrder(orderId).then(response => {
+            this.order = response.order;
+            if(response.order.certificates != null){
+              this.certificates = response.order.certificates;
+            }
+            if(response.order.status != null){
+              this.orderStatus = response.order.status;
+            }
+            this.user = response.user;
+            this.logs = response.logs;
+            this.items = response.items;
+            this.payments=response.payments;
+            this.tuiMoneyLogs=response.tuiMoneyLogs;
+            this.auditLogs = response.auditLogs;
+        });
+     }
+  }
+};
+</script>
+<style scoped>
+.order-content{
+  margin: 10px;
+
+}
+
+.operate-container {
+  background: #F2F6FC;
+  height: 60px;
+  margin: -20px -20px 0;
+  line-height: 60px;
+}
+
+.operate-button-container {
+  float: right;
+  margin-right: 20px
+}
+</style>

+ 357 - 0
src/views/qw/assignRule/index.vue

@@ -0,0 +1,357 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
+      <el-form-item label="规则名称" prop="ruleName">
+        <el-input
+          v-model="queryParams.ruleName"
+          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 label="启用" :value="1" />
+          <el-option label="停用" :value="0" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+        >新增</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="assignRuleList" border>
+      <el-table-column label="规则名称" align="center" prop="ruleName" />
+      <el-table-column label="分配类型" align="center" prop="assignType">
+        <template slot-scope="scope">
+          <span v-if="scope.row.assignType === 1">轮询</span>
+          <span v-else-if="scope.row.assignType === 2">依次</span>
+          <span v-else-if="scope.row.assignType === 3">按权重</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="员工数" align="center" prop="qwAssignRuleUsers">
+        <template slot-scope="scope">
+          {{ scope.row.qwAssignRuleUsers && Array.isArray(scope.row.qwAssignRuleUsers) ? scope.row.qwAssignRuleUsers.length : 0 }}
+        </template>
+      </el-table-column>
+      <el-table-column label="状态" align="center" prop="status">
+        <template slot-scope="scope">
+          <el-tag :type="scope.row.status === 1 ? 'success' : 'danger'">
+            {{ scope.row.status === 1 ? '启用' : '停用' }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="创建时间" align="center" prop="createTime" />
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="280px">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+          >编辑</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            @click="handleStatusChange(scope.row)"
+          >{{ scope.row.status === 1 ? '停用' : '启用' }}</el-button>
+          <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="700px" append-to-body @close="cancel">
+      <el-form ref="form" :model="form" :rules="rules" label-width="100px">
+        <el-form-item label="规则名称" prop="ruleName">
+          <el-input v-model="form.ruleName" placeholder="请输入规则名称" />
+        </el-form-item>
+        <el-form-item label="分配类型" prop="assignType">
+          <el-radio-group v-model="form.assignType">
+            <el-radio :label="3">按权重</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="选择员工" prop="qwAssignRuleUsers" style="margin-top: 2%">
+          <div>
+            <el-button
+              size="medium"
+              icon="el-icon-circle-plus-outline"
+              plain
+              @click="handleSelectUser">请选择使用员工</el-button>
+          </div>
+          <div style="margin-top: 10px;">
+            <el-table :data="personnelList" border size="small" style="margin-top: 10px;">
+              <el-table-column label="企微号" align="center" prop="qwUserId" width="120px" />
+              <el-table-column label="企微姓名" align="center" prop="qwUserName" width="120px" />
+              <el-table-column label="权重数" align="center" width="100px">
+                <template slot-scope="scope">
+                  <el-input v-model.number="scope.row.weight" type="number" size="small" />
+                </template>
+              </el-table-column>
+              <el-table-column label="操作" align="center" width="80px">
+                <template slot-scope="scope">
+                  <el-button type="danger" size="mini" icon="el-icon-delete" @click="handleRemovePersonnel(scope.row)"></el-button>
+                </template>
+              </el-table-column>
+            </el-table>
+          </div>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 分配详情对话框 -->
+    <el-dialog title="分配详情" :visible.sync="detailsOpen" width="900px" append-to-body>
+      <el-table :data="detailsPersonnelList" border style="margin-bottom: 20px;">
+        <el-table-column label="企微号" align="center" prop="qwUserId" />
+        <el-table-column label="企微姓名" align="center" prop="qwUserName" />
+        <el-table-column label="权重数" align="center" prop="weight" />
+        <el-table-column label="今日分配数" align="center" prop="assignNumToDay" />
+        <el-table-column label="累积分配数" align="center" prop="assignNumCount" />
+        <el-table-column label="今日添加数" align="center" prop="addNumToDay" />
+        <el-table-column label="累积添加数" align="center" prop="addNumCount" />
+      </el-table>
+    </el-dialog>
+
+    <!-- 选择员工对话框 -->
+    <el-dialog :title="selectUserDialog.title" :visible.sync="selectUserDialog.open" width="1300px" append-to-body>
+      <qwUserList ref="QwUserList" @selectUserList="selectUserList"></qwUserList>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listAssignRule, getAssignRule, addOrUpdateAssignRule, enableAssignRule } from "@/api/qw/assignRule";
+import qwUserList from '@/views/qw/user/qwUserList.vue'
+
+export default {
+  name: "AssignRule",
+  components: { qwUserList },
+  data() {
+    return {
+      loading: true,
+      showSearch: true,
+      total: 0,
+      assignRuleList: [],
+      title: "",
+      open: false,
+      detailsOpen: false,
+      detailsPersonnelList: [],
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        ruleName: null,
+        status: null,
+      },
+      form: {
+        assignType: 3,
+        qwAssignRuleUsers: null
+      },
+      personnelList: [],
+      selectUserDialog: {
+        title: "选择员工",
+        open: false
+      },
+      rules: {
+        ruleName: [
+          { required: true, message: "规则名称不能为空", trigger: "blur" }
+        ]
+      },
+
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询分配规则列表 */
+    getList() {
+      this.loading = true;
+      listAssignRule(this.queryParams).then(response => {
+        this.assignRuleList = response.data.records;
+        this.total = response.data.total;
+        this.loading = false;
+      }).catch(() => {
+        this.loading = false;
+      });
+    },
+
+    // ... existing code ...
+
+    /** 新增按钮 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "新增分配规则";
+    },
+
+    /** 修改按钮 */
+    handleUpdate(row) {
+      this.reset();
+      this.open = true;
+      this.title = "修改分配规则";
+      getAssignRule(row.id).then(response => {
+        this.form = response.data;
+        if (this.form.qwAssignRuleUsers && Array.isArray(this.form.qwAssignRuleUsers)) {
+          this.personnelList = this.form.qwAssignRuleUsers;
+        }
+      });
+    },
+
+    /** 启用/停用 */
+    handleStatusChange(row) {
+      const status = row.status === 1 ? 0 : 1;
+      const statusText = status === 1 ? "启用" : "停用";
+      this.$confirm(`确认要${statusText}该分配规则吗?`, "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        enableAssignRule(row.id, status).then(response => {
+          this.msgSuccess(`${statusText}成功`);
+          this.getList();
+        });
+      }).catch(() => {});
+    },
+
+    /** 分配详情 */
+    handleDetails(row) {
+      if (row.qwAssignRuleUsers && Array.isArray(row.qwAssignRuleUsers)) {
+        this.detailsPersonnelList = row.qwAssignRuleUsers;
+      } else {
+        this.detailsPersonnelList = [];
+      }
+      this.detailsOpen = true;
+    },
+
+    /** 选择员工 */
+    handleSelectUser() {
+      this.selectUserDialog.open = true;
+      this.$nextTick(() => {
+        this.$refs.QwUserList.getDetails(null, null, null, null);
+      });
+    },
+
+    /** 接收选中的员工 */
+    selectUserList(selectUsers) {
+      this.selectUserDialog.open = false;
+      if (selectUsers && selectUsers.length > 0) {
+        selectUsers.forEach(user => {
+          const exists = this.personnelList.find(p => p.sysQwUserId === user.id);
+          if (!exists) {
+            this.personnelList.push({
+              assignId: this.form.id || null,
+              sysQwUserId: user.id,
+              qwUserName: user.qwUserName,
+              qwUserId: user.qwUserId,
+              weight: 1,
+              assignNumToDay: 0,
+              assignNumCount: 0,
+              addNumToDay: 0,
+              addNumCount: 0
+            });
+          }
+        });
+      }
+    },
+
+    /** 移除员工 */
+    handleRemovePersonnel(item) {
+      const index = this.personnelList.findIndex(p => p.qwUserId === item.qwUserId);
+      if (index > -1) {
+        this.personnelList.splice(index, 1);
+      }
+    },
+
+    /** 表单提交 */
+    submitForm() {
+      this.$refs.form.validate(valid => {
+        if (valid) {
+          if (this.personnelList.length === 0) {
+            this.$message.error("请选择至少一个员工");
+            return;
+          }
+          // 准备发送的数据,包含 weight
+          const submitPersonnelList = this.personnelList.map(p => ({
+            assignId: this.form.id || null,
+            sysQwUserId: p.sysQwUserId,
+            qwUserName: p.qwUserName,
+            qwUserId: p.qwUserId,
+            weight: p.weight
+          }));
+          const data = {
+            ...this.form,
+            qwAssignRuleUsers: submitPersonnelList
+          };
+          addOrUpdateAssignRule(data).then(response => {
+            this.msgSuccess(this.form.id ? "修改成功" : "新增成功");
+            this.open = false;
+            this.getList();
+          });
+        }
+      });
+    },
+
+    /** 取消 */
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+
+    /** 表单重置 */
+    reset() {
+      this.form = {
+        id: null,
+        ruleName: null,
+        assignType: 3,
+        qwAssignRuleUsers: null,
+        status: 1
+      };
+      this.personnelList = [];
+      this.resetForm("form");
+    },
+
+    /** 搜索 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+
+    /** 重置 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    }
+  }
+};
+</script>

+ 693 - 0
src/views/qw/customerLink/index.vue

@@ -0,0 +1,693 @@
+<template>
+  <div class="app-container">
+    <!-- 查询表单 -->
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="链接名称" prop="linkName">
+        <el-input
+          v-model="queryParams.linkName"
+          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>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+        >新增</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <!-- 列表 -->
+    <el-table v-loading="loading" :data="list" border>
+      <el-table-column label="链接名称" align="center" prop="linkName" />
+      <el-table-column label="企业微信" align="center" prop="corpName" />
+      <el-table-column label="渠道链接" align="center" prop="url" show-overflow-tooltip />
+      <el-table-column label="创建人" align="center" prop="createBy" />
+      <el-table-column label="创建时间" align="center" prop="createTime" />
+      <el-table-column label="最后修改人" align="center" prop="updateBy" />
+      <el-table-column label="修改时间" align="center" prop="updateTime" />
+      <el-table-column label="今日添加数" align="center" prop="toDayAddNum" />
+      <el-table-column label="累积添加数" align="center" prop="countAddNum" />
+      <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)">编辑</el-button>
+          <el-button size="mini" type="text" icon="el-icon-view" @click="handleViewChannel(scope.row)">查看渠道</el-button>
+          <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(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 @close="cancel">
+      <el-form ref="form" :model="form" :rules="rules" label-width="100px">
+        <el-form-item label="链接名称" prop="linkName">
+          <el-input v-model="form.linkName" placeholder="请输入链接名称" />
+        </el-form-item>
+        <el-form-item label="企业微信" prop="corpId">
+          <el-select v-model="form.corpId" placeholder="请选择企业微信" @change="handleCorpChange">
+            <el-option v-for="item in qwCompanyList" :key="item.corpId" :label="item.corpName" :value="item.corpId" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="自动通过好友" prop="skipVerify">
+          <div style="display: flex; align-items: center; height: 32px;">
+            <el-switch v-model="form.skipVerify" :active-value="1" :inactive-value="0" />
+          </div>
+        </el-form-item>
+        <el-form-item label="员工选择" prop="linkUser">
+          <div>
+            <el-button
+              size="medium"
+              icon="el-icon-circle-plus-outline"
+              plain
+              @click="handleSelectUser">请选择员工</el-button>
+          </div>
+          <div style="margin-top: 10px;">
+            <el-table :data="personnelList" border size="small" style="margin-top: 10px;">
+              <el-table-column label="企微号" align="center" prop="qwUserId" width="120px" />
+              <el-table-column label="企微姓名" align="center" prop="qwUserName" width="120px" />
+              <el-table-column label="操作" align="center" width="80px">
+                <template slot-scope="scope">
+                  <el-button type="danger" size="mini" icon="el-icon-delete" @click="handleRemovePersonnel(scope.row)"></el-button>
+                </template>
+              </el-table-column>
+            </el-table>
+          </div>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 查看渠道链接对话框 -->
+    <el-dialog title="查看渠道链接" :visible.sync="channelDialogOpen" width="900px" append-to-body @close="cancelChannel">
+      <!-- 查询表单 -->
+      <el-form :model="channelQueryParams" ref="channelQueryForm" :inline="true" v-show="channelShowSearch" label-width="68px">
+        <el-form-item label="渠道名称" prop="linkName">
+          <el-input
+            v-model="channelQueryParams.linkName"
+            placeholder="请输入渠道名称"
+            clearable
+            size="small"
+            @keyup.enter.native="getChannelList"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" icon="el-icon-search" size="mini" @click="getChannelList">搜索</el-button>
+          <el-button icon="el-icon-refresh" size="mini" @click="resetChannelQuery">重置</el-button>
+        </el-form-item>
+      </el-form>
+
+      <el-row :gutter="10" class="mb8">
+        <el-col :span="1.5">
+          <el-button
+            type="primary"
+            plain
+            icon="el-icon-plus"
+            size="mini"
+            @click="handleAddChannel"
+          >新增</el-button>
+        </el-col>
+        <right-toolbar :showSearch.sync="channelShowSearch" @queryTable="getChannelList"></right-toolbar>
+      </el-row>
+
+      <!-- 渠道列表 -->
+      <el-table v-loading="channelLoading" :data="channelList" border>
+        <el-table-column label="渠道名称" align="center" prop="linkName" />
+        <el-table-column label="渠道链接" align="center" prop="url" show-overflow-tooltip />
+        <el-table-column label="今日添加数" align="center" prop="toDayAddNum" />
+        <el-table-column label="累积添加数" align="center" prop="countAddNum" />
+        <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-delete" @click="handleDeleteChannel(scope.row)">删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <!-- 分页 -->
+      <pagination
+        v-show="channelTotal>0"
+        :total="channelTotal"
+        :page.sync="channelQueryParams.pageNum"
+        :limit.sync="channelQueryParams.pageSize"
+        @pagination="getChannelList"
+      />
+
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="cancelChannel">关 闭</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 新增渠道对话框 -->
+    <el-dialog title="选择渠道" :visible.sync="selectChannelDialogOpen" width="900px" append-to-body>
+      <el-row :gutter="20">
+        <!-- 左侧:分组和渠道选择 -->
+        <el-col :xs="24" :sm="12">
+          <div style="border-right: 1px solid #dcdfe6; padding-right: 20px;">
+            <h4 style="margin-top: 0; margin-bottom: 15px;">分组管理</h4>
+            <div style="margin-bottom: 15px;">
+              <el-input 
+                v-model="selectChannelQueryParams.channelName" 
+                placeholder="搜索分组"
+                clearable
+                size="small"
+                @input="loadGroupList"
+              />
+            </div>
+            <div style="border: 1px solid #dcdfe6; border-radius: 4px; max-height: 400px; overflow-y: auto;">
+              <div 
+                v-for="item in groupList" 
+                :key="item.id"
+                :class="['group-item', { active: selectedGroupId === item.id }]"
+                @click="selectGroup(item)"
+                style="padding: 10px 15px; cursor: pointer; border-bottom: 1px solid #f0f0f0; user-select: none;"
+              >
+                <span>{{ item.channelName }}</span>
+              </div>
+              <div v-if="groupList.length === 0" style="padding: 20px; text-align: center; color: #909399;">暂无分组</div>
+            </div>
+          </div>
+        </el-col>
+    
+        <!-- 中间:渠道列表 -->
+        <el-col :xs="24" :sm="12">
+          <div style="padding: 0 20px;">
+            <h4 style="margin-top: 0; margin-bottom: 15px;">渠道模板</h4>
+            <div v-if="!selectedGroupId" style="padding: 40px 0; text-align: center; color: #909399;">
+              请上方选择分组
+            </div>
+            <div v-else>
+              <div style="border: 1px solid #dcdfe6; border-radius: 4px; max-height: 400px; overflow-y: auto;">
+                <div 
+                  v-for="item in selectChannelList" 
+                  :key="item.id"
+                  style="padding: 10px 15px; border-bottom: 1px solid #f0f0f0; display: flex; align-items: center; user-select: none; cursor: pointer; background-color: #fff; transition: all 0.3s;"
+                  :style="{ backgroundColor: isChannelSelected(item) ? '#e6f7ff' : '#fff' }"
+                  @click="toggleChannelSelection(item)"
+                >
+                  <el-checkbox 
+                    :value="isChannelSelected(item)"
+                    style="margin-right: 8px;"
+                  />
+                  <span>{{ item.channelName }}</span>
+                </div>
+                <div v-if="selectChannelList.length === 0" style="padding: 20px; text-align: center; color: #909399;">暂无渠道</div>
+              </div>
+            </div>
+          </div>
+        </el-col>
+      </el-row>
+    
+      <!-- 右侧:已选渠道 -->
+      <div style="margin-top: 20px; padding-top: 20px; border-top: 1px solid #dcdfe6;">
+        <h4 style="margin-top: 0; margin-bottom: 10px;">已选渠道({{ selectedChannels.length }})</h4>
+        <div style="border: 1px solid #dcdfe6; border-radius: 4px; padding: 10px; min-height: 80px; max-height: 150px; overflow-y: auto;">
+          <el-tag 
+            v-for="(item, index) in selectedChannels" 
+            :key="item.channelId || index"
+            closable
+            @close="removeSelectedChannel(item)"
+            style="margin: 5px; cursor: pointer;"
+          >
+            {{ item.channelName }}
+          </el-tag>
+          <div v-if="selectedChannels.length === 0" style="color: #909399; text-align: center; padding: 20px 0;">未选择任何渠道</div>
+        </div>
+      </div>
+    
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="selectChannelDialogOpen = false">取 消</el-button>
+        <el-button type="primary" @click="confirmAddChannel">确 定</el-button>
+      </div>
+    </el-dialog>
+    <!-- 选择员工对话框 -->
+    <el-dialog :title="selectUserDialog.title" :visible.sync="selectUserDialog.open" width="1300px" append-to-body>
+      <qwUserList ref="QwUserList" @selectUserList="selectUserList"></qwUserList>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import {
+  pageCustomerLink,
+  getCustomerLink,
+  createOrUpdateCustomerLink,
+  deleteCustomerLink,
+  pageCustomerLinkChannel,
+  createCustomerLinkChannel,
+  deleteCustomerLinkChannel
+} from '@/api/qw/customerLink'
+import { getMyQwCompanyList, listUser } from '@/api/qw/user'
+import { pageProject } from '@/api/adv/channel'
+import qwUserList from '@/views/qw/user/qwUserList.vue'
+import Pagination from '@/components/Pagination'
+import RightToolbar from '@/components/RightToolbar'
+
+export default {
+  name: 'CustomerLink',
+  components: {
+    qwUserList,
+    Pagination,
+    RightToolbar
+  },
+  data() {
+    return {
+      // 加载状态
+      loading: false,
+      channelLoading: false,
+      // 是否显示查询表单
+      showSearch: true,
+      channelShowSearch: true,
+      // 列表数据
+      list: [],
+      total: 0,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        linkName: undefined,
+        corpName: undefined
+      },
+      // 表单参数
+      form: {},
+      // 表单验证规则
+      rules: {
+        linkName: [
+          { required: true, message: '请输入链接名称', trigger: 'blur' }
+        ],
+        corpId: [
+          { required: true, message: '请选择企业微信', trigger: 'change' }
+        ],
+        linkUser: [
+          { required: true, message: '请选择员工', trigger: 'change' }
+        ]
+      },
+      // 对话框
+      open: false,
+      title: '',
+      // 企业微信列表
+      qwCompanyList: [],
+      // 企业微信员工列表
+      qwUserList: [],
+      // 分配规则人员列表
+      personnelList: [],
+      // 选择员工对话框
+      selectUserDialog: {
+        title: "选择员工",
+        open: false
+      },
+      
+      // 渠道链接对话框
+      channelDialogOpen: false,
+      channelList: [],
+      channelTotal: 0,
+      channelQueryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        linkName: undefined,
+        sysLinkId: undefined
+      },
+      // 新增渠道对话框
+      selectChannelDialogOpen: false,
+      selectChannelParams: {
+        channelId: undefined
+      },
+      channelOptions: [],
+      currentSysLinkId: undefined,
+      
+      // 渠道选择相关数据
+      groupList: [],  // 分组列表
+      selectedGroupId: null,  // 选中的分组ID
+      selectChannelList: [],  // 当前分组的渠道列表
+      selectChannelLoading: false,  // 渠道列表加载中
+      selectedChannels: [],  // 已选择的渠道列表(右侧)
+      selectChannelPageNum: 1,
+      selectChannelPageSize: 10,
+      selectChannelTotal: 0,
+      selectChannelQueryParams: {
+        channelName: ''
+      }
+    }
+  },
+  created() {
+    this.getList()
+    this.loadQwCompanyList()
+  },
+  methods: {
+    // 查询列表
+    getList() {
+      this.loading = true
+      pageCustomerLink(this.queryParams).then(response => {
+        this.list = response.data.records || []
+        this.total = response.data.total || 0
+        this.loading = false
+      }).catch(() => {
+        this.loading = false
+      })
+    },
+    // 搜索
+    handleQuery() {
+      this.queryParams.pageNum = 1
+      this.getList()
+    },
+    // 重置查询
+    resetQuery() {
+      this.resetForm('queryForm')
+      this.handleQuery()
+    },
+    // 新增
+    handleAdd() {
+      this.reset()
+      this.open = true
+      this.title = '新增渠道管理'
+    },
+    // 编辑
+    handleUpdate(row) {
+      this.reset()
+      this.open = true
+      this.title = '编辑渠道管理'
+      // 调用详情接口获取按键数据
+      getCustomerLink(row.id).then(response => {
+        this.form = response.data
+        // 从 qwCustomerLinkUsers 映射到 personnelList
+        if (this.form.qwCustomerLinkUsers && Array.isArray(this.form.qwCustomerLinkUsers)) {
+          this.personnelList = this.form.qwCustomerLinkUsers.map(user => ({
+            sysQwUserId: user.sysQwUserId,
+            qwUserName: user.qwUserName,
+            qwUserId: user.qwUserId
+          }))
+          // 同步 linkUser
+          this.form.linkUser = this.personnelList
+        }
+        this.loadQwUserList(this.form.corpId)
+      })
+    },
+    // 删除
+    handleDelete(row) {
+      this.$confirm('确定删除该渠道吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        deleteCustomerLink(row.id).then(response => {
+          this.$message.success('删除成功')
+          this.getList()
+        })
+      }).catch(() => {})
+    },
+    // 查看渠道链接
+    handleViewChannel(row) {
+      this.channelDialogOpen = true
+      this.currentSysLinkId = row.id
+      this.channelQueryParams.sysLinkId = row.id
+      this.getChannelList()
+    },
+    // 查询渠道链接列表
+    getChannelList() {
+      this.channelLoading = true
+      pageCustomerLinkChannel(this.channelQueryParams).then(response => {
+        this.channelList = response.data.records || []
+        this.channelTotal = response.data.total || 0
+        this.channelLoading = false
+      }).catch(() => {
+        this.channelLoading = false
+      })
+    },
+    // 重置渠道查询
+    resetChannelQuery() {
+      this.channelQueryParams = {
+        pageNum: 1,
+        pageSize: 10,
+        linkName: undefined,
+        sysLinkId: this.currentSysLinkId
+      }
+      this.getChannelList()
+    },
+    // 新增渠道链接
+    handleAddChannel() {
+      this.selectChannelDialogOpen = true
+      this.selectedChannels = []
+      this.selectedGroupId = null
+      this.selectChannelList = []
+      this.selectChannelQueryParams.channelName = ''
+      this.loadGroupList()
+    },
+    // 加载分组列表
+    loadGroupList() {
+      const params = { pageNum: 1, pageSize: 1000, parentId: 0 }
+      pageProject(params).then(response => {
+        this.groupList = response.data.records || []
+      }).catch(() => {
+        this.groupList = []
+      })
+    },
+    // 选择分组
+    selectGroup(group) {
+      this.selectedGroupId = group.id
+      this.selectChannelPageNum = 1
+      this.getSelectChannelList()
+    },
+    // 获取渠道列表
+    getSelectChannelList() {
+      if (!this.selectedGroupId) return
+      this.selectChannelLoading = true
+      const params = { pageNum: this.selectChannelPageNum, pageSize: this.selectChannelPageSize, parentId: this.selectedGroupId }
+      if (this.selectChannelQueryParams.channelName) {
+        params.channelName = this.selectChannelQueryParams.channelName
+      }
+      pageProject(params).then(response => {
+        this.selectChannelList = response.data.records || []
+        this.selectChannelTotal = response.data.total || 0
+        this.selectChannelLoading = false
+      }).catch(() => {
+        this.selectChannelLoading = false
+      })
+    },
+    // 勾选渠道
+    toggleChannelSelection(channel) {
+      const index = this.selectedChannels.findIndex(item => item.channelId === channel.id)
+      if (index > -1) {
+        // 已选中,取消选择
+        this.selectedChannels.splice(index, 1)
+      } else {
+        // 未选中,添加选择
+        this.selectedChannels.push({ channelId: channel.id, channelName: channel.channelName })
+      }
+    },
+    // 判断渠道是否已选择
+    isChannelSelected(channel) {
+      return this.selectedChannels.some(item => item.channelId === channel.id)
+    },
+    // 删除已选渠道
+    removeSelectedChannel(channel) {
+      const index = this.selectedChannels.findIndex(item => item.channelId === channel.channelId)
+      if (index > -1) {
+        this.selectedChannels.splice(index, 1)
+      }
+    },
+    // 加载渠道选项
+    loadChannelOptions() {
+      // 这里需要加载渠道列表,根据实际接口调整
+      // 暂时使用空数组,需要后续实现
+      this.channelOptions = []
+    },
+    // 确认新增渠道
+    confirmAddChannel() {
+      if (this.selectedChannels.length === 0) {
+        this.$message.error('请选择渠道')
+        return
+      }
+      const data = {
+        sysLinkId: this.currentSysLinkId,
+        channelList: this.selectedChannels
+      }
+      createCustomerLinkChannel(data).then(response => {
+        this.$message.success('新增成功')
+        this.selectChannelDialogOpen = false
+        this.getChannelList()
+      })
+    },
+    // 删除渠道链接
+    handleDeleteChannel(row) {
+      this.$confirm('确定删除该渠道链接吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        deleteCustomerLinkChannel(row.id).then(response => {
+          this.$message.success('删除成功')
+          this.getChannelList()
+        })
+      }).catch(() => {})
+    },
+    // 关闭渠道链接对话框
+    cancelChannel() {
+      this.channelDialogOpen = false
+      this.currentSysLinkId = undefined
+    },
+    // 表单提交
+    submitForm() {
+      this.$refs.form.validate(valid => {
+        if (valid) {
+          // 将 personnelList 赋值给 form.linkUser
+          this.form.linkUser = this.personnelList.map(p => ({
+            sysQwUserId: p.sysQwUserId,
+            qwUserName: p.qwUserName,
+            qwUserId: p.qwUserId
+          }))
+          const data = {
+            linkName: this.form.linkName,
+            corpId: this.form.corpId,
+            corpName: this.form.corpName,
+            skipVerify: this.form.skipVerify,
+            linkUser: this.form.linkUser
+          }
+          if (this.form.id) {
+            data.id = this.form.id
+          }
+          createOrUpdateCustomerLink(data).then(response => {
+            this.$message.success(this.form.id ? '编辑成功' : '新增成功')
+            this.open = false
+            this.getList()
+          })
+        }
+      })
+    },
+    // 重置表单
+    reset() {
+      this.form = {
+        linkName: undefined,
+        corpId: undefined,
+        corpName: undefined,
+        skipVerify: 0,
+        linkUser: []
+      }
+      this.personnelList = []
+      this.qwUserList = []
+      if (this.$refs.form) {
+        this.$refs.form.clearValidate()
+      }
+    },
+    // 取消
+    cancel() {
+      this.open = false
+      this.reset()
+    },
+    // 加载企业微信列表
+    loadQwCompanyList() {
+      getMyQwCompanyList().then(response => {
+        this.qwCompanyList = response.data || []
+      }).catch(() => {
+        this.qwCompanyList = []
+      })
+    },
+    // 企业微信改变
+    handleCorpChange(corpId) {
+      this.personnelList = []
+      this.form.linkUser = []
+      this.qwUserList = []
+      const selectedCompany = this.qwCompanyList.find(item => item.corpId === corpId)
+      if (selectedCompany) {
+        this.form.corpName = selectedCompany.corpName
+      }
+      // 不加载员工列表,等待用户点击"请选择员工"按钮时再加载
+    },
+    // 加载企业微信员工列表
+    loadQwUserList(corpId) {
+      if (!corpId) {
+        this.qwUserList = []
+        return
+      }
+      listUser({ corpId: corpId }).then(response => {
+        this.qwUserList = response.rows || []
+      }).catch(() => {
+        this.qwUserList = []
+      })
+    },
+    // 选择员工
+    handleSelectUser() {
+      if (!this.form.corpId) {
+        this.$message.warning('请先选择企业微信')
+        return
+      }
+      this.selectUserDialog.open = true
+      this.$nextTick(() => {
+        // 传递 corpId 给 qwUserList 组件,确保加载正确的企业微信员工
+        this.$refs.QwUserList.getDetails(this.form.corpId, null, null, null)
+      })
+    },
+
+    /** 接收选中的员工 */
+    selectUserList(selectUsers) {
+      this.selectUserDialog.open = false
+      if (selectUsers && selectUsers.length > 0) {
+        selectUsers.forEach(user => {
+          const exists = this.personnelList.find(p => p.sysQwUserId === user.id)
+          if (!exists) {
+            this.personnelList.push({
+              sysQwUserId: user.id,
+              qwUserName: user.qwUserName,
+              qwUserId: user.qwUserId
+            })
+          }
+        })
+        // 更新 form.linkUser
+        this.form.linkUser = this.personnelList.map(p => ({
+          sysQwUserId: p.sysQwUserId,
+          qwUserName: p.qwUserName,
+          qwUserId: p.qwUserId
+        }))
+      }
+    },
+
+    /** 移除员工 */
+    handleRemovePersonnel(item) {
+      const index = this.personnelList.findIndex(p => p.qwUserId === item.qwUserId)
+      if (index > -1) {
+        this.personnelList.splice(index, 1)
+      }
+    },
+    // 表单重置
+    resetForm(refName) {
+      if (this.$refs[refName]) {
+        this.$refs[refName].resetFields()
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+</style>

+ 25 - 1
src/views/qw/externalContactTransfer/index.vue

@@ -96,13 +96,25 @@
           @keyup.enter.native="handleQuery"
         />
       </el-form-item>
-      <el-form-item label="添加时间" prop="createTime">
+<!--      <el-form-item label="添加时间" prop="createTime">
         <el-date-picker clearable size="small"
                         v-model="queryParams.createTime"
                         type="date"
                         value-format="yyyy-MM-dd"
                         placeholder="选择添加时间">
         </el-date-picker>
+      </el-form-item>-->
+      <el-form-item label="添加时间" prop="createTime">
+        <el-date-picker
+          style="width:225.4px"
+          clearable size="small"
+          v-model="dateRange"
+          type="daterange"
+          @change="changeTime"
+          value-format="yyyy-MM-dd"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期">
+        </el-date-picker>
       </el-form-item>
       <el-form-item>
         <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
@@ -315,6 +327,7 @@ export default {
       total: 0,
       // 企业微信客户表格数据
       externalContactList: [],
+      dateRange: null,
       // 弹出层标题
       title: "",
       // 是否显示弹出层
@@ -422,6 +435,15 @@ export default {
         this.loading = false;
       });
     },
+    changeTime(){
+      if(this.dateRange!=null){
+        this.queryParams.sTime=this.dateRange[0];
+        this.queryParams.eTime=this.dateRange[1];
+      }else{
+        this.queryParams.sTime=null;
+        this.queryParams.eTime=null;
+      }
+    },
     // 取消按钮
     cancel() {
       this.open = false;
@@ -465,6 +487,8 @@ export default {
     /** 重置按钮操作 */
     resetQuery() {
       this.selectTags=[];
+      this.dateRange=null;
+      this.changeTime();
       this.resetForm("queryForm");
       this.handleQuery();
     },

+ 304 - 0
src/views/qw/groupActual/index.vue

@@ -0,0 +1,304 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="群活码" prop="liveCodeId">
+        <el-select v-model="queryParams.liveCodeId" placeholder="请选择群活码" clearable size="small" @change="handleQuery">
+          <el-option
+            v-for="item in groupLiveCodeList"
+            :key="item.id"
+            :label="item.groupName"
+            :value="item.id"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="群名称" prop="groupName">
+        <el-input
+          v-model="queryParams.groupName"
+          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 label="启用" :value="1" />
+          <el-option label="停用" :value="0" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+        >新增</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="groupActualList" border>
+      <el-table-column label="群名称" align="center" prop="groupName" />
+      <el-table-column label="实际群二维码" align="center" prop="groupUrl">
+        <template slot-scope="scope">
+          <el-image
+            v-if="scope.row.groupUrl"
+            :src="scope.row.groupUrl"
+            :preview-src-list="[scope.row.groupUrl]"
+            style="width: 80px; height: 80px;"
+          />
+          <span v-else>-</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="分配数" align="center" prop="assignNum" />
+      <el-table-column label="状态" align="center" prop="status">
+        <template slot-scope="scope">
+          <el-tag :type="scope.row.status === 1 ? 'success' : 'danger'">
+            {{ scope.row.status === 1 ? '启用' : '停用' }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="创建时间" align="center" prop="createTime" />
+      <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)"
+          >编辑</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            @click="handleStatusChange(scope.row)"
+          >{{ scope.row.status === 1 ? '停用' : '启用' }}</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 @close="cancel">
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="群活码" prop="liveCodeId">
+          <el-select v-model="form.liveCodeId" placeholder="请选择群活码">
+            <el-option
+              v-for="item in groupLiveCodeList"
+              :key="item.id"
+              :label="item.groupName"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="群名称" prop="groupName">
+          <el-input v-model="form.groupName" placeholder="请输入群名称" />
+        </el-form-item>
+        <el-form-item label="群二维码" prop="groupUrl">
+          <image-upload v-model="form.groupUrl" :limit="1" />
+        </el-form-item>
+        <el-form-item label="有效期" prop="efficientTime">
+          <el-date-picker v-model="form.efficientTime" type="date" placeholder="请选择有效期" value-format="yyyy-MM-dd" />
+        </el-form-item>
+        <el-form-item label="分配数" prop="assignNum">
+          <el-input-number v-model="form.assignNum" :min="0" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listGroupActual, getGroupActual, addOrUpdateGroupActual, delGroupActual } from "@/api/qw/groupActual";
+import { listGroupLiveCode } from "@/api/qw/groupLiveCode";
+import ImageUpload from "@/components/ImageUpload";
+
+export default {
+  name: "GroupActual",
+  components: {
+    ImageUpload
+  },
+  data() {
+    return {
+      loading: true,
+      showSearch: true,
+      total: 0,
+      groupActualList: [],
+      groupLiveCodeList: [],
+      title: "",
+      open: false,
+      liveCodeId: null,
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        liveCodeId: null,
+        groupName: null,
+        status: null,
+      },
+      form: {},
+      rules: {
+        groupName: [
+          { required: true, message: "群名称不能为空", trigger: "blur" }
+        ],
+        groupUrl: [
+          { required: true, message: "群二维码不能为空", trigger: "change" }
+        ],
+        liveCodeId: [
+          { required: true, message: "群活码不能为空", trigger: "change" }
+        ]
+      }
+    };
+  },
+  created() {
+    // 从路由参数中获取liveCodeId
+    this.liveCodeId = this.$route.query.liveCodeId;
+    // 如果从群活码页面跳转过来,自动设置筛选条件
+    if (this.liveCodeId) {
+      this.queryParams.liveCodeId = this.liveCodeId;
+    }
+    this.loadGroupLiveCodeList();
+    this.getList();
+  },
+  methods: {
+    /** 查询群实际码列表 */
+    getList() {
+      this.loading = true;
+      const params = { ...this.queryParams };
+      if (this.liveCodeId) {
+        params.liveCodeId = this.liveCodeId;
+      }
+      listGroupActual(params).then(response => {
+        this.groupActualList = response.data.records;
+        this.total = response.data.total;
+        this.loading = false;
+      }).catch(() => {
+        this.loading = false;
+      });
+    },
+
+    /** 新增按钮 */
+    handleAdd() {
+      this.reset();
+      // 如果是从群活码页面跳转过来的,新增时自动带入liveCodeId
+      if (this.liveCodeId) {
+        this.form.liveCodeId = parseInt(this.liveCodeId);
+      }
+      this.loadGroupLiveCodeList();
+      this.open = true;
+      this.title = "新增群实际码";
+    },
+
+    /** 加载群活码列表 */
+    loadGroupLiveCodeList() {
+      listGroupLiveCode({ pageNum: 1, pageSize: 1000 }).then(response => {
+        this.groupLiveCodeList = response.data.records;
+      }).catch(() => {
+        this.groupLiveCodeList = [];
+      });
+    },
+
+    // ... existing code ...
+    handleUpdate(row) {
+      this.reset();
+      this.loadGroupLiveCodeList();
+      this.open = true;
+      this.title = "修改群实际码";
+      getGroupActual(row.id).then(response => {
+        this.form = response.data;
+      });
+    },
+
+    /** 启用/停用 */
+    handleStatusChange(row) {
+      const status = row.status === 1 ? 0 : 1;
+      const statusText = status === 1 ? "启用" : "停用";
+      this.$confirm(`确认要${statusText}该群实际码吗?`, "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        const data = {
+          id: row.id,
+          status: status
+        };
+        addOrUpdateGroupActual(data).then(response => {
+          this.msgSuccess(`${statusText}成功`);
+          this.getList();
+        });
+      }).catch(() => {});
+    },
+
+    /** 表单提交 */
+    submitForm() {
+      this.$refs.form.validate(valid => {
+        if (valid) {
+          const data = { ...this.form };
+          // 有效期处理:剆除时間秘,添加默认时間秘 00:00:00
+          if (data.efficientTime && typeof data.efficientTime === 'string') {
+            data.efficientTime = data.efficientTime + 'T00:00:00';
+          }
+          addOrUpdateGroupActual(data).then(response => {
+            this.msgSuccess(this.form.id ? "修改成功" : "新增成功");
+            this.open = false;
+            this.getList();
+          });
+        }
+      });
+    },
+
+    /** 取消 */
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+
+    /** 表单重置 */
+    reset() {
+      this.form = {
+        id: null,
+        liveCodeId: null,
+        groupName: null,
+        groupUrl: null,
+        efficientTime: null,
+        assignNum: 0,
+        status: 1
+      };
+      this.resetForm("form");
+    },
+
+    /** 搜索 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+
+    /** 重置 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      // 重置时,如果是从群活码页面跳转过来的,保留liveCodeId筛选
+      if (this.liveCodeId) {
+        this.queryParams.liveCodeId = this.liveCodeId;
+      }
+      this.handleQuery();
+    }
+  }
+};
+</script>

+ 10 - 0
src/views/qw/groupChatTransfer/index.vue

@@ -38,6 +38,15 @@
           @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>
         <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>
@@ -138,6 +147,7 @@ export default {
         name: null,
         ownerName: null,
         qwName: null,
+        userName: null,
         pageNum: 1,
         pageSize: 10
       },

+ 10 - 0
src/views/qw/groupChatTransferOnJob/index.vue

@@ -38,6 +38,15 @@
           @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>
         <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>
@@ -139,6 +148,7 @@ export default {
         name: null,
         ownerName: null,
         qwName: null,
+        userName:null,
         pageNum: 1,
         pageSize: 10
       },

+ 263 - 0
src/views/qw/groupLiveCode/index.vue

@@ -0,0 +1,263 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="活码名称" prop="groupName">
+        <el-input
+          v-model="queryParams.groupName"
+          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 label="启用" :value="1" />
+          <el-option label="停用" :value="0" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+        >新增</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="groupLiveCodeList" border>
+      <el-table-column label="活码名称" align="center" prop="groupName" />
+      <el-table-column label="实际二维码数量" align="center" prop="qrcodeNum" />
+      <el-table-column label="今日加群数" align="center" prop="toDayNum" />
+      <el-table-column label="累计加群数" align="center" prop="countNum" />
+      <el-table-column label="状态" align="center" prop="status">
+        <template slot-scope="scope">
+          <el-tag :type="scope.row.status === 1 ? 'success' : 'danger'">
+            {{ scope.row.status === 1 ? '启用' : '停用' }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="创建时间" align="center" prop="createTime" />
+      <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)"
+          >编辑</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            @click="handleStatusChange(scope.row)"
+          >{{ scope.row.status === 1 ? '停用' : '启用' }}</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-arrow-right"
+            @click="handleGotoActual(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 @close="cancel">
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="活码名称" prop="groupName">
+          <el-input v-model="form.groupName" placeholder="请输入活码名称" />
+        </el-form-item>
+        <el-form-item label="项目" prop="projectId">
+          <el-select v-model="form.projectId" placeholder="请选择项目" @change="handleProjectChange">
+            <el-option
+              v-for="item in projectList"
+              :key="item.id"
+              :label="item.projectName"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listGroupLiveCode, getGroupLiveCode, addOrUpdateGroupLiveCode, delGroupLiveCode } from "@/api/qw/groupLiveCode";
+import { pageProject } from "@/api/adv/project";
+
+export default {
+  name: "GroupLiveCode",
+  data() {
+    return {
+      loading: true,
+      showSearch: true,
+      total: 0,
+      groupLiveCodeList: [],
+      projectList: [],
+      title: "",
+      open: false,
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        groupName: null,
+        status: null,
+      },
+      form: {},
+      rules: {
+        groupName: [
+          { required: true, message: "活码名称不能为空", trigger: "blur" }
+        ],
+        projectId: [
+          { required: true, message: "项目不能为空", trigger: "change" }
+        ],
+      }
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询活码列表 */
+    getList() {
+      this.loading = true;
+      listGroupLiveCode(this.queryParams).then(response => {
+        this.groupLiveCodeList = response.data.records;
+        this.total = response.data.total;
+        this.loading = false;
+      }).catch(() => {
+        this.loading = false;
+      });
+    },
+
+    /** 加载项目列表 */
+    loadProjectList() {
+      pageProject({ pageNum: 1, pageSize: 1000 }).then(response => {
+        this.projectList = response.data.records;
+      }).catch(() => {
+        this.projectList = [];
+      });
+    },
+
+    /** 处理项目变更 */
+    handleProjectChange() {
+      const project = this.projectList.find(p => p.id === this.form.projectId);
+      if (project) {
+        this.form.projectName = project.projectName;
+      }
+    },
+
+    /** 新增按钮 */
+    handleAdd() {
+      this.reset();
+      this.loadProjectList();
+      this.open = true;
+      this.title = "新增活码";
+    },
+
+    /** 修改按钮 */
+    handleUpdate(row) {
+      this.reset();
+      this.loadProjectList();
+      this.open = true;
+      this.title = "修改活码";
+      getGroupLiveCode(row.id).then(response => {
+        this.form = response.data;
+      });
+    },
+
+    /** 启用/停用 */
+    handleStatusChange(row) {
+      const status = row.status === 1 ? 0 : 1;
+      const statusText = status === 1 ? "启用" : "停用";
+      this.$confirm(`确认要${statusText}该活码吗?`, "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        const data = {
+          id: row.id,
+          status: status
+        };
+        addOrUpdateGroupLiveCode(data).then(response => {
+          this.msgSuccess(`${statusText}成功`);
+          this.getList();
+        });
+      }).catch(() => {});
+    },
+
+    /** 进入群实际码页面 */
+    handleGotoActual(row) {
+      this.$router.push({
+        path: '/qw/contactWayC/groupActual',
+        query: { liveCodeId: row.id }
+      });
+    },
+
+    /** 表单提交 */
+    submitForm() {
+      this.$refs.form.validate(valid => {
+        if (valid) {
+          addOrUpdateGroupLiveCode(this.form).then(response => {
+            this.msgSuccess(this.form.id ? "修改成功" : "新增成功");
+            this.open = false;
+            this.getList();
+          });
+        }
+      });
+    },
+
+    /** 取消 */
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+
+    /** 表单重置 */
+    reset() {
+      this.form = {
+        id: null,
+        groupName: null,
+        projectId: null,
+        projectName: null,
+        status: 1
+      };
+      this.resetForm("form");
+    },
+
+    /** 搜索 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+
+    /** 重置 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    }
+  }
+};
+</script>

+ 78 - 4
src/views/qw/sop/sop.vue

@@ -739,6 +739,37 @@
                     </el-radio-group>
                   </el-form-item>
                   <el-form-item label="内容" style="margin-bottom: 2%" >
+                     <div v-if="item.contentType == 12">
+                        <!--                                           <div >-->
+                        <el-card class="box-card">
+                          <el-form-item label="直播间" >
+                            <el-select  v-model="item.liveId"
+                                        placeholder="请选择直播间" size="mini"
+                                        @change="liveChange(item)" >
+                              <el-option
+                                v-for="dict in liveList"
+                                :key="dict.liveId"
+                                :label="dict.liveName"
+                                :value="dict.liveId"
+                              />
+                            </el-select>
+                          </el-form-item>
+
+                          <el-form-item label="标题" prop="miniprogramTitle">
+                            <el-input v-model="item.miniprogramTitle" placeholder="请输入小程序消息标题,最长为64字节" :rows="2" maxlength="64"
+                                      type="textarea"   />
+                          </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-form-item label="appid" prop="miniprogramAppid" v-show="false">
+                            <el-input v-model="item.miniprogramAppid='wx503cf8ab31f83dd4' " disabled />
+                          </el-form-item>
+                          <el-form-item label="page路径" prop="miniprogramPage"  v-show="false" label-width="100px" style="margin-left: -30px">
+                            <el-input v-model="item.miniprogramPage" placeholder="小程序消息打开后的路径"  disabled />
+                          </el-form-item>
+                        </el-card>
+                      </div>
                     <el-input v-if="item.contentType == 1 " v-model="item.value" type="textarea" :rows="3" placeholder="内容" style="width: 90%;margin-top: 10px;"/>
 
                     <ImageUpload v-if="item.contentType == 2 " v-model="item.imgUrl" type="image" :num="1"  :width="150" :height="150" />
@@ -913,6 +944,7 @@ import {listTag,} from "@/api/qw/tag";
 import {getMyQwCompanyList} from "@/api/qw/user";
 import {allList} from "@/api/qw/groupChat";
 import SendMsgSopOpenTool from '@/views/qw/sopUserLogsInfo/sendMsgSopOpenTool.vue'
+import {listToLiveNoEnd} from "@/api/live/live";
 
 export default {
   name: "Sop",
@@ -941,7 +973,7 @@ export default {
       chatList: [],
       courseList: [],
       sendMsgOpen:{
-        title:'一键批量群发',
+        title:'一键批量群发',//标记todo copyLMX
         open:false,
         row: {},
         ids:null,
@@ -1060,10 +1092,14 @@ export default {
         autoSopType: [{required: true, message: "选项不能为空", trigger: "submit"}],
         autoStartTime: [{required: true, message: "起始时间不能为空", trigger: "submit"}],
         autoEndTime: [{required: true, message: "结束时间不能为空", trigger: "submit"}],
-      }
+      },
+      liveList: [],
     };
   },
   created() {
+    listToLiveNoEnd().then(response => {
+      this.liveList = response.rows;
+    })
 
     this.getDicts("sys_sop_status").then(response => {
       this.sysSopStatus = response.data;
@@ -1809,11 +1845,31 @@ export default {
           if (this.setting.length <= 0) {
             return this.$message.error("请添加规则")
           }
-          if (this.msgForm.courseId===null || this.msgForm.courseId===''){
+          console.log(this.msgForm.setting)
+          let jsonSetting = JSON.parse(this.msgForm.setting);
+          let hasLiveSetting = false;
+          console.log(jsonSetting)
+          for(let index=0;index < jsonSetting.length;index++){
+           if(!!jsonSetting[index] && jsonSetting[index].contentType == "12") {
+            if(!!hasLiveSetting){
+               return this.$message.error("发送直播间只能一次只能发送一条");
+            }
+            hasLiveSetting = true;
+           }
+          }
+          if(!!hasLiveSetting && (this.msgForm.courseId!=null && this.msgForm.courseId!='')){
+              return this.$message.error("直播间不能选取课程");
+          }
+          if(!!hasLiveSetting && (this.msgForm.videoId!=null && this.msgForm.videoId!='')){
+              return this.$message.error("直播间不能选取课节");
+          }
+          console.log(hasLiveSetting);
+          // debugger;
+          if ((this.msgForm.courseId===null || this.msgForm.courseId==='') && !hasLiveSetting){
             return this.$message.error("课程不能为空")
           }
 
-          if (this.msgForm.videoId===null || this.msgForm.videoId===''){
+          if ((this.msgForm.videoId===null || this.msgForm.videoId==='')&& !hasLiveSetting){
             return this.$message.error("课节不能为空")
           }
 
@@ -1821,6 +1877,7 @@ export default {
             return this.$message.error("消息类型不能为空")
           }
 
+
           for (let i = 0; i < this.setting.length; i++) {
             if (this.setting[i].contentType == 1 && (this.setting[i].value == null || this.setting[i].value == "")) {
               return this.$message.error("内容不能为空")
@@ -1849,6 +1906,9 @@ export default {
             if (this.setting[i].contentType == 7 && (this.setting[i].value == null || this.setting[i].value == "")) {
               return this.$message.error("语音不能为空")
             }
+            if(this.setting[i].contentType == 12 && (this.setting[i].liveId==null || this.setting[i].liveId == "")){
+             return this.$message.error("直播间不能为空");
+            }
           }
 
           this.sendMsgOpen.open = false;
@@ -1909,6 +1969,20 @@ export default {
     delSetList(index){
       this.setting.splice(index,1)
     },
+    liveChange(content) {
+      // content.liveId 是选中的直播间 ID(liveId)
+      const selectedLive = this.liveList.find(live => live.liveId === content.liveId);
+      if (selectedLive) {
+        // 从选中的直播间对象中提取标题和封面,赋值给当前内容的对应字段
+        // 假设直播间对象中标题字段为 liveTitle,封面字段为 coverImg(根据实际接口字段调整)
+        content.miniprogramTitle = selectedLive.liveName || ''; // 自动填充标题
+        content.miniprogramPicUrl = selectedLive.liveImgUrl || ''; // 自动填充封面
+      } else {
+        // 若未找到对应直播间,清空标题和封面(可选)
+        content.miniprogramTitle = '';
+        content.miniprogramPicUrl = '';
+      }
+    },
   }
 };
 </script>

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

@@ -29,7 +29,7 @@
           @keyup.enter.native="handleQuery"
         />
       </el-form-item>
-      <el-form-item label="发送类型" prop="sendType" >
+      <el-form-item label="发送类型" prop="sendType" label-width="100px" >
         <el-select v-model="queryParams.sendType" placeholder="请选择发送类型" clearable size="small">
           <el-option
             v-for="dict in sysQwSopType"

+ 3 - 3
src/views/qw/sopTemp/addSopTemp.vue

@@ -150,7 +150,7 @@
                                           </div>
                                         </el-form-item>
                                         <el-form-item label="内容"  >
-                                          <el-input v-if="setList.contentType == 1 " v-model="setList.value" type="textarea" :rows="3" placeholder="内容" style="width: 90%;margin-top: 10px;"/>
+                                          <el-input v-if="setList.contentType == 1 || setList.contentType == 15" v-model="setList.value" type="textarea" :rows="3" placeholder="内容" style="width: 90%;margin-top: 10px;"/>
 
                                           <ImageUpload v-if="setList.contentType == 2 " v-model="setList.imgUrl" type="image" :num="1"  :width="150" :height="150" />
 
@@ -231,7 +231,7 @@
                                               </video>
                                             </el-form-item>
                                           </div>
-                                          <div v-if="setList.contentType == 7 ">
+                                          <div v-if="setList.contentType == 7 || setList.contentType == 16">
                                               <el-input
                                                         v-model="setList.value"
                                                         type="textarea" :rows="3" maxlength="66" show-word-limit
@@ -273,7 +273,7 @@
 
                                         </el-form-item>
 
-                                        <el-form-item label="添加短链" v-if="content.type == 2 && setList.contentType == 1 "  >
+                                        <el-form-item label="添加短链" v-if="content.type == 2 && setList.contentType == 1 || setList.contentType == 15"  >
                                           <el-tooltip content="请先根据课程选定课程小节之后再添加" effect="dark" :disabled="!!content.videoId">
                                             <el-switch
                                               v-model="setList.isBindUrl"

+ 87 - 2
src/views/qw/sopTemp/index.vue

@@ -191,6 +191,15 @@
             v-hasPermi="['qw:sopTemp:edit']"
           >管理规则
           </el-button>
+          <el-button
+            v-if="scope.row.sendType == 11"
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleBatchOfficial(scope.row)"
+            v-hasPermi="['qw:sopTemp:edit']"
+          >开关官方群发
+          </el-button>
           <el-button
             size="mini"
             type="text"
@@ -283,6 +292,18 @@
             </el-radio>
           </el-radio-group>
         </el-form-item>
+
+        <el-form-item label="是否开启@所有人" v-if="form.sendType == 11 && (form.id === null || form.id === undefined)">
+          <el-radio-group v-model="form.openIsAtAll">
+            <el-radio
+              v-for="dict in openIsAtAllOptions"
+              :key="dict.dictValue"
+              :label="dict.dictValue"
+            >{{ dict.dictLabel }}
+            </el-radio>
+          </el-radio-group>
+        </el-form-item>
+
         <el-form-item label="内容" prop="modeContent">
           <el-input v-model="form.modeContent" placeholder="请输入文字内容"  type="textarea" :rows="3"/>
         </el-form-item>
@@ -339,6 +360,20 @@
         <el-button type="primary" @click="updateRedData" :disabled="redData.loading">保 存</el-button>
       </div>
     </el-dialog>
+    
+    <el-dialog title="批量编辑官方群发" :visible.sync="official.open" width="500px" append-to-body>
+       <el-form :model="officialForm" ref="officialForm" :inline="true">
+         <el-form-item  label="官方群发" prop="isOfficial">
+            <el-radio-group v-model="officialForm.isOfficial">
+              <el-radio :label="0">关</el-radio>
+              <el-radio :label="1">开</el-radio>
+            </el-radio-group>
+         </el-form-item>
+       </el-form>
+      <div slot="footer" class="dialog-footer" style="display: flex;justify-content: flex-end;">
+        <el-button type="primary" @click="updateOfficial" :loading="official.loading">保 存</el-button>
+      </div>
+    </el-dialog>
 
     <el-dialog :title="shareOptions.title" :visible.sync="shareOptions.open" width="800px" append-to-body>
 
@@ -416,7 +451,8 @@ import {
   redList,
   shareSopTemp,
   updateRedPackage,
-  updateTemp
+  updateTemp,
+  batchOpenOrCloseOfficial
 } from "@/api/qw/sopTemp";
 import { getCompanyList, listCompany } from '@/api/company/company'
 import {courseList, getRoles} from "@/api/qw/sop";
@@ -429,6 +465,14 @@ export default {
   components: {Treeselect},
   data() {
     return {
+      officialForm:{
+        tempId:null,
+        isOfficial:null,
+      },
+      official:{
+        open:false,
+        loading:false,
+      },
       // 遮罩层
       loading: true,
       companysloading: false,
@@ -465,7 +509,7 @@ export default {
       // 状态字典
       statusOptions: [],
       openOfficialOptions: [],
-
+      openIsAtAllOptions: [],
       shareOptions: {
         title: '分享模板',
         open: false,
@@ -558,6 +602,10 @@ export default {
       this.openOfficialOptions = response.data;
     });
 
+    this.getDicts("sys_company_or").then(response => {
+      this.openIsAtAllOptions = response.data;
+    });
+
     getSelectableRange().then(e => {
       this.startTimeRange = e.data;
     })
@@ -696,6 +744,7 @@ export default {
         sendType: this.sendType,
         sort: 0,
         openOfficial: "1",
+        openIsAtAll: "1",
         time: "",
         num: 1,
         timeList: [{value: "",desc:""}],
@@ -771,6 +820,17 @@ export default {
       this.$router.push(url)
       // }
     },
+    /**
+     * 批量编辑官方群发
+     */
+    handleBatchOfficial(row){
+        this.officialForm={
+           tempId:null,
+           isOfficial:null
+        }
+        this.officialForm.tempId = row.id;
+        this.official.open = true;
+    },
     /** 修改按钮操作 */
     handleUpdateRed(row) {
       this.redData.open = true;
@@ -816,6 +876,9 @@ export default {
           this.title = "修改";
         }
         this.form = response.data;
+        if (!this.form.IsAtAll) {
+        this.form.IsAtAll = "1"; // 默认值
+    }
         this.open = true;
       });
     },
@@ -935,6 +998,28 @@ export default {
         let len = old - val;
         this.form.timeList.splice(Math.max(this.form.timeList.length - len, 0), len);
       }
+    },
+    updateOfficial(){
+      if(!!this.officialForm){
+        if(this.officialForm.tempId == null || this.officialForm.tempId == '' || this.officialForm.tempId == undefined){
+            this.$message.error("参数错误");
+            return;
+        }
+        if(this.officialForm.isOfficial === null || this.officialForm.isOfficial === undefined){
+            this.$message.error("请选择官方群发操作结果");
+            return;
+        }
+      }else{
+          this.$message.error("参数错误");
+      }
+      this.official.loading = true;
+      batchOpenOrCloseOfficial(this.officialForm).then(res=>{
+         this.msgSuccess("保存成功");
+      this.official.loading = false;
+      this.official.open = false;
+      }).catch(res=>{
+      this.official.loading = false;
+      });
     }
   }
 };

+ 73 - 7
src/views/qw/sopTemp/updateSopTemp.vue

@@ -122,6 +122,22 @@
                                     请注意:官方群发 【第1天】 只能设置1点-1点30 的时间,【其他天数时间】 只能设置0点-0点30。此处仅为生成发送记录时间,实际发送可由 销售在公司规定的 【暂早上8点(前!!)】,在企业微信的【群发助手】处点击发送
                                   </div>
                                 </el-form-item>
+
+                                <el-form-item label="是否@所有人" v-if="form.sendType != 4">
+                                  <el-switch
+                                    v-model="content.isAtAll"
+                                    active-color="#13ce66"
+                                    inactive-color="#DCDFE6"
+                                    :active-value="1"
+                                    :inactive-value="0">
+                                  </el-switch>
+                                  <div
+                                    style="color: #999;font-size: 14px;display: flex;align-items: center;margin-bottom: 5px">
+                                    <i class="el-icon-info"></i>
+                                    开启后将在群发消息时@所有人(默认关闭)
+                                  </div>
+                                </el-form-item>
+
                                 <el-form-item label="消息类型" v-if="form.sendType != 4">
                                   <el-select :disabled="formType == 3" v-model="content.courseType"
                                              placeholder="请选择消息类型" size="mini"
@@ -303,7 +319,7 @@
                                           </div>
                                         </el-form-item>
                                         <el-form-item label="内容">
-                                          <el-input :disabled="formType == 3 || !roles.includes('edit_sop_temp_content')" v-if="setList.contentType == 1 "
+                                          <el-input :disabled="formType == 3 || !roles.includes('edit_sop_temp_content')" v-if="setList.contentType == 1 || setList.contentType == 15"
                                                     v-model="setList.value"
                                                     type="textarea" :rows="3" placeholder="内容"
                                                     style="width: 90%;margin-top: 10px;"
@@ -313,7 +329,7 @@
 
                                           <!-- 修改按钮部分 -->
                                           <el-link
-                                            v-if="setList.contentType == 1 && roles.includes('edit_sop_temp_content')"
+                                            v-if="(setList.contentType == 1 || setList.contentType == 15) && roles.includes('edit_sop_temp_content')"
                                             type="primary"
                                             @click="toggleSalesCall(index, contentIndex, setIndex)"
                                             style="margin-top: 10px;"
@@ -321,7 +337,7 @@
                                             {{ setList.isSalesCallAdded ? '移除#销售称呼#' : '添加#销售称呼#' }}
                                           </el-link>
                                           <el-link
-                                            v-if="setList.contentType == 1 && roles.includes('edit_sop_temp_content')"
+                                            v-if="(setList.contentType == 1 || setList.contentType == 15) && roles.includes('edit_sop_temp_content')"
                                             type="primary"
                                             @click="toggleUserNameCall(index, contentIndex, setIndex)"
                                             style="margin-top: 10px; margin-left: 20px"
@@ -334,7 +350,7 @@
                                                        type="image" :num="1" :width="150" :height="150"/>
 
                                           <div
-                                            v-if="setList.contentType == 3  || (setList.contentType == 9 && content.type==2 )">
+                                            v-if="(setList.contentType == 3 || item.contentType ==9) || (setList.contentType == 9 && content.type==2 )">
                                             <el-card class="box-card">
                                               <el-form-item label="链接标题:" label-width="100px" required>
                                                 <el-input :disabled="formType == 3 || (form.sendType == 11 && contentIndex != 0 && setIndex == 0)" v-model="setList.linkTitle"
@@ -474,7 +490,7 @@
                                               </video>
                                             </el-form-item>
                                           </div>
-                                          <div v-if="setList.contentType == 7 ">
+                                          <div v-if="setList.contentType == 7 || setList.contentType == 16">
                                             <el-input
                                               :disabled="formType == 3 || !roles.includes('edit_sop_temp_content')"
                                               v-model="setList.value"
@@ -526,6 +542,27 @@
 
                                             </el-card>
                                           </div>
+                                          <div v-if="setList.contentType == 14">
+                                            <el-card class="box-card">
+                                              <el-form-item label="福袋名称" >
+                                                <el-select :disabled="formType == 3"    v-model="setList.luckyBagId"
+                                                           placeholder="请选择福袋" size="mini"
+                                                           @change="getLuckyBagStatus(setList)" >
+                                                  <el-option
+                                                    v-for="dict in luckyBagList"
+                                                    :key="dict.id"
+                                                    :label="dict.name"
+                                                    :value="dict.id"
+                                                  />
+                                                </el-select>
+                                              </el-form-item>
+                                              <el-form-item  label="福袋状态" >
+                                                <el-tag  :disabled="formType == 3" :type="setList.luckyBagDataStatus == 1 ? 'success' : 'danger'">
+                                                  {{ setList.luckyBagDataStatus == 1 ? '启用' : '禁用' }}
+                                                </el-tag>
+                                              </el-form-item>
+                                            </el-card>
+                                          </div>
                                         </el-form-item>
                                         <!--                                        <el-form-item label="添加短链"-->
                                         <!--                                                      v-if="content.type == 2 && setList.contentType == 1  ">-->
@@ -731,6 +768,7 @@ import {courseList, videoList} from "@/api/qw/sop";
 import {listToLiveNoEnd} from "@/api/live/live";
 import ImageUpload from "@/views/qw/sop/ImageUpload";
 import userVideo from "@/views/qw/userVideo/userVideo.vue";
+import {listReward} from "@/api/qw/luckyBag";
 import {
   getRoles,
 } from "@/api/qw/sop";
@@ -740,6 +778,14 @@ export default {
   components: {ImageUpload, userVideo, draggable},
   data() {
     return {
+      queryParams1: {
+        pageNum: 1,
+        pageSize: 10,
+        dataStatus: '1',
+        name: null,
+        type: null
+      },
+      luckyBagList: [],
       projectFrom:process.env.VUE_APP_PROJECT_FROM,
       addTag: [{
         addTag: [],
@@ -874,6 +920,9 @@ export default {
     listToLiveNoEnd().then(response => {
       this.liveList = response.rows;
     })
+    listReward(this.queryParams1).then(response => {
+      this.luckyBagList = response.rows;
+    });
 
     const id = this.$route.params && this.$route.params.id;
     this.id = id;
@@ -882,6 +931,15 @@ export default {
     this.handleUpdate(id);
   },
   methods: {
+    getLuckyBagStatus(content) {
+      const selectedLuckyBag = this.luckyBagList.find(item => item.id === content.luckyBagId);
+      if (selectedLuckyBag) {
+        content.luckyBagDataStatus = selectedLuckyBag.dataStatus; // 自动填充标题
+      } else {
+        // 若未找到对应直播间,清空标题和封面(可选)
+        content.luckyBagDataStatus = null;
+      }
+    },
     liveChange(content) {
       // content.liveId 是选中的直播间 ID(liveId)
       const selectedLive = this.liveList.find(live => live.liveId === content.liveId);
@@ -1468,7 +1526,8 @@ export default {
           // 复制课程相关字段,如 courseId、videoId,根据实际数据结构补充
           courseId: sourceContent.courseId,
           videoId: sourceContent.videoId,
-          courseType: sourceContent.courseType
+          courseType: sourceContent.courseType,
+          isAtAll: '0',
         };
         this.setting[index].content.push(newContent);
         this.videoList.push([]);
@@ -1492,7 +1551,7 @@ export default {
       this.addTag.splice(contentIndex, 1)
     },
     addSetting() {
-      let content = [{type: this.defaultContentType, contentType: '1', setting: [{contentType: '1', value: "",}]}];
+      let content = [{type: this.defaultContentType, contentType: '1', setting: [{contentType: '1', value: "",}],isAtAll: '0'}];
       if (this.form.sendType == 4) {
         content = [{type: 2, contentType: '1', setting: [{contentType: '1', value: "",}]}];
         content[0].setting[0].intervalTime = 5;
@@ -1975,6 +2034,13 @@ export default {
           this.setting.forEach(item => {
             if (item && Array.isArray(item.content)) {
               item.content.forEach(content => {
+                if (content.isAtAll === undefined || content.isAtAll === null) {
+                  this.$set(content, 'isAtAll', '0') // 默认关闭
+                }
+                // 如果后端返回的是数字,转换为字符串
+                if (typeof content.isAtAll === 'number') {
+                  this.$set(content, 'isAtAll', content.isAtAll.toString())
+                }
                 if (content && Array.isArray(content.setting)) {
                   content.setting.forEach(setList => {
                     if (setList && !Object.hasOwn(setList, 'isSalesCallAdded')) {

+ 1 - 0
src/views/qw/sopUserLogs/sopUserLogsSchedule.vue

@@ -77,6 +77,7 @@
         <el-tooltip class="item" effect="dark"
                     content="此功能用于给 选中的 营期 内【所有的】客户发送 消息【或者发送草稿-/-清楚草稿】" placement="top"
         >
+        <!-- copylmx -->
           <el-button
             type="warning"
             icon="el-icon-s-promotion"

+ 407 - 278
src/views/qw/sopUserLogsInfo/sendMsgOpenTool.vue

@@ -11,290 +11,370 @@
                 </span>
         </template>
       </el-alert>
-        <el-form ref="msgForm" :model="msgForm" :rules="msgRules" label-width="100px">
-          <el-form-item label="策略" prop="draftStrategy">
-            <el-radio-group v-model="msgForm.draftStrategy">
-              <el-radio :label="1">正常群发</el-radio>
-              <el-radio :label="2">清除草稿</el-radio>
-              <el-radio :label="3">发送草稿</el-radio>
-            </el-radio-group>
-          </el-form-item>
-
-          <el-form-item label="选择课程" v-if="msgForm.draftStrategy==1" >
-            <el-select  v-model="msgForm.courseId" placeholder="请选择课程" style=" margin-right: 10px;" size="mini"  @change="courseChange()">
-              <el-option
-                v-for="dict in courseList"
-                :key="dict.dictValue"
-                :label="dict.dictLabel"
-                :value="parseInt(dict.dictValue)"
-              />
-            </el-select>
-            <el-select  v-model="msgForm.videoId" placeholder="请选择小节" size="mini" style=" margin-right: 10px;" @change="videoIdChange()"  >
-              <el-option
-                v-for="dict in videoList"
-                :key="dict.dictValue"
-                :label="dict.dictLabel"
-                :value="parseInt(dict.dictValue)"
-              />
-            </el-select>
-            <el-select  v-model="msgForm.courseType" placeholder="请选择消息类型" size="mini" style=" margin-right: 10px;">
-              <el-option
-                v-for="dict in sysFsSopWatchStatus"
-                :key="dict.dictValue"
-                :label="dict.dictLabel"
-                :value="parseInt(dict.dictValue)"
-              />
-            </el-select>
-          </el-form-item>
-          <el-form-item label="规则" prop="setting" v-if="msgForm.draftStrategy==1"  >
-            <div v-for="(item, index) in setting" :key="index" style="background-color: #fdfdfd; border: 1px solid #e6e6e6; margin-bottom: 20px;">
-              <el-row>
-                <el-col :span="22">
-                  <el-form :model="item" label-width="70px">
-                    <el-form-item label="内容类别" style="margin: 2%">
-                      <el-radio-group  v-model="item.contentType">
-                        <el-radio   :label="item.dictValue" v-for="item in sysQwSopAiContentType"  @change="handleContentTypeChange()">{{item.dictLabel}}</el-radio>
-                      </el-radio-group>
-                    </el-form-item>
-                    <el-form-item label="内容" style="margin-bottom: 2%" >
-                      <div v-if="item.contentType == 12">
-                        <!--                                           <div >-->
-                        <el-card class="box-card">
-                          <el-form-item label="直播间" >
-                            <el-select  v-model="item.liveId"
-                                        placeholder="请选择直播间" size="mini"
-                                        @change="liveChange(item)" >
-                              <el-option
-                                v-for="dict in liveList"
-                                :key="dict.liveId"
-                                :label="dict.liveName"
-                                :value="dict.liveId"
-                              />
-                            </el-select>
-                          </el-form-item>
-
-                          <el-form-item label="标题" prop="miniprogramTitle">
-                            <el-input v-model="item.miniprogramTitle" placeholder="请输入小程序消息标题,最长为64字节" :rows="2" maxlength="64"
-                                      type="textarea"   />
-                          </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-form-item label="appid" prop="miniprogramAppid" v-show="false">
-                            <el-input v-model="item.miniprogramAppid='wx503cf8ab31f83dd4' " disabled />
-                          </el-form-item>
-                          <el-form-item label="page路径" prop="miniprogramPage"  v-show="false" label-width="100px" style="margin-left: -30px">
-                            <el-input v-model="item.miniprogramPage" placeholder="小程序消息打开后的路径"  disabled />
-                          </el-form-item>
-                        </el-card>
-                      </div>
+      <el-form ref="msgForm" :model="msgForm" :rules="msgRules" label-width="100px">
+        <el-form-item label="策略" prop="draftStrategy">
+          <el-radio-group v-model="msgForm.draftStrategy">
+            <el-radio :label="1">正常群发</el-radio>
+            <el-radio :label="2">清除草稿</el-radio>
+            <el-radio :label="3">发送草稿</el-radio>
+          </el-radio-group>
+        </el-form-item>
+
+        <el-form-item label="选择课程" v-if="msgForm.draftStrategy==1" >
+          <el-select  v-model="msgForm.courseId" placeholder="请选择课程" style=" margin-right: 10px;" size="mini"  @change="courseChange()">
+            <el-option
+              v-for="dict in courseList"
+              :key="dict.dictValue"
+              :label="dict.dictLabel"
+              :value="parseInt(dict.dictValue)"
+            />
+          </el-select>
+          <el-select  v-model="msgForm.videoId" placeholder="请选择小节" size="mini" style=" margin-right: 10px;" @change="videoIdChange()"  >
+            <el-option
+              v-for="dict in videoList"
+              :key="dict.dictValue"
+              :label="dict.dictLabel"
+              :value="parseInt(dict.dictValue)"
+            />
+          </el-select>
+          <el-select  v-model="msgForm.courseType" placeholder="请选择消息类型" size="mini" style=" margin-right: 10px;">
+            <el-option
+              v-for="dict in sysFsSopWatchStatus"
+              :key="dict.dictValue"
+              :label="dict.dictLabel"
+              :value="parseInt(dict.dictValue)"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="规则" prop="setting" v-if="msgForm.draftStrategy==1"  >
+          <div v-for="(item, index) in setting" :key="index" style="background-color: #fdfdfd; border: 1px solid #e6e6e6; margin-bottom: 20px;">
+            <el-row>
+              <el-col :span="22">
+                <el-form :model="item" label-width="70px">
+                  <el-form-item label="内容类别" style="margin: 2%">
+                    <el-radio-group  v-model="item.contentType">
+                      <el-radio   :label="item.dictValue" v-for="item in sysQwSopAiContentType"  @change="handleContentTypeChange()">{{item.dictLabel}}</el-radio>
+                    </el-radio-group>
+                  </el-form-item>
+                  <!-- <div v-if="item.contentType == 12">
+                    <el-card class="box-card">
+                      <el-form-item label="直播间" >
+                        <el-select  v-model="item.liveId"
+                                    placeholder="请选择直播间" size="mini"
+                                    @change="liveChange(item)" >
+                          <el-option
+                            v-for="dict in liveList"
+                            :key="dict.liveId"
+                            :label="dict.liveName"
+                            :value="dict.liveId"
+                          />
+                        </el-select>
+                      </el-form-item>
+
+                      <el-form-item label="标题" prop="miniprogramTitle">
+                        <el-input v-model="item.miniprogramTitle" placeholder="请输入小程序消息标题,最长为64字节" :rows="2" maxlength="64"
+                                  type="textarea"   />
+                      </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-form-item label="appid" prop="miniprogramAppid" v-show="false">
+                        <el-input v-model="item.miniprogramAppid='wx503cf8ab31f83dd4' " disabled />
+                      </el-form-item>
+                      <el-form-item label="page路径" prop="miniprogramPage"  v-show="false" label-width="100px" style="margin-left: -30px">
+                        <el-input v-model="item.miniprogramPage" placeholder="小程序消息打开后的路径"  disabled />
+                      </el-form-item>
+                    </el-card>
+                  </div> -->
+                  <el-form-item label="内容" style="margin-bottom: 2%" >
+                    <div v-if="item.contentType == 12">
+                      <el-card class="box-card">
+                        <el-form-item label="直播间" >
+                          <el-select  v-model="item.liveId"
+                                      placeholder="请选择直播间" size="mini"
+                                      @change="liveChange(item)" >
+                            <el-option
+                              v-for="dict in liveList"
+                              :key="dict.liveId"
+                              :label="dict.liveName"
+                              :value="dict.liveId"
+                            />
+                          </el-select>
+                        </el-form-item>
+
+                        <el-form-item label="标题" prop="miniprogramTitle">
+                          <el-input v-model="item.miniprogramTitle" placeholder="请输入小程序消息标题,最长为64字节" :rows="2" maxlength="64"
+                                    type="textarea"   />
+                        </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-form-item label="appid" prop="miniprogramAppid" v-show="false">
+                          <el-input v-model="item.miniprogramAppid='wx503cf8ab31f83dd4' " disabled />
+                        </el-form-item>
+                        <el-form-item label="page路径" prop="miniprogramPage"  v-show="false" label-width="100px" style="margin-left: -30px">
+                          <el-input v-model="item.miniprogramPage" placeholder="小程序消息打开后的路径"  disabled />
+                        </el-form-item>
+                      </el-card>
+                    </div>
+                    <el-input
+                      v-if="item.contentType == 1 || item.contentType == 15|| item.contentType == 13"
+                      v-model="item.value"
+                      type="textarea"
+                      :rows="3"
+                      placeholder="内容"
+                      style="width: 90%; margin-top: 10px;"
+                      @keydown.native="handleKeydown($event, index)"
+                      :ref="`textarea-${index}`"
+                    >
+                    </el-input>
+                    <el-link
+                      v-if="item.contentType == 1 || item.contentType == 15|| item.contentType == 13"
+                      type="primary"
+                      @click="toggleSalesCall(index)"
+                      style="margin-top: 10px;"
+                    >
+                      {{ item.isSalesCallAdded ? '移除#销售称呼#' : '添加#销售称呼#' }}
+                    </el-link>
+                    <el-link
+                      v-if="item.contentType == 1 || item.contentType == 15|| item.contentType == 13"
+                      type="primary"
+                      @click="toggleSalesCallCustomer(index)"
+                      style="margin-top: 10px;margin-left: 2%"
+                    >
+                      {{ item.isSalesCallCustomerAdded ? '移除#客户称呼#' : '添加#客户称呼#' }}
+                    </el-link>
+
+
+                    <ImageUpload v-if="item.contentType == 2 " v-model="item.imgUrl" type="image" :num="1"  :width="150" :height="150" />
+
+                    <div v-if="item.contentType == 3 || item.contentType ==9 ">
+                      <el-card class="box-card">
+                        <el-form-item label="链接标题:"  label-width="100px">
+                          <el-input v-model="item.linkTitle" placeholder="请输入链接标题" style="width: 90%;"/>
+                        </el-form-item>
+                        <el-form-item label="链接描述:"   label-width="100px" >
+                          <el-input type="textarea" :rows="3" v-model="item.linkDescribe" placeholder="请输入链接描述" style="width: 90%;margin-top: 1%;"/>
+                        </el-form-item>
+                        <el-form-item label="链接封面:"   label-width="100px">
+                          <ImageUpload v-model="item.linkImageUrl" type="image" :num="1" :file-size="2" :width="150" :height="150" style="margin-top: 1%;" />
+                        </el-form-item>
+                        <el-form-item label="链接地址:"  label-width="100px" >
+                          <el-tag type="warning" v-model="item.isBindUrl=1">选择的课程小节 即为卡片链接地址</el-tag>
+                        </el-form-item>
+                      </el-card>
+                    </div>
+                    <div v-if="item.contentType == 4 || item.contentType == 10 ">
+                      <el-card class="box-card">
+                        <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-form-item label="appid" prop="miniprogramAppid" v-show="false" >
+                          <el-input v-model="item.miniprogramAppid='wx73f85f8d62769119' " disabled />
+                        </el-form-item>
+                        <el-form-item label="page路径" prop="miniprogramPage" v-show="item.contentType == 10" label-width="100px" style="margin-left: -30px" >
+                          <el-input v-model="item.miniprogramPage" placeholder="小程序消息打开后的路径" type="textarea" :rows="3" />
+                        </el-form-item>
+                      </el-card>
+                    </div>
+                    <div v-if="item.contentType == 5 ">
+
+                      <el-form-item label="上传文件:" prop="fileUrl" label-width="100px">
+                        <el-upload
+                          v-model="item.fileUrl"
+                          class="avatar-uploader"
+                          :action="uploadUrl"
+                          :show-file-list="false"
+                          :on-success="(res, file) => handleAvatarSuccessFile(res, file, item)"
+                          :before-upload="beforeAvatarUploadFile">
+                          <i class="el-icon-plus avatar-uploader-icon"></i>
+                        </el-upload>
+                        <el-link v-if="item.fileUrl" type="primary" :href="downloadUrl(item.fileUrl)" download>
+                          {{item.fileUrl}}
+                        </el-link>
+                      </el-form-item>
+
+                    </div>
+
+                    <div v-if="item.contentType == 6 ">
+                      <el-form-item label="上传视频:" prop="videoUrl" label-width="100px">
+                        <el-upload
+                          v-model="item.videoUrl"
+                          class="avatar-uploader"
+                          :action="uploadUrl"
+                          :show-file-list="false"
+                          :on-success="(res, file) => handleAvatarSuccessVideo(res, file, item)"
+                          :before-upload="beforeAvatarUploadVideo">
+                          <i class="el-icon-plus avatar-uploader-icon"></i>
+                        </el-upload>
+                        <video v-if="item.videoUrl"
+                               :src="item.videoUrl"
+                               controls style="width: 200px;height: 100px">
+                        </video>
+                      </el-form-item>
+                    </div>
+                    <div v-if="item.contentType == 7 || item.contentType == 16">
                       <el-input
-                        v-if="item.contentType == 1"
                         v-model="item.value"
-                        type="textarea"
-                        :rows="3"
-                        placeholder="内容"
-                        style="width: 90%; margin-top: 10px;"
-                        @keydown.native="handleKeydown($event, index)"
-                        :ref="`textarea-${index}`"
-                      >
-                      </el-input>
-                      <el-link
-                        v-if="item.contentType == 1"
-                        type="primary"
-                        @click="toggleSalesCall(index)"
-                        style="margin-top: 10px;"
-                      >
-                        {{ item.isSalesCallAdded ? '移除#销售称呼#' : '添加#销售称呼#' }}
-                      </el-link>
-                      <el-link
-                        v-if="item.contentType == 1"
-                        type="primary"
-                        @click="toggleSalesCallCustomer(index)"
-                        style="margin-top: 10px;margin-left: 2%"
-                      >
-                        {{ item.isSalesCallCustomerAdded ? '移除#客户称呼#' : '添加#客户称呼#' }}
-                      </el-link>
-
-
-                      <ImageUpload v-if="item.contentType == 2 " v-model="item.imgUrl" type="image" :num="1"  :width="150" :height="150" />
-
-                      <div v-if="item.contentType == 3 || item.contentType ==9 ">
-                        <el-card class="box-card">
-                          <el-form-item label="链接标题:"  label-width="100px">
-                            <el-input v-model="item.linkTitle" placeholder="请输入链接标题" style="width: 90%;"/>
-                          </el-form-item>
-                          <el-form-item label="链接描述:"   label-width="100px" >
-                            <el-input type="textarea" :rows="3" v-model="item.linkDescribe" placeholder="请输入链接描述" style="width: 90%;margin-top: 1%;"/>
-                          </el-form-item>
-                          <el-form-item label="链接封面:"   label-width="100px">
-                            <ImageUpload v-model="item.linkImageUrl" type="image" :num="1" :file-size="2" :width="150" :height="150" style="margin-top: 1%;" />
-                          </el-form-item>
-                          <el-form-item label="链接地址:"  label-width="100px" >
-                            <el-tag type="warning" v-model="item.isBindUrl=1">选择的课程小节 即为卡片链接地址</el-tag>
-                          </el-form-item>
-                        </el-card>
-                      </div>
-                      <div v-if="item.contentType == 4 || item.contentType == 10 ">
-                        <el-card class="box-card">
-                          <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-form-item label="appid" prop="miniprogramAppid" v-show="false" >
-                            <el-input v-model="item.miniprogramAppid='wx73f85f8d62769119' " disabled />
-                          </el-form-item>
-                          <el-form-item label="page路径" prop="miniprogramPage" v-show="item.contentType == 10" label-width="100px" style="margin-left: -30px" >
-                            <el-input v-model="item.miniprogramPage" placeholder="小程序消息打开后的路径" type="textarea" :rows="3" />
-                          </el-form-item>
-                        </el-card>
-                      </div>
-                      <div v-if="item.contentType == 5 ">
-
-                        <el-form-item label="上传文件:" prop="fileUrl" label-width="100px">
-                          <el-upload
-                            v-model="item.fileUrl"
-                            class="avatar-uploader"
-                            :action="uploadUrl"
-                            :show-file-list="false"
-                            :on-success="(res, file) => handleAvatarSuccessFile(res, file, item)"
-                            :before-upload="beforeAvatarUploadFile">
-                            <i class="el-icon-plus avatar-uploader-icon"></i>
-                          </el-upload>
-                          <el-link v-if="item.fileUrl" type="primary" :href="downloadUrl(item.fileUrl)" download>
-                            {{item.fileUrl}}
-                          </el-link>
+                        type="textarea" :rows="3" maxlength="66" show-word-limit
+                        placeholder="输入要转为语音的内容" style="width: 90%;margin-top: 10px;"
+                        @input="handleInputVideoText(item.value,item)"/>
+                    </div>
+                    <!-- <div v-if="item.contentType == 12">
+                      <el-card class="box-card">
+                        <el-form-item label="直播间3" >
+                          <el-select  v-model="item.liveId"
+                                      placeholder="请选择直播间" size="mini"
+                                      @change="liveChange(item)" >
+                            <el-option
+                              v-for="dict in liveList"
+                              :key="dict.liveId"
+                              :label="dict.liveName"
+                              :value="dict.liveId"
+                            />
+                          </el-select>
                         </el-form-item>
 
-                      </div>
-
-                      <div v-if="item.contentType == 6 ">
-                        <el-form-item label="上传视频:" prop="videoUrl" label-width="100px">
-                          <el-upload
-                            v-model="item.videoUrl"
-                            class="avatar-uploader"
-                            :action="uploadUrl"
-                            :show-file-list="false"
-                            :on-success="(res, file) => handleAvatarSuccessVideo(res, file, item)"
-                            :before-upload="beforeAvatarUploadVideo">
-                            <i class="el-icon-plus avatar-uploader-icon"></i>
-                          </el-upload>
-                          <video v-if="item.videoUrl"
-                                 :src="item.videoUrl"
-                                 controls style="width: 200px;height: 100px">
-                          </video>
+                        <el-form-item label="标题" prop="miniprogramTitle">
+                          <el-input v-model="item.miniprogramTitle" placeholder="请输入小程序消息标题,最长为64字节" :rows="2" maxlength="64"
+                                    type="textarea"   />
+                        </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-form-item label="appid" prop="miniprogramAppid" v-show="false">
+                          <el-input v-model="item.miniprogramAppid='wx503cf8ab31f83dd4' " disabled />
+                        </el-form-item>
+                        <el-form-item label="page路径" prop="miniprogramPage"  v-show="false" label-width="100px" style="margin-left: -30px">
+                          <el-input v-model="item.miniprogramPage" placeholder="小程序消息打开后的路径"  disabled />
                         </el-form-item>
-                      </div>
-                      <div v-if="item.contentType == 7 ">
-                        <el-input
-                          v-model="item.value"
-                          type="textarea" :rows="3" maxlength="66" show-word-limit
-                          placeholder="输入要转为语音的内容" style="width: 90%;margin-top: 10px;"
-                          @input="handleInputVideoText(item.value,item)"/>
-                      </div>
-                      <div v-if="item.contentType == 8">
-                        <el-button type="primary"
-                                   style="margin-bottom: 1%"
-                                   @click="hanldeSelectVideoNum(setting,index)">
-                          选择视频号
-                        </el-button>
-                        <el-card class="box-card" v-if="item.coverUrl">
-                          <el-form-item label="封面标题:" label-width="100px">
-                            <el-input v-model="item.nickname"
-                                      style="width: 90%;margin-bottom: 1%" disabled/>
-                          </el-form-item>
-                          <el-form-item label="头像:" label-width="100px">
-                            <el-image
-                              v-if="item.avatar != null"
-                              :src="item.avatar"
-                              :preview-src-list="[item.avatar]"
-                              :style="{ width: '50px', height: '50px' }"
-                            ></el-image>
-                          </el-form-item>
-                          <el-form-item label="封面:" label-width="100px">
-                            <el-image
-                              v-if="item.coverUrl != null"
-                              :src="item.coverUrl"
-                              :preview-src-list="[item.coverUrl]"
-                              :style="{ width: '200px', height: '200px' }"
-                            ></el-image>
-
-                          </el-form-item>
-
-                          <el-form-item label="简介:" label-width="100px">
-                            <el-input type="textarea" :rows="3"
-                                      v-model="item.desc"
-                                      style="width: 90%;margin-top: 1%;" disabled/>
-                          </el-form-item>
-                          <el-form-item label="视频地址:" label-width="100px"
-                                        style="margin-top: 1%">
-                            <el-input v-model="item.url"
-                                      style="width: 90%;" disabled/>
-                          </el-form-item>
-                        </el-card>
-                      </div>
-
-
-                    </el-form-item>
-
-                    <el-form-item label="添加短链" v-if="item.contentType == 1 "  >
-                      <el-tooltip content="请先根据课程选定课程小节之后再添加" effect="dark" :disabled="!!msgForm.videoId">
-                        <el-switch
-                          v-model="item.isBindUrl"
-                          :disabled="!msgForm.videoId"
-                          active-color="#13ce66"
-                          inactive-color="#DCDFE6"
-                          active-value="1"
-                          inactive-value="2">
-                        </el-switch>
-                      </el-tooltip>
-
-                      <span v-if="item.isBindUrl == '1'" style="margin-left: 10px; color: #13ce66">添加URL</span>
-                      <span v-if="item.isBindUrl == '2'" style="margin-left: 10px; color: #b1b4ba">不加URL</span>
-                    </el-form-item>
-                    <el-form-item label="课节过期时间" v-if="item.isBindUrl == '1'
+                      </el-card>
+                    </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>
+                    <div v-if="item.contentType == 14">
+                      <el-card class="box-card">
+                        <el-form-item label="福袋名称" >
+                          <el-select  v-model="item.luckyBagId"
+                                      placeholder="请选择福袋" size="mini"
+                                      @change="getLuckyBagStatus(item)" >
+                            <el-option
+                              v-for="dict in luckyBagList"
+                              :key="dict.id"
+                              :label="dict.name"
+                              :value="dict.id"
+                            />
+                          </el-select>
+                        </el-form-item>
+                        <el-form-item label="福袋状态" >
+                          <el-tag :type="item.luckyBagDataStatus == 1 ? 'success' : 'danger'">
+                            {{ item.luckyBagDataStatus == 1 ? '启用' : '禁用' }}
+                          </el-tag>
+                        </el-form-item>
+                      </el-card>
+                    </div>
+
+
+                  </el-form-item>
+
+                  <el-form-item label="添加短链" v-if="item.contentType == 1 || item.contentType == 15"  >
+                    <el-tooltip content="请先根据课程选定课程小节之后再添加" effect="dark" :disabled="!!msgForm.videoId">
+                      <el-switch
+                        v-model="item.isBindUrl"
+                        :disabled="!msgForm.videoId"
+                        active-color="#13ce66"
+                        inactive-color="#DCDFE6"
+                        active-value="1"
+                        inactive-value="2">
+                      </el-switch>
+                    </el-tooltip>
+
+                    <span v-if="item.isBindUrl == '1'" style="margin-left: 10px; color: #13ce66">添加URL</span>
+                    <span v-if="item.isBindUrl == '2'" style="margin-left: 10px; color: #b1b4ba">不加URL</span>
+                  </el-form-item>
+                  <el-form-item label="课节过期时间" v-if="item.isBindUrl == '1'
                                                           && item.contentType != 2
                                                           && item.contentType != 5
                                                           && item.contentType != 6
                                                           && item.contentType != 8"
-                                  style="margin-top: 1%" label-width="100px">
-                      <el-row>
-                        <el-input-number  v-model="item.expiresDays"  :min="1" :max="100" ></el-input-number>
-                        (天)
-                      </el-row>
-                      <el-row>
-                        <span class="tip">填写0或不填时,默认为系统配置的默认时间</span>
-                      </el-row>
-                    </el-form-item>
-                  </el-form>
-                </el-col>
-                <el-col :span="1" :offset="1">
-                  <i class="el-icon-delete" @click="delSetList(index)" style="margin-top: 20px;" v-if="setting.length>1"></i>
-                </el-col>
-              </el-row>
-            </div>
-            <el-link type="primary" class="el-icon-plus" :underline="false" @click='addSetList()'  >添加内容</el-link>
-
-          </el-form-item>
-          <el-form-item label="发送时间" prop="sendTime" v-if="msgForm.draftStrategy==1" >
-            <el-time-picker
-              class="custom-input"
-              v-model="msgForm.sendTime"
-              value-format="HH:mm"
-              format="HH:mm"
-              placeholder="时间"
-              style="width: 100px;height: 20px;">
-            </el-time-picker>
-            <span class="tip" style="margin-left: 2%"> 不填时,默认为系统当前时间(立即发送)</span>
-          </el-form-item>
-
-        </el-form>
-        <div slot="footer" class="dialog-footer">
-          <el-button type="primary" @click="submitMsgForm">确 定</el-button>
-          <el-button @click="cancelMsgForm">取 消</el-button>
-        </div>
+                                style="margin-top: 1%" label-width="100px">
+                    <el-row>
+                      <el-input-number  v-model="item.expiresDays"  :min="1" :max="100" ></el-input-number>
+                      (天)
+                    </el-row>
+                    <el-row>
+                      <span class="tip">填写0或不填时,默认为系统配置的默认时间</span>
+                    </el-row>
+                  </el-form-item>
+                </el-form>
+              </el-col>
+              <el-col :span="1" :offset="1">
+                <i class="el-icon-delete" @click="delSetList(index)" style="margin-top: 20px;" v-if="setting.length>1"></i>
+              </el-col>
+            </el-row>
+          </div>
+          <el-link type="primary" class="el-icon-plus" :underline="false" @click='addSetList()'  >添加内容</el-link>
+
+        </el-form-item>
+        <el-form-item label="发送时间" prop="sendTime" v-if="msgForm.draftStrategy==1" >
+          <el-time-picker
+            class="custom-input"
+            v-model="msgForm.sendTime"
+            value-format="HH:mm"
+            format="HH:mm"
+            placeholder="时间"
+            style="width: 100px;height: 20px;">
+          </el-time-picker>
+          <span class="tip" style="margin-left: 2%"> 不填时,默认为系统当前时间(立即发送)</span>
+        </el-form-item>
+
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitMsgForm">确 定</el-button>
+        <el-button @click="cancelMsgForm">取 消</el-button>
+      </div>
     </el-dialog>
     <el-dialog :title="videoNumOptions.title" :visible.sync="videoNumOptions.open" width="1500px" append-to-body>
       <userVideo ref="QwUserVideo" @videoResult="qwUserVideoResult"></userVideo>
@@ -308,6 +388,7 @@ import ImageUpload from "@/views/qw/sop/ImageUpload.vue";
 import {courseList, videoList} from "@/api/qw/sop";
 import userVideo from "@/views/qw/userVideo/userVideo";
 import {listToLiveNoEnd} from "@/api/live/live";
+import {listReward} from "@/api/qw/luckyBag";
 
 
 export default {
@@ -315,6 +396,14 @@ export default {
   components: {ImageUpload,userVideo},
   data() {
     return {
+      queryParams1: {
+        pageNum: 1,
+        pageSize: 10,
+        dataStatus: '1',
+        name: null,
+        type: null
+      },
+      luckyBagList: [],
       projectFrom:process.env.VUE_APP_PROJECT_FROM,
       videoNumOptions: {
         title: '选择视频号',
@@ -425,8 +514,21 @@ export default {
     listToLiveNoEnd().then(response => {
       this.liveList = response.rows;
     })
+    listReward(this.queryParams1).then(response => {
+      this.luckyBagList = response.rows;
+    });
   },
   methods: {
+    getLuckyBagStatus(content) {
+      const selectedLuckyBag = this.luckyBagList.find(item => item.id === content.luckyBagId);
+      if (selectedLuckyBag) {
+        content.luckyBagDataStatus = selectedLuckyBag.dataStatus; // 自动填充标题
+      } else {
+        // 若未找到对应直播间,清空标题和封面(可选)
+        content.luckyBagDataStatus = null;
+      }
+    },
+
     liveChange(content) {
       // content.liveId 是选中的直播间 ID(liveId)
       const selectedLive = this.liveList.find(live => live.liveId === content.liveId);
@@ -799,18 +901,38 @@ export default {
       this.$refs["msgForm"].validate(valid => {
         if (valid) {
 
+         
+
           if (this.msgForm.draftStrategy==1){
 
+          if(!!hasLiveSetting && (this.msgForm.courseId!=null && this.msgForm.courseId!='')){
+              return this.$message.error("直播间不能选取课程");
+          }
+          if(!!hasLiveSetting && (this.msgForm.videoId!=null && this.msgForm.videoId!='')){
+              return this.$message.error("直播间不能选取课节");
+          }
+
             this.msgForm.setting=JSON.stringify(this.setting)
 
+        let jsonSetting = JSON.parse(this.msgForm.setting);
+          let hasLiveSetting = false;
+          console.log(jsonSetting)
+          for(let index=0;index < jsonSetting.length;index++){
+           if(!!jsonSetting[index] && jsonSetting[index].contentType == "12") {
+            if(!!hasLiveSetting){
+               return this.$message.error("发送直播间只能一次只能发送一条");
+            }
+            hasLiveSetting = true;
+           }
+          }
             if (this.setting.length <= 0) {
               return this.$message.error("请添加规则")
             }
-            if (this.msgForm.courseId===null || this.msgForm.courseId===''){
+            if ((this.msgForm.courseId===null || this.msgForm.courseId==='') && !hasLiveSetting){
               return this.$message.error("课程不能为空")
             }
 
-            if (this.msgForm.videoId===null || this.msgForm.videoId===''){
+            if ((this.msgForm.videoId===null || this.msgForm.videoId==='') && !hasLiveSetting){
               return this.$message.error("课节不能为空")
             }
 
@@ -855,9 +977,16 @@ export default {
               if (this.setting[i].contentType == 6 && (this.setting[i].videoUrl == null || this.setting[i].videoUrl == "")) {
                 return this.$message.error("视频不能为空")
               }
-              if (this.setting[i].contentType == 7 && (this.setting[i].value == null || this.setting[i].value == "")) {
+              if ((this.setting[i].contentType == 7 || this.setting[i].contentType == 16) && (this.setting[i].value == null || this.setting[i].value == "")) {
                 return this.$message.error("语音不能为空")
               }
+              // 添加对福袋的验证
+              if (this.setting[i].contentType == 14 && (this.setting[i].luckyBagId == null || this.setting[i].luckyBagId === "")) {
+                return this.$message.error("福袋不能为空")
+              }
+              if(this.setting[i].contentType == 12 && (this.setting[i].liveId==null || this.setting[i].liveId == "")){
+                return this.$message.error("直播间不能为空");
+              }
             }
           }
 

+ 316 - 242
src/views/qw/sopUserLogsInfo/sendMsgSopOpenTool.vue

@@ -11,257 +11,310 @@
                 </span>
         </template>
       </el-alert>
-        <el-form ref="msgForm" :model="msgForm" :rules="msgRules" label-width="100px">
-          <el-form-item label="策略" prop="draftStrategy">
-            <el-radio-group v-model="msgForm.draftStrategy">
-              <el-radio :label="1">正常群发</el-radio>
-              <el-radio :label="2">清除草稿</el-radio>
-              <el-radio :label="3">发送草稿</el-radio>
-            </el-radio-group>
-          </el-form-item>
-
-          <el-form-item label="选择课程" v-if="msgForm.draftStrategy==1" >
-            <el-select  v-model="msgForm.courseId" placeholder="请选择课程" style=" margin-right: 10px;" size="mini"  @change="courseChange()">
-              <el-option
-                v-for="dict in courseList"
-                :key="dict.dictValue"
-                :label="dict.dictLabel"
-                :value="parseInt(dict.dictValue)"
-              />
-            </el-select>
-            <el-select  v-model="msgForm.videoId" placeholder="请选择小节" size="mini" style=" margin-right: 10px;" @change="videoIdChange()"  >
-              <el-option
-                v-for="dict in videoList"
-                :key="dict.dictValue"
-                :label="dict.dictLabel"
-                :value="parseInt(dict.dictValue)"
-              />
-            </el-select>
-            <el-select  v-model="msgForm.courseType" placeholder="请选择消息类型" size="mini" style=" margin-right: 10px;">
-              <el-option
-                v-for="dict in sysFsSopWatchStatus"
-                :key="dict.dictValue"
-                :label="dict.dictLabel"
-                :value="parseInt(dict.dictValue)"
-              />
-            </el-select>
-          </el-form-item>
-          <el-form-item label="规则" prop="setting" v-if="msgForm.draftStrategy==1"  >
-            <div v-for="(item, index) in setting" :key="index" style="background-color: #fdfdfd; border: 1px solid #e6e6e6; margin-bottom: 20px;">
-              <el-row>
-                <el-col :span="22">
-                  <el-form :model="item" label-width="70px">
-                    <el-form-item label="内容类别" style="margin: 2%">
-                      <el-radio-group  v-model="item.contentType">
-                        <el-radio   :label="item.dictValue" v-for="item in sysQwSopAiContentType"  @change="handleContentTypeChange()">{{item.dictLabel}}</el-radio>
-                      </el-radio-group>
-                    </el-form-item>
-                    <el-form-item label="内容" style="margin-bottom: 2%" >
+      <el-form ref="msgForm" :model="msgForm" :rules="msgRules" label-width="100px">
+        <el-form-item label="策略" prop="draftStrategy">
+          <el-radio-group v-model="msgForm.draftStrategy">
+            <el-radio :label="1">正常群发</el-radio>
+            <el-radio :label="2">清除草稿</el-radio>
+            <el-radio :label="3">发送草稿</el-radio>
+          </el-radio-group>
+        </el-form-item>
+
+        <el-form-item label="选择课程" v-if="msgForm.draftStrategy==1" >
+          <el-select  v-model="msgForm.courseId" placeholder="请选择课程" style=" margin-right: 10px;" size="mini"  @change="courseChange()">
+            <el-option
+              v-for="dict in courseList"
+              :key="dict.dictValue"
+              :label="dict.dictLabel"
+              :value="parseInt(dict.dictValue)"
+            />
+          </el-select>
+          <el-select  v-model="msgForm.videoId" placeholder="请选择小节" size="mini" style=" margin-right: 10px;" @change="videoIdChange()"  >
+            <el-option
+              v-for="dict in videoList"
+              :key="dict.dictValue"
+              :label="dict.dictLabel"
+              :value="parseInt(dict.dictValue)"
+            />
+          </el-select>
+          <el-select  v-model="msgForm.courseType" placeholder="请选择消息类型" size="mini" style=" margin-right: 10px;">
+            <el-option
+              v-for="dict in sysFsSopWatchStatus"
+              :key="dict.dictValue"
+              :label="dict.dictLabel"
+              :value="parseInt(dict.dictValue)"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="规则" prop="setting" v-if="msgForm.draftStrategy==1"  >
+          <div v-for="(item, index) in setting" :key="index" style="background-color: #fdfdfd; border: 1px solid #e6e6e6; margin-bottom: 20px;">
+            <el-row>
+              <el-col :span="22">
+                <el-form :model="item" label-width="70px">
+                  <el-form-item label="内容类别" style="margin: 2%">
+                    <el-radio-group  v-model="item.contentType">
+                      <el-radio   :label="item.dictValue" v-for="item in sysQwSopAiContentType"  @change="handleContentTypeChange()">{{item.dictLabel}}</el-radio>
+                    </el-radio-group>
+                  </el-form-item>
+                  <el-form-item label="内容" style="margin-bottom: 2%" >
+                    <el-input
+                      v-if="item.contentType == 1 || item.contentType == 15"
+                      v-model="item.value"
+                      type="textarea"
+                      :rows="3"
+                      placeholder="内容"
+                      style="width: 90%; margin-top: 10px;"
+                      @keydown.native="handleKeydown($event, index)"
+                      :ref="`textarea-${index}`"
+                    >
+                    </el-input>
+                    <el-link
+                      v-if="item.contentType == 1 || item.contentType == 15"
+                      type="primary"
+                      @click="toggleSalesCall(index)"
+                      style="margin-top: 10px;"
+                    >
+                      {{ item.isSalesCallAdded ? '移除#销售称呼#' : '添加#销售称呼#' }}
+                    </el-link>
+                    <el-link
+                      v-if="item.contentType == 1 || item.contentType == 15"
+                      type="primary"
+                      @click="toggleSalesCallCustomer(index)"
+                      style="margin-top: 10px;margin-left: 2%"
+                    >
+                      {{ item.isSalesCallCustomerAdded ? '移除#客户称呼#' : '添加#客户称呼#' }}
+                    </el-link>
+
+
+                    <ImageUpload v-if="item.contentType == 2 " v-model="item.imgUrl" type="image" :num="1"  :width="150" :height="150" />
+
+                    <div v-if="item.contentType == 3 || item.contentType ==9 ">
+                      <el-card class="box-card">
+                        <el-form-item label="链接标题:"  label-width="100px">
+                          <el-input v-model="item.linkTitle" placeholder="请输入链接标题" style="width: 90%;"/>
+                        </el-form-item>
+                        <el-form-item label="链接描述:"   label-width="100px" >
+                          <el-input type="textarea" :rows="3" v-model="item.linkDescribe" placeholder="请输入链接描述" style="width: 90%;margin-top: 1%;"/>
+                        </el-form-item>
+                        <el-form-item label="链接封面:"   label-width="100px">
+                          <ImageUpload v-model="item.linkImageUrl" type="image" :num="1" :file-size="2" :width="150" :height="150" style="margin-top: 1%;" />
+                        </el-form-item>
+                        <el-form-item label="链接地址:"  label-width="100px" >
+                          <el-tag type="warning" v-model="item.isBindUrl=1">选择的课程小节 即为卡片链接地址</el-tag>
+                        </el-form-item>
+                      </el-card>
+                    </div>
+                    <div v-if="item.contentType == 4 || item.contentType == 10 ">
+                      <el-card class="box-card">
+                        <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-form-item label="appid" prop="miniprogramAppid" v-show="false" >
+                          <el-input v-model="item.miniprogramAppid='wx73f85f8d62769119' " disabled />
+                        </el-form-item>
+                        <el-form-item label="page路径" prop="miniprogramPage" v-show="item.contentType == 10" label-width="100px" style="margin-left: -30px" >
+                          <el-input v-model="item.miniprogramPage" placeholder="小程序消息打开后的路径" type="textarea" :rows="3" />
+                        </el-form-item>
+                      </el-card>
+                    </div>
+                    <div v-if="item.contentType == 5 ">
+
+                      <el-form-item label="上传文件:" prop="fileUrl" label-width="100px">
+                        <el-upload
+                          v-model="item.fileUrl"
+                          class="avatar-uploader"
+                          :action="uploadUrl"
+                          :show-file-list="false"
+                          :on-success="(res, file) => handleAvatarSuccessFile(res, file, item)"
+                          :before-upload="beforeAvatarUploadFile">
+                          <i class="el-icon-plus avatar-uploader-icon"></i>
+                        </el-upload>
+                        <el-link v-if="item.fileUrl" type="primary" :href="downloadUrl(item.fileUrl)" download>
+                          {{item.fileUrl}}
+                        </el-link>
+                      </el-form-item>
+
+                    </div>
+
+                    <div v-if="item.contentType == 6 ">
+                      <el-form-item label="上传视频:" prop="videoUrl" label-width="100px">
+                        <el-upload
+                          v-model="item.videoUrl"
+                          class="avatar-uploader"
+                          :action="uploadUrl"
+                          :show-file-list="false"
+                          :on-success="(res, file) => handleAvatarSuccessVideo(res, file, item)"
+                          :before-upload="beforeAvatarUploadVideo">
+                          <i class="el-icon-plus avatar-uploader-icon"></i>
+                        </el-upload>
+                        <video v-if="item.videoUrl"
+                               :src="item.videoUrl"
+                               controls style="width: 200px;height: 100px">
+                        </video>
+                      </el-form-item>
+                    </div>
+                    <div v-if="item.contentType == 7 || item.contentType == 16">
                       <el-input
-                        v-if="item.contentType == 1"
                         v-model="item.value"
-                        type="textarea"
-                        :rows="3"
-                        placeholder="内容"
-                        style="width: 90%; margin-top: 10px;"
-                        @keydown.native="handleKeydown($event, index)"
-                        :ref="`textarea-${index}`"
-                      >
-                      </el-input>
-                      <el-link
-                        v-if="item.contentType == 1"
-                        type="primary"
-                        @click="toggleSalesCall(index)"
-                        style="margin-top: 10px;"
-                      >
-                        {{ item.isSalesCallAdded ? '移除#销售称呼#' : '添加#销售称呼#' }}
-                      </el-link>
-                      <el-link
-                        v-if="item.contentType == 1"
-                        type="primary"
-                        @click="toggleSalesCallCustomer(index)"
-                        style="margin-top: 10px;margin-left: 2%"
-                      >
-                        {{ item.isSalesCallCustomerAdded ? '移除#客户称呼#' : '添加#客户称呼#' }}
-                      </el-link>
-
-
-                      <ImageUpload v-if="item.contentType == 2 " v-model="item.imgUrl" type="image" :num="1"  :width="150" :height="150" />
-
-                      <div v-if="item.contentType == 3 || item.contentType ==9 ">
-                        <el-card class="box-card">
-                          <el-form-item label="链接标题:"  label-width="100px">
-                            <el-input v-model="item.linkTitle" placeholder="请输入链接标题" style="width: 90%;"/>
-                          </el-form-item>
-                          <el-form-item label="链接描述:"   label-width="100px" >
-                            <el-input type="textarea" :rows="3" v-model="item.linkDescribe" placeholder="请输入链接描述" style="width: 90%;margin-top: 1%;"/>
-                          </el-form-item>
-                          <el-form-item label="链接封面:"   label-width="100px">
-                            <ImageUpload v-model="item.linkImageUrl" type="image" :num="1" :file-size="2" :width="150" :height="150" style="margin-top: 1%;" />
-                          </el-form-item>
-                          <el-form-item label="链接地址:"  label-width="100px" >
-                            <el-tag type="warning" v-model="item.isBindUrl=1">选择的课程小节 即为卡片链接地址</el-tag>
-                          </el-form-item>
-                        </el-card>
-                      </div>
-                      <div v-if="item.contentType == 4 || item.contentType == 10 ">
-                        <el-card class="box-card">
-                          <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-form-item label="appid" prop="miniprogramAppid" v-show="false" >
-                            <el-input v-model="item.miniprogramAppid='wx73f85f8d62769119' " disabled />
-                          </el-form-item>
-                          <el-form-item label="page路径" prop="miniprogramPage" v-show="item.contentType == 10" label-width="100px" style="margin-left: -30px" >
-                            <el-input v-model="item.miniprogramPage" placeholder="小程序消息打开后的路径" type="textarea" :rows="3" />
-                          </el-form-item>
-                        </el-card>
-                      </div>
-                      <div v-if="item.contentType == 5 ">
-
-                        <el-form-item label="上传文件:" prop="fileUrl" label-width="100px">
-                          <el-upload
-                            v-model="item.fileUrl"
-                            class="avatar-uploader"
-                            :action="uploadUrl"
-                            :show-file-list="false"
-                            :on-success="(res, file) => handleAvatarSuccessFile(res, file, item)"
-                            :before-upload="beforeAvatarUploadFile">
-                            <i class="el-icon-plus avatar-uploader-icon"></i>
-                          </el-upload>
-                          <el-link v-if="item.fileUrl" type="primary" :href="downloadUrl(item.fileUrl)" download>
-                            {{item.fileUrl}}
-                          </el-link>
+                        type="textarea" :rows="3" maxlength="66" show-word-limit
+                        placeholder="输入要转为语音的内容" style="width: 90%;margin-top: 10px;"
+                        @input="handleInputVideoText(item.value,item)"/>
+                    </div>
+                    <div v-if="item.contentType == 8">
+                      <el-button type="primary"
+                                 style="margin-bottom: 1%"
+                                 @click="hanldeSelectVideoNum(setting,index)">
+                        选择视频号
+                      </el-button>
+                      <el-card class="box-card" v-if="item.coverUrl">
+                        <el-form-item label="封面标题:" label-width="100px">
+                          <el-input v-model="item.nickname"
+                                    style="width: 90%;margin-bottom: 1%" disabled/>
+                        </el-form-item>
+                        <el-form-item label="头像:" label-width="100px">
+                          <el-image
+                            v-if="item.avatar != null"
+                            :src="item.avatar"
+                            :preview-src-list="[item.avatar]"
+                            :style="{ width: '50px', height: '50px' }"
+                          ></el-image>
+                        </el-form-item>
+                        <el-form-item label="封面:" label-width="100px">
+                          <el-image
+                            v-if="item.coverUrl != null"
+                            :src="item.coverUrl"
+                            :preview-src-list="[item.coverUrl]"
+                            :style="{ width: '200px', height: '200px' }"
+                          ></el-image>
+
+                        </el-form-item>
+                        <el-form-item label="简介:" label-width="100px">
+                          <el-input type="textarea" :rows="3"
+                                    v-model="item.desc"
+                                    style="width: 90%;margin-top: 1%;" disabled/>
+                        </el-form-item>
+                        <el-form-item label="视频地址:" label-width="100px"
+                                      style="margin-top: 1%">
+                          <el-input v-model="item.url"
+                                    style="width: 90%;" disabled/>
+                        </el-form-item>
+                      </el-card>
+                    </div>
+
+                    <div v-if="item.contentType == 12">
+                      <el-card class="box-card">
+                        <el-form-item label="直播间" >
+                          <el-select  v-model="item.liveId"
+                                      placeholder="请选择直播间" size="mini"
+                                      @change="liveChange(item)" >
+                            <el-option
+                              v-for="dict in liveList"
+                              :key="dict.liveId"
+                              :label="dict.liveName"
+                              :value="dict.liveId"
+                            />
+                          </el-select>
                         </el-form-item>
 
-                      </div>
-
-                      <div v-if="item.contentType == 6 ">
-                        <el-form-item label="上传视频:" prop="videoUrl" label-width="100px">
-                          <el-upload
-                            v-model="item.videoUrl"
-                            class="avatar-uploader"
-                            :action="uploadUrl"
-                            :show-file-list="false"
-                            :on-success="(res, file) => handleAvatarSuccessVideo(res, file, item)"
-                            :before-upload="beforeAvatarUploadVideo">
-                            <i class="el-icon-plus avatar-uploader-icon"></i>
-                          </el-upload>
-                          <video v-if="item.videoUrl"
-                                 :src="item.videoUrl"
-                                 controls style="width: 200px;height: 100px">
-                          </video>
+                        <el-form-item label="标题" prop="miniprogramTitle">
+                          <el-input v-model="item.miniprogramTitle" placeholder="请输入小程序消息标题,最长为64字节" :rows="2" maxlength="64"
+                                    type="textarea"   />
+                        </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-form-item label="appid" prop="miniprogramAppid" v-show="false">
+                          <el-input v-model="item.miniprogramAppid='wx503cf8ab31f83dd4' " disabled />
+                        </el-form-item>
+                        <el-form-item label="page路径" prop="miniprogramPage"  v-show="false" label-width="100px" style="margin-left: -30px">
+                          <el-input v-model="item.miniprogramPage" placeholder="小程序消息打开后的路径"  disabled />
                         </el-form-item>
-                      </div>
-                      <div v-if="item.contentType == 7 ">
-                        <el-input
-                          v-model="item.value"
-                          type="textarea" :rows="3" maxlength="66" show-word-limit
-                          placeholder="输入要转为语音的内容" style="width: 90%;margin-top: 10px;"
-                          @input="handleInputVideoText(item.value,item)"/>
-                      </div>
-                      <div v-if="item.contentType == 8">
-                        <el-button type="primary"
-                                   style="margin-bottom: 1%"
-                                   @click="hanldeSelectVideoNum(setting,index)">
-                          选择视频号
-                        </el-button>
-                        <el-card class="box-card" v-if="item.coverUrl">
-                          <el-form-item label="封面标题:" label-width="100px">
-                            <el-input v-model="item.nickname"
-                                      style="width: 90%;margin-bottom: 1%" disabled/>
-                          </el-form-item>
-                          <el-form-item label="头像:" label-width="100px">
-                            <el-image
-                              v-if="item.avatar != null"
-                              :src="item.avatar"
-                              :preview-src-list="[item.avatar]"
-                              :style="{ width: '50px', height: '50px' }"
-                            ></el-image>
-                          </el-form-item>
-                          <el-form-item label="封面:" label-width="100px">
-                            <el-image
-                              v-if="item.coverUrl != null"
-                              :src="item.coverUrl"
-                              :preview-src-list="[item.coverUrl]"
-                              :style="{ width: '200px', height: '200px' }"
-                            ></el-image>
-
-                          </el-form-item>
-                          <el-form-item label="简介:" label-width="100px">
-                            <el-input type="textarea" :rows="3"
-                                      v-model="item.desc"
-                                      style="width: 90%;margin-top: 1%;" disabled/>
-                          </el-form-item>
-                          <el-form-item label="视频地址:" label-width="100px"
-                                        style="margin-top: 1%">
-                            <el-input v-model="item.url"
-                                      style="width: 90%;" disabled/>
-                          </el-form-item>
-                        </el-card>
-                      </div>
-
-                    </el-form-item>
-
-                    <el-form-item label="添加短链" v-if="item.contentType == 1 "  >
-                      <el-tooltip content="请先根据课程选定课程小节之后再添加" effect="dark" :disabled="!!msgForm.videoId">
-                        <el-switch
-                          v-model="item.isBindUrl"
-                          :disabled="!msgForm.videoId"
-                          active-color="#13ce66"
-                          inactive-color="#DCDFE6"
-                          active-value="1"
-                          inactive-value="2">
-                        </el-switch>
-                      </el-tooltip>
-
-                      <span v-if="item.isBindUrl == '1'" style="margin-left: 10px; color: #13ce66">添加URL</span>
-                      <span v-if="item.isBindUrl == '2'" style="margin-left: 10px; color: #b1b4ba">不加URL</span>
-                    </el-form-item>
-                    <el-form-item label="课节过期时间" v-if="item.isBindUrl == '1'
+                      </el-card>
+                    </div>
+
+                    <div v-if="item.contentType == 14">
+                      <el-card class="box-card">
+                        <el-form-item label="福袋名称" >
+                          <el-select  v-model="item.luckyBagId"
+                                      placeholder="请选择福袋" size="mini"
+                                      @change="getLuckyBagStatus(item)" >
+                            <el-option
+                              v-for="dict in luckyBagList"
+                              :key="dict.id"
+                              :label="dict.name"
+                              :value="dict.id"
+                            />
+                          </el-select>
+                        </el-form-item>
+                        <el-form-item label="福袋状态" >
+                          <el-tag :type="item.luckyBagDataStatus == 1 ? 'success' : 'danger'">
+                            {{ item.luckyBagDataStatus == 1 ? '启用' : '禁用' }}
+                          </el-tag>
+                        </el-form-item>
+                      </el-card>
+                    </div>
+
+                  </el-form-item>
+
+                  <el-form-item label="添加短链" v-if="item.contentType == 1 || item.contentType == 15"  >
+                    <el-tooltip content="请先根据课程选定课程小节之后再添加" effect="dark" :disabled="!!msgForm.videoId">
+                      <el-switch
+                        v-model="item.isBindUrl"
+                        :disabled="!msgForm.videoId"
+                        active-color="#13ce66"
+                        inactive-color="#DCDFE6"
+                        active-value="1"
+                        inactive-value="2">
+                      </el-switch>
+                    </el-tooltip>
+
+                    <span v-if="item.isBindUrl == '1'" style="margin-left: 10px; color: #13ce66">添加URL</span>
+                    <span v-if="item.isBindUrl == '2'" style="margin-left: 10px; color: #b1b4ba">不加URL</span>
+                  </el-form-item>
+                  <el-form-item label="课节过期时间" v-if="item.isBindUrl == '1'
                                                           && item.contentType != 2
                                                           && item.contentType != 5
                                                           && item.contentType != 6
                                                           && item.contentType != 8"
-                                  style="margin-top: 1%" label-width="100px">
-                      <el-row>
-                        <el-input-number  v-model="item.expiresDays"  :min="1" :max="100" ></el-input-number>
-                        (天)
-                      </el-row>
-                      <el-row>
-                        <span class="tip">填写0或不填时,默认为系统配置的默认时间</span>
-                      </el-row>
-                    </el-form-item>
-                  </el-form>
-                </el-col>
-                <el-col :span="1" :offset="1">
-                  <i class="el-icon-delete" @click="delSetList(index)" style="margin-top: 20px;" v-if="setting.length>1"></i>
-                </el-col>
-              </el-row>
-            </div>
-            <el-link type="primary" class="el-icon-plus" :underline="false" @click='addSetList()'  >添加内容</el-link>
-
-          </el-form-item>
-          <el-form-item label="发送时间" prop="sendTime" v-if="msgForm.draftStrategy==1" >
-            <el-time-picker
-              class="custom-input"
-              v-model="msgForm.sendTime"
-              value-format="HH:mm"
-              format="HH:mm"
-              placeholder="时间"
-              style="width: 100px;height: 20px;">
-            </el-time-picker>
-            <span class="tip" style="margin-left: 2%"> 不填时,默认为系统当前时间(立即发送)</span>
-          </el-form-item>
-
-        </el-form>
-        <div slot="footer" class="dialog-footer">
-          <el-button type="primary" @click="submitMsgForm">确 定</el-button>
-          <el-button @click="cancelMsgForm">取 消</el-button>
-        </div>
+                                style="margin-top: 1%" label-width="100px">
+                    <el-row>
+                      <el-input-number  v-model="item.expiresDays"  :min="1" :max="100" ></el-input-number>
+                      (天)
+                    </el-row>
+                    <el-row>
+                      <span class="tip">填写0或不填时,默认为系统配置的默认时间</span>
+                    </el-row>
+                  </el-form-item>
+                </el-form>
+              </el-col>
+              <el-col :span="1" :offset="1">
+                <i class="el-icon-delete" @click="delSetList(index)" style="margin-top: 20px;" v-if="setting.length>1"></i>
+              </el-col>
+            </el-row>
+          </div>
+          <el-link type="primary" class="el-icon-plus" :underline="false" @click='addSetList()'  >添加内容</el-link>
+
+        </el-form-item>
+        <el-form-item label="发送时间" prop="sendTime" v-if="msgForm.draftStrategy==1" >
+          <el-time-picker
+            class="custom-input"
+            v-model="msgForm.sendTime"
+            value-format="HH:mm"
+            format="HH:mm"
+            placeholder="时间"
+            style="width: 100px;height: 20px;">
+          </el-time-picker>
+          <span class="tip" style="margin-left: 2%"> 不填时,默认为系统当前时间(立即发送)</span>
+        </el-form-item>
+
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitMsgForm">确 定</el-button>
+        <el-button @click="cancelMsgForm">取 消</el-button>
+      </div>
     </el-dialog>
     <el-dialog :title="videoNumOptions.title" :visible.sync="videoNumOptions.open" width="1500px" append-to-body>
       <userVideo ref="QwUserVideo" @videoResult="qwUserVideoResult"></userVideo>
@@ -274,6 +327,7 @@ import { sendMsg,} from "../../../api/qw/sopUserLogsInfo";
 import ImageUpload from "@/views/qw/sop/ImageUpload.vue";
 import {courseList, videoList} from "@/api/qw/sop";
 import userVideo from "@/views/qw/userVideo/userVideo";
+import {listReward} from "@/api/qw/luckyBag";
 
 
 export default {
@@ -281,6 +335,14 @@ export default {
   components: {ImageUpload,userVideo},
   data() {
     return {
+      queryParams1: {
+        pageNum: 1,
+        pageSize: 10,
+        dataStatus: '1',
+        name: null,
+        type: null
+      },
+      luckyBagList: [],
       projectFrom:process.env.VUE_APP_PROJECT_FROM,
       videoNumOptions: {
         title: '选择视频号',
@@ -387,8 +449,20 @@ export default {
     courseList().then(response => {
       this.courseList = response.list;
     });
+    listReward(this.queryParams1).then(response => {
+      this.luckyBagList = response.rows;
+    });
   },
   methods: {
+    getLuckyBagStatus(content) {
+      const selectedLuckyBag = this.luckyBagList.find(item => item.id === content.luckyBagId);
+      if (selectedLuckyBag) {
+        content.luckyBagDataStatus = selectedLuckyBag.dataStatus; // 自动填充标题
+      } else {
+        // 若未找到对应直播间,清空标题和封面(可选)
+        content.luckyBagDataStatus = null;
+      }
+    },
 
     qwUserVideoResult(val) {
 

+ 56 - 14
src/views/qw/sopUserLogsInfo/sopUserLogsInfoDetails.vue

@@ -189,12 +189,12 @@
       <el-table-column label="添加日期" align="center" prop="createTime" width="180"/>
       <el-table-column label="添加时间" align="center" prop="crtTime" width="180"/>
       <el-table-column label="修改时间" align="center" prop="updateTime" width="180"/>
-<!--      <el-table-column label="官方群发许可" align="center" prop="fsUserId" width="70">-->
-<!--        <template slot-scope="scope">-->
-<!--          <el-tag v-if="scope.row.fsUserId > 0" type="success">是</el-tag>-->
-<!--          <el-tag v-else type="danger">否</el-tag>-->
-<!--        </template>-->
-<!--      </el-table-column>-->
+      <!--      <el-table-column label="官方群发许可" align="center" prop="fsUserId" width="70">-->
+      <!--        <template slot-scope="scope">-->
+      <!--          <el-tag v-if="scope.row.fsUserId > 0" type="success">是</el-tag>-->
+      <!--          <el-tag v-else type="danger">否</el-tag>-->
+      <!--        </template>-->
+      <!--      </el-table-column>-->
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="120" fixed="right">
         <template slot-scope="scope">
           <!--          <el-button-->
@@ -228,7 +228,7 @@
         </template>
       </el-table-column>
       <el-table-column label="加群时间" align="center" prop="joinTime" width="180"/>
-<!--      <el-table-column label="进线时间" align="center" prop="inComTime" width="180"/>-->
+      <!--      <el-table-column label="进线时间" align="center" prop="inComTime" width="180"/>-->
     </el-table>
 
     <pagination-more
@@ -279,7 +279,7 @@
                   </el-form-item>
                   <el-form-item label="内容" style="margin-bottom: 2%" >
                     <el-input
-                      v-if="item.contentType == 1"
+                      v-if="item.contentType == 1 || item.contentType == 15"
                       v-model="item.value"
                       type="textarea"
                       :rows="3"
@@ -290,7 +290,7 @@
                     >
                     </el-input>
                     <el-link
-                      v-if="item.contentType == 1"
+                      v-if="item.contentType == 1 || item.contentType == 15"
                       type="primary"
                       @click="toggleSalesCall(index)"
                       style="margin-top: 10px;"
@@ -298,7 +298,7 @@
                       {{ item.isSalesCallAdded ? '移除#客服称呼#' : '添加#客服称呼#' }}
                     </el-link>
                     <el-link
-                      v-if="item.contentType == 1"
+                      v-if="item.contentType == 1 || item.contentType == 15"
                       type="primary"
                       @click="toggleSalesCallCustomer(index)"
                       style="margin-top: 10px;margin-left: 2%"
@@ -377,7 +377,7 @@
                         </video>
                       </el-form-item>
                     </div>
-                    <div v-if="item.contentType == 7 ">
+                    <div v-if="item.contentType == 7 || item.contentType == 16">
                       <el-input
                         v-model="item.value"
                         type="textarea" :rows="3" maxlength="66" show-word-limit
@@ -455,10 +455,31 @@
                         </el-form-item>
                       </el-card>
                     </div>
+                    <div v-if="item.contentType == 14">
+                      <el-card class="box-card">
+                        <el-form-item label="福袋名称" >
+                          <el-select  v-model="item.luckyBagId"
+                                      placeholder="请选择福袋" size="mini"
+                                      @change="getLuckyBagStatus(item)" >
+                            <el-option
+                              v-for="dict in luckyBagList"
+                              :key="dict.id"
+                              :label="dict.name"
+                              :value="dict.id"
+                            />
+                          </el-select>
+                        </el-form-item>
+                        <el-form-item label="福袋状态" >
+                          <el-tag :type="item.luckyBagDataStatus == 1 ? 'success' : 'danger'">
+                            {{ item.luckyBagDataStatus == 1 ? '启用' : '禁用' }}
+                          </el-tag>
+                        </el-form-item>
+                      </el-card>
+                    </div>
 
                   </el-form-item>
 
-                  <el-form-item label="添加短链" v-if="item.contentType == 1 "  >
+                  <el-form-item label="添加短链" v-if="item.contentType == 1 || item.contentType == 15"  >
                     <el-tooltip content="请先根据课程选定课程小节之后再添加" effect="dark" :disabled="!!msgForm.videoId">
                       <el-switch
                         v-model="item.isBindUrl"
@@ -585,13 +606,21 @@ import {searchTags} from "../../../api/qw/tag";
 import userVideo from "@/views/qw/userVideo/userVideo.vue";
 import PaginationMore from "../../../components/PaginationMore/index.vue";
 import {listToLiveNoEnd} from "@/api/live/live";
+import {listReward} from "@/api/qw/luckyBag";
 
 export default {
   name: "sopUserLogsInfoDetails",
   components: {PaginationMore, userVideo, ImageUpload},
   data() {
     return {
-
+      queryParams1: {
+        pageNum: 1,
+        pageSize: 10,
+        dataStatus: '1',
+        name: null,
+        type: null
+      },
+      luckyBagList: [],
       projectFrom:process.env.VUE_APP_PROJECT_FROM,
 
       statusOptions:[],
@@ -732,10 +761,23 @@ export default {
 
     listToLiveNoEnd().then(response => {
       this.liveList = response.rows;
-    })
+    });
+    listReward(this.queryParams1).then(response => {
+      this.luckyBagList = response.rows;
+    });
 
   },
   methods: {
+    getLuckyBagStatus(content) {
+      const selectedLuckyBag = this.luckyBagList.find(item => item.id === content.luckyBagId);
+      if (selectedLuckyBag) {
+        content.luckyBagDataStatus = selectedLuckyBag.dataStatus; // 自动填充标题
+      } else {
+        // 若未找到对应直播间,清空标题和封面(可选)
+        content.luckyBagDataStatus = null;
+      }
+    },
+
     liveChange(content) {
       // content.liveId 是选中的直播间 ID(liveId)
       const selectedLive = this.liveList.find(live => live.liveId === content.liveId);

部分文件因文件數量過多而無法顯示