فهرست منبع

Merge branch 'master' of http://1.14.104.71:10880/root/ylrz_his_scrm_adminUI

caoliqin 1 هفته پیش
والد
کامیت
0b82a8d7ae
43فایلهای تغییر یافته به همراه11978 افزوده شده و 193 حذف شده
  1. 2 0
      package.json
  2. 46 0
      src/api/course/courseWatchLog.js
  3. 62 0
      src/api/course/qw/courseAnswerlogs.js
  4. 114 0
      src/api/course/qw/courseWatchLog.js
  5. 259 0
      src/api/qw/user.js
  6. 12 0
      src/api/statistics/member.js
  7. 311 0
      src/api/statistics/statistics.js
  8. 103 0
      src/api/store/user.js
  9. 53 0
      src/api/user/approval.js
  10. 82 0
      src/api/users/user.js
  11. 0 5
      src/components/MinimizableDialog/index.vue
  12. 3 0
      src/components/VideoUpload/index.vue
  13. 2 1
      src/utils/obs.js
  14. 29 0
      src/views/components/course/userCourseCatalogDetails.vue
  15. 1 1
      src/views/course/courseQuestionBank/index.vue
  16. 43 0
      src/views/course/courseUserStatistics/courseUserStatisticsTabIndex.vue
  17. 432 0
      src/views/course/courseUserStatistics/index.vue
  18. 410 0
      src/views/course/courseUserStatistics/my.vue
  19. 711 0
      src/views/course/courseUserStatistics/myStatistics.vue
  20. 43 0
      src/views/course/courseUserStatistics/myStatisticsTabIndex.vue
  21. 43 0
      src/views/course/courseUserStatistics/myTabIndex.vue
  22. 384 0
      src/views/course/courseUserStatistics/qw/index.vue
  23. 381 0
      src/views/course/courseUserStatistics/qw/my.vue
  24. 680 0
      src/views/course/courseUserStatistics/qw/myStatistics.vue
  25. 697 0
      src/views/course/courseUserStatistics/qw/statistics.vue
  26. 752 0
      src/views/course/courseUserStatistics/statistics.vue
  27. 43 0
      src/views/course/courseUserStatistics/statisticsTabIndex.vue
  28. 178 34
      src/views/course/courseWatchLog/index.vue
  29. 388 0
      src/views/course/courseWatchLog/myCourseWatchLog.vue
  30. 43 0
      src/views/course/courseWatchLog/myWatchLogTabIndex.vue
  31. 489 0
      src/views/course/courseWatchLog/qw/index.vue
  32. 396 0
      src/views/course/courseWatchLog/qw/myCourseWatchLog.vue
  33. 318 0
      src/views/course/courseWatchLog/qw/statistics.vue
  34. 602 0
      src/views/course/courseWatchLog/qw/watchLog.vue
  35. 314 0
      src/views/course/courseWatchLog/qw/watchLogStatistics.vue
  36. 390 0
      src/views/course/courseWatchLog/statistics.vue
  37. 43 0
      src/views/course/courseWatchLog/statisticsTabIndex.vue
  38. 631 0
      src/views/course/courseWatchLog/watchLog.vue
  39. 321 0
      src/views/course/courseWatchLog/watchLogStatistics.vue
  40. 43 0
      src/views/course/courseWatchLog/watchLogStatisticsTabIndex.vue
  41. 43 0
      src/views/course/courseWatchLog/watchLogTabIndex.vue
  42. 1643 152
      src/views/index.vue
  43. 438 0
      src/views/user/transfer/index.vue

+ 2 - 0
package.json

@@ -11,6 +11,7 @@
     "build:prod-hzyy": "vue-cli-service build --mode prod-hzyy",
     "build:prod-jzzx": "vue-cli-service build --mode prod-jzzx",
     "build:prod-hcl": "vue-cli-service build --mode prod-hcl",
+    "build:prod-myhk": "vue-cli-service build --mode prod-myhk",
     "build:prod-sxjz": "vue-cli-service build --mode prod-sxjz",
     "build:prod-jnmy": "vue-cli-service build --mode prod-jnmy",
     "build:prod-hdt": "vue-cli-service build --mode prod-hdt",
@@ -49,6 +50,7 @@
     "clipboard": "2.0.6",
     "core-js": "3.8.1",
     "cos-js-sdk-v5": "^1.8.3",
+    "dayjs": "^1.11.13",
     "echarts": "^4.9.0",
     "element-ui": "2.15.5",
     "esdk-obs-browserjs": "^3.24.3",

+ 46 - 0
src/api/course/courseWatchLog.js

@@ -51,3 +51,49 @@ export function exportCourseWatchLog(query) {
     params: query
   })
 }
+
+
+export function statisticsList(query) {
+  return request({
+    url: '/course/courseWatchLog/statisticsList',
+    method: 'get',
+    params: query
+  })
+}
+
+export function qwWatchLogStatisticsList(query) {
+  return request({
+    url: '/course/courseWatchLog/qwWatchLogStatisticsList',
+    method: 'get',
+    params: query
+  })
+}
+
+export function qwWatchLogAllStatisticsList(query) {
+  return request({
+    url: '/course/courseWatchLog/qwWatchLogAllStatisticsList',
+    method: 'get',
+    params: query
+  })
+}
+export function myQwWatchLogStatisticsList(query) {
+  return request({
+    url: '/course/courseWatchLog/myQwWatchLogStatisticsList',
+    method: 'get',
+    params: query
+  })
+}
+export function myQwWatchLogAllStatisticsList(query) {
+  return request({
+    url: '/course/courseWatchLog/myQwWatchLogAllStatisticsList',
+    method: 'get',
+    params: query
+  })
+}
+export function watchLogStatistics(query) {
+  return request({
+    url: '/course/courseWatchLog/watchLogStatistics',
+    method: 'get',
+    params: query
+  })
+}

+ 62 - 0
src/api/course/qw/courseAnswerlogs.js

@@ -0,0 +1,62 @@
+import request from '@/utils/request'
+
+// 查询答题日志列表
+export function listLogs(query) {
+  return request({
+    url: '/qw/course/courseAnswerLog/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function myListLogs(query) {
+  return request({
+    url: '/qw/course/courseAnswerLog/myList',
+    method: 'get',
+    params: query
+  })
+}
+
+
+// 查询答题日志详细
+export function getLogs(logId) {
+  return request({
+    url: '/qw/course/courseAnswerLog/' + logId,
+    method: 'get'
+  })
+}
+
+// 新增答题日志
+export function addLogs(data) {
+  return request({
+    url: '/qw/course/courseAnswerLog',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改答题日志
+export function updateLogs(data) {
+  return request({
+    url: '/qw/course/courseAnswerLog',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除答题日志
+export function delLogs(logId) {
+  return request({
+    url: '/qw/course/courseAnswerLog/' + logId,
+    method: 'delete'
+  })
+}
+
+// 导出答题日志
+export function exportLogs(query) {
+  return request({
+    url: '/qw/course/courseAnswerLog/export',
+    method: 'get',
+    params: query
+  })
+}

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

@@ -0,0 +1,114 @@
+import request from '@/utils/request'
+
+// 查询短链课程看课记录列表
+export function listCourseWatchLog(query) {
+  return request({
+    url: '/qw/course/courseWatchLog/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function myListCourseWatchLog(query) {
+  return request({
+    url: '/qw/course/courseWatchLog/myList',
+    method: 'get',
+    params: query
+  })
+}
+export function statisticsList(query) {
+  return request({
+    url: '/qw/course/courseWatchLog/statisticsList',
+    method: 'get',
+    params: query
+  })
+}
+export function qwWatchLogStatisticsList(query) {
+  return request({
+    url: '/qw/course/courseWatchLog/qwWatchLogStatisticsList',
+    method: 'get',
+    params: query
+  })
+}
+export function myQwWatchLogStatisticsList(query) {
+  return request({
+    url: '/qw/course/courseWatchLog/myQwWatchLogStatisticsList',
+    method: 'get',
+    params: query
+  })
+}
+
+
+export function qwWatchLogAllStatisticsList(query) {
+  return request({
+    url: '/qw/course/courseWatchLog/qwWatchLogAllStatisticsList',
+    method: 'get',
+    params: query
+  })
+}
+export function myQwWatchLogAllStatisticsList(query) {
+  return request({
+    url: '/qw/course/courseWatchLog/myQwWatchLogAllStatisticsList',
+    method: 'get',
+    params: query
+  })
+}
+
+export function watchLogStatistics(query) {
+  return request({
+    url: '/qw/course/courseWatchLog/watchLogStatistics',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询短链课程看课记录详细
+export function getCourseWatchLog(logId) {
+  return request({
+    url: '/qw/course/courseWatchLog/' + logId,
+    method: 'get'
+  })
+}
+
+// 新增短链课程看课记录
+export function addCourseWatchLog(data) {
+  return request({
+    url: '/qw/course/courseWatchLog',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改短链课程看课记录
+export function updateCourseWatchLog(data) {
+  return request({
+    url: '/qw/course/courseWatchLog',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除短链课程看课记录
+export function delCourseWatchLog(logId) {
+  return request({
+    url: '/qw/course/courseWatchLog/' + logId,
+    method: 'delete'
+  })
+}
+
+// 导出短链课程看课记录
+export function exportCourseWatchLog(query) {
+  return request({
+    url: '/qw/course/courseWatchLog/export',
+    method: 'get',
+    params: query
+  })
+}
+
+export function watchLogStatisticsExport(query) {
+  return request({
+    url: '/qw/course/courseWatchLog/watchLogStatisticsExport',
+    method: 'get',
+    params: query
+  })
+}

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

@@ -0,0 +1,259 @@
+import request from '@/utils/request'
+
+// 查询企微员工列表
+export function staffListUser(query) {
+  return request({
+    url: '/qw/user/staffList',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询企微用户列表
+export function listUser(query) {
+  return request({
+    url: '/qw/user/list',
+    method: 'get',
+    params: query
+  })
+}
+// 查询企微用户列表
+export function userList(query) {
+  return request({
+    url: '/qw/user/userList',
+    method: 'get',
+    params: query
+  })
+}
+export function qwUserList(id) {
+  return request({
+    url: '/qw/user/qwUserList/' + id,
+    method: 'get',
+  })
+}
+export function getQwUserList(query) {
+  return request({
+    url: '/qw/user/getQwUserList',
+    method: 'get',
+    params: query
+  })
+}
+export function getMyQwUserList(query) {
+  return request({
+    url: '/qw/user/getMyQwUserList',
+    method: 'get',
+    params: query
+  })
+}
+export function getMyQwCompanyList(query) {
+  return request({
+    url: '/qw/user/getMyQwCompanyList',
+    method: 'get',
+    params: query
+  })
+}
+// 查询企微用户详细
+export function getQwUser(id) {
+  return request({
+    url: '/qw/user/' + id,
+    method: 'get'
+  })
+}
+//批量查询企微用户详细
+export function getQwUserByIds(ids) {
+  return request({
+    url: '/qw/user/getInfo/' + ids,
+    method: 'get'
+  })
+}
+
+// 新增企微用户
+export function addUser(data) {
+  return request({
+    url: '/qw/user',
+    method: 'post',
+    data: data
+  })
+}
+export function addQwUser(id) {
+  return request({
+    url: '/qw/user/sync/' + id,
+    method: 'post',
+  })
+}
+// 修改企微用户
+export function updateUser(data) {
+  return request({
+    url: '/qw/user',
+    method: 'put',
+    data: data
+  })
+}
+
+// 绑定Ai客服
+export function qwUserBindAi(data) {
+  return request({
+    url: '/qw/user/bindAi',
+    method: 'put',
+    data: data
+  })
+}
+
+//解绑AI客服
+export function relieveFastGptRoleById(id) {
+  return request({
+    url: '/qw/user/relieveFastGptRoleById/' + id,
+    method: 'get'
+  })
+}
+
+//绑定企微用户
+export function bindQwUser(data) {
+  return request({
+    url: '/qw/user/bindQwUser',
+    method: 'put',
+    data: data
+  })
+}
+
+//修改企微用户的欢迎语管理
+export function updateUserWeclome(data) {
+  return request({
+    url: '/qw/user/weclomeQwUser',
+    method: 'post',
+    data: data
+  })
+}
+
+
+
+// 删除企微用户
+export function delUser(id) {
+  return request({
+    url: '/qw/user/' + id,
+    method: 'delete'
+  })
+}
+
+// 导出企微用户
+export function exportUser(query) {
+  return request({
+    url: '/qw/user/export',
+    method: 'get',
+    params: query
+  })
+}
+
+
+/**
+ * 登录企业微信(发起登录)
+ */
+export function loginQwCode(data) {
+  return request({
+    url: '/qw/user/loginQwCode',
+    method: 'post',
+    data: data
+  })
+}
+/**
+ * 取redis里的登录二维码
+ */
+export function getQwCodeUrl(data) {
+  return request({
+    url: '/qw/user/getQwCodeUrl',
+    method: 'post',
+    data: data
+  })
+}
+/**
+ * 登录请求-刷新获取二维码
+ */
+export function loginQwCodeUrl(data) {
+  return request({
+    url: '/qw/user/loginQwCodeUrl',
+    method: 'post',
+    data: data
+  })
+}
+/**
+ * 登录企业微信(传输验证信息)
+ */
+export function loginQwCodeMsg(data) {
+  return request({
+    url: '/qw/user/loginQwCodeMsg',
+    method: 'post',
+    data: data
+  })
+}
+/**
+ * 退出企业微信
+ */
+export function logoutQwLogout(data) {
+  return request({
+    url: '/qw/user/logoutQwLogout',
+    method: 'post',
+    data: data
+  })
+}
+
+/**
+ * 查询登录状态
+ */
+export function getLoginQwStatus(data) {
+  return request({
+    url: '/qw/user/getLoginQwStatus',
+    method: 'post',
+    data: data
+  })
+}
+/**
+ * 企业微信员工账号 直接授权key
+ */
+export function handleAuthAppKey(data) {
+  return request({
+    url: '/qw/user/authAppKey',
+    method: 'post',
+    data: data
+  })
+}
+
+/**
+ * 企业微信员工账号 输入的授权key
+ */
+export function handleInputAuthAppKey(data) {
+  return request({
+    url: '/qw/user/handleInputAuthAppKey',
+    method: 'post',
+    data: data
+  })
+}
+
+/**
+* 企业微信员工账号 绑定 云主机
+*/
+export function qwBindCloudHost(appkey) {
+  return request({
+    url: '/qw/user/qwBindCloudHost/'+appkey,
+    method: 'get',
+  })
+}
+/**
+ * 企业微信员工账号 解除绑定 云主机
+ */
+export function qwUnbindCloudHost(appkey) {
+  return request({
+    url: '/qw/user/qwUnbindCloudHost/'+appkey,
+    method: 'get',
+  })
+}
+
+/**
+ * 获取云主机的账密
+ */
+export function selectCloudAP(data) {
+  return request({
+    url: '/qw/user/selectCloudAP',
+    method: 'post',
+    data: data
+  })
+}

+ 12 - 0
src/api/statistics/member.js

@@ -0,0 +1,12 @@
+import request from "@/utils/request"
+
+/**
+ * 获取会员统计数据
+ */
+export function dailyData(query) {
+  return request({
+    url: '/stats/member/dailyData',
+    method: 'get',
+    params: query
+  })
+}

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

@@ -0,0 +1,311 @@
+import request from "@/utils/request";
+
+/**
+ * 分析概览
+ * @param param
+ * @returns {AxiosPromise}
+ */
+export function analysisPreview(param) {
+  const safeParam = JSON.parse(JSON.stringify(param));
+
+  if (safeParam.startTime) {
+    const startDate = new Date(safeParam.startTime);
+    if (!isNaN(startDate.getTime())) {
+      safeParam.startTime = `${safeParam.startTime.trim()} 00:00:00`;
+    }
+  }
+
+  if (safeParam.endTime) {
+    const endDate = new Date(safeParam.endTime);
+    if (!isNaN(endDate.getTime())) {
+      safeParam.endTime = `${safeParam.endTime.trim()} 23:59:59`;
+    }
+  }
+  return request({
+    url: '/index/statistics/analysisPreview',
+    method: 'post',
+    data: safeParam
+  })
+}
+
+/**
+ * 数据概览
+ * @param query
+ * @returns {AxiosPromise}
+ */
+export function dealerAggregated() {
+  return request({
+    url: '/index/statistics/dealerAggregated',
+    method: 'get',
+    params: {}
+  })
+}
+
+/**
+ * 消费余额
+ * @returns {AxiosPromise}
+ */
+export function rechargeComsumption(){
+  return request({
+    url: '/index/statistics/rechargeComsumption',
+    method: 'get',
+    params: {}
+  })
+}
+/**
+ * 获取统计流量
+ * @returns {AxiosPromise}
+ */
+export function trafficLog(){
+  return request({
+    url: '/index/statistics/trafficLog',
+    method: 'get',
+    params: {}
+  })
+}
+
+
+
+/**
+ * 数据概览
+ * @param query
+ * @returns {AxiosPromise}
+ */
+export function smsBalance() {
+  return request({
+    url: '/index/statistics/smsBalance',
+    method: 'get',
+    params: {}
+  })
+}
+
+/**
+ * 授权信息
+ * @returns {*}
+ */
+export function authorizationInfo() {
+  return request({
+    url: '/index/statistics/authorizationInfo',
+    method: 'get',
+    params: {}
+  })
+}
+
+/**
+ * 课程观看top10
+ * @returns {*}
+ */
+// export function watchCourseTopTen(param){
+//   const safeParam = JSON.parse(JSON.stringify(param));
+//
+//   if (safeParam.startTime) {
+//     const startDate = new Date(safeParam.startTime);
+//     if (!isNaN(startDate.getTime())) {
+//       safeParam.startTime = `${safeParam.startTime.trim()} 00:00:00`;
+//     }
+//   }
+//
+//   if (safeParam.endTime) {
+//     const endDate = new Date(safeParam.endTime);
+//     if (!isNaN(endDate.getTime())) {
+//       safeParam.endTime = `${safeParam.endTime.trim()} 23:59:59`;
+//     }
+//   }
+//   return request({
+//     url: '/index/statistics/watchCourseTopTen',
+//     method: 'post',
+//     data: safeParam
+//   })
+// }
+
+/**
+ * 课程观看趋势
+ * @returns {*}
+ */
+export function watchCourseTrend(param){
+  const safeParam = JSON.parse(JSON.stringify(param));
+
+  if (safeParam.startTime) {
+    const startDate = new Date(safeParam.startTime);
+    if (!isNaN(startDate.getTime())) {
+      safeParam.startTime = `${safeParam.startTime.trim()} 00:00:00`;
+    }
+  }
+
+  if (safeParam.endTime) {
+    const endDate = new Date(safeParam.endTime);
+    if (!isNaN(endDate.getTime())) {
+      safeParam.endTime = `${safeParam.endTime.trim()} 23:59:59`;
+    }
+  }
+  return request({
+    url: '/index/statistics/watchCourseTrend',
+    method: 'post',
+    data: safeParam
+  })
+}
+
+/**
+ * 课程观看趋势
+ * @returns {*}
+ */
+export function watchEndPlayTrend(param){
+  const safeParam = JSON.parse(JSON.stringify(param));
+
+  if (safeParam.startTime) {
+    const startDate = new Date(safeParam.startTime);
+    if (!isNaN(startDate.getTime())) {
+      safeParam.startTime = `${safeParam.startTime.trim()} 00:00:00`;
+    }
+  }
+
+  if (safeParam.endTime) {
+    const endDate = new Date(safeParam.endTime);
+    if (!isNaN(endDate.getTime())) {
+      safeParam.endTime = `${safeParam.endTime.trim()} 23:59:59`;
+    }
+  }
+  return request({
+    url: '/index/statistics/watchEndPlayTrend',
+    method: 'post',
+    data: safeParam
+  })
+}
+
+/**
+ * 经销商会员观看
+ * @param param
+ * @returns {*}
+ */
+export function deaMemberTopTen(param){
+  const safeParam = JSON.parse(JSON.stringify(param));
+
+  if (safeParam.startTime) {
+    const startDate = new Date(safeParam.startTime);
+    if (!isNaN(startDate.getTime())) {
+      safeParam.startTime = `${safeParam.startTime.trim()} 00:00:00`;
+    }
+  }
+
+  if (safeParam.endTime) {
+    const endDate = new Date(safeParam.endTime);
+    if (!isNaN(endDate.getTime())) {
+      safeParam.endTime = `${safeParam.endTime.trim()} 23:59:59`;
+    }
+  }
+  return request({
+    url: '/index/statistics/deaMemberTopTen',
+    method: 'post',
+    data: safeParam
+  })
+}
+
+/**
+ * 课程观看TOP10
+ * @param param
+ * @returns {AxiosPromise}
+ */
+export function watchCourseTopTen(param){
+  const safeParam = JSON.parse(JSON.stringify(param));
+
+  if (safeParam.startTime) {
+    const startDate = new Date(safeParam.startTime);
+    if (!isNaN(startDate.getTime())) {
+      safeParam.startTime = `${safeParam.startTime.trim()} 00:00:00`;
+    }
+  }
+
+  if (safeParam.endTime) {
+    const endDate = new Date(safeParam.endTime);
+    if (!isNaN(endDate.getTime())) {
+      safeParam.endTime = `${safeParam.endTime.trim()} 23:59:59`;
+    }
+  }
+  return request({
+    url: '/index/statistics/watchCourseTopTen',
+    method: 'post',
+    data: safeParam
+  })
+}
+
+
+/**
+ * 答题红包金额TOP10
+ * @returns {*}
+ */
+export function rewardMoneyTopTen(param){
+  const safeParam = JSON.parse(JSON.stringify(param));
+
+  if (safeParam.startTime) {
+    const startDate = new Date(safeParam.startTime);
+    if (!isNaN(startDate.getTime())) {
+      safeParam.startTime = `${safeParam.startTime.trim()} 00:00:00`;
+    }
+  }
+
+  if (safeParam.endTime) {
+    const endDate = new Date(safeParam.endTime);
+    if (!isNaN(endDate.getTime())) {
+      safeParam.endTime = `${safeParam.endTime.trim()} 23:59:59`;
+    }
+  }
+  return request({
+    url: '/index/statistics/rewardMoneyTopTen',
+    method: 'post',
+    data: safeParam
+  })
+}
+
+/**
+ * 答题红包金额趋势
+ * @returns {*}
+ * @param param
+ * @returns {*}
+ */
+export function rewardMoneyTrend(param){
+  const safeParam = JSON.parse(JSON.stringify(param));
+
+  if (safeParam.startTime) {
+    const startDate = new Date(safeParam.startTime);
+    if (!isNaN(startDate.getTime())) {
+      safeParam.startTime = `${safeParam.startTime.trim()} 00:00:00`;
+    }
+  }
+
+  if (safeParam.endTime) {
+    const endDate = new Date(safeParam.endTime);
+    if (!isNaN(endDate.getTime())) {
+      safeParam.endTime = `${safeParam.endTime.trim()} 23:59:59`;
+    }
+  }
+  return request({
+    url: '/index/statistics/rewardMoneyTrend',
+    method: 'post',
+    data: safeParam
+  })
+}
+
+
+/**
+ * 获取当月订单数
+ * @returns {*}
+ */
+export function thisMonthOrderCount(){
+  return request({
+    url: '/index/statistics/thisMonthOrderCount',
+    method: 'get',
+    params: {}
+  })
+}
+
+/**
+ * 获取当月收款数
+ * @returns {*}
+ */
+export function thisMonthRecvCount(){
+  return request({
+    url: '/index/statistics/thisMonthRecvCount',
+    method: 'get',
+    params: {}
+  })
+}

+ 103 - 0
src/api/store/user.js

@@ -0,0 +1,103 @@
+import request from '@/utils/request'
+
+// 查询用户列表
+export function listUser(query) {
+  return request({
+    url: '/store/user/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询用户详细
+export function getUser(userId) {
+  return request({
+    url: '/store/user/' + userId,
+    method: 'get'
+  })
+}
+
+// 查询用户详细
+export function queryUserVo(userId) {
+  return request({
+    url: '/store/user/queryvo/' + userId,
+    method: 'get'
+  })
+}
+
+// 新增用户
+export function addUser(data) {
+  return request({
+    url: '/store/user',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改用户
+export function updateUser(data) {
+  return request({
+    url: '/store/user',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除用户
+export function delUser(userId) {
+  return request({
+    url: '/store/user/' + userId,
+    method: 'delete'
+  })
+}
+
+// 导出用户
+export function exportUser(query) {
+  return request({
+    url: '/store/user/export',
+    method: 'get',
+    params: query
+  })
+}
+
+export function getUserList(query) {
+  return request({
+    url: '/store/user/getUserList',
+    method: 'get',
+    params: query
+  })
+}
+
+export function listBySearch(query) {
+  return request({
+    url: '/store/user/listBySearch',
+    method: 'get',
+    params: query
+  })
+}
+
+export function getAllUserListLimit(query) {
+  return request({
+    url: '/company/companyUser/getAllUserListLimit',
+    method: 'get',
+    params: query
+  })
+}
+
+// 获取小黑屋用户列表
+export function darkRoomList(query) {
+  return request({
+    url: '/store/user/darkRoomList',
+    method: 'get',
+    params: query
+  })
+}
+
+// 批量解禁
+export function enabledUsers(data) {
+  return request({
+    url: '/store/user/enabledUsers',
+    method: 'post',
+    data: data
+  })
+}

+ 53 - 0
src/api/user/approval.js

@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 查询客户转移审批列表
+export function listApproval(query) {
+  return request({
+    url: '/system/approval/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询客户转移审批详细
+export function getApproval(id) {
+  return request({
+    url: '/system/approval/' + id,
+    method: 'get'
+  })
+}
+
+// 新增客户转移审批
+export function addApproval(data) {
+  return request({
+    url: '/system/approval',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改客户转移审批
+export function updateApproval(data) {
+  return request({
+    url: '/system/approval',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除客户转移审批
+export function delApproval(id) {
+  return request({
+    url: '/system/approval/' + id,
+    method: 'delete'
+  })
+}
+
+// 导出客户转移审批
+export function exportApproval(query) {
+  return request({
+    url: '/system/approval/export',
+    method: 'get',
+    params: query
+  })
+}

+ 82 - 0
src/api/users/user.js

@@ -0,0 +1,82 @@
+import request from '@/utils/request'
+
+export function list(query) {
+  return request({
+    url: '/users/user/list',
+    method: 'get',
+    params: query
+  })
+}
+export function myList(query) {
+  return request({
+    url: '/users/user/myList',
+    method: 'get',
+    params: query
+  })
+}
+export function getUser(userId) {
+  return request({
+    url: '/users/user/' + userId,
+    method: 'get'
+  })
+}
+
+export function getUserList(query) {
+  return request({
+    url: '/users/user/getUserList',
+    method: 'get',
+    params: query
+  })
+}
+export function getFsUserList(query) {
+  return request({
+    url: '/store/user/getUserListLimit',
+    method: 'get',
+    params: query
+  })
+}
+// 新增用户
+export function addUser(data) {
+  return request({
+    url: '/users/user',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改用户
+export function updateUser(data) {
+  return request({
+    url: '/users/user',
+    method: 'put',
+    data: data
+  })
+}
+
+// 列出当前公司的客户
+export function listUser(data) {
+  return request({
+    url: '/fsuser/user/list',
+    method: 'get',
+    params: data
+  })
+}
+
+// 转移客户
+export function transferUser(data) {
+  return request({
+    url: '/fsuser/user/transfer',
+    method: 'post',
+    data: data
+  })
+}
+
+// 查询会员选项列表
+export function getUserListLikeName(query) {
+  return request({
+    url: '/user/fsUser/getUserListLikeName',
+    method: 'get',
+    params: query
+  })
+}
+

+ 0 - 5
src/components/MinimizableDialog/index.vue

@@ -7,7 +7,6 @@
       :title="title"
       v-bind="$attrs"
       v-on="$listeners"
-      :before-close="handleClose"
     >
       <template #title>
         <span>{{ title }}</span>
@@ -99,10 +98,6 @@ export default {
       this.visibleSync = true;
       this.$emit("restore");
     },
-    handleClose(done) {
-      this.$emit("close");
-      done();
-    }
   }
 };
 </script>

+ 3 - 0
src/components/VideoUpload/index.vue

@@ -389,6 +389,9 @@ export default {
       // 设置预览URL
       this.$emit("update:videoUrl", this.selectedVideo.videoUrl);
 
+      // 题目
+      this.$emit("selectProjects", this.selectedVideo.projectIds)
+
       this.libraryOpen = false;
     },
     /** 取消视频选择 */

+ 2 - 1
src/utils/obs.js

@@ -6,7 +6,8 @@ export const uploadToOBS = async(file,progressCallback,type) =>  {
         const obsClient = new ObsClient({
           access_key_id: process.env.VUE_APP_OBS_ACCESS_KEY_ID,
           secret_access_key: process.env.VUE_APP_OBS_SECRET_ACCESS_KEY,
-          server: process.env.VUE_APP_OBS_SERVER
+          server: process.env.VUE_APP_OBS_SERVER,
+          timeout: 1200,
         });
         let fileName = file.name || ""
         const upload_file_name = new Date().getTime() + '.' + fileName.split(".")[fileName.split(".").length - 1];

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

@@ -192,6 +192,7 @@
           :uploadType.sync="form.uploadType"
           @video-duration="handleVideoDuration"
           @change="handleVideoChange"
+          @selectProjects="handleSelectProjects"
           ref="videoUpload"
           append-to-body
         />
@@ -362,6 +363,7 @@ import {
 import QuestionBank from "@/views/course/courseQuestionBank/QuestionBank.vue";
 import VideoUpload from "@/components/VideoUpload/index.vue";
 import { listVideoResource } from '@/api/course/videoResource';
+import { getByIds } from '@/api/course/courseQuestionBank'
 
 export default {
   name: "userCourseCatalog",
@@ -554,6 +556,25 @@ export default {
       }
       // console.log("选择的video=======>>>>>>>",this.videoUrl)
     },
+    // 视频库课题
+    handleSelectProjects(projectIds) {
+      this.form.questionBankList = []
+      if (!projectIds || projectIds.length === 0 || this.isPrivate === 0) {
+        return
+      }
+
+      const params = {ids: projectIds}
+      getByIds(params).then(response => {
+        if (response.code === 200) {
+          response.data.forEach(item => {
+            let isExist = this.form.questionBankList.some(q => q.id === item.id)
+            if (!isExist) {
+              this.form.questionBankList.push(item)
+            }
+          });
+        }
+      })
+    },
     handleVideoDuration(duration) {
       this.form.duration = duration;
     },
@@ -811,6 +832,14 @@ export default {
       });
     },
     batchVideoSave(){
+      if (this.addBatchData.select.length===0){
+        this.$message({
+          message: '请选择视频!!',
+          type: 'warning'
+        });
+        return
+      }
+      console.log(this.addBatchData.select)
       this.addBatchData.form.ids = this.addBatchData.select;
       batchSaveVideo(this.addBatchData.form).then(response => {
         this.addBatchData.open = false;

+ 1 - 1
src/views/course/courseQuestionBank/index.vue

@@ -118,7 +118,7 @@
           icon="el-icon-upload2"
           size="mini"
           @click="handleImport"
-          v-hasPermi="['course:courseQuestionBank:import']"
+          v-hasPermi="['course:courseQuestionBank:importData']"
         >导入</el-button>
       </el-col>
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>

+ 43 - 0
src/views/course/courseUserStatistics/courseUserStatisticsTabIndex.vue

@@ -0,0 +1,43 @@
+<!-- TabComponent.vue -->
+<template>
+  <div class="tab-container">
+    <el-tabs type="card" style="background-color: white">
+      <el-tab-pane label="会员">
+        <member-view/>
+      </el-tab-pane>
+      <el-tab-pane label="企微">
+        <qw-view/>
+      </el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+
+<script>
+import MemberView from './index.vue';
+import QwView from './qw/index.vue';
+
+
+export default {
+  name: 'TabComponent',
+  components: {
+    MemberView,
+    QwView
+  },
+  data() {
+    return {
+    };
+  }
+};
+</script>
+
+<style scoped>
+.tab-container {
+  width: 100%;
+  height: 100%;
+}
+.app-container{
+  padding: 15px !important;
+  margin: 0px !important;
+  background-color: #fff !important;
+}
+</style>

+ 432 - 0
src/views/course/courseUserStatistics/index.vue

@@ -0,0 +1,432 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="项目" prop="courseId">
+        <el-select filterable  v-model="queryParams.project" placeholder="请选择项目"  clearable size="small">
+          <el-option
+            v-for="dict in projectLists"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="parseInt(dict.dictValue)"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="课程" prop="courseId">
+        <el-select filterable  v-model="queryParams.courseId" placeholder="请选择课程"  clearable size="small" @change="courseChange(queryParams.courseId)">
+          <el-option
+            v-for="dict in courseLists"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="parseInt(dict.dictValue)"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="小节" prop="videoId">
+        <el-select filterable  v-model="queryParams.videoId" placeholder="请选择小节"  clearable size="small">
+          <el-option
+            v-for="dict in videoList"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="parseInt(dict.dictValue)"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="公司名称" prop="companyId">
+        <el-select style="width: 220px" filterable v-model="queryParams.companyId" placeholder="请选择公司名" clearable size="small"  @change="handleSeller">
+          <el-option
+            v-for="item in companys"
+            :key="item.companyId"
+            :label="item.companyName"
+            :value="item.companyId"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="销售" prop="nickName" v-if="queryParams.companyId">
+        <el-select v-model="queryParams.companyUserId" remote placeholder="请选择" filterable clearable  style="width: 100%;" @keyup.enter.native="handleQuery">
+          <el-option
+            v-for="dict in companyUserList"
+            :key="`${dict.nickName} - ${dict.userName}`"
+            :label="`${dict.nickName} - ${dict.userName}`"
+            :value="dict.userId">
+          </el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="添加时间" prop="createTime">
+        <el-date-picker v-model="createTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="change"></el-date-picker>
+      </el-form-item>
+
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-table border v-loading="loading" :data="courseWatchLogList" @selection-change="handleSelectionChange"  show-summary height="600">
+      <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="销售名称" align="center" prop="companyUserName" />
+      <el-table-column label="项目" align="center" prop="projectName" />
+      <el-table-column label="课程" align="center" prop="courseName" />
+      <el-table-column label="小节" align="center" prop="videoName" />
+            <!-- 发课时间 -->
+            <el-table-column label="进线时间" align="center" prop="createTime"/>
+            <!-- 进线数 -->
+            <el-table-column label="进线数" align="center" prop="line" />
+
+            <!-- 先导课上线 -->
+            <el-table-column label="先导课上线" align="center" prop="firstOnline">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.firstOnline }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.firstOnline / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 先导课完课 -->
+            <el-table-column label="先导课完课" align="center" prop="firstOver">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.firstOver }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.firstOver / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 首日上线 -->
+            <el-table-column label="首日上线" align="center" prop="d1Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d1Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d1Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 首日完课 -->
+            <el-table-column label="首日完课" align="center" prop="d1Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d1Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d1Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 综合报名数 -->
+<!--            <el-table-column label="综合报名数" align="center" prop="sign">-->
+<!--                <template slot-scope="scope">-->
+<!--                    <span>{{ scope.row.sign }}</span>-->
+<!--                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.sign / scope.row.line) * 100).toFixed(2) }}%)</span>-->
+<!--                </template>-->
+<!--            </el-table-column>-->
+
+            <!-- 互动数 -->
+            <el-table-column label="互动数" align="center" prop="interact">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.interact }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.interact / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- A级客户 -->
+            <el-table-column label="A级客户" align="center" prop="a">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.a }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.a / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- B级客户 -->
+            <el-table-column label="B级客户" align="center" prop="b">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.b }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.b / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- C级客户 -->
+            <el-table-column label="C级客户" align="center" prop="c">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.c }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.c / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- D级客户 -->
+            <el-table-column label="D级客户" align="center" prop="d">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 流失数 -->
+<!--            <el-table-column label="流失数" align="center" prop="los">-->
+<!--                <template slot-scope="scope">-->
+<!--                    <span>{{ scope.row.los }}</span>-->
+<!--                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.los / scope.row.line) * 100).toFixed(2) }}%)</span>-->
+<!--                </template>-->
+<!--            </el-table-column>-->
+
+            <!-- 删除数 -->
+<!--            <el-table-column label="删除数" align="center" prop="del">-->
+<!--                <template slot-scope="scope">-->
+<!--                    <span>{{ scope.row.del }}</span>-->
+<!--                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.del / scope.row.line) * 100).toFixed(2) }}%)</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 { listCourseWatchLog, getCourseWatchLog, delCourseWatchLog, addCourseWatchLog, updateCourseWatchLog, exportCourseWatchLog,statisticsList,qwWatchLogStatisticsList } from "@/api/course/courseWatchLog";
+import { courseList,videoList } from '@/api/course/courseRedPacketLog'
+import {getUserList} from "@/api/company/companyUser";
+import {getCompanyList} from "@/api/company/company";
+export default {
+  name: "CourseWatchLog",
+  data() {
+    return {
+      companys: [],
+      activeName:"00",
+      createTime:null,
+      courseLists:[],
+      videoList:[],
+      logTypeOptions:[],
+      companyUserList: [],
+      projectLists: [],
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 短链课程看课记录表格数据
+      courseWatchLogList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        userId: null,
+        nickName: null,
+        videoId: null,
+        logType: null,
+        qwExternalContactId: null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null,
+        sTime:null,
+        eTime:null,
+        scheduleStartTime: null,
+        scheduleEndTime: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      },
+      scheduleTime: null,
+    };
+  },
+  created() {
+    getCompanyList().then(response => {
+      this.companys = response.data;
+      if(this.companys!=null&&this.companys.length>0){
+      }
+    });
+    courseList().then(response => {
+      this.courseLists = response.list;
+    });
+    this.getList();
+    this.getDicts("sys_course_watch_log_type").then(response => {
+      this.logTypeOptions = response.data;
+    });
+    this.getDicts("sys_course_project").then(response => {
+      this.projectLists = response.data;
+    })
+  },
+  methods: {
+    handleSeller(){
+      if(this.queryParams.companyId != null) {
+        getUserList(this.queryParams.companyId).then(res=>{
+          if(res.code === 200) {
+            this.companyUserList = res.data
+          }
+        })
+      }
+    },
+    courseChange(row){
+      this.queryParams.videoId=null;
+      if(row === ''){
+        this.videoList=[];
+        return
+      }
+      videoList(row).then(response => {
+        this.videoList=response.list
+      });
+    },
+    change() {
+      if (this.createTime != null) {
+        this.queryParams.sTime = this.createTime[0];
+        this.queryParams.eTime = this.createTime[1];
+      } else {
+        this.queryParams.sTime = null;
+        this.queryParams.eTime = null;
+      }
+    },
+    handleClickX(tab,event){
+      this.activeName=tab.name;
+      if(tab.name=="00"){
+        this.queryParams.logType=null;
+      }else{
+        this.queryParams.logType=tab.name;
+      }
+      this.getList()
+    },
+    /** 查询短链课程看课记录列表 */
+    getList() {
+      this.loading = true;
+      qwWatchLogStatisticsList(this.queryParams).then(response => {
+        this.courseWatchLogList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        logId: null,
+        userId: null,
+        videoId: null,
+        logType: null,
+        createTime: null,
+        updateTime: null,
+        qwExternalContactId: null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.createTime = null;
+      this.scheduleTime = null;
+      this.queryParams.sTime = null;
+      this.queryParams.eTime = null;
+      this.queryParams.scheduleStartTime = null;
+      this.queryParams.scheduleEndTime = null;
+      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
+      getCourseWatchLog(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) {
+            updateCourseWatchLog(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addCourseWatchLog(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 delCourseWatchLog(logIds);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有短链课程看课记录数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportCourseWatchLog(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    },
+    handleScheduleTimeChange(val) {
+      if (val) {
+        this.queryParams.scheduleStartTime = val[0];
+        this.queryParams.scheduleEndTime = val[1];
+      } else {
+        this.queryParams.scheduleStartTime = null;
+        this.queryParams.scheduleEndTime = null;
+      }
+    },
+  }
+};
+</script>

+ 410 - 0
src/views/course/courseUserStatistics/my.vue

@@ -0,0 +1,410 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="项目" prop="courseId">
+        <el-select filterable  v-model="queryParams.project" placeholder="请选择项目"  clearable size="small">
+          <el-option
+            v-for="dict in projectLists"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="parseInt(dict.dictValue)"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="课程" prop="courseId">
+        <el-select filterable  v-model="queryParams.courseId" placeholder="请选择课程"  clearable size="small" @change="courseChange(queryParams.courseId)">
+          <el-option
+            v-for="dict in courseLists"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="parseInt(dict.dictValue)"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="小节" prop="videoId">
+        <el-select filterable  v-model="queryParams.videoId" placeholder="请选择小节"  clearable size="small">
+          <el-option
+            v-for="dict in videoList"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="parseInt(dict.dictValue)"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="添加时间" prop="createTime">
+        <el-date-picker v-model="createTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="change"></el-date-picker>
+      </el-form-item>
+      <el-form-item label="公司名称" prop="companyId">
+        <el-select style="width: 220px" filterable v-model="queryParams.companyId" placeholder="请选择公司名" clearable size="small">
+          <el-option
+            v-for="item in companys"
+            :key="item.companyId"
+            :label="item.companyName"
+            :value="item.companyId"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-table border v-loading="loading" :data="courseWatchLogList" @selection-change="handleSelectionChange"  show-summary height="600">
+      <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="销售名称" align="center" prop="companyUserName" />
+        <el-table-column label="项目" align="center" prop="projectName" />
+        <el-table-column label="课程" align="center" prop="courseName" />
+        <el-table-column label="小节" align="center" prop="videoName" />
+
+            <!-- 发课时间 -->
+            <el-table-column label="进线时间" align="center" prop="createTime"/>
+            <!-- 进线数 -->
+            <el-table-column label="进线数" align="center" prop="line" />
+
+            <!-- 先导课上线 -->
+            <el-table-column label="先导课上线" align="center" prop="firstOnline">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.firstOnline }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.firstOnline / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 先导课完课 -->
+            <el-table-column label="先导课完课" align="center" prop="firstOver">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.firstOver }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.firstOver / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 首日上线 -->
+            <el-table-column label="首日上线" align="center" prop="d1Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d1Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d1Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 首日完课 -->
+            <el-table-column label="首日完课" align="center" prop="d1Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d1Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d1Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 综合报名数 -->
+            <el-table-column label="综合报名数" align="center" prop="sign">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.sign }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.sign / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 互动数 -->
+            <el-table-column label="互动数" align="center" prop="interact">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.interact }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.interact / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- A级客户 -->
+            <el-table-column label="A级客户" align="center" prop="a">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.a }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.a / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- B级客户 -->
+            <el-table-column label="B级客户" align="center" prop="b">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.b }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.b / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- C级客户 -->
+            <el-table-column label="C级客户" align="center" prop="c">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.c }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.c / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- D级客户 -->
+            <el-table-column label="D级客户" align="center" prop="d">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 流失数 -->
+            <el-table-column label="流失数" align="center" prop="los">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.los }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.los / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 删除数 -->
+            <el-table-column label="删除数" align="center" prop="del">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.del }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.del / scope.row.line) * 100).toFixed(2) }}%)</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 { listCourseWatchLog, getCourseWatchLog, delCourseWatchLog, addCourseWatchLog, updateCourseWatchLog, exportCourseWatchLog,statisticsList,qwWatchLogStatisticsList,myQwWatchLogStatisticsList } from "@/api/course/courseWatchLog";
+import { courseList,videoList } from '@/api/course/courseRedPacketLog'
+import {getCompanyList} from "@/api/company/company";
+export default {
+  name: "CourseWatchLog",
+  data() {
+    return {
+      companys:[],
+      activeName:"00",
+      createTime:null,
+      courseLists:[],
+      videoList:[],
+      logTypeOptions:[],
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 短链课程看课记录表格数据
+      courseWatchLogList: [],
+      projectLists: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        userId: null,
+        nickName: null,
+        videoId: null,
+        logType: null,
+        qwExternalContactId: null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null,
+        sTime:null,
+        eTime:null,
+        scheduleStartTime: null,
+        scheduleEndTime: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      },
+      scheduleTime: null,
+    };
+  },
+  created() {
+    getCompanyList().then(response => {
+      this.companys = response.data;
+
+    });
+    courseList().then(response => {
+      this.courseLists = response.list;
+    });
+    this.getList();
+    this.getDicts("sys_course_watch_log_type").then(response => {
+      this.logTypeOptions = response.data;
+    });
+    this.getDicts("sys_course_project").then(response => {
+      this.projectLists = response.data;
+    })
+  },
+  methods: {
+    courseChange(row){
+      this.queryParams.videoId=null;
+      if(row === ''){
+        this.videoList=[];
+        return
+      }
+      videoList(row).then(response => {
+        this.videoList=response.list
+      });
+    },
+    change() {
+      if (this.createTime != null) {
+        this.queryParams.sTime = this.createTime[0];
+        this.queryParams.eTime = this.createTime[1];
+      } else {
+        this.queryParams.sTime = null;
+        this.queryParams.eTime = null;
+      }
+    },
+    handleClickX(tab,event){
+      this.activeName=tab.name;
+      if(tab.name=="00"){
+        this.queryParams.logType=null;
+      }else{
+        this.queryParams.logType=tab.name;
+      }
+      this.getList()
+    },
+    /** 查询短链课程看课记录列表 */
+    getList() {
+      this.loading = true;
+      myQwWatchLogStatisticsList(this.queryParams).then(response => {
+        this.courseWatchLogList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        logId: null,
+        userId: null,
+        videoId: null,
+        logType: null,
+        createTime: null,
+        updateTime: null,
+        qwExternalContactId: null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.createTime = null;
+      this.scheduleTime = null;
+      this.queryParams.sTime = null;
+      this.queryParams.eTime = null;
+      this.queryParams.scheduleStartTime = null;
+      this.queryParams.scheduleEndTime = null;
+      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
+      getCourseWatchLog(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) {
+            updateCourseWatchLog(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addCourseWatchLog(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 delCourseWatchLog(logIds);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有短链课程看课记录数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportCourseWatchLog(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    },
+    handleScheduleTimeChange(val) {
+      if (val) {
+        this.queryParams.scheduleStartTime = val[0];
+        this.queryParams.scheduleEndTime = val[1];
+      } else {
+        this.queryParams.scheduleStartTime = null;
+        this.queryParams.scheduleEndTime = null;
+      }
+    },
+  }
+};
+</script>

+ 711 - 0
src/views/course/courseUserStatistics/myStatistics.vue

@@ -0,0 +1,711 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="项目" prop="courseId">
+        <el-select filterable  v-model="queryParams.project" placeholder="请选择项目"  clearable size="small">
+          <el-option
+            v-for="dict in projectLists"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="parseInt(dict.dictValue)"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="课程" prop="courseId">
+        <el-select filterable  v-model="queryParams.courseId" placeholder="请选择课程"  clearable size="small" @change="courseChange(queryParams.courseId)">
+          <el-option
+            v-for="dict in courseLists"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="parseInt(dict.dictValue)"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="小节" prop="videoId">
+        <el-select filterable  v-model="queryParams.videoId" placeholder="请选择小节"  clearable size="small">
+          <el-option
+            v-for="dict in videoList"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="parseInt(dict.dictValue)"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="添加时间" prop="createTime">
+        <el-date-picker v-model="createTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="change"></el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-table border v-loading="loading" :data="courseWatchLogList" @selection-change="handleSelectionChange"  show-summary height="600">
+      <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="员工名称" align="center" prop="qwUserName" />
+
+        <el-table-column label="销售名称" align="center" prop="qwUserName" />
+        <el-table-column label="项目" align="center" prop="projectName" />
+        <el-table-column label="课程" align="center" prop="courseName" />
+        <el-table-column label="小节" align="center" prop="videoName" />
+            <!-- 发课时间 -->
+            <el-table-column label="进线时间" align="center" prop="createTime"/>
+            <!-- 进线数 -->
+            <el-table-column label="进线数" align="center" prop="line" />
+
+            <!-- 先导课上线 -->
+            <el-table-column label="先导上线" align="center" prop="firstOnline">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.firstOnline }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.firstOnline / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 先导课完课 -->
+            <el-table-column label="先导完课" align="center" prop="firstOver">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.firstOver }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.firstOver / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+
+            <el-table-column label="D1上线" align="center" prop="d1Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d1Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d1Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D1完课" align="center" prop="d1Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d1Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d1Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D2上线" align="center" prop="d2Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d2Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d2Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D2完课" align="center" prop="d2Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d2Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d2Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D3上线" align="center" prop="d3Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d3Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d3Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D3完课" align="center" prop="d3Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d3Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d3Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D4上线" align="center" prop="d4Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d4Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d4Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D4完课" align="center" prop="d4Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d4Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d4Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D5上线" align="center" prop="d5Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d5Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d5Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D5完课" align="center" prop="d5Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d5Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d5Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D6上线" align="center" prop="d6Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d6Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d6Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D6完课" align="center" prop="d6Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d6Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d6Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D7上线" align="center" prop="d7Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d7Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d7Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D7完课" align="center" prop="d7Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d7Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d7Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D8上线" align="center" prop="d8Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d8Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d8Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D8完课" align="center" prop="d8Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d8Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d8Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D9上线" align="center" prop="d9Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d9Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d9Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D9完课" align="center" prop="d9Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d9Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d9Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D10上线" align="center" prop="d10Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d10Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d10Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D10完课" align="center" prop="d10Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d10Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d10Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D11上线" align="center" prop="d11Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d11Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d11Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D11完课" align="center" prop="d11Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d11Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d11Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D12上线" align="center" prop="d12Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d12Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d12Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D12完课" align="center" prop="d12Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d12Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d12Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D13上线" align="center" prop="d13Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d13Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d13Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D13完课" align="center" prop="d13Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d13Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d13Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D14上线" align="center" prop="d14Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d14Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d14Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D14完课" align="center" prop="d14Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d14Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d14Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D15上线" align="center" prop="d15Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d15Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d15Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D15完课" align="center" prop="d15Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d15Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d15Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D16上线" align="center" prop="d16Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d16Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d16Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D16完课" align="center" prop="d16Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d16Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d16Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D17上线" align="center" prop="d17Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d17Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d17Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D17完课" align="center" prop="d17Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d17Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d17Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D18上线" align="center" prop="d18Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d18Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d18Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D18完课" align="center" prop="d18Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d18Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d18Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D19上线" align="center" prop="d19Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d19Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d19Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D19完课" align="center" prop="d19Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d19Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d19Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D20上线" align="center" prop="d20Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d20Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d20Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D20完课" align="center" prop="d20Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d20Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d20Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D21上线" align="center" prop="d21Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d21Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d21Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D21完课" align="center" prop="d21Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d21Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d21Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D22上线" align="center" prop="d22Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d22Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d22Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D22完课" align="center" prop="d22Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d22Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d22Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D23上线" align="center" prop="d23Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d23Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d23Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D23完课" align="center" prop="d23Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d23Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d23Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D24上线" align="center" prop="d24Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d24Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d24Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D24完课" align="center" prop="d24Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d24Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d24Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D25上线" align="center" prop="d25Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d25Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d25Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D25完课" align="center" prop="d25Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d25Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d25Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D26上线" align="center" prop="d26Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d26Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d26Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D26完课" align="center" prop="d26Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d26Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d26Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D27上线" align="center" prop="d27Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d27Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d27Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D27完课" align="center" prop="d27Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d27Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d27Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D28上线" align="center" prop="d28Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d28Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d28Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D28完课" align="center" prop="d28Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d28Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d28Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D29上线" align="center" prop="d29Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d29Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d29Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D29完课" align="center" prop="d29Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d29Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d29Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D30上线" align="center" prop="d30Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d30Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d30Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D30完课" align="center" prop="d30Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d30Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d30Over / scope.row.line) * 100).toFixed(2) }}%)</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 { listCourseWatchLog, getCourseWatchLog, delCourseWatchLog, addCourseWatchLog, updateCourseWatchLog, exportCourseWatchLog,statisticsList,qwWatchLogStatisticsList,qwWatchLogAllStatisticsList,myQwWatchLogAllStatisticsList } from "@/api/course/courseWatchLog";
+import { courseList,videoList } from '@/api/course/courseRedPacketLog'
+export default {
+  name: "CourseWatchLog",
+  data() {
+    return {
+      activeName:"00",
+      createTime:null,
+      courseLists:[],
+      videoList:[],
+      logTypeOptions:[],
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 短链课程看课记录表格数据
+      courseWatchLogList: [],
+      projectLists: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        userId: null,
+        nickName: null,
+        videoId: null,
+        logType: null,
+        qwExternalContactId: null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null,
+        sTime:null,
+        eTime:null,
+        scheduleStartTime: null,
+        scheduleEndTime: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      },
+      scheduleTime: null,
+    };
+  },
+  created() {
+    this.getDicts("sys_course_watch_log_type").then(response => {
+      this.logTypeOptions = response.data;
+    });
+    this.getDicts("sys_course_project").then(response => {
+      this.projectLists = response.data;
+    })
+    courseList().then(response => {
+      this.courseLists = response.list;
+    });
+
+    this.getList();
+
+  },
+  methods: {
+    courseChange(row){
+      this.queryParams.videoId=null;
+      if(row === ''){
+        this.videoList=[];
+        return
+      }
+      videoList(row).then(response => {
+        this.videoList=response.list
+      });
+    },
+    change() {
+      if (this.createTime != null) {
+        this.queryParams.sTime = this.createTime[0];
+        this.queryParams.eTime = this.createTime[1];
+      } else {
+        this.queryParams.sTime = null;
+        this.queryParams.eTime = null;
+      }
+    },
+    handleClickX(tab,event){
+      this.activeName=tab.name;
+      if(tab.name=="00"){
+        this.queryParams.logType=null;
+      }else{
+        this.queryParams.logType=tab.name;
+      }
+      this.getList()
+    },
+    /** 查询短链课程看课记录列表 */
+    getList() {
+      this.loading = true;
+      myQwWatchLogAllStatisticsList(this.queryParams).then(response => {
+        this.courseWatchLogList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        logId: null,
+        userId: null,
+        videoId: null,
+        logType: null,
+        createTime: null,
+        updateTime: null,
+        qwExternalContactId: null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.createTime = null;
+      this.scheduleTime = null;
+      this.queryParams.sTime = null;
+      this.queryParams.eTime = null;
+      this.queryParams.scheduleStartTime = null;
+      this.queryParams.scheduleEndTime = null;
+      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
+      getCourseWatchLog(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) {
+            updateCourseWatchLog(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addCourseWatchLog(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 delCourseWatchLog(logIds);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有短链课程看课记录数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportCourseWatchLog(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    },
+    handleScheduleTimeChange(val) {
+      if (val) {
+        this.queryParams.scheduleStartTime = val[0];
+        this.queryParams.scheduleEndTime = val[1];
+      } else {
+        this.queryParams.scheduleStartTime = null;
+        this.queryParams.scheduleEndTime = null;
+      }
+    },
+  }
+};
+</script>

+ 43 - 0
src/views/course/courseUserStatistics/myStatisticsTabIndex.vue

@@ -0,0 +1,43 @@
+<!-- TabComponent.vue -->
+<template>
+  <div class="tab-container">
+    <el-tabs type="card" style="background-color: white">
+      <el-tab-pane label="会员">
+        <member-view/>
+      </el-tab-pane>
+      <el-tab-pane label="企微">
+        <qw-view/>
+      </el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+
+<script>
+import MemberView from './myStatistics.vue';
+import QwView from './qw/myStatistics.vue';
+
+
+export default {
+  name: 'TabComponent',
+  components: {
+    MemberView,
+    QwView
+  },
+  data() {
+    return {
+    };
+  }
+};
+</script>
+
+<style scoped>
+.tab-container {
+  width: 100%;
+  height: 100%;
+}
+.app-container{
+  padding: 15px !important;
+  margin: 0px !important;
+  background-color: #fff !important;
+}
+</style>

+ 43 - 0
src/views/course/courseUserStatistics/myTabIndex.vue

@@ -0,0 +1,43 @@
+<!-- TabComponent.vue -->
+<template>
+  <div class="tab-container">
+    <el-tabs type="card" style="background-color: white">
+      <el-tab-pane label="会员">
+        <member-view/>
+      </el-tab-pane>
+      <el-tab-pane label="企微">
+        <qw-view/>
+      </el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+
+<script>
+import MemberView from './my.vue';
+import QwView from './qw/my.vue';
+
+
+export default {
+  name: 'TabComponent',
+  components: {
+    MemberView,
+    QwView
+  },
+  data() {
+    return {
+    };
+  }
+};
+</script>
+
+<style scoped>
+.tab-container {
+  width: 100%;
+  height: 100%;
+}
+.app-container{
+  padding: 15px !important;
+  margin: 0px !important;
+  background-color: #fff !important;
+}
+</style>

+ 384 - 0
src/views/course/courseUserStatistics/qw/index.vue

@@ -0,0 +1,384 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="企微昵称" prop="nickName">
+        <el-input
+          v-model="queryParams.nickName"
+          placeholder="请输入企微昵称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="添加时间" prop="createTime">
+        <el-date-picker v-model="createTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="change"></el-date-picker>
+      </el-form-item>
+
+      <el-form-item label="公司名称" prop="companyId">
+        <el-select style="width: 220px" filterable v-model="queryParams.companyId" placeholder="请选择公司名" clearable size="small">
+          <el-option
+            v-for="item in companys"
+            :key="item.companyId"
+            :label="item.companyName"
+            :value="item.companyId"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-table border v-loading="loading" :data="courseWatchLogList" @selection-change="handleSelectionChange"  show-summary>
+      <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="企微员工名称" align="center" prop="qwUserName" />
+
+            <!-- 发课时间 -->
+            <el-table-column label="进线时间" align="center" prop="createTime"/>
+            <!-- 进线数 -->
+            <el-table-column label="进线数" align="center" prop="line" />
+
+            <!-- 先导课上线 -->
+            <el-table-column label="先导课上线" align="center" prop="firstOnline">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.firstOnline }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.firstOnline / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 先导课完课 -->
+            <el-table-column label="先导课完课" align="center" prop="firstOver">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.firstOver }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.firstOver / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 首日上线 -->
+            <el-table-column label="首日上线" align="center" prop="d1Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d1Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d1Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 首日完课 -->
+            <el-table-column label="首日完课" align="center" prop="d1Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d1Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d1Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 综合报名数 -->
+            <el-table-column label="综合报名数" align="center" prop="sign">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.sign }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.sign / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 互动数 -->
+            <el-table-column label="互动数" align="center" prop="interact">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.interact }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.interact / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- A级客户 -->
+            <el-table-column label="A级客户" align="center" prop="a">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.a }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.a / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- B级客户 -->
+            <el-table-column label="B级客户" align="center" prop="b">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.b }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.b / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- C级客户 -->
+            <el-table-column label="C级客户" align="center" prop="c">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.c }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.c / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- D级客户 -->
+            <el-table-column label="D级客户" align="center" prop="d">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 流失数 -->
+            <el-table-column label="流失数" align="center" prop="los">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.los }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.los / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 删除数 -->
+            <el-table-column label="删除数" align="center" prop="del">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.del }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.del / scope.row.line) * 100).toFixed(2) }}%)</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 { listCourseWatchLog, getCourseWatchLog, delCourseWatchLog, addCourseWatchLog, updateCourseWatchLog, exportCourseWatchLog,statisticsList,qwWatchLogStatisticsList } from "@/api/course/qw/courseWatchLog";
+import { courseList,videoList } from '@/api/course/courseRedPacketLog'
+import {getCompanyList} from "@/api/company/company";
+export default {
+  name: "CourseWatchLog",
+  data() {
+    return {
+      companys: [],
+      activeName:"00",
+      createTime:null,
+      courseLists:[],
+      videoList:[],
+      logTypeOptions:[],
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 短链课程看课记录表格数据
+      courseWatchLogList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        userId: null,
+        nickName: null,
+        videoId: null,
+        logType: null,
+        qwExternalContactId: null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null,
+        sTime:null,
+        eTime:null,
+        scheduleStartTime: null,
+        scheduleEndTime: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      },
+      scheduleTime: null,
+    };
+  },
+  created() {
+    getCompanyList().then(response => {
+      this.companys = response.data;
+      if(this.companys!=null&&this.companys.length>0){
+      }
+    });
+    courseList().then(response => {
+      this.courseLists = response.list;
+    });
+    this.getList();
+    this.getDicts("sys_course_watch_log_type").then(response => {
+      this.logTypeOptions = response.data;
+    });
+  },
+  methods: {
+    courseChange(row){
+      this.queryParams.videoId=null;
+      if(row === ''){
+        this.videoList=[];
+        return
+      }
+      videoList(row).then(response => {
+        this.videoList=response.list
+      });
+    },
+    change() {
+      if (this.createTime != null) {
+        this.queryParams.sTime = this.createTime[0];
+        this.queryParams.eTime = this.createTime[1];
+      } else {
+        this.queryParams.sTime = null;
+        this.queryParams.eTime = null;
+      }
+    },
+    handleClickX(tab,event){
+      this.activeName=tab.name;
+      if(tab.name=="00"){
+        this.queryParams.logType=null;
+      }else{
+        this.queryParams.logType=tab.name;
+      }
+      this.getList()
+    },
+    /** 查询短链课程看课记录列表 */
+    getList() {
+      this.loading = true;
+      qwWatchLogStatisticsList(this.queryParams).then(response => {
+        this.courseWatchLogList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        logId: null,
+        userId: null,
+        videoId: null,
+        logType: null,
+        createTime: null,
+        updateTime: null,
+        qwExternalContactId: null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.createTime = null;
+      this.scheduleTime = null;
+      this.queryParams.sTime = null;
+      this.queryParams.eTime = null;
+      this.queryParams.scheduleStartTime = null;
+      this.queryParams.scheduleEndTime = null;
+      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
+      getCourseWatchLog(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) {
+            updateCourseWatchLog(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addCourseWatchLog(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 delCourseWatchLog(logIds);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有短链课程看课记录数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportCourseWatchLog(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    },
+    handleScheduleTimeChange(val) {
+      if (val) {
+        this.queryParams.scheduleStartTime = val[0];
+        this.queryParams.scheduleEndTime = val[1];
+      } else {
+        this.queryParams.scheduleStartTime = null;
+        this.queryParams.scheduleEndTime = null;
+      }
+    },
+  }
+};
+</script>

+ 381 - 0
src/views/course/courseUserStatistics/qw/my.vue

@@ -0,0 +1,381 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="企微昵称" prop="nickName">
+        <el-input
+          v-model="queryParams.nickName"
+          placeholder="请输入企微昵称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="添加时间" prop="createTime">
+        <el-date-picker v-model="createTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="change"></el-date-picker>
+      </el-form-item>
+      <el-form-item label="公司名称" prop="companyId">
+        <el-select style="width: 220px" filterable v-model="queryParams.companyId" placeholder="请选择公司名" clearable size="small">
+          <el-option
+            v-for="item in companys"
+            :key="item.companyId"
+            :label="item.companyName"
+            :value="item.companyId"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-table border v-loading="loading" :data="courseWatchLogList" @selection-change="handleSelectionChange"  show-summary >
+      <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="企微员工名称" align="center" prop="qwUserName" />
+
+            <!-- 发课时间 -->
+            <el-table-column label="进线时间" align="center" prop="createTime"/>
+            <!-- 进线数 -->
+            <el-table-column label="进线数" align="center" prop="line" />
+
+            <!-- 先导课上线 -->
+            <el-table-column label="先导课上线" align="center" prop="firstOnline">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.firstOnline }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.firstOnline / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 先导课完课 -->
+            <el-table-column label="先导课完课" align="center" prop="firstOver">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.firstOver }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.firstOver / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 首日上线 -->
+            <el-table-column label="首日上线" align="center" prop="d1Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d1Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d1Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 首日完课 -->
+            <el-table-column label="首日完课" align="center" prop="d1Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d1Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d1Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 综合报名数 -->
+            <el-table-column label="综合报名数" align="center" prop="sign">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.sign }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.sign / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 互动数 -->
+            <el-table-column label="互动数" align="center" prop="interact">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.interact }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.interact / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- A级客户 -->
+            <el-table-column label="A级客户" align="center" prop="a">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.a }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.a / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- B级客户 -->
+            <el-table-column label="B级客户" align="center" prop="b">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.b }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.b / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- C级客户 -->
+            <el-table-column label="C级客户" align="center" prop="c">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.c }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.c / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- D级客户 -->
+            <el-table-column label="D级客户" align="center" prop="d">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 流失数 -->
+            <el-table-column label="流失数" align="center" prop="los">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.los }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.los / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 删除数 -->
+            <el-table-column label="删除数" align="center" prop="del">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.del }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.del / scope.row.line) * 100).toFixed(2) }}%)</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 { listCourseWatchLog, getCourseWatchLog, delCourseWatchLog, addCourseWatchLog, updateCourseWatchLog, exportCourseWatchLog,statisticsList,qwWatchLogStatisticsList,myQwWatchLogStatisticsList } from "@/api/course/qw/courseWatchLog";
+import { courseList,videoList } from '@/api/course/courseRedPacketLog'
+import {getCompanyList} from "@/api/company/company";
+export default {
+  name: "CourseWatchLog",
+  data() {
+    return {
+      companys:[],
+      activeName:"00",
+      createTime:null,
+      courseLists:[],
+      videoList:[],
+      logTypeOptions:[],
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 短链课程看课记录表格数据
+      courseWatchLogList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        userId: null,
+        nickName: null,
+        videoId: null,
+        logType: null,
+        qwExternalContactId: null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null,
+        sTime:null,
+        eTime:null,
+        scheduleStartTime: null,
+        scheduleEndTime: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      },
+      scheduleTime: null,
+    };
+  },
+  created() {
+    getCompanyList().then(response => {
+      this.companys = response.data;
+    });
+    courseList().then(response => {
+      this.courseLists = response.list;
+    });
+    this.getList();
+    this.getDicts("sys_course_watch_log_type").then(response => {
+      this.logTypeOptions = response.data;
+    });
+  },
+  methods: {
+    courseChange(row){
+      this.queryParams.videoId=null;
+      if(row === ''){
+        this.videoList=[];
+        return
+      }
+      videoList(row).then(response => {
+        this.videoList=response.list
+      });
+    },
+    change() {
+      if (this.createTime != null) {
+        this.queryParams.sTime = this.createTime[0];
+        this.queryParams.eTime = this.createTime[1];
+      } else {
+        this.queryParams.sTime = null;
+        this.queryParams.eTime = null;
+      }
+    },
+    handleClickX(tab,event){
+      this.activeName=tab.name;
+      if(tab.name=="00"){
+        this.queryParams.logType=null;
+      }else{
+        this.queryParams.logType=tab.name;
+      }
+      this.getList()
+    },
+    /** 查询短链课程看课记录列表 */
+    getList() {
+      this.loading = true;
+      myQwWatchLogStatisticsList(this.queryParams).then(response => {
+        this.courseWatchLogList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        logId: null,
+        userId: null,
+        videoId: null,
+        logType: null,
+        createTime: null,
+        updateTime: null,
+        qwExternalContactId: null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.createTime = null;
+      this.scheduleTime = null;
+      this.queryParams.sTime = null;
+      this.queryParams.eTime = null;
+      this.queryParams.scheduleStartTime = null;
+      this.queryParams.scheduleEndTime = null;
+      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
+      getCourseWatchLog(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) {
+            updateCourseWatchLog(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addCourseWatchLog(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 delCourseWatchLog(logIds);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有短链课程看课记录数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportCourseWatchLog(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    },
+    handleScheduleTimeChange(val) {
+      if (val) {
+        this.queryParams.scheduleStartTime = val[0];
+        this.queryParams.scheduleEndTime = val[1];
+      } else {
+        this.queryParams.scheduleStartTime = null;
+        this.queryParams.scheduleEndTime = null;
+      }
+    },
+  }
+};
+</script>

+ 680 - 0
src/views/course/courseUserStatistics/qw/myStatistics.vue

@@ -0,0 +1,680 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="企微昵称" prop="nickName">
+        <el-input
+          v-model="queryParams.nickName"
+          placeholder="请输入企微昵称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="添加时间" prop="createTime">
+        <el-date-picker v-model="createTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="change"></el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-table border v-loading="loading" :data="courseWatchLogList" @selection-change="handleSelectionChange"  show-summary height="600">
+      <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="员工名称" align="center" prop="qwUserName" />
+
+            <!-- 发课时间 -->
+            <el-table-column label="进线时间" align="center" prop="createTime"/>
+            <!-- 进线数 -->
+            <el-table-column label="进线数" align="center" prop="line" />
+
+            <!-- 先导课上线 -->
+            <el-table-column label="先导上线" align="center" prop="firstOnline">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.firstOnline }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.firstOnline / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 先导课完课 -->
+            <el-table-column label="先导完课" align="center" prop="firstOver">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.firstOver }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.firstOver / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+
+            <el-table-column label="D1上线" align="center" prop="d1Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d1Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d1Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D1完课" align="center" prop="d1Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d1Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d1Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D2上线" align="center" prop="d2Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d2Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d2Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D2完课" align="center" prop="d2Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d2Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d2Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D3上线" align="center" prop="d3Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d3Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d3Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D3完课" align="center" prop="d3Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d3Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d3Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D4上线" align="center" prop="d4Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d4Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d4Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D4完课" align="center" prop="d4Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d4Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d4Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D5上线" align="center" prop="d5Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d5Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d5Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D5完课" align="center" prop="d5Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d5Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d5Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D6上线" align="center" prop="d6Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d6Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d6Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D6完课" align="center" prop="d6Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d6Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d6Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D7上线" align="center" prop="d7Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d7Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d7Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D7完课" align="center" prop="d7Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d7Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d7Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D8上线" align="center" prop="d8Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d8Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d8Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D8完课" align="center" prop="d8Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d8Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d8Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D9上线" align="center" prop="d9Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d9Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d9Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D9完课" align="center" prop="d9Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d9Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d9Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D10上线" align="center" prop="d10Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d10Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d10Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D10完课" align="center" prop="d10Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d10Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d10Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D11上线" align="center" prop="d11Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d11Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d11Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D11完课" align="center" prop="d11Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d11Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d11Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D12上线" align="center" prop="d12Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d12Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d12Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D12完课" align="center" prop="d12Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d12Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d12Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D13上线" align="center" prop="d13Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d13Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d13Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D13完课" align="center" prop="d13Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d13Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d13Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D14上线" align="center" prop="d14Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d14Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d14Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D14完课" align="center" prop="d14Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d14Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d14Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D15上线" align="center" prop="d15Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d15Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d15Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D15完课" align="center" prop="d15Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d15Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d15Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D16上线" align="center" prop="d16Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d16Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d16Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D16完课" align="center" prop="d16Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d16Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d16Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D17上线" align="center" prop="d17Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d17Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d17Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D17完课" align="center" prop="d17Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d17Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d17Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D18上线" align="center" prop="d18Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d18Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d18Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D18完课" align="center" prop="d18Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d18Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d18Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D19上线" align="center" prop="d19Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d19Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d19Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D19完课" align="center" prop="d19Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d19Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d19Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D20上线" align="center" prop="d20Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d20Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d20Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D20完课" align="center" prop="d20Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d20Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d20Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D21上线" align="center" prop="d21Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d21Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d21Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D21完课" align="center" prop="d21Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d21Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d21Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D22上线" align="center" prop="d22Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d22Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d22Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D22完课" align="center" prop="d22Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d22Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d22Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D23上线" align="center" prop="d23Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d23Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d23Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D23完课" align="center" prop="d23Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d23Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d23Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D24上线" align="center" prop="d24Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d24Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d24Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D24完课" align="center" prop="d24Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d24Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d24Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D25上线" align="center" prop="d25Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d25Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d25Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D25完课" align="center" prop="d25Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d25Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d25Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D26上线" align="center" prop="d26Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d26Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d26Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D26完课" align="center" prop="d26Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d26Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d26Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D27上线" align="center" prop="d27Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d27Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d27Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D27完课" align="center" prop="d27Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d27Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d27Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D28上线" align="center" prop="d28Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d28Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d28Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D28完课" align="center" prop="d28Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d28Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d28Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D29上线" align="center" prop="d29Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d29Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d29Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D29完课" align="center" prop="d29Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d29Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d29Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D30上线" align="center" prop="d30Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d30Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d30Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D30完课" align="center" prop="d30Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d30Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d30Over / scope.row.line) * 100).toFixed(2) }}%)</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 { listCourseWatchLog, getCourseWatchLog, delCourseWatchLog, addCourseWatchLog, updateCourseWatchLog, exportCourseWatchLog,statisticsList,qwWatchLogStatisticsList,qwWatchLogAllStatisticsList,myQwWatchLogAllStatisticsList } from "@/api/course/qw/courseWatchLog";
+import { courseList,videoList } from '@/api/course/courseRedPacketLog'
+export default {
+  name: "CourseWatchLog",
+  data() {
+    return {
+      activeName:"00",
+      createTime:null,
+      courseLists:[],
+      videoList:[],
+      logTypeOptions:[],
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 短链课程看课记录表格数据
+      courseWatchLogList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        userId: null,
+        nickName: null,
+        videoId: null,
+        logType: null,
+        qwExternalContactId: null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null,
+        sTime:null,
+        eTime:null,
+        scheduleStartTime: null,
+        scheduleEndTime: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      },
+      scheduleTime: null,
+    };
+  },
+  created() {
+    courseList().then(response => {
+      this.courseLists = response.list;
+    });
+    this.getList();
+    this.getDicts("sys_course_watch_log_type").then(response => {
+      this.logTypeOptions = response.data;
+    });
+  },
+  methods: {
+    courseChange(row){
+      this.queryParams.videoId=null;
+      if(row === ''){
+        this.videoList=[];
+        return
+      }
+      videoList(row).then(response => {
+        this.videoList=response.list
+      });
+    },
+    change() {
+      if (this.createTime != null) {
+        this.queryParams.sTime = this.createTime[0];
+        this.queryParams.eTime = this.createTime[1];
+      } else {
+        this.queryParams.sTime = null;
+        this.queryParams.eTime = null;
+      }
+    },
+    handleClickX(tab,event){
+      this.activeName=tab.name;
+      if(tab.name=="00"){
+        this.queryParams.logType=null;
+      }else{
+        this.queryParams.logType=tab.name;
+      }
+      this.getList()
+    },
+    /** 查询短链课程看课记录列表 */
+    getList() {
+      this.loading = true;
+      myQwWatchLogAllStatisticsList(this.queryParams).then(response => {
+        this.courseWatchLogList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        logId: null,
+        userId: null,
+        videoId: null,
+        logType: null,
+        createTime: null,
+        updateTime: null,
+        qwExternalContactId: null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.createTime = null;
+      this.scheduleTime = null;
+      this.queryParams.sTime = null;
+      this.queryParams.eTime = null;
+      this.queryParams.scheduleStartTime = null;
+      this.queryParams.scheduleEndTime = null;
+      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
+      getCourseWatchLog(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) {
+            updateCourseWatchLog(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addCourseWatchLog(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 delCourseWatchLog(logIds);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有短链课程看课记录数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportCourseWatchLog(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    },
+    handleScheduleTimeChange(val) {
+      if (val) {
+        this.queryParams.scheduleStartTime = val[0];
+        this.queryParams.scheduleEndTime = val[1];
+      } else {
+        this.queryParams.scheduleStartTime = null;
+        this.queryParams.scheduleEndTime = null;
+      }
+    },
+  }
+};
+</script>

+ 697 - 0
src/views/course/courseUserStatistics/qw/statistics.vue

@@ -0,0 +1,697 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="企微昵称" prop="nickName">
+        <el-input
+          v-model="queryParams.nickName"
+          placeholder="请输入企微昵称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="添加时间" prop="createTime">
+        <el-date-picker v-model="createTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="change"></el-date-picker>
+      </el-form-item>
+      <el-form-item label="公司名称" prop="companyId">
+        <el-select style="width: 220px" filterable v-model="queryParams.companyId" placeholder="请选择公司名" clearable size="small">
+          <el-option
+            v-for="item in companys"
+            :key="item.companyId"
+            :label="item.companyName"
+            :value="item.companyId"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-table border v-loading="loading" :data="courseWatchLogList" @selection-change="handleSelectionChange"  show-summary height="600">
+      <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="员工名称" align="center" prop="qwUserName" />
+
+            <!-- 发课时间 -->
+            <el-table-column label="进线时间" align="center" prop="createTime"/>
+            <!-- 进线数 -->
+            <el-table-column label="进线数" align="center" prop="line" />
+
+            <!-- 先导课上线 -->
+            <el-table-column label="先导上线" align="center" prop="firstOnline">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.firstOnline }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.firstOnline / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 先导课完课 -->
+            <el-table-column label="先导完课" align="center" prop="firstOver">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.firstOver }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.firstOver / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+
+            <el-table-column label="D1上线" align="center" prop="d1Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d1Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d1Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D1完课" align="center" prop="d1Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d1Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d1Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D2上线" align="center" prop="d2Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d2Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d2Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D2完课" align="center" prop="d2Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d2Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d2Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D3上线" align="center" prop="d3Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d3Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d3Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D3完课" align="center" prop="d3Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d3Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d3Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D4上线" align="center" prop="d4Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d4Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d4Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D4完课" align="center" prop="d4Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d4Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d4Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D5上线" align="center" prop="d5Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d5Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d5Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D5完课" align="center" prop="d5Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d5Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d5Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D6上线" align="center" prop="d6Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d6Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d6Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D6完课" align="center" prop="d6Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d6Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d6Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D7上线" align="center" prop="d7Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d7Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d7Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D7完课" align="center" prop="d7Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d7Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d7Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D8上线" align="center" prop="d8Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d8Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d8Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D8完课" align="center" prop="d8Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d8Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d8Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D9上线" align="center" prop="d9Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d9Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d9Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D9完课" align="center" prop="d9Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d9Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d9Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D10上线" align="center" prop="d10Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d10Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d10Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D10完课" align="center" prop="d10Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d10Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d10Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D11上线" align="center" prop="d11Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d11Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d11Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D11完课" align="center" prop="d11Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d11Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d11Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D12上线" align="center" prop="d12Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d12Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d12Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D12完课" align="center" prop="d12Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d12Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d12Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D13上线" align="center" prop="d13Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d13Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d13Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D13完课" align="center" prop="d13Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d13Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d13Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D14上线" align="center" prop="d14Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d14Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d14Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D14完课" align="center" prop="d14Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d14Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d14Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D15上线" align="center" prop="d15Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d15Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d15Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D15完课" align="center" prop="d15Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d15Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d15Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D16上线" align="center" prop="d16Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d16Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d16Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D16完课" align="center" prop="d16Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d16Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d16Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D17上线" align="center" prop="d17Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d17Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d17Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D17完课" align="center" prop="d17Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d17Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d17Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D18上线" align="center" prop="d18Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d18Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d18Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D18完课" align="center" prop="d18Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d18Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d18Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D19上线" align="center" prop="d19Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d19Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d19Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D19完课" align="center" prop="d19Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d19Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d19Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D20上线" align="center" prop="d20Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d20Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d20Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D20完课" align="center" prop="d20Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d20Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d20Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D21上线" align="center" prop="d21Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d21Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d21Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D21完课" align="center" prop="d21Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d21Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d21Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D22上线" align="center" prop="d22Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d22Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d22Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D22完课" align="center" prop="d22Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d22Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d22Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D23上线" align="center" prop="d23Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d23Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d23Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D23完课" align="center" prop="d23Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d23Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d23Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D24上线" align="center" prop="d24Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d24Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d24Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D24完课" align="center" prop="d24Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d24Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d24Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D25上线" align="center" prop="d25Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d25Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d25Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D25完课" align="center" prop="d25Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d25Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d25Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D26上线" align="center" prop="d26Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d26Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d26Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D26完课" align="center" prop="d26Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d26Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d26Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D27上线" align="center" prop="d27Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d27Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d27Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D27完课" align="center" prop="d27Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d27Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d27Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D28上线" align="center" prop="d28Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d28Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d28Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D28完课" align="center" prop="d28Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d28Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d28Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D29上线" align="center" prop="d29Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d29Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d29Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D29完课" align="center" prop="d29Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d29Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d29Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D30上线" align="center" prop="d30Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d30Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d30Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D30完课" align="center" prop="d30Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d30Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d30Over / scope.row.line) * 100).toFixed(2) }}%)</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 { listCourseWatchLog, getCourseWatchLog, delCourseWatchLog, addCourseWatchLog, updateCourseWatchLog, exportCourseWatchLog,statisticsList,qwWatchLogStatisticsList,qwWatchLogAllStatisticsList } from "@/api/course/courseWatchLog";
+import { courseList,videoList } from '@/api/course/courseRedPacketLog'
+import {getCompanyList} from "@/api/company/company";
+export default {
+  name: "CourseWatchLog",
+  data() {
+    return {
+      companys: [],
+      activeName:"00",
+      createTime:null,
+      courseLists:[],
+      videoList:[],
+      logTypeOptions:[],
+      // 遮罩层
+      loading: false,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 短链课程看课记录表格数据
+      courseWatchLogList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        userId: null,
+        nickName: null,
+        videoId: null,
+        logType: null,
+        qwExternalContactId: null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null,
+        sTime:null,
+        eTime:null,
+        scheduleStartTime: null,
+        scheduleEndTime: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      },
+      scheduleTime: null,
+    };
+  },
+  created() {
+    getCompanyList().then(response => {
+      this.companys = response.data;
+      if(this.companys!=null&&this.companys.length>0){
+      }
+    });
+    // courseList().then(response => {
+    //   this.courseLists = response.list;
+    // });
+    this.getDicts("sys_course_watch_log_type").then(response => {
+      this.logTypeOptions = response.data;
+    });
+  },
+  methods: {
+    courseChange(row){
+      this.queryParams.videoId=null;
+      if(row === ''){
+        this.videoList=[];
+        return
+      }
+      videoList(row).then(response => {
+        this.videoList=response.list
+      });
+    },
+    change() {
+      if (this.createTime != null) {
+        this.queryParams.sTime = this.createTime[0];
+        this.queryParams.eTime = this.createTime[1];
+      } else {
+        this.queryParams.sTime = null;
+        this.queryParams.eTime = null;
+      }
+    },
+    handleClickX(tab,event){
+      this.activeName=tab.name;
+      if(tab.name=="00"){
+        this.queryParams.logType=null;
+      }else{
+        this.queryParams.logType=tab.name;
+      }
+      this.getList()
+    },
+    /** 查询短链课程看课记录列表 */
+    getList() {
+      this.loading = true;
+      qwWatchLogAllStatisticsList(this.queryParams).then(response => {
+        this.courseWatchLogList = response.rows;
+        this.total = response.total;
+      }).finally(()=>{
+        this.loading = false;
+      })
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        logId: null,
+        userId: null,
+        videoId: null,
+        logType: null,
+        createTime: null,
+        updateTime: null,
+        qwExternalContactId: null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.createTime = null;
+      this.scheduleTime = null;
+      this.queryParams.sTime = null;
+      this.queryParams.eTime = null;
+      this.queryParams.scheduleStartTime = null;
+      this.queryParams.scheduleEndTime = null;
+      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
+      getCourseWatchLog(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) {
+            updateCourseWatchLog(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addCourseWatchLog(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 delCourseWatchLog(logIds);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有短链课程看课记录数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportCourseWatchLog(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    },
+    handleScheduleTimeChange(val) {
+      if (val) {
+        this.queryParams.scheduleStartTime = val[0];
+        this.queryParams.scheduleEndTime = val[1];
+      } else {
+        this.queryParams.scheduleStartTime = null;
+        this.queryParams.scheduleEndTime = null;
+      }
+    },
+  }
+};
+</script>

+ 752 - 0
src/views/course/courseUserStatistics/statistics.vue

@@ -0,0 +1,752 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="项目" prop="courseId">
+        <el-select filterable  v-model="queryParams.project" placeholder="请选择项目"  clearable size="small">
+          <el-option
+            v-for="dict in projectLists"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="parseInt(dict.dictValue)"
+          />
+        </el-select>
+      </el-form-item>
+
+      <el-form-item label="课程" prop="courseId">
+        <el-select filterable  v-model="queryParams.courseId" placeholder="请选择课程"  clearable size="small" @change="courseChange(queryParams.courseId)">
+          <el-option
+            v-for="dict in courseLists"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="parseInt(dict.dictValue)"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="小节" prop="videoId">
+        <el-select filterable  v-model="queryParams.videoId" placeholder="请选择小节"  clearable size="small">
+          <el-option
+            v-for="dict in videoList"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="parseInt(dict.dictValue)"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="公司名称" prop="companyId">
+        <el-select style="width: 220px" @change="handleSeller" filterable v-model="queryParams.companyId" placeholder="请选择公司名" clearable size="small">
+          <el-option
+            v-for="item in companys"
+            :key="item.companyId"
+            :label="item.companyName"
+            :value="item.companyId"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="销售" prop="nickName" v-if="queryParams.companyId">
+        <el-select v-model="queryParams.companyUserId" remote placeholder="请选择" filterable clearable @keyup.enter.native="handleQuery">
+          <el-option
+            v-for="dict in companyUserList"
+            :key="`${dict.nickName} - ${dict.userName}`"
+            :label="`${dict.nickName} - ${dict.userName}`"
+            :value="dict.userId">
+          </el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="添加时间" prop="createTime">
+        <el-date-picker v-model="createTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="change"></el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-table border v-loading="loading" :data="courseWatchLogList" @selection-change="handleSelectionChange"  show-summary height="600">
+      <el-table-column type="selection" width="55" align="center" />
+<!--        <el-table-column label="会员名称" align="center" prop="fsUserName" />-->
+      <el-table-column label="销售名称" align="center" prop="qwUserName" />
+      <el-table-column label="项目" align="center" prop="projectName" />
+      <el-table-column label="课程" align="center" prop="courseName" />
+      <el-table-column label="小节" align="center" prop="videoName" />
+            <!-- 发课时间 -->
+            <el-table-column label="进线时间" align="center" prop="createTime"/>
+            <!-- 进线数 -->
+            <el-table-column label="进线数" align="center" prop="line" />
+
+            <!-- 先导课上线 -->
+            <el-table-column label="先导上线" align="center" prop="firstOnline">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.firstOnline }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.firstOnline / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <!-- 先导课完课 -->
+            <el-table-column label="先导完课" align="center" prop="firstOver">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.firstOver }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.firstOver / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+
+            <el-table-column label="D1上线" align="center" prop="d1Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d1Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d1Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D1完课" align="center" prop="d1Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d1Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d1Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D2上线" align="center" prop="d2Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d2Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d2Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D2完课" align="center" prop="d2Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d2Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d2Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D3上线" align="center" prop="d3Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d3Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d3Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D3完课" align="center" prop="d3Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d3Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d3Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D4上线" align="center" prop="d4Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d4Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d4Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D4完课" align="center" prop="d4Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d4Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d4Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D5上线" align="center" prop="d5Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d5Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d5Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D5完课" align="center" prop="d5Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d5Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d5Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D6上线" align="center" prop="d6Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d6Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d6Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D6完课" align="center" prop="d6Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d6Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d6Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D7上线" align="center" prop="d7Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d7Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d7Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D7完课" align="center" prop="d7Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d7Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d7Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D8上线" align="center" prop="d8Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d8Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d8Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D8完课" align="center" prop="d8Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d8Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d8Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D9上线" align="center" prop="d9Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d9Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d9Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D9完课" align="center" prop="d9Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d9Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d9Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D10上线" align="center" prop="d10Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d10Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d10Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D10完课" align="center" prop="d10Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d10Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d10Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D11上线" align="center" prop="d11Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d11Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d11Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D11完课" align="center" prop="d11Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d11Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d11Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D12上线" align="center" prop="d12Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d12Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d12Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D12完课" align="center" prop="d12Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d12Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d12Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D13上线" align="center" prop="d13Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d13Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d13Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D13完课" align="center" prop="d13Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d13Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d13Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D14上线" align="center" prop="d14Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d14Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d14Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D14完课" align="center" prop="d14Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d14Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d14Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D15上线" align="center" prop="d15Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d15Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d15Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D15完课" align="center" prop="d15Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d15Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d15Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D16上线" align="center" prop="d16Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d16Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d16Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D16完课" align="center" prop="d16Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d16Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d16Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D17上线" align="center" prop="d17Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d17Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d17Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D17完课" align="center" prop="d17Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d17Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d17Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D18上线" align="center" prop="d18Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d18Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d18Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D18完课" align="center" prop="d18Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d18Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d18Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D19上线" align="center" prop="d19Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d19Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d19Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D19完课" align="center" prop="d19Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d19Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d19Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D20上线" align="center" prop="d20Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d20Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d20Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D20完课" align="center" prop="d20Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d20Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d20Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D21上线" align="center" prop="d21Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d21Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d21Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D21完课" align="center" prop="d21Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d21Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d21Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D22上线" align="center" prop="d22Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d22Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d22Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D22完课" align="center" prop="d22Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d22Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d22Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D23上线" align="center" prop="d23Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d23Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d23Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D23完课" align="center" prop="d23Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d23Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d23Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D24上线" align="center" prop="d24Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d24Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d24Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D24完课" align="center" prop="d24Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d24Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d24Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D25上线" align="center" prop="d25Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d25Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d25Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D25完课" align="center" prop="d25Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d25Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d25Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D26上线" align="center" prop="d26Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d26Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d26Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D26完课" align="center" prop="d26Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d26Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d26Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D27上线" align="center" prop="d27Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d27Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d27Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D27完课" align="center" prop="d27Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d27Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d27Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D28上线" align="center" prop="d28Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d28Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d28Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D28完课" align="center" prop="d28Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d28Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d28Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D29上线" align="center" prop="d29Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d29Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d29Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D29完课" align="center" prop="d29Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d29Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d29Over / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="D30上线" align="center" prop="d30Online">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d30Online }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d30Online / scope.row.line) * 100).toFixed(2) }}%)</span>
+                </template>
+            </el-table-column>
+
+            <el-table-column label="D30完课" align="center" prop="d30Over">
+                <template slot-scope="scope">
+                    <span>{{ scope.row.d30Over }}</span>
+                    <span style="font-size: 12px; color: #959595;margin-left: 5px;">({{ ((scope.row.d30Over / scope.row.line) * 100).toFixed(2) }}%)</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 { listCourseWatchLog, getCourseWatchLog, delCourseWatchLog, addCourseWatchLog, updateCourseWatchLog, exportCourseWatchLog,statisticsList,qwWatchLogStatisticsList,qwWatchLogAllStatisticsList } from "@/api/course/courseWatchLog";
+import { courseList,videoList } from '@/api/course/courseRedPacketLog'
+import {getUserList} from "@/api/company/companyUser";
+import {getCompanyList} from "@/api/company/company";
+export default {
+  name: "CourseWatchLog",
+  data() {
+    return {
+      companys:[],
+      activeName:"00",
+      createTime:null,
+      courseLists:[],
+      videoList:[],
+      logTypeOptions:[],
+      // 遮罩层
+      loading: false,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 短链课程看课记录表格数据
+      courseWatchLogList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      companyUserList: [],
+      projectLists: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        userId: null,
+        nickName: null,
+        videoId: null,
+        logType: null,
+        qwExternalContactId: null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null,
+        sTime:null,
+        eTime:null,
+        scheduleStartTime: null,
+        scheduleEndTime: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      },
+      scheduleTime: null,
+    };
+  },
+  created() {
+    getCompanyList().then(response => {
+      this.companys = response.data;
+      if(this.companys!=null&&this.companys.length>0){
+      }
+    });
+    courseList().then(response => {
+      this.courseLists = response.list;
+    });
+    // this.getList();
+    this.getDicts("sys_course_watch_log_type").then(response => {
+      this.logTypeOptions = response.data;
+    });
+    this.getDicts("sys_course_project").then(response => {
+      this.projectLists = response.data;
+    })
+  },
+  methods: {
+    handleSeller(){
+      if(this.queryParams.companyId != null) {
+        getUserList(this.queryParams.companyId).then(res=>{
+          if(res.code === 200) {
+            this.companyUserList = res.data
+          }
+        })
+      }
+    },
+    courseChange(row){
+      this.queryParams.videoId=null;
+      if(row === ''){
+        this.videoList=[];
+        return
+      }
+      videoList(row).then(response => {
+        this.videoList=response.list
+      });
+    },
+    change() {
+      if (this.createTime != null) {
+        this.queryParams.sTime = this.createTime[0];
+        this.queryParams.eTime = this.createTime[1];
+      } else {
+        this.queryParams.sTime = null;
+        this.queryParams.eTime = null;
+      }
+    },
+    handleClickX(tab,event){
+      this.activeName=tab.name;
+      if(tab.name=="00"){
+        this.queryParams.logType=null;
+      }else{
+        this.queryParams.logType=tab.name;
+      }
+      this.getList()
+    },
+    /** 查询短链课程看课记录列表 */
+    getList() {
+      this.loading = true;
+      qwWatchLogAllStatisticsList(this.queryParams).then(response => {
+        this.courseWatchLogList = response.rows;
+        this.total = response.total;
+      }).finally(()=>{
+        this.loading = false;
+      })
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        logId: null,
+        userId: null,
+        videoId: null,
+        logType: null,
+        createTime: null,
+        updateTime: null,
+        qwExternalContactId: null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      if(this.queryParams.companyId == null) {
+        this.$message.warning("公司为必选!");
+        return;
+      }
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.createTime = null;
+      this.scheduleTime = null;
+      this.queryParams.sTime = null;
+      this.queryParams.eTime = null;
+      this.queryParams.scheduleStartTime = null;
+      this.queryParams.scheduleEndTime = null;
+      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
+      getCourseWatchLog(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) {
+            updateCourseWatchLog(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addCourseWatchLog(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 delCourseWatchLog(logIds);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有短链课程看课记录数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportCourseWatchLog(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    },
+    handleScheduleTimeChange(val) {
+      if (val) {
+        this.queryParams.scheduleStartTime = val[0];
+        this.queryParams.scheduleEndTime = val[1];
+      } else {
+        this.queryParams.scheduleStartTime = null;
+        this.queryParams.scheduleEndTime = null;
+      }
+    },
+  }
+};
+</script>

+ 43 - 0
src/views/course/courseUserStatistics/statisticsTabIndex.vue

@@ -0,0 +1,43 @@
+<!-- TabComponent.vue -->
+<template>
+  <div class="tab-container">
+    <el-tabs type="card" style="background-color: white">
+      <el-tab-pane label="会员">
+        <member-view/>
+      </el-tab-pane>
+      <el-tab-pane label="企微">
+        <qw-view/>
+      </el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+
+<script>
+import MemberView from './statistics.vue';
+import QwView from './qw/statistics.vue';
+
+
+export default {
+  name: 'TabComponent',
+  components: {
+    MemberView,
+    QwView
+  },
+  data() {
+    return {
+    };
+  }
+};
+</script>
+
+<style scoped>
+.tab-container {
+  width: 100%;
+  height: 100%;
+}
+.app-container{
+  padding: 15px !important;
+  margin: 0px !important;
+  background-color: #fff !important;
+}
+</style>

+ 178 - 34
src/views/course/courseWatchLog/index.vue

@@ -1,28 +1,30 @@
 <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="请输入会员ID"
-          clearable
-          size="small"
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="公司名称" prop="companyId" >
-        <el-select v-model="queryParams.companyId" placeholder="请选择所属公司" filterable clearable size="small">
-          <el-option v-for="(option, index) in companyList" :key="index" :value="option.dictValue" :label="option.dictLabel"></el-option>
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
+      <el-form-item label="用户" prop="userId">
+        <el-select v-model="queryParams.userId" remote placeholder="用户名/手机号" filterable clearable  style="width: 100%;"
+                   @keyup.enter.native="handleQuery"
+                   :remote-method="remoteGetFsUserList"
+                   @clear="handleClear"
+                   :loading="queryUserLoading"
+        >
+          <el-option
+            v-for="dict in fsUserList"
+            :key="`${dict.nickname} - ${dict.phone}`"
+            :label="`${dict.nickname} - ${dict.phone}`"
+            :value="dict.userId">
+          </el-option>
         </el-select>
       </el-form-item>
       <el-form-item label="所属销售" prop="companyUserName">
-        <el-input
-          v-model="queryParams.companyUserName"
-          placeholder="请输入所属销售"
-          clearable
-          size="small"
-          @keyup.enter.native="handleQuery"
-        />
+        <el-select v-model="queryParams.companyUserId" remote placeholder="请选择" filterable clearable  style="width: 100%;" @keyup.enter.native="handleQuery">
+          <el-option
+            v-for="dict in companyUserList"
+            :key="`${dict.nickName} - ${dict.userName}`"
+            :label="`${dict.nickName} - ${dict.userName}`"
+            :value="dict.userId">
+          </el-option>
+        </el-select>
       </el-form-item>
       <el-form-item label="课程" prop="courseId">
         <el-select filterable  v-model="queryParams.courseId" placeholder="请选择课程"  clearable size="small" @change="courseChange(queryParams.courseId)">
@@ -44,8 +46,37 @@
           />
         </el-select>
       </el-form-item>
+      <el-form-item label="营期时间" prop="scheduleTime">
+        <el-date-picker
+          v-model="scheduleTime"
+          type="daterange"
+          size="small"
+          style="width: 240px"
+          value-format="yyyy-MM-dd"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          @change="handleScheduleTimeChange">
+        </el-date-picker>
+      </el-form-item>
       <el-form-item label="创建时间" prop="createTime">
-        <el-date-picker v-model="createTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="change"></el-date-picker>
+        <el-date-picker v-model="createTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange"
+                        range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="createChange"></el-date-picker>
+      </el-form-item>
+      <el-form-item label="最新更新时间" prop="updateTime">
+        <el-date-picker v-model="updateTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange"
+                        range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="updateChange"></el-date-picker>
+      </el-form-item>
+
+      <el-form-item label="类型" prop="type">
+        <el-select filterable  v-model="sourceTypeModel" placeholder="请选择小节"  clearable size="small">
+          <el-option
+            v-for="dict in userSourceTypeOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
       </el-form-item>
       <el-form-item>
         <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
@@ -75,9 +106,28 @@
     <el-table border v-loading="loading" :data="courseWatchLogList" @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="企微客户" align="center" prop="externalUserName" v-if="queryParams.sourceType == 2"/>
+      <el-table-column label="会员昵称" align="center" prop="fsNickName">
+        <template slot-scope="scope">
+          <div style="display: flex;white-space: nowrap">
+            <div style="margin: auto">
+              {{scope.row.fsNickName}}
+            </div>
+            <el-popover
+              placement="right"
+              title=""
+              trigger="hover">
+              <img slot="reference" :src="scope.row.fsAvatar" style="width: 30px;height: 30px">
+              <img :src="scope.row.fsAvatar" style="max-width: 200px;max-height: 200px">
+            </el-popover>
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column label="项目" align="center" prop="projectName" />
       <el-table-column label="课程名称" align="center" prop="courseName" />
       <el-table-column label="小节名称" align="center" prop="videoName" />
+      <el-table-column label="企微员工名称" align="center" prop="qwUserName" v-if="queryParams.sourceType == 2"/>
       <el-table-column label="记录类型" align="center" prop="logType">
         <template slot-scope="scope">
           <dict-tag :options="logTypeOptions" :value="scope.row.logType"/>
@@ -85,9 +135,13 @@
       </el-table-column>
       <el-table-column label="播放时长" align="center" prop="duration" />
       <el-table-column label="所属销售" align="center" prop="companyUserName" />
-      <el-table-column label="所属公司" align="center" prop="companyName" />
+<!--      <el-table-column label="所属公司" align="center" prop="companyName" />-->
+<!--      <el-table-column label="企微员工名称" align="center" prop="qwUserName" />-->
       <el-table-column label="所属发送方式" align="center" prop="sendType" />
       <el-table-column label="创建时间" align="center" prop="createTime" />
+      <el-table-column label="更新时间" align="center" prop="updateTime" />
+      <el-table-column label="完课时间" align="center" prop="finishTime" />
+      <el-table-column label="营期时间" align="center" prop="campPeriodTime" />
     </el-table>
 
     <pagination
@@ -105,16 +159,20 @@
 import { listCourseWatchLog, getCourseWatchLog, delCourseWatchLog, addCourseWatchLog, updateCourseWatchLog, exportCourseWatchLog } from "@/api/course/courseWatchLog";
 import {allList}from "@/api/company/company";
 import { courseList,videoList } from '@/api/course/courseRedPacketLog'
+import {getUserList} from "@/api/company/companyUser";
+import {getFsUserList} from "@/api/users/user";
 export default {
   name: "CourseWatchLog",
   data() {
     return {
+      userSourceTypeOptions: [],
       activeName:"00",
       createTime:null,
+      updateTime:null,
       courseLists:[],
       videoList:[],
-      companyList:[],
       logTypeOptions:[],
+      queryUserLoading: false,
       // 遮罩层
       loading: true,
       // 导出遮罩层
@@ -129,20 +187,25 @@ export default {
       showSearch: true,
       // 总条数
       total: 0,
+      companyUserList: [],
       // 短链课程看课记录表格数据
       courseWatchLogList: [],
+      fsUserList: [],
       // 弹出层标题
       title: "",
       // 是否显示弹出层
       open: false,
+
       // 查询参数
       queryParams: {
         pageNum: 1,
         pageSize: 10,
         userId: null,
+        nickName: null,
         videoId: null,
         logType: null,
         qwExternalContactId: null,
+        externalUserName:null,
         duration: null,
         qwUserId: null,
         companyUserId: null,
@@ -150,25 +213,78 @@ export default {
         courseId: null,
         sTime:null,
         eTime:null,
+        upSTime:null,
+        upETime:null,
+        scheduleStartTime: null,
+        scheduleEndTime: null,
+        sourceType: 1
       },
       // 表单参数
       form: {},
       // 表单校验
       rules: {
-      }
+      },
+      scheduleTime: null,
     };
   },
   created() {
     courseList().then(response => {
       this.courseLists = response.list;
     });
-    this.getAllCompany();
     this.getList();
     this.getDicts("sys_course_watch_log_type").then(response => {
       this.logTypeOptions = response.data;
     });
+    getUserList().then(res=>{
+      if(res.code === 200) {
+        this.companyUserList = res.data
+      }
+    })
+
+    this.getDicts('user_source_type').then(response => {
+      this.userSourceTypeOptions = response.data;
+    })
+
+  },
+  computed: {
+    sourceTypeModel: {
+      get() {
+        return this.queryParams.sourceType !== null && this.queryParams.sourceType !== undefined ? this.queryParams.sourceType.toString() : null;
+      },
+      set(newVal) {
+        this.queryParams.sourceType = newVal;
+      }
+    }
   },
   methods: {
+    handleClear(){
+      this.queryUserLoading = false;
+      this.fsUserList = [];
+    },
+    remoteGetFsUserList(query){
+      if(query){
+        this.queryUserLoading = true;
+        const isNumeric = /^\d+$/.test(query);
+        let payload = {
+          username: query,
+          nickname: query
+        }
+        if(isNumeric) {
+          payload = {
+            userId: query,
+            username: query,
+            phone: query
+          }
+        }
+        getFsUserList(payload).then(res=>{
+          if(res.code === 200) {
+            this.fsUserList = res.data
+          }
+        }).finally(()=>{
+          this.queryUserLoading = false;
+        })
+      }
+    },
     courseChange(row){
       this.queryParams.videoId=null;
       if(row === ''){
@@ -179,7 +295,7 @@ export default {
         this.videoList=response.list
       });
     },
-    change() {
+    createChange() {
       if (this.createTime != null) {
         this.queryParams.sTime = this.createTime[0];
         this.queryParams.eTime = this.createTime[1];
@@ -188,11 +304,15 @@ export default {
         this.queryParams.eTime = null;
       }
     },
-    getAllCompany() {
-      allList().then(response => {
-        this.companyList = response.rows;
-        this.companyList.push({dictValue:"-1",dictLabel:"无"})
-      });
+
+    updateChange(){
+      if (this.updateTime != null) {
+        this.queryParams.upSTime = this.updateTime[0];
+        this.queryParams.upETime = this.updateTime[1];
+      } else {
+        this.queryParams.upSTime = null;
+        this.queryParams.upETime = null;
+      }
     },
     handleClickX(tab,event){
       this.activeName=tab.name;
@@ -206,6 +326,10 @@ export default {
     /** 查询短链课程看课记录列表 */
     getList() {
       this.loading = true;
+      if(this.queryParams.logType == "10"){
+        this.queryParams.logType = null;
+      }
+
       listCourseWatchLog(this.queryParams).then(response => {
         this.courseWatchLogList = response.rows;
         this.total = response.total;
@@ -227,12 +351,16 @@ export default {
         createTime: null,
         updateTime: null,
         qwExternalContactId: null,
+        externalUserName:null,
         duration: null,
         qwUserId: null,
         companyUserId: null,
         companyId: null,
-        courseId: null
+        courseId: null,
+        scheduleStartTime: null,
+        scheduleEndTime: null,
       };
+      this.scheduleTime=null;
       this.resetForm("form");
     },
     /** 搜索按钮操作 */
@@ -244,8 +372,15 @@ export default {
     resetQuery() {
       this.resetForm("queryForm");
       this.createTime = null;
+      this.scheduleTime = null;
       this.queryParams.sTime = null;
       this.queryParams.eTime = null;
+      this.queryParams.upSTime = null;
+      this.queryParams.upETime = null;
+      this.queryParams.scheduleStartTime = null;
+      this.queryParams.scheduleEndTime = null;
+      this.scheduleTime=null;
+      this.updateTime=null;
       this.handleQuery();
     },
     // 多选框选中数据
@@ -318,7 +453,16 @@ export default {
           this.download(response.msg);
           this.exportLoading = false;
         }).catch(() => {});
-    }
+    },
+    handleScheduleTimeChange(val) {
+      if (val) {
+        this.queryParams.scheduleStartTime = val[0];
+        this.queryParams.scheduleEndTime = val[1];
+      } else {
+        this.queryParams.scheduleStartTime = null;
+        this.queryParams.scheduleEndTime = null;
+      }
+    },
   }
 };
 </script>

+ 388 - 0
src/views/course/courseWatchLog/myCourseWatchLog.vue

@@ -0,0 +1,388 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
+      <el-form-item label="企微账号" prop="qwUserId">
+        <el-select v-model="queryParams.qwUserId" placeholder="企微账号" clearable size="small" @change="updateQwuser()">
+          <el-option
+            v-for="dict in myQwUserList"
+            :key="dict.dictValue"
+            :label="dict.dictLabel + '('+dict.corpName+')'"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="客户ID" prop="qwExternalContactId">
+        <el-input
+          v-model="queryParams.qwExternalContactId"
+          placeholder="请输入会员ID"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="会员ID" prop="userId">
+        <el-input
+          v-model="queryParams.userId"
+          placeholder="请输入会员ID"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="会员昵称" prop="nickName">
+        <el-input
+          v-model="queryParams.nickName"
+          placeholder="请输入会员昵称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="企微客户昵称" prop="nickName">
+        <el-input
+          v-model="queryParams.externalUserName"
+          placeholder="请输入企微客户昵称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="课程" prop="courseId">
+        <el-select filterable  v-model="queryParams.courseId" placeholder="请选择课程"  clearable size="small" @change="courseChange(queryParams.courseId)">
+          <el-option
+            v-for="dict in courseLists"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="小节" prop="videoId">
+        <el-select filterable  v-model="queryParams.videoId" placeholder="请选择小节"  clearable size="small">
+          <el-option
+            v-for="dict in videoList"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="创建时间" prop="createTime">
+        <el-date-picker v-model="createTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="change"></el-date-picker>
+      </el-form-item>
+      <el-table-column label="完课时间" align="center" prop="finishTime" />
+      <el-table-column label="营期时间" align="center" prop="campPeriodTime" />
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          :loading="exportLoading"
+          @click="handleExport"
+          v-hasPermi="['course:courseWatchLog:myExport']"
+        >导出</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-tabs type="card" v-model="queryParams.logType" @tab-click="handleClickX">
+      <el-tab-pane label="全部" name="10"></el-tab-pane>
+      <el-tab-pane v-for="(item,index) in logTypeOptions" :label="item.dictLabel" :name="item.dictValue"></el-tab-pane>
+    </el-tabs>
+    <el-table border v-loading="loading" :data="courseWatchLogList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="记录编号" align="center" prop="logId" />
+      <el-table-column label="企微客户" align="center" prop="externalUserName"/>
+      <el-table-column label="会员ID" align="center" prop="userId" />
+      <el-table-column label="会员昵称" align="center" prop="fsNickName">
+        <template slot-scope="scope">
+          <div style="display: flex;white-space: nowrap">
+            <div style="margin: auto">
+              {{scope.row.fsNickName}}
+            </div>
+            <el-popover
+              placement="right"
+              title=""
+              trigger="hover">
+              <img slot="reference" :src="scope.row.fsAvatar" style="width: 30px;height: 30px">
+              <img :src="scope.row.fsAvatar" style="max-width: 200px;max-height: 200px">
+            </el-popover>
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column label="课程名称" align="center" prop="courseName" />
+      <el-table-column label="小节名称" align="center" prop="videoName" />
+      <el-table-column label="记录类型" align="center" prop="logType">
+        <template slot-scope="scope">
+          <dict-tag :options="logTypeOptions" :value="scope.row.logType"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="播放时长" align="center" prop="duration" />
+      <el-table-column label="所属销售" align="center" prop="companyUserName" />
+<!--      <el-table-column label="所属公司" align="center" prop="companyName" />-->
+      <el-table-column label="企微员工名称" align="center" prop="qwUserName" />
+      <el-table-column label="企微账号" align="center" prop="qwUserName" />
+<!--      <el-table-column label="所属发送方式" align="center" prop="sendType" />-->
+      <el-table-column label="创建时间" align="center" prop="createTime" />
+<!--      <el-table-column label="更新时间" align="center" prop="updateTime" />-->
+      <el-table-column label="完课时间" align="center" prop="lastHeartbeatTime" />
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+  </div>
+</template>
+
+<script>
+import { myListCourseWatchLog, getCourseWatchLog, delCourseWatchLog, addCourseWatchLog, updateCourseWatchLog, exportCourseWatchLog } from "@/api/course/courseWatchLog";
+import { courseList,videoList } from '@/api/course/courseRedPacketLog'
+import {getMyQwUserList} from "@/api/qw/user";
+import {allListTagGroup} from "@/api/qw/tagGroup";
+import {listTag} from "@/api/qw/tag";
+export default {
+  name: "CourseWatchLog",
+  data() {
+    return {
+      createTime:null,
+      courseLists:[],
+      videoList:[],
+      myQwUserList:[],
+      logTypeOptions:[],
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 短链课程看课记录表格数据
+      courseWatchLogList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        userId: null,
+        videoId: null,
+        nickName:null,
+        logType: "10",
+        qwExternalContactId: null,
+        externalUserName:null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null,
+        sTime:null,
+        eTime:null,
+        scheduleStartTime: null,
+        scheduleEndTime: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      }
+    };
+  },
+  created() {
+    courseList().then(response => {
+      this.courseLists = response.list;
+    });
+    this.getList();
+    this.getDicts("sys_course_watch_log_type").then(response => {
+      this.logTypeOptions = response.data;
+    });
+
+    getMyQwUserList().then(response => {
+      this.myQwUserList = response.data;
+    });
+  },
+  methods: {
+    courseChange(row){
+      this.queryParams.videoId=null;
+      if(row === ''){
+        this.videoList=[];
+        return
+      }
+      videoList(row).then(response => {
+        this.videoList=response.list
+      });
+    },
+    updateQwuser(){
+      for (const user of this.myQwUserList) {
+        if (user.dictValue == this.queryParams.qwUserId) {
+          this.queryParams.corpId=user.corpId;
+          break;
+        }
+      }
+      this.getList();
+    },
+    change() {
+      if (this.createTime != null) {
+        this.queryParams.sTime = this.createTime[0];
+        this.queryParams.eTime = this.createTime[1];
+      } else {
+        this.queryParams.sTime = null;
+        this.queryParams.eTime = null;
+      }
+    },
+
+    handleClickX(tab){
+      if(tab.name==="10"){
+        this.queryParams.logType=null;
+      }else{
+        this.queryParams.logType=tab.name;
+      }
+      this.getList()
+    },
+    /** 查询短链课程看课记录列表 */
+    getList() {
+      this.loading = true;
+      myListCourseWatchLog(this.queryParams).then(response => {
+        this.courseWatchLogList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        logId: null,
+        userId: null,
+        videoId: null,
+        logType: null,
+        createTime: null,
+        updateTime: null,
+        qwExternalContactId: null,
+        externalUserName:null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null,
+        scheduleStartTime: null,
+        scheduleEndTime: null,
+      };
+      this.scheduleTime=null;
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.createTime = null;
+      this.queryParams.sTime = null;
+      this.queryParams.eTime = null;
+      this.queryParams.scheduleStartTime = null;
+      this.queryParams.scheduleEndTime = null;
+      this.scheduleTime=null;
+      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
+      getCourseWatchLog(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) {
+            updateCourseWatchLog(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addCourseWatchLog(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 delCourseWatchLog(logIds);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有短链课程看课记录数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportCourseWatchLog(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    }
+  }
+};
+</script>

+ 43 - 0
src/views/course/courseWatchLog/myWatchLogTabIndex.vue

@@ -0,0 +1,43 @@
+<!-- TabComponent.vue -->
+<template>
+  <div class="tab-container">
+    <el-tabs type="card" style="background-color: white">
+      <el-tab-pane label="会员">
+        <member-view/>
+      </el-tab-pane>
+      <el-tab-pane label="企微">
+        <qw-view/>
+      </el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+
+<script>
+import MemberView from './watchLog.vue';
+import QwView from './qw/watchLog.vue';
+
+
+export default {
+  name: 'TabComponent',
+  components: {
+    MemberView,
+    QwView
+  },
+  data() {
+    return {
+    };
+  }
+};
+</script>
+
+<style scoped>
+.tab-container {
+  width: 100%;
+  height: 100%;
+}
+.app-container{
+  padding: 15px !important;
+  margin: 0px !important;
+  background-color: #fff !important;
+}
+</style>

+ 489 - 0
src/views/course/courseWatchLog/qw/index.vue

@@ -0,0 +1,489 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
+      <el-form-item label="会员ID" prop="userId">
+        <el-input
+          v-model="queryParams.userId"
+          placeholder="请输入会员ID"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="会员昵称" prop="nickName">
+        <el-input
+          v-model="queryParams.nickName"
+          placeholder="请输入会员昵称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="企微客户昵称" prop="nickName" >
+        <el-input
+          v-model="queryParams.externalUserName"
+          placeholder="请输入企微客户昵称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="所属销售" prop="companyUserId">
+        <el-select v-model="queryParams.companyUserId" clearable filterable remote
+                   placeholder="请输入关键词" :remote-method="loadCompanyUserOptions"
+                   v-select-load-more="loadMoreCompanyUserOptions"
+                   :loading="companyUserOptionsLoading">
+          <el-option
+            v-for="item in companyUserOptions"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value">
+          </el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="课程" prop="courseId">
+        <el-select filterable  v-model="queryParams.courseId" placeholder="请选择课程"  clearable size="small" @change="courseChange(queryParams.courseId)">
+          <el-option
+            v-for="dict in courseLists"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="小节" prop="videoId">
+        <el-select filterable  v-model="queryParams.videoId" placeholder="请选择小节"  clearable size="small">
+          <el-option
+            v-for="dict in videoList"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="企微ID" prop="qwUserUserId">
+        <el-input
+          v-model="queryParams.qwUserUserId"
+          placeholder="请输入所属企微ID"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="企微员工名称" prop="qwUserName">
+        <el-input
+          v-model="queryParams.qwUserName"
+          placeholder="请输入所属企微员工名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="部门名称" prop="deptName">
+        <el-input
+          v-model="queryParams.deptName"
+          placeholder="请输入部门名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="营期时间" prop="scheduleTime">
+        <el-date-picker
+          v-model="scheduleTime"
+          type="daterange"
+          size="small"
+          style="width: 240px"
+          value-format="yyyy-MM-dd"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          @change="handleScheduleTimeChange">
+        </el-date-picker>
+      </el-form-item>
+      <el-form-item label="创建时间" prop="createTime">
+        <el-date-picker v-model="createTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange"
+                        range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="createChange"></el-date-picker>
+      </el-form-item>
+      <el-form-item label="最新更新时间" prop="updateTime">
+        <el-date-picker v-model="updateTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange"
+                        range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="updateChange"></el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          :loading="exportLoading"
+          @click="handleExport"
+          v-hasPermi="['course:courseWatchLog:export']"
+        >导出</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-tabs type="card" v-model="activeName" @tab-click="handleClickX">
+      <el-tab-pane label="全部" name="00"></el-tab-pane>
+      <el-tab-pane v-for="(item,index) in logTypeOptions" :label="item.dictLabel" :name="item.dictValue"></el-tab-pane>
+    </el-tabs>
+    <el-table border v-loading="loading" :data="courseWatchLogList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="记录编号" align="center" prop="logId" />
+      <el-table-column label="企微客户" align="center" prop="externalUserName"/>
+      <el-table-column label="会员ID" align="center" prop="userId" />
+      <el-table-column label="会员昵称" align="center" prop="fsNickName">
+        <template slot-scope="scope">
+          <div style="display: flex;white-space: nowrap">
+            <div style="margin: auto">
+              {{scope.row.fsNickName}}
+            </div>
+            <el-popover
+              placement="right"
+              title=""
+              trigger="hover">
+              <img slot="reference" :src="scope.row.fsAvatar" style="width: 30px;height: 30px">
+              <img :src="scope.row.fsAvatar" style="max-width: 200px;max-height: 200px">
+            </el-popover>
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column label="课程名称" align="center" prop="courseName" />
+      <el-table-column label="小节名称" align="center" prop="videoName" />
+      <el-table-column label="记录类型" align="center" prop="logType">
+        <template slot-scope="scope">
+          <dict-tag :options="logTypeOptions" :value="scope.row.logType"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="播放时长" align="center" prop="duration" />
+      <el-table-column label="所属销售" align="center" prop="companyUserName" />
+<!--      <el-table-column label="所属公司" align="center" prop="companyName" />-->
+      <el-table-column label="企微员工名称" align="center" prop="qwUserName" />
+<!--      <el-table-column label="所属发送方式" align="center" prop="sendType" />-->
+      <el-table-column label="创建时间" align="center" prop="createTime" />
+      <el-table-column label="更新时间" align="center" prop="updateTime" />
+      <el-table-column label="完课时间" align="center" prop="finishTime" />
+      <el-table-column label="营期时间" align="center" prop="campPeriodTime" />
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+  </div>
+</template>
+
+<script>
+import { listCourseWatchLog, getCourseWatchLog, delCourseWatchLog, addCourseWatchLog, updateCourseWatchLog, exportCourseWatchLog } from "@/api/course/qw/courseWatchLog";
+import { courseList,videoList } from '@/api/course/courseRedPacketLog'
+import { getCompanyUserListLikeName } from "@/api/company/companyUser";
+export default {
+  name: "CourseWatchLog",
+  data() {
+    return {
+      activeName:"00",
+      createTime:null,
+      updateTime:null,
+      courseLists:[],
+      videoList:[],
+      logTypeOptions:[],
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 短链课程看课记录表格数据
+      courseWatchLogList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        userId: null,
+        nickName: null,
+        videoId: null,
+        logType: null,
+        qwExternalContactId: null,
+        externalUserName:null,
+        duration: null,
+        qwUserId: null,
+        qwUserName: null, //企微名称
+        qwUserUserId: null, //企微id
+        deptName: null, //部门名称
+        companyUserId: null,
+        companyId: null,
+        courseId: null,
+        sTime:null,
+        eTime:null,
+        upSTime:null,
+        upETime:null,
+        scheduleStartTime: null,
+        scheduleEndTime: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      },
+      scheduleTime: null,
+      // 员工选项列表
+      companyUserOptionsParams: {
+        name: undefined,
+        hasNextPage: false,
+        pageNum: 1,
+        pageSize: 10
+      },
+      companyUserOptionsLoading: false,
+      companyUserOptions: [],
+    };
+  },
+  created() {
+    courseList().then(response => {
+      this.courseLists = response.list;
+    });
+    this.getList();
+    this.getDicts("sys_course_watch_log_type").then(response => {
+      this.logTypeOptions = response.data;
+    });
+  },
+  methods: {
+    courseChange(row){
+      this.queryParams.videoId=null;
+      if(row === ''){
+        this.videoList=[];
+        return
+      }
+      videoList(row).then(response => {
+        this.videoList=response.list
+      });
+    },
+    createChange() {
+      if (this.createTime != null) {
+        this.queryParams.sTime = this.createTime[0];
+        this.queryParams.eTime = this.createTime[1];
+      } else {
+        this.queryParams.sTime = null;
+        this.queryParams.eTime = null;
+      }
+    },
+
+    updateChange(){
+      if (this.updateTime != null) {
+        this.queryParams.upSTime = this.updateTime[0];
+        this.queryParams.upETime = this.updateTime[1];
+      } else {
+        this.queryParams.upSTime = null;
+        this.queryParams.upETime = null;
+      }
+    },
+    handleClickX(tab,event){
+      this.activeName=tab.name;
+      if(tab.name=="00"){
+        this.queryParams.logType=null;
+      }else{
+        this.queryParams.logType=tab.name;
+      }
+      this.getList()
+    },
+    /** 查询短链课程看课记录列表 */
+    getList() {
+      this.loading = true;
+      if(this.queryParams.logType == "10"){
+        this.queryParams.logType = null;
+      }
+
+      listCourseWatchLog(this.queryParams).then(response => {
+        this.courseWatchLogList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        logId: null,
+        userId: null,
+        videoId: null,
+        logType: null,
+        createTime: null,
+        updateTime: null,
+        qwExternalContactId: null,
+        externalUserName:null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null,
+        scheduleStartTime: null,
+        scheduleEndTime: null,
+      };
+      this.scheduleTime=null;
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.createTime = null;
+      this.scheduleTime = null;
+      this.queryParams.sTime = null;
+      this.queryParams.eTime = null;
+      this.queryParams.upSTime = null;
+      this.queryParams.upETime = null;
+      this.queryParams.scheduleStartTime = null;
+      this.queryParams.scheduleEndTime = null;
+      this.scheduleTime=null;
+      this.updateTime=null;
+      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
+      getCourseWatchLog(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) {
+            updateCourseWatchLog(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addCourseWatchLog(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 delCourseWatchLog(logIds);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有短链课程看课记录数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportCourseWatchLog(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    },
+    handleScheduleTimeChange(val) {
+      if (val) {
+        this.queryParams.scheduleStartTime = val[0];
+        this.queryParams.scheduleEndTime = val[1];
+      } else {
+        this.queryParams.scheduleStartTime = null;
+        this.queryParams.scheduleEndTime = null;
+      }
+    },
+    /**
+     * 根据名称模糊查询用户列表
+     * @param query 参数
+     */
+    loadCompanyUserOptions(query) {
+      this.companyUserOptions = [];
+      if (query === '') {
+        return;
+      }
+
+      this.companyUserOptionsParams.pageNum = 1
+      this.companyUserOptionsParams.name = query
+      this.companyUserOptionsLoading = true;
+      this.getCompanyUserListLikeName()
+    },
+    /**
+     * 获取员工列表
+     */
+    getCompanyUserListLikeName() {
+      getCompanyUserListLikeName(this.companyUserOptionsParams).then(response => {
+        this.companyUserOptions = [...this.companyUserOptions, ...response.data.list]
+        this.companyUserOptionsParams.hasNextPage = response.data.hasNextPage
+        this.companyUserOptionsLoading = false;
+      });
+    },
+    /**
+     * 加载更多员工选项
+     */
+    loadMoreCompanyUserOptions() {
+      if (!this.companyUserOptionsParams.hasNextPage) {
+        return;
+      }
+
+      this.companyUserOptionsParams.pageNum += 1
+      this.getCompanyUserListLikeName()
+    },
+  }
+};
+</script>

+ 396 - 0
src/views/course/courseWatchLog/qw/myCourseWatchLog.vue

@@ -0,0 +1,396 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
+      <el-form-item label="企微账号" prop="qwUserId">
+        <el-select v-model="queryParams.qwUserId" placeholder="企微账号" clearable size="small" @change="updateQwuser()">
+          <el-option
+            v-for="dict in myQwUserList"
+            :key="dict.dictValue"
+            :label="dict.dictLabel + '('+dict.corpName+')'"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="客户ID" prop="qwExternalContactId">
+        <el-input
+          v-model="queryParams.qwExternalContactId"
+          placeholder="请输入会员ID"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="会员ID" prop="userId">
+        <el-input
+          v-model="queryParams.userId"
+          placeholder="请输入会员ID"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="会员昵称" prop="nickName">
+        <el-input
+          v-model="queryParams.nickName"
+          placeholder="请输入会员昵称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="企微客户昵称" prop="nickName">
+        <el-input
+          v-model="queryParams.externalUserName"
+          placeholder="请输入企微客户昵称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="课程" prop="courseId">
+        <el-select filterable  v-model="queryParams.courseId" placeholder="请选择课程"  clearable size="small" @change="courseChange(queryParams.courseId)">
+          <el-option
+            v-for="dict in courseLists"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="小节" prop="videoId">
+        <el-select filterable  v-model="queryParams.videoId" placeholder="请选择小节"  clearable size="small">
+          <el-option
+            v-for="dict in videoList"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="创建时间" prop="createTime">
+        <el-date-picker v-model="createTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="change"></el-date-picker>
+      </el-form-item>
+      <el-table-column label="完课时间" align="center" prop="finishTime" />
+      <el-table-column label="营期时间" align="center" prop="campPeriodTime" />
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          :loading="exportLoading"
+          @click="handleExport"
+          v-hasPermi="['course:courseWatchLog:myExport']"
+        >导出</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-tabs type="card" v-model="queryParams.logType" @tab-click="handleClickX">
+      <el-tab-pane label="全部" name="10"></el-tab-pane>
+      <el-tab-pane v-for="(item,index) in logTypeOptions" :label="item.dictLabel" :name="item.dictValue"></el-tab-pane>
+    </el-tabs>
+    <el-table border v-loading="loading" :data="courseWatchLogList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="记录编号" align="center" prop="logId" />
+      <el-table-column label="企微客户" align="center" prop="externalUserName"/>
+      <el-table-column label="会员ID" align="center" prop="userId" />
+      <el-table-column label="会员昵称" align="center" prop="fsNickName">
+        <template slot-scope="scope">
+          <div style="display: flex;white-space: nowrap">
+            <div style="margin: auto">
+              {{scope.row.fsNickName}}
+            </div>
+            <el-popover
+              placement="right"
+              title=""
+              trigger="hover">
+              <img slot="reference" :src="scope.row.fsAvatar" style="width: 30px;height: 30px">
+              <img :src="scope.row.fsAvatar" style="max-width: 200px;max-height: 200px">
+            </el-popover>
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column label="课程名称" align="center" prop="courseName" />
+      <el-table-column label="小节名称" align="center" prop="videoName" />
+      <el-table-column label="记录类型" align="center" prop="logType">
+        <template slot-scope="scope">
+          <dict-tag :options="logTypeOptions" :value="scope.row.logType"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="播放时长" align="center" prop="duration" />
+      <el-table-column label="所属销售" align="center" prop="companyUserName" />
+<!--      <el-table-column label="所属公司" align="center" prop="companyName" />-->
+      <el-table-column label="企微员工名称" align="center" prop="qwUserName" />
+      <el-table-column label="企微账号" align="center" prop="qwUserName" />
+<!--      <el-table-column label="所属发送方式" align="center" prop="sendType" />-->
+      <el-table-column label="创建时间" align="center" prop="createTime" />
+<!--      <el-table-column label="更新时间" align="center" prop="updateTime" />-->
+      <el-table-column label="完课时间" align="center" prop="lastHeartbeatTime" />
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+  </div>
+</template>
+
+<script>
+import {
+  myListCourseWatchLog,
+  getCourseWatchLog,
+  delCourseWatchLog,
+  addCourseWatchLog,
+  updateCourseWatchLog,
+  exportCourseWatchLog,
+  exportCourseWatchLogMy
+} from "@/api/course/courseWatchLog";
+import { courseList,videoList } from '@/api/course/courseRedPacketLog'
+import {getMyQwUserList} from "@/api/qw/user";
+import {allListTagGroup} from "@/api/qw/tagGroup";
+import {listTag} from "@/api/qw/tag";
+export default {
+  name: "CourseWatchLog",
+  data() {
+    return {
+      createTime:null,
+      courseLists:[],
+      videoList:[],
+      myQwUserList:[],
+      logTypeOptions:[],
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 短链课程看课记录表格数据
+      courseWatchLogList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        userId: null,
+        videoId: null,
+        nickName:null,
+        logType: "10",
+        qwExternalContactId: null,
+        externalUserName:null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null,
+        sTime:null,
+        eTime:null,
+        scheduleStartTime: null,
+        scheduleEndTime: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      }
+    };
+  },
+  created() {
+    courseList().then(response => {
+      this.courseLists = response.list;
+    });
+    this.getList();
+    this.getDicts("sys_course_watch_log_type").then(response => {
+      this.logTypeOptions = response.data;
+    });
+
+    getMyQwUserList().then(response => {
+      this.myQwUserList = response.data;
+    });
+  },
+  methods: {
+    courseChange(row){
+      this.queryParams.videoId=null;
+      if(row === ''){
+        this.videoList=[];
+        return
+      }
+      videoList(row).then(response => {
+        this.videoList=response.list
+      });
+    },
+    updateQwuser(){
+      for (const user of this.myQwUserList) {
+        if (user.dictValue == this.queryParams.qwUserId) {
+          this.queryParams.corpId=user.corpId;
+          break;
+        }
+      }
+      this.getList();
+    },
+    change() {
+      if (this.createTime != null) {
+        this.queryParams.sTime = this.createTime[0];
+        this.queryParams.eTime = this.createTime[1];
+      } else {
+        this.queryParams.sTime = null;
+        this.queryParams.eTime = null;
+      }
+    },
+
+    handleClickX(tab){
+      if(tab.name==="10"){
+        this.queryParams.logType=null;
+      }else{
+        this.queryParams.logType=tab.name;
+      }
+      this.getList()
+    },
+    /** 查询短链课程看课记录列表 */
+    getList() {
+      this.loading = true;
+      myListCourseWatchLog(this.queryParams).then(response => {
+        this.courseWatchLogList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        logId: null,
+        userId: null,
+        videoId: null,
+        logType: null,
+        createTime: null,
+        updateTime: null,
+        qwExternalContactId: null,
+        externalUserName:null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null,
+        scheduleStartTime: null,
+        scheduleEndTime: null,
+      };
+      this.scheduleTime=null;
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.createTime = null;
+      this.queryParams.sTime = null;
+      this.queryParams.eTime = null;
+      this.queryParams.scheduleStartTime = null;
+      this.queryParams.scheduleEndTime = null;
+      this.scheduleTime=null;
+      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
+      getCourseWatchLog(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) {
+            updateCourseWatchLog(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addCourseWatchLog(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 delCourseWatchLog(logIds);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有短链课程看课记录数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportCourseWatchLogMy(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    }
+  }
+};
+</script>

+ 318 - 0
src/views/course/courseWatchLog/qw/statistics.vue

@@ -0,0 +1,318 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="公司名" prop="companyId">
+        <el-select filterable style="width: 220px" v-model="queryParams.companyId" placeholder="请选择公司名" clearable size="small">
+          <el-option
+            v-for="item in companys"
+            :key="item.companyId"
+            :label="item.companyName"
+            :value="item.companyId"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="课程" prop="courseId">
+        <el-select filterable  v-model="queryParams.courseId" placeholder="请选择课程"  clearable size="small" @change="courseChange(queryParams.courseId)">
+          <el-option
+            v-for="dict in courseLists"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="小节" prop="videoId">
+        <el-select filterable  v-model="queryParams.videoId" placeholder="请选择小节"  clearable size="small">
+          <el-option
+            v-for="dict in videoList"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="企微昵称" prop="nickName">
+        <el-input
+          v-model="queryParams.nickName"
+          placeholder="请输入企微昵称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="创建时间" prop="createTime">
+        <el-date-picker v-model="createTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="change"></el-date-picker>
+      </el-form-item>
+
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-table border v-loading="loading" :data="courseWatchLogList" @selection-change="handleSelectionChange"  show-summary>
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="企微员工名称" align="center" prop="qwUserName" />
+      <el-table-column label="发课时间" align="center" prop="createTime"/>
+      <el-table-column label="课程名称" align="center" prop="courseName" />
+      <el-table-column label="小节名称" align="center" prop="videoName" />
+      <el-table-column label="待看课" align="center" prop="type3" />
+      <el-table-column label="看课中" align="center" prop="type1" />
+      <el-table-column label="已完课" align="center" prop="type2" />
+      <el-table-column label="看课中断" align="center" prop="type4" />
+
+
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+  </div>
+</template>
+
+<script>
+import { listCourseWatchLog, getCourseWatchLog, delCourseWatchLog, addCourseWatchLog, updateCourseWatchLog, exportCourseWatchLog,statisticsList } from "@/api/course/qw/courseWatchLog";
+import { courseList,videoList } from '@/api/course/courseRedPacketLog'
+import {getCompanyList} from "@/api/company/company";
+export default {
+  name: "CourseWatchLog",
+  data() {
+    return {
+      companys:[],
+      activeName:"00",
+      createTime:null,
+      courseLists:[],
+      videoList:[],
+      logTypeOptions:[],
+      // 遮罩层
+      loading: false,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 短链课程看课记录表格数据
+      courseWatchLogList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        userId: null,
+        nickName: null,
+        videoId: null,
+        logType: null,
+        qwExternalContactId: null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null,
+        sTime:null,
+        eTime:null,
+        scheduleStartTime: null,
+        scheduleEndTime: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      },
+      scheduleTime: 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();
+      }
+    });
+    courseList().then(response => {
+      this.courseLists = response.list;
+    });
+    this.getList();
+    this.getDicts("sys_course_watch_log_type").then(response => {
+      this.logTypeOptions = response.data;
+    });
+  },
+  methods: {
+    courseChange(row){
+      this.queryParams.videoId=null;
+      if(row === ''){
+        this.videoList=[];
+        return
+      }
+      videoList(row).then(response => {
+        this.videoList=response.list
+      });
+    },
+    change() {
+      if (this.createTime != null) {
+        this.queryParams.sTime = this.createTime[0];
+        this.queryParams.eTime = this.createTime[1];
+      } else {
+        this.queryParams.sTime = null;
+        this.queryParams.eTime = null;
+      }
+    },
+    handleClickX(tab,event){
+      this.activeName=tab.name;
+      if(tab.name=="00"){
+        this.queryParams.logType=null;
+      }else{
+        this.queryParams.logType=tab.name;
+      }
+      this.getList()
+    },
+    /** 查询短链课程看课记录列表 */
+    getList() {
+      this.loading = true;
+      statisticsList(this.queryParams).then(response => {
+        this.courseWatchLogList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        logId: null,
+        userId: null,
+        videoId: null,
+        logType: null,
+        createTime: null,
+        updateTime: null,
+        qwExternalContactId: null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      if(this.queryParams.companyId == null) {
+        this.$message.warning("公司不能为空!")
+        return;
+      }
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.createTime = null;
+      this.scheduleTime = null;
+      this.queryParams.sTime = null;
+      this.queryParams.eTime = null;
+      this.queryParams.scheduleStartTime = null;
+      this.queryParams.scheduleEndTime = null;
+      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
+      getCourseWatchLog(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) {
+            updateCourseWatchLog(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addCourseWatchLog(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 delCourseWatchLog(logIds);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有短链课程看课记录数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportCourseWatchLog(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    },
+    handleScheduleTimeChange(val) {
+      if (val) {
+        this.queryParams.scheduleStartTime = val[0];
+        this.queryParams.scheduleEndTime = val[1];
+      } else {
+        this.queryParams.scheduleStartTime = null;
+        this.queryParams.scheduleEndTime = null;
+      }
+    },
+  }
+};
+</script>

+ 602 - 0
src/views/course/courseWatchLog/qw/watchLog.vue

@@ -0,0 +1,602 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
+      <el-form-item label="企微账号" prop="qwUserId">
+        <el-select v-model="queryParams.qwUserId" placeholder="企微账号" clearable size="small"
+                   @change="updateQwuser()">
+          <el-option
+            v-for="dict in myQwUserList"
+            :key="dict.dictValue"
+            :label="dict.dictLabel + '('+dict.corpName+')'"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="客户ID" prop="qwExternalContactId">
+        <el-input
+          v-model="queryParams.qwExternalContactId"
+          placeholder="请输入会员ID"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="会员ID" prop="userId">
+        <el-input
+          v-model="queryParams.userId"
+          placeholder="请输入会员ID"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="会员昵称" prop="nickName">
+        <el-input
+          v-model="queryParams.nickName"
+          placeholder="请输入会员昵称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="企微客户昵称" prop="nickName">
+        <el-input
+          v-model="queryParams.externalUserName"
+          placeholder="请输入企微客户昵称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="课程" prop="courseId">
+        <el-select filterable v-model="queryParams.courseId" placeholder="请选择课程" clearable size="small"
+                   @change="courseChange(queryParams.courseId)">
+          <el-option
+            v-for="dict in courseLists"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="小节" prop="videoId">
+        <el-select filterable v-model="queryParams.videoId" placeholder="请选择小节" clearable size="small">
+          <el-option
+            v-for="dict in videoList"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="营期时间" prop="scheduleTime">
+        <el-date-picker
+          v-model="scheduleTime"
+          type="daterange"
+          size="small"
+          style="width: 240px"
+          value-format="yyyy-MM-dd"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          @change="handleScheduleTimeChange">
+        </el-date-picker>
+      </el-form-item>
+      <el-form-item label="创建时间" prop="createTime">
+        <el-date-picker v-model="createTime" size="small" style="width: 220px" value-format="yyyy-MM-dd"
+                        type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
+                        @change="createChange"></el-date-picker>
+      </el-form-item>
+      <el-form-item label="最新更新时间" prop="updateTime">
+        <el-date-picker v-model="updateTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange"
+                        range-separator="-" start-placeholder="开始日期"
+                        end-placeholder="结束日期" @change="updateChange"></el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          :loading="exportLoading"
+          @click="handleExport"
+          v-hasPermi="['course:courseWatchLog:myExport']"
+        >导出
+        </el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-tabs type="card" v-model="activeName" @tab-click="handleClickX">
+      <el-tab-pane label="全部" name="00"></el-tab-pane>
+      <el-tab-pane v-for="(item,index) in logTypeOptions" :label="item.dictLabel" :name="item.dictValue"></el-tab-pane>
+    </el-tabs>
+    <el-table border v-loading="loading" :data="courseWatchLogList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center"/>
+      <el-table-column label="记录编号" align="center" prop="logId"/>
+      <el-table-column label="企微客户" align="center" prop="externalUserName"/>
+      <el-table-column label="会员ID" align="center" prop="userId"/>
+      <el-table-column label="会员昵称" align="center" prop="fsNickName">
+        <template slot-scope="scope">
+          <div style="display: flex;white-space: nowrap">
+            <div style="margin: auto">
+              {{ scope.row.fsNickName }}
+            </div>
+            <el-popover
+              placement="right"
+              title=""
+              trigger="hover">
+              <img slot="reference" :src="scope.row.fsAvatar" style="width: 30px;height: 30px">
+              <img :src="scope.row.fsAvatar" style="max-width: 200px;max-height: 200px">
+            </el-popover>
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column label="课程名称" align="center" prop="courseName"/>
+      <el-table-column label="小节名称" align="center" prop="videoName"/>
+      <el-table-column label="记录类型" align="center" prop="logType">
+        <template slot-scope="scope">
+          <dict-tag :options="logTypeOptions" :value="scope.row.logType"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="播放时长" align="center" prop="duration"/>
+      <el-table-column label="所属销售" align="center" prop="companyUserName"/>
+<!--      <el-table-column label="所属公司" align="center" prop="companyName"/>-->
+      <el-table-column label="企微员工名称" align="center" prop="qwUserName"/>
+      <el-table-column label="企微账号" align="center" prop="qwUserName"/>
+<!--      <el-table-column label="所属发送方式" align="center" prop="sendType"/>-->
+      <el-table-column label="创建时间" align="center" prop="createTime" width="100px"/>
+      <el-table-column label="更新时间" align="center" prop="updateTime" width="100px" />
+      <el-table-column label="完课时间" align="center" prop="finishTime" width="100px" />
+      <el-table-column label="营期时间" align="center" prop="campPeriodTime" />
+      <el-table-column
+        fixed="right"
+        label="操作"
+        width="100">
+        <template slot-scope="scope">
+          <el-button @click="openAnswerLogFun(scope.row)" type="text" size="small">答题记录</el-button>
+          <el-button @click="openRedLogFun(scope.row)" type="text" size="small">红包记录</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="做题记录" :visible.sync="openAnswerLog" size="70%" append-to-body>
+      <el-table border v-loading="" :data="answerLogsList">
+        <el-table-column label="小程序用户名" align="center" prop="userName">
+          <template slot-scope="scope">
+            <div style="display: flex;white-space: nowrap">
+              <div style="margin: auto">
+                {{ scope.row.userName }}
+              </div>
+              <el-popover
+                placement="right"
+                title=""
+                trigger="hover">
+                <img slot="reference" :src="scope.row.fsAvatar" style="width: 30px;height: 30px">
+                <img :src="scope.row.fsAvatar" style="max-width: 200px;max-height: 200px">
+              </el-popover>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="课程名称" align="center" prop="courseName"/>
+        <el-table-column label="小节名称" align="center" prop="videoName"/>
+        <el-table-column label="是否全部正确" align="center" prop="isRight">
+          <template slot-scope="scope">
+            <dict-tag :options="sysCompanyOr" :value="scope.row.isRight"></dict-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="销售名称" align="center" prop="companyUserName"/>
+        <el-table-column label="企微员工名称" align="center" prop="qwUserName"/>
+        <el-table-column label="公司名称" align="center" prop="companyName"/>
+        <el-table-column label="创建时间" align="center" prop="createTime"/>
+      </el-table>
+
+      <pagination
+        v-show="answerLogTotal>0"
+        :total="answerLogTotal"
+        :page.sync="answerLogQueryParams.pageNum"
+        :limit.sync="answerLogQueryParams.pageSize"
+        @pagination="answerLogList"
+      />
+    </el-drawer>
+
+    <el-drawer title="红包记录" :visible.sync="openRedLog" size="70%" append-to-body>
+      <el-table border v-loading="" :data="redLogsList">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="记录编号" align="center" prop="logId" />
+        <el-table-column label="批次单号" align="center" prop="outBatchNo" />
+        <el-table-column label="课程名称" align="center" prop="courseId" >
+          <template slot-scope="scope">
+            <span prop="status" v-for="(item, index) in courseLists"    v-if="scope.row.courseId==item.dictValue">{{item.dictLabel}}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="小节名称" align="center" prop="title" />
+        <el-table-column label="会员id" align="center" prop="userId" />
+        <el-table-column label="会员昵称" align="center" prop="fsNickName">
+          <template slot-scope="scope">
+            <div style="display: flex;white-space: nowrap">
+              <div style="margin: auto">
+                {{scope.row.fsNickName}}
+              </div>
+              <el-popover
+                placement="right"
+                title=""
+                trigger="hover">
+                <img slot="reference" :src="scope.row.fsAvatar" style="width: 30px;height: 30px">
+                <img :src="scope.row.fsAvatar" style="max-width: 200px;max-height: 200px">
+              </el-popover>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="会员电话" align="center" prop="phone" />
+        <el-table-column label="所属销售" align="center" prop="companyUserName" />
+        <el-table-column label="所属公司" align="center" prop="companyName" />
+        <el-table-column label="转帐金额" align="center" prop="amount" />
+        <el-table-column label="状态" align="center" prop="status" >
+          <template slot-scope="scope">
+            <el-tag>{{ scope.row.status === 0 ? "发送中" : "已完成" }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="企微员工名称" align="center" prop="qwUserName" />
+        <el-table-column label="创建时间" align="center" prop="createTime" />
+      </el-table>
+
+      <pagination
+        v-show="redLogTotal>0"
+        :total="redLogTotal"
+        :page.sync="redLogQueryParams.pageNum"
+        :limit.sync="redLogQueryParams.pageSize"
+        @pagination="redLogList"
+      />
+    </el-drawer>
+
+  </div>
+</template>
+
+<script>
+import {
+  addCourseWatchLog,
+  delCourseWatchLog,
+  exportCourseWatchLog, exportCourseWatchLogMy,
+  getCourseWatchLog,
+  myListCourseWatchLog,
+  updateCourseWatchLog
+} from "@/api/course/courseWatchLog";
+import {courseList, myListCourseRedPacketLog, videoList} from '@/api/course/courseRedPacketLog'
+import {myListLogs} from "@/api/course/courseAnswerlogs";
+import {getMyQwUserList} from "@/api/qw/user";
+
+
+export default {
+  name: "CourseWatchLog",
+  data() {
+    return {
+      activeName:"00",
+      createTime: null,
+      updateTime:null,
+      courseLists: [],
+      videoList: [],
+      myQwUserList: [],
+      logTypeOptions: [],
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      scheduleTime: null,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      sysCompanyOr: [],
+      // 总条数
+      total: 0,
+      // 短链课程看课记录表格数据
+      courseWatchLogList: [],
+
+
+      openAnswerLog: false,
+      loadingAnswerLog: true,
+      answerLogsList: [],
+      answerLogTotal: 0,
+      answerLogQueryParams: {
+        pageNum: 1,
+        pageSize: 10,
+      },
+
+
+      openRedLog: false,
+      loadingRedLog: true,
+      redLogsList: [],
+      redLogTotal: 0,
+      redLogQueryParams: {
+        pageNum: 1,
+        pageSize: 10,
+      },
+
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        userId: null,
+        videoId: null,
+        nickName: null,
+        logType: "10",
+        qwExternalContactId: null,
+        externalUserName:null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null,
+        sTime: null,
+        eTime: null,
+        upSTime:null,
+        upETime:null,
+        scheduleStartTime: null,
+        scheduleEndTime: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {}
+    };
+  },
+  created() {
+    courseList().then(response => {
+      this.courseLists = response.list;
+    });
+    this.getList();
+    this.getDicts("sys_course_watch_log_type").then(response => {
+      this.logTypeOptions = response.data;
+    });
+
+    this.getDicts("sys_company_or").then(response => {
+      this.sysCompanyOr = response.data;
+    });
+    getMyQwUserList().then(response => {
+      this.myQwUserList = response.data;
+    });
+  },
+  methods: {
+    courseChange(row) {
+      this.queryParams.videoId = null;
+      if (row === '') {
+        this.videoList = [];
+        return
+      }
+      videoList(row).then(response => {
+        this.videoList = response.list
+      });
+    },
+    updateQwuser() {
+      for (const user of this.myQwUserList) {
+        if (user.dictValue == this.queryParams.qwUserId) {
+          this.queryParams.corpId = user.corpId;
+          break;
+        }
+      }
+      this.getList();
+    },
+    createChange() {
+      if (this.createTime != null) {
+        this.queryParams.sTime = this.createTime[0];
+        this.queryParams.eTime = this.createTime[1];
+      } else {
+        this.queryParams.sTime = null;
+        this.queryParams.eTime = null;
+      }
+    },
+    updateChange(){
+      if (this.updateTime != null) {
+        this.queryParams.upSTime = this.updateTime[0];
+        this.queryParams.upETime = this.updateTime[1];
+      } else {
+        this.queryParams.upSTime = null;
+        this.queryParams.upETime = null;
+      }
+    },
+    handleClickX(tab) {
+      this.activeName=tab.name;
+      if(tab.name=="00"){
+        this.queryParams.logType=null;
+      }else{
+        this.queryParams.logType=tab.name;
+      }
+      this.getList()
+    },
+
+    handleScheduleTimeChange(val) {
+      if (val) {
+        this.queryParams.scheduleStartTime = val[0];
+        this.queryParams.scheduleEndTime = val[1];
+      } else {
+        this.queryParams.scheduleStartTime = null;
+        this.queryParams.scheduleEndTime = null;
+      }
+    },
+    /** 查询短链课程看课记录列表 */
+    getList() {
+      this.loading = true;
+      let param = JSON.parse(JSON.stringify(this.queryParams));
+      if (param.logType == "10") {
+        param.logType = null;
+      }
+      myListCourseWatchLog(param).then(response => {
+        this.courseWatchLogList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        logId: null,
+        userId: null,
+        videoId: null,
+        logType: null,
+        createTime: null,
+        updateTime: null,
+        qwExternalContactId: null,
+        externalUserName:null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null,
+        scheduleStartTime: null,
+        scheduleEndTime: null,
+      };
+      this.scheduleTime=null;
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.createTime = null;
+      this.queryParams.sTime = null;
+      this.queryParams.eTime = null;
+      this.queryParams.upSTime = null;
+      this.queryParams.upETime = null;
+      this.queryParams.scheduleStartTime = null;
+      this.queryParams.scheduleEndTime = null;
+      this.scheduleTime=null;
+      this.updateTime=null;
+      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
+      getCourseWatchLog(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) {
+            updateCourseWatchLog(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addCourseWatchLog(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 delCourseWatchLog(logIds);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("删除成功");
+      }).catch(() => {
+      });
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有短链课程看课记录数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        this.exportLoading = true;
+        return exportCourseWatchLogMy(queryParams);
+      }).then(response => {
+        this.download(response.msg);
+        this.exportLoading = false;
+      }).catch(() => {
+      });
+    },
+    openAnswerLogFun(row) {
+      this.openAnswerLog = true;
+      this.answerLogQueryParams.watchLogId = row.logId;
+      this.answerLogList();
+    },
+    answerLogList() {
+      this.loadingAnswerLog = true;
+      myListLogs(this.answerLogQueryParams).then(e => {
+        this.answerLogsList = e.rows;
+        this.answerLogTotal = e.total;
+        this.loadingAnswerLog = false;
+      })
+    },
+    openRedLogFun(row) {
+      this.openRedLog = true;
+      this.redLogQueryParams.watchLogId = row.logId;
+      this.redLogList();
+    },
+    redLogList() {
+      this.loadingRedLog = true;
+      console.info(this.redLogQueryParams)
+      myListCourseRedPacketLog(this.redLogQueryParams).then(e => {
+        this.redLogsList = e.rows;
+        this.redLogTotal = e.total;
+        this.loadingRedLog = false;
+      })
+    },
+  }
+};
+</script>

+ 314 - 0
src/views/course/courseWatchLog/qw/watchLogStatistics.vue

@@ -0,0 +1,314 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
+
+      <el-form-item label="企微昵称" prop="nickName" >
+        <el-input
+          v-model="queryParams.nickName"
+          placeholder="请输入企微昵称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+
+
+      <el-form-item label="创建时间" prop="createTime">
+        <el-date-picker v-model="createTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange"
+                        range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="createChange"></el-date-picker>
+      </el-form-item>
+
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          :loading="exportLoading"
+          @click="handleExport"
+        >导出</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table border v-loading="loading" :data="courseWatchLogList" @selection-change="handleSelectionChange">
+      <el-table-column label="企微客户" align="center" prop="externalUserName"/>
+<!--      <el-table-column label="所属销售" align="center" prop="companyUserName" /> -->
+      <el-table-column label="企微员工名称" align="center" prop="qwUserName" />
+      <el-table-column label="创建时间" align="center" prop="createTime" />
+      <el-table-column label="进线时间" align="center" prop="userCreateTime" />
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+  </div>
+</template>
+
+<script>
+import { listCourseWatchLog, getCourseWatchLog, delCourseWatchLog, addCourseWatchLog, updateCourseWatchLog, exportCourseWatchLog,watchLogStatistics,watchLogStatisticsExport } from "@/api/course/qw/courseWatchLog";
+import {allList}from "@/api/company/company";
+import { courseList,videoList } from '@/api/course/courseRedPacketLog'
+export default {
+  name: "CourseWatchLog",
+  data() {
+    return {
+      activeName:"00",
+      createTime:null,
+      updateTime:null,
+      courseLists:[],
+      videoList:[],
+      logTypeOptions:[],
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 短链课程看课记录表格数据
+      courseWatchLogList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        userId: null,
+        nickName: null,
+        videoId: null,
+        logType: null,
+        qwExternalContactId: null,
+        externalUserName:null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null,
+        sTime:null,
+        eTime:null,
+        upSTime:null,
+        upETime:null,
+        scheduleStartTime: null,
+        scheduleEndTime: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      },
+      scheduleTime: null,
+    };
+  },
+  created() {
+    courseList().then(response => {
+      this.courseLists = response.list;
+    });
+    this.getList();
+    this.getDicts("sys_course_watch_log_type").then(response => {
+      this.logTypeOptions = response.data;
+    });
+  },
+  methods: {
+    courseChange(row){
+      this.queryParams.videoId=null;
+      if(row === ''){
+        this.videoList=[];
+        return
+      }
+      videoList(row).then(response => {
+        this.videoList=response.list
+      });
+    },
+    createChange() {
+      if (this.createTime != null) {
+        this.queryParams.sTime = this.createTime[0];
+        this.queryParams.eTime = this.createTime[1];
+      } else {
+        this.queryParams.sTime = null;
+        this.queryParams.eTime = null;
+      }
+    },
+
+    updateChange(){
+      if (this.updateTime != null) {
+        this.queryParams.upSTime = this.updateTime[0];
+        this.queryParams.upETime = this.updateTime[1];
+      } else {
+        this.queryParams.upSTime = null;
+        this.queryParams.upETime = null;
+      }
+    },
+    handleClickX(tab,event){
+      this.activeName=tab.name;
+      if(tab.name=="00"){
+        this.queryParams.logType=null;
+      }else{
+        this.queryParams.logType=tab.name;
+      }
+      this.getList()
+    },
+    /** 查询短链课程看课记录列表 */
+    getList() {
+      this.loading = true;
+      if(this.queryParams.logType == "10"){
+        this.queryParams.logType = null;
+      }
+
+      watchLogStatistics(this.queryParams).then(response => {
+        this.courseWatchLogList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        logId: null,
+        userId: null,
+        videoId: null,
+        logType: null,
+        createTime: null,
+        updateTime: null,
+        qwExternalContactId: null,
+        externalUserName:null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null,
+        scheduleStartTime: null,
+        scheduleEndTime: null,
+      };
+      this.scheduleTime=null;
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.createTime = null;
+      this.scheduleTime = null;
+      this.queryParams.sTime = null;
+      this.queryParams.eTime = null;
+      this.queryParams.upSTime = null;
+      this.queryParams.upETime = null;
+      this.queryParams.scheduleStartTime = null;
+      this.queryParams.scheduleEndTime = null;
+      this.scheduleTime=null;
+      this.updateTime=null;
+      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
+      getCourseWatchLog(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) {
+            updateCourseWatchLog(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addCourseWatchLog(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 delCourseWatchLog(logIds);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有短链课程看课记录数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return watchLogStatisticsExport(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    },
+    handleScheduleTimeChange(val) {
+      if (val) {
+        this.queryParams.scheduleStartTime = val[0];
+        this.queryParams.scheduleEndTime = val[1];
+      } else {
+        this.queryParams.scheduleStartTime = null;
+        this.queryParams.scheduleEndTime = null;
+      }
+    },
+  }
+};
+</script>

+ 390 - 0
src/views/course/courseWatchLog/statistics.vue

@@ -0,0 +1,390 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="用户" prop="nickName">
+        <el-select v-model="queryParams.userId" remote placeholder="用户名/手机号" filterable clearable  style="width: 100%;"
+                   @keyup.enter.native="handleQuery"
+                   :remote-method="remoteGetFsUserList"
+                   @clear="handleClear"
+                   :loading="queryUserLoading"
+        >
+          <el-option
+            v-for="dict in fsUserList"
+            :key="`${dict.nickname} - ${dict.phone}`"
+            :label="`${dict.nickname} - ${dict.phone}`"
+            :value="dict.userId">
+          </el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="公司名" prop="companyId">
+        <el-select filterable style="width: 220px" v-model="queryParams.companyId" @change="handleSeller" placeholder="请选择公司名" clearable size="small">
+          <el-option
+            v-for="item in companys"
+            :key="item.companyId"
+            :label="item.companyName"
+            :value="item.companyId"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="销售" prop="nickName" v-if="queryParams.companyId">
+        <el-select v-model="queryParams.companyUserId" remote placeholder="请选择" filterable clearable  style="width: 100%;" @keyup.enter.native="handleQuery">
+          <el-option
+            v-for="dict in companyUserList"
+            :key="`${dict.nickName} - ${dict.userName}`"
+            :label="`${dict.nickName} - ${dict.userName}`"
+            :value="dict.userId">
+          </el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="项目" prop="courseId">
+        <el-select filterable  v-model="queryParams.project" placeholder="请选择项目"  clearable size="small">
+          <el-option
+            v-for="dict in projectLists"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="parseInt(dict.dictValue)"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="课程" prop="courseId">
+        <el-select filterable  v-model="queryParams.courseId" placeholder="请选择课程"  clearable size="small" @change="courseChange(queryParams.courseId)">
+          <el-option
+            v-for="dict in courseLists"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="小节" prop="videoId">
+        <el-select filterable  v-model="queryParams.videoId" placeholder="请选择小节"  clearable size="small">
+          <el-option
+            v-for="dict in videoList"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="创建时间" prop="createTime">
+        <el-date-picker v-model="createTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="change"></el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-table border v-loading="loading" :data="courseWatchLogList" @selection-change="handleSelectionChange"  show-summary>
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="用户" align="center" prop="userName" />
+      <el-table-column label="对应销售" align="center" prop="companyUserName" />
+      <el-table-column label="发课时间" align="center" prop="createTime"/>
+      <el-table-column label="项目" align="center" prop="projectName" />
+      <el-table-column label="课程名称" align="center" prop="courseName" />
+      <el-table-column label="小节名称" align="center" prop="videoName" />
+      <el-table-column label="待看课" align="center" prop="type3" />
+      <el-table-column label="看课中" align="center" prop="type1" />
+      <el-table-column label="已完课" align="center" prop="type2" />
+      <el-table-column label="看课中断" align="center" prop="type4" />
+    </el-table>
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </div>
+</template>
+
+<script>
+import { listCourseWatchLog, getCourseWatchLog, delCourseWatchLog, addCourseWatchLog, updateCourseWatchLog, exportCourseWatchLog,statisticsList } from "@/api/course/courseWatchLog";
+import { courseList,videoList } from '@/api/course/courseRedPacketLog'
+import {getUserList} from "@/api/company/companyUser";
+import {getFsUserList} from "@/api/users/user";
+import {getCompanyList} from "@/api/company/company";
+export default {
+  name: "CourseWatchLog",
+  data() {
+    return {
+      companys:[],
+      activeName:"00",
+      createTime:null,
+      courseLists:[],
+      videoList:[],
+      logTypeOptions:[],
+      // 遮罩层
+      loading: false,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 短链课程看课记录表格数据
+      companyUserList: [],
+      courseWatchLogList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      projectLists: [],
+      fsUserList: [],
+      queryUserLoading: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        userId: null,
+        nickName: null,
+        videoId: null,
+        logType: null,
+        qwExternalContactId: null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null,
+        sTime:null,
+        eTime:null,
+        scheduleStartTime: null,
+        scheduleEndTime: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      },
+      scheduleTime: 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();
+      }
+    });
+    courseList().then(response => {
+      this.courseLists = response.list;
+    });
+    this.getDicts("sys_course_watch_log_type").then(response => {
+      this.logTypeOptions = response.data;
+    });
+    this.getDicts("sys_course_project").then(response => {
+      this.projectLists = response.data;
+    })
+
+  },
+  methods: {
+    handleSeller(){
+      console.log(this.queryParams.companyId)
+      if(this.queryParams.companyId != null) {
+        getUserList(this.queryParams.companyId).then(res=>{
+          if(res.code === 200) {
+            this.companyUserList = res.data
+          }
+        })
+      }
+    },
+    handleClear(){
+      this.queryUserLoading = false;
+      this.fsUserList = [];
+    },
+    remoteGetFsUserList(query){
+      if(query){
+        this.queryUserLoading = true;
+        const isNumeric = /^\d+$/.test(query);
+        let payload = {
+          username: query,
+          nickname: query
+        }
+        if(isNumeric) {
+          payload = {
+            userId: query,
+            username: query,
+            phone: query
+          }
+        }
+        getFsUserList(payload).then(res=>{
+          if(res.code === 200) {
+            this.fsUserList = res.data
+          }
+        }).finally(()=>{
+          this.queryUserLoading = false;
+        })
+      }
+    },
+    courseChange(row){
+      this.queryParams.videoId=null;
+      if(row === ''){
+        this.videoList=[];
+        return
+      }
+      videoList(row).then(response => {
+        this.videoList=response.list
+      });
+    },
+    change() {
+      if (this.createTime != null) {
+        this.queryParams.sTime = this.createTime[0];
+        this.queryParams.eTime = this.createTime[1];
+      } else {
+        this.queryParams.sTime = null;
+        this.queryParams.eTime = null;
+      }
+    },
+    handleClickX(tab,event){
+      this.activeName=tab.name;
+      if(tab.name=="00"){
+        this.queryParams.logType=null;
+      }else{
+        this.queryParams.logType=tab.name;
+      }
+      this.getList()
+    },
+    /** 查询短链课程看课记录列表 */
+    getList() {
+      this.loading = true;
+      statisticsList(this.queryParams).then(response => {
+        this.courseWatchLogList = response.rows;
+        this.total = response.total;
+
+      }).finally(()=>{
+        this.loading = false;
+      })
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        logId: null,
+        userId: null,
+        videoId: null,
+        logType: null,
+        createTime: null,
+        updateTime: null,
+        qwExternalContactId: null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      if(this.queryParams.companyId == null){
+        this.$message.error("请选择公司!");
+        return;
+      }
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.createTime = null;
+      this.scheduleTime = null;
+      this.queryParams.sTime = null;
+      this.queryParams.eTime = null;
+      this.queryParams.scheduleStartTime = null;
+      this.queryParams.scheduleEndTime = null;
+      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
+      getCourseWatchLog(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) {
+            updateCourseWatchLog(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addCourseWatchLog(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 delCourseWatchLog(logIds);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有短链课程看课记录数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportCourseWatchLog(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    },
+    handleScheduleTimeChange(val) {
+      if (val) {
+        this.queryParams.scheduleStartTime = val[0];
+        this.queryParams.scheduleEndTime = val[1];
+      } else {
+        this.queryParams.scheduleStartTime = null;
+        this.queryParams.scheduleEndTime = null;
+      }
+    },
+  }
+};
+</script>

+ 43 - 0
src/views/course/courseWatchLog/statisticsTabIndex.vue

@@ -0,0 +1,43 @@
+<!-- TabComponent.vue -->
+<template>
+  <div class="tab-container">
+    <el-tabs type="card" style="background-color: white">
+      <el-tab-pane label="会员">
+        <member-view/>
+      </el-tab-pane>
+      <el-tab-pane label="企微">
+        <qw-view/>
+      </el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+
+<script>
+import MemberView from './statistics.vue';
+import QwView from './qw/statistics.vue';
+
+
+export default {
+  name: 'TabComponent',
+  components: {
+    MemberView,
+    QwView
+  },
+  data() {
+    return {
+    };
+  }
+};
+</script>
+
+<style scoped>
+.tab-container {
+  width: 100%;
+  height: 100%;
+}
+.app-container{
+  padding: 15px !important;
+  margin: 0px !important;
+  background-color: #fff !important;
+}
+</style>

+ 631 - 0
src/views/course/courseWatchLog/watchLog.vue

@@ -0,0 +1,631 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
+      <el-form-item label="用户" prop="qwUserId">
+        <el-select v-model="queryParams.userId" remote placeholder="用户名/手机号" filterable clearable  style="width: 100%;"
+                   @keyup.enter.native="handleQuery"
+                   :remote-method="remoteGetFsUserList"
+                   @clear="handleClear"
+                   :loading="queryUserLoading"
+        >
+          <el-option
+            v-for="dict in fsUserList"
+            :key="`${dict.nickname} - ${dict.phone}`"
+            :label="`${dict.nickname} - ${dict.phone}`"
+            :value="dict.userId">
+          </el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="课程" prop="courseId">
+        <el-select filterable v-model="queryParams.courseId" placeholder="请选择课程" clearable size="small"
+                   @change="courseChange(queryParams.courseId)">
+          <el-option
+            v-for="dict in courseLists"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="小节" prop="videoId">
+        <el-select filterable v-model="queryParams.videoId" placeholder="请选择小节" clearable size="small">
+          <el-option
+            v-for="dict in videoList"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="营期时间" prop="scheduleTime">
+        <el-date-picker
+          v-model="scheduleTime"
+          type="daterange"
+          size="small"
+          style="width: 240px"
+          value-format="yyyy-MM-dd"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          @change="handleScheduleTimeChange">
+        </el-date-picker>
+      </el-form-item>
+      <el-form-item label="创建时间" prop="createTime">
+        <el-date-picker v-model="createTime" size="small" style="width: 220px" value-format="yyyy-MM-dd"
+                        type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
+                        @change="createChange"></el-date-picker>
+      </el-form-item>
+      <el-form-item label="最新更新时间" prop="updateTime">
+        <el-date-picker v-model="updateTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange"
+                        range-separator="-" start-placeholder="开始日期"
+                        end-placeholder="结束日期" @change="updateChange"></el-date-picker>
+      </el-form-item>
+
+      <el-form-item label="类型" prop="type">
+        <el-select filterable  v-model="sourceTypeModel" placeholder="请选择小节"  clearable size="small">
+          <el-option
+            v-for="dict in userSourceTypeOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          :loading="exportLoading"
+          @click="handleExport"
+          v-hasPermi="['course:courseWatchLog:myExport']"
+        >导出
+        </el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-tabs type="card" v-model="activeName" @tab-click="handleClickX">
+      <el-tab-pane label="全部" name="00"></el-tab-pane>
+      <el-tab-pane v-for="(item,index) in logTypeOptions" :label="item.dictLabel" :name="item.dictValue"></el-tab-pane>
+    </el-tabs>
+    <el-table border v-loading="loading" :data="courseWatchLogList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center"/>
+      <el-table-column label="记录编号" align="center" prop="logId"/>
+<!--      <el-table-column label="企微客户" align="center" prop="externalUserName"/>-->
+      <el-table-column label="用户账号" align="center" prop="userName" />
+      <el-table-column label="企微客户" align="center" prop="externalUserName" v-if="queryParams.sourceType == 2"/>
+
+      <el-table-column label="会员昵称" align="center" prop="fsNickName">
+        <template slot-scope="scope">
+          <div style="display: flex;white-space: nowrap">
+            <div style="margin: auto">
+              {{ scope.row.fsNickName }}
+            </div>
+            <el-popover
+              placement="right"
+              title=""
+              trigger="hover">
+              <img slot="reference" :src="scope.row.fsAvatar" style="width: 30px;height: 30px">
+              <img :src="scope.row.fsAvatar" style="max-width: 200px;max-height: 200px">
+            </el-popover>
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column label="项目" align="center" prop="projectName" />
+      <el-table-column label="课程名称" align="center" prop="courseName"/>
+      <el-table-column label="小节名称" align="center" prop="videoName"/>
+      <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="播放时长" align="center" prop="duration"/>
+      <el-table-column label="所属销售" align="center" prop="companyUserName"/>
+<!--      <el-table-column label="所属公司" align="center" prop="companyName"/>-->
+<!--      <el-table-column label="企微员工名称" align="center" prop="qwUserName"/>-->
+<!--      <el-table-column label="企微账号" align="center" prop="qwUserName"/>-->
+<!--      <el-table-column label="所属发送方式" align="center" prop="sendType"/>-->
+      <el-table-column label="企微员工名称" align="center" prop="qwUserName" v-if="queryParams.sourceType == 2"/>
+      <el-table-column label="创建时间" align="center" prop="createTime" width="100px"/>
+      <el-table-column label="更新时间" align="center" prop="updateTime" width="100px" />
+      <el-table-column label="完课时间" align="center" prop="finishTime" width="100px" />
+      <el-table-column label="营期时间" align="center" prop="campPeriodTime" />
+      <el-table-column
+        fixed="right"
+        label="操作"
+        width="100">
+        <template slot-scope="scope">
+          <el-button @click="openAnswerLogFun(scope.row)" type="text" size="small">答题记录</el-button>
+          <el-button @click="openRedLogFun(scope.row)" type="text" size="small">红包记录</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="做题记录" :visible.sync="openAnswerLog" size="70%" append-to-body>
+      <el-table border v-loading="" :data="answerLogsList">
+        <el-table-column label="小程序用户名" align="center" prop="userName">
+          <template slot-scope="scope">
+            <div style="display: flex;white-space: nowrap">
+              <div style="margin: auto">
+                {{ scope.row.userName }}
+              </div>
+              <el-popover
+                placement="right"
+                title=""
+                trigger="hover">
+                <img slot="reference" :src="scope.row.fsAvatar" style="width: 30px;height: 30px">
+                <img :src="scope.row.fsAvatar" style="max-width: 200px;max-height: 200px">
+              </el-popover>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="课程名称" align="center" prop="courseName"/>
+        <el-table-column label="小节名称" align="center" prop="videoName"/>
+        <el-table-column label="是否全部正确" align="center" prop="isRight">
+          <template slot-scope="scope">
+            <dict-tag :options="sysCompanyOr" :value="scope.row.isRight"></dict-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="销售名称" align="center" prop="companyUserName"/>
+        <el-table-column label="企微员工名称" align="center" prop="qwUserName"/>
+        <el-table-column label="公司名称" align="center" prop="companyName"/>
+        <el-table-column label="创建时间" align="center" prop="createTime"/>
+      </el-table>
+
+      <pagination
+        v-show="answerLogTotal>0"
+        :total="answerLogTotal"
+        :page.sync="answerLogQueryParams.pageNum"
+        :limit.sync="answerLogQueryParams.pageSize"
+        @pagination="answerLogList"
+      />
+    </el-drawer>
+
+    <el-drawer title="红包记录" :visible.sync="openRedLog" size="70%" append-to-body>
+      <el-table border v-loading="" :data="redLogsList">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="记录编号" align="center" prop="logId" />
+        <el-table-column label="批次单号" align="center" prop="outBatchNo" />
+        <el-table-column label="课程名称" align="center" prop="courseId" >
+          <template slot-scope="scope">
+            <span prop="status" v-for="(item, index) in courseLists"    v-if="scope.row.courseId==item.dictValue">{{item.dictLabel}}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="小节名称" align="center" prop="title" />
+        <el-table-column label="会员id" align="center" prop="userId" />
+        <el-table-column label="会员昵称" align="center" prop="fsNickName">
+          <template slot-scope="scope">
+            <div style="display: flex;white-space: nowrap">
+              <div style="margin: auto">
+                {{scope.row.fsNickName}}
+              </div>
+              <el-popover
+                placement="right"
+                title=""
+                trigger="hover">
+                <img slot="reference" :src="scope.row.fsAvatar" style="width: 30px;height: 30px">
+                <img :src="scope.row.fsAvatar" style="max-width: 200px;max-height: 200px">
+              </el-popover>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="会员电话" align="center" prop="phone" />
+        <el-table-column label="所属销售" align="center" prop="companyUserName" />
+        <el-table-column label="所属公司" align="center" prop="companyName" />
+        <el-table-column label="转帐金额" align="center" prop="amount" />
+        <el-table-column label="状态" align="center" prop="status" >
+          <template slot-scope="scope">
+            <el-tag>{{ scope.row.status === 0 ? "发送中" : "已完成" }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="企微员工名称" align="center" prop="qwUserName" />
+        <el-table-column label="创建时间" align="center" prop="createTime" />
+      </el-table>
+
+      <pagination
+        v-show="redLogTotal>0"
+        :total="redLogTotal"
+        :page.sync="redLogQueryParams.pageNum"
+        :limit.sync="redLogQueryParams.pageSize"
+        @pagination="redLogList"
+      />
+    </el-drawer>
+
+  </div>
+</template>
+
+<script>
+import {
+  addCourseWatchLog,
+  delCourseWatchLog,
+  exportCourseWatchLog,
+  getCourseWatchLog,
+  myListCourseWatchLog,
+  updateCourseWatchLog
+} from "@/api/course/courseWatchLog";
+import {courseList, myListCourseRedPacketLog, videoList} from '@/api/course/courseRedPacketLog'
+import {myListLogs} from "@/api/course/courseAnswerlogs";
+import {getMyQwUserList} from "@/api/qw/user";
+import {getFsUserList} from "@/api/users/user";
+
+
+export default {
+  name: "CourseWatchLog",
+  data() {
+    return {
+      userSourceTypeOptions: [],
+      activeName:"00",
+      createTime: null,
+      updateTime:null,
+      courseLists: [],
+      videoList: [],
+      myQwUserList: [],
+      logTypeOptions: [],
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      queryUserLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      scheduleTime: null,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      sysCompanyOr: [],
+      // 总条数
+      total: 0,
+      // 短链课程看课记录表格数据
+      courseWatchLogList: [],
+      fsUserList: [],
+
+      openAnswerLog: false,
+      loadingAnswerLog: true,
+      answerLogsList: [],
+      answerLogTotal: 0,
+      answerLogQueryParams: {
+        pageNum: 1,
+        pageSize: 10,
+      },
+
+
+      openRedLog: false,
+      loadingRedLog: true,
+      redLogsList: [],
+      redLogTotal: 0,
+      redLogQueryParams: {
+        pageNum: 1,
+        pageSize: 10,
+      },
+
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        userId: null,
+        videoId: null,
+        nickName: null,
+        logType: "10",
+        qwExternalContactId: null,
+        externalUserName:null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null,
+        sTime: null,
+        eTime: null,
+        upSTime:null,
+        upETime:null,
+        scheduleStartTime: null,
+        scheduleEndTime: null,
+        sourceType: 1
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {}
+    };
+  },
+  computed: {
+    sourceTypeModel: {
+      get() {
+        return this.queryParams.sourceType !== null && this.queryParams.sourceType !== undefined ? this.queryParams.sourceType.toString() : null;
+      },
+      set(newVal) {
+        this.queryParams.sourceType = newVal;
+      }
+    }
+  },
+  created() {
+    this.getDicts('user_source_type').then(response => {
+      this.userSourceTypeOptions = response.data;
+    })
+
+    courseList().then(response => {
+      this.courseLists = response.list;
+    });
+    this.getList();
+    this.getDicts("sys_course_watch_log_type").then(response => {
+      this.logTypeOptions = response.data;
+    });
+
+    this.getDicts("sys_company_or").then(response => {
+      this.sysCompanyOr = response.data;
+    });
+    getMyQwUserList().then(response => {
+      this.myQwUserList = response.data;
+    });
+  },
+  methods: {
+    handleClear(){
+      this.queryUserLoading = false;
+      this.fsUserList = [];
+    },
+    remoteGetFsUserList(query){
+      if(query){
+        this.queryUserLoading = true;
+        const isNumeric = /^\d+$/.test(query);
+        let payload = {
+          username: query,
+          nickname: query
+        }
+        if(isNumeric) {
+          payload = {
+            userId: query,
+            username: query,
+            phone: query
+          }
+        }
+        getFsUserList(payload).then(res=>{
+          if(res.code === 200) {
+            this.fsUserList = res.data
+          }
+        }).finally(()=>{
+          this.queryUserLoading = false;
+        })
+      }
+    },
+    courseChange(row) {
+      this.queryParams.videoId = null;
+      if (row === '') {
+        this.videoList = [];
+        return
+      }
+      videoList(row).then(response => {
+        this.videoList = response.list
+      });
+    },
+    updateQwuser() {
+      for (const user of this.myQwUserList) {
+        if (user.dictValue == this.queryParams.qwUserId) {
+          this.queryParams.corpId = user.corpId;
+          break;
+        }
+      }
+      this.getList();
+    },
+    createChange() {
+      if (this.createTime != null) {
+        this.queryParams.sTime = this.createTime[0];
+        this.queryParams.eTime = this.createTime[1];
+      } else {
+        this.queryParams.sTime = null;
+        this.queryParams.eTime = null;
+      }
+    },
+    updateChange(){
+      if (this.updateTime != null) {
+        this.queryParams.upSTime = this.updateTime[0];
+        this.queryParams.upETime = this.updateTime[1];
+      } else {
+        this.queryParams.upSTime = null;
+        this.queryParams.upETime = null;
+      }
+    },
+    handleClickX(tab) {
+      this.activeName=tab.name;
+      if(tab.name=="00"){
+        this.queryParams.logType=null;
+      }else{
+        this.queryParams.logType=tab.name;
+      }
+      this.getList()
+    },
+
+    handleScheduleTimeChange(val) {
+      if (val) {
+        this.queryParams.scheduleStartTime = val[0];
+        this.queryParams.scheduleEndTime = val[1];
+      } else {
+        this.queryParams.scheduleStartTime = null;
+        this.queryParams.scheduleEndTime = null;
+      }
+    },
+    /** 查询短链课程看课记录列表 */
+    getList() {
+      this.loading = true;
+      let param = JSON.parse(JSON.stringify(this.queryParams));
+      if (param.logType == "10") {
+        param.logType = null;
+      }
+      myListCourseWatchLog(param).then(response => {
+        this.courseWatchLogList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        logId: null,
+        userId: null,
+        videoId: null,
+        logType: null,
+        createTime: null,
+        updateTime: null,
+        qwExternalContactId: null,
+        externalUserName:null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null,
+        scheduleStartTime: null,
+        scheduleEndTime: null,
+      };
+      this.scheduleTime=null;
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.createTime = null;
+      this.queryParams.sTime = null;
+      this.queryParams.eTime = null;
+      this.queryParams.upSTime = null;
+      this.queryParams.upETime = null;
+      this.queryParams.scheduleStartTime = null;
+      this.queryParams.scheduleEndTime = null;
+      this.scheduleTime=null;
+      this.updateTime=null;
+      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
+      getCourseWatchLog(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) {
+            updateCourseWatchLog(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addCourseWatchLog(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 delCourseWatchLog(logIds);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("删除成功");
+      }).catch(() => {
+      });
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有短链课程看课记录数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        this.exportLoading = true;
+        return exportCourseWatchLog(queryParams);
+      }).then(response => {
+        this.download(response.msg);
+        this.exportLoading = false;
+      }).catch(() => {
+      });
+    },
+    openAnswerLogFun(row) {
+      this.openAnswerLog = true;
+      this.answerLogQueryParams.watchLogId = row.logId;
+      this.answerLogList();
+    },
+    answerLogList() {
+      this.loadingAnswerLog = true;
+      myListLogs(this.answerLogQueryParams).then(e => {
+        this.answerLogsList = e.rows;
+        this.answerLogTotal = e.total;
+        this.loadingAnswerLog = false;
+      })
+    },
+    openRedLogFun(row) {
+      this.openRedLog = true;
+      this.redLogQueryParams.watchLogId = row.logId;
+      this.redLogList();
+    },
+    redLogList() {
+      this.loadingRedLog = true;
+      console.info(this.redLogQueryParams)
+      myListCourseRedPacketLog(this.redLogQueryParams).then(e => {
+        this.redLogsList = e.rows;
+        this.redLogTotal = e.total;
+        this.loadingRedLog = false;
+      })
+    },
+  }
+};
+</script>

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

@@ -0,0 +1,321 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
+
+      <el-form-item label="销售用户名" prop="nickName" >
+        <el-select v-model="queryParams.companyUserId" remote placeholder="请选择" filterable clearable  style="width: 100%;" @keyup.enter.native="handleQuery">
+          <el-option
+            v-for="dict in companyUserList"
+            :key="`${dict.nickName} - ${dict.userName}`"
+            :label="`${dict.nickName} - ${dict.userName}`"
+            :value="dict.userId">
+          </el-option>
+        </el-select>
+      </el-form-item>
+
+
+      <el-form-item label="创建时间" prop="createTime">
+        <el-date-picker v-model="createTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange"
+                        range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="createChange"></el-date-picker>
+      </el-form-item>
+
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          :loading="exportLoading"
+          @click="handleExport"
+        >导出</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table border v-loading="loading" :data="courseWatchLogList" @selection-change="handleSelectionChange">
+      <el-table-column label="销售名称" align="center" prop="userName"/>
+<!--      <el-table-column label="所属销售" align="center" prop="companyUserName" /> -->
+      <el-table-column label="创建时间" align="center" prop="createTime" />
+      <el-table-column label="进线时间" align="center" prop="userCreateTime" />
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+  </div>
+</template>
+
+<script>
+import { listCourseWatchLog, getCourseWatchLog, delCourseWatchLog, addCourseWatchLog, updateCourseWatchLog, exportCourseWatchLog,watchLogStatistics,watchLogStatisticsExport } from "@/api/course/courseWatchLog";
+import {allList}from "@/api/company/company";
+import { courseList,videoList } from '@/api/course/courseRedPacketLog'
+import {getUserList} from "@/api/company/companyUser";
+export default {
+  name: "CourseWatchLog",
+  data() {
+    return {
+      activeName:"00",
+      createTime:null,
+      updateTime:null,
+      courseLists:[],
+      videoList:[],
+      logTypeOptions:[],
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 短链课程看课记录表格数据
+      courseWatchLogList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        userId: null,
+        nickName: null,
+        videoId: null,
+        logType: null,
+        qwExternalContactId: null,
+        externalUserName:null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null,
+        sTime:null,
+        eTime:null,
+        upSTime:null,
+        upETime:null,
+        scheduleStartTime: null,
+        scheduleEndTime: null,
+      },
+      companyUserList: [],
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      },
+      scheduleTime: null,
+    };
+  },
+  created() {
+    courseList().then(response => {
+      this.courseLists = response.list;
+    });
+    this.getList();
+    this.getDicts("sys_course_watch_log_type").then(response => {
+      this.logTypeOptions = response.data;
+    });
+    getUserList().then(res=>{
+      if(res.code === 200) {
+        this.companyUserList = res.data
+      }
+    })
+  },
+  methods: {
+    courseChange(row){
+      this.queryParams.videoId=null;
+      if(row === ''){
+        this.videoList=[];
+        return
+      }
+      videoList(row).then(response => {
+        this.videoList=response.list
+      });
+    },
+    createChange() {
+      if (this.createTime != null) {
+        this.queryParams.sTime = this.createTime[0];
+        this.queryParams.eTime = this.createTime[1];
+      } else {
+        this.queryParams.sTime = null;
+        this.queryParams.eTime = null;
+      }
+    },
+
+    updateChange(){
+      if (this.updateTime != null) {
+        this.queryParams.upSTime = this.updateTime[0];
+        this.queryParams.upETime = this.updateTime[1];
+      } else {
+        this.queryParams.upSTime = null;
+        this.queryParams.upETime = null;
+      }
+    },
+    handleClickX(tab,event){
+      this.activeName=tab.name;
+      if(tab.name=="00"){
+        this.queryParams.logType=null;
+      }else{
+        this.queryParams.logType=tab.name;
+      }
+      this.getList()
+    },
+    /** 查询短链课程看课记录列表 */
+    getList() {
+      this.loading = true;
+      if(this.queryParams.logType == "10"){
+        this.queryParams.logType = null;
+      }
+
+      watchLogStatistics(this.queryParams).then(response => {
+        this.courseWatchLogList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        logId: null,
+        userId: null,
+        videoId: null,
+        logType: null,
+        createTime: null,
+        updateTime: null,
+        qwExternalContactId: null,
+        externalUserName:null,
+        duration: null,
+        qwUserId: null,
+        companyUserId: null,
+        companyId: null,
+        courseId: null,
+        scheduleStartTime: null,
+        scheduleEndTime: null,
+      };
+      this.scheduleTime=null;
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.createTime = null;
+      this.scheduleTime = null;
+      this.queryParams.sTime = null;
+      this.queryParams.eTime = null;
+      this.queryParams.upSTime = null;
+      this.queryParams.upETime = null;
+      this.queryParams.scheduleStartTime = null;
+      this.queryParams.scheduleEndTime = null;
+      this.scheduleTime=null;
+      this.updateTime=null;
+      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
+      getCourseWatchLog(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) {
+            updateCourseWatchLog(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addCourseWatchLog(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 delCourseWatchLog(logIds);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有短链课程看课记录数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return watchLogStatisticsExport(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    },
+    handleScheduleTimeChange(val) {
+      if (val) {
+        this.queryParams.scheduleStartTime = val[0];
+        this.queryParams.scheduleEndTime = val[1];
+      } else {
+        this.queryParams.scheduleStartTime = null;
+        this.queryParams.scheduleEndTime = null;
+      }
+    },
+  }
+};
+</script>

+ 43 - 0
src/views/course/courseWatchLog/watchLogStatisticsTabIndex.vue

@@ -0,0 +1,43 @@
+<!-- TabComponent.vue -->
+<template>
+  <div class="tab-container">
+    <el-tabs type="card" style="background-color: white">
+      <el-tab-pane label="会员">
+        <member-view/>
+      </el-tab-pane>
+      <el-tab-pane label="企微">
+        <qw-view/>
+      </el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+
+<script>
+import MemberView from './watchLogStatistics.vue';
+import QwView from './qw/watchLogStatistics.vue';
+
+
+export default {
+  name: 'TabComponent',
+  components: {
+    MemberView,
+    QwView
+  },
+  data() {
+    return {
+    };
+  }
+};
+</script>
+
+<style scoped>
+.tab-container {
+  width: 100%;
+  height: 100%;
+}
+.app-container{
+  padding: 15px !important;
+  margin: 0px !important;
+  background-color: #fff !important;
+}
+</style>

+ 43 - 0
src/views/course/courseWatchLog/watchLogTabIndex.vue

@@ -0,0 +1,43 @@
+<!-- TabComponent.vue -->
+<template>
+  <div class="tab-container">
+    <el-tabs type="card" style="background-color: white">
+      <el-tab-pane label="会员">
+        <member-view/>
+      </el-tab-pane>
+      <el-tab-pane label="企微">
+        <qw-view/>
+      </el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+
+<script>
+import MemberView from './index.vue';
+import QwView from './qw/index.vue';
+
+
+export default {
+  name: 'TabComponent',
+  components: {
+    MemberView,
+    QwView
+  },
+  data() {
+    return {
+    };
+  }
+};
+</script>
+
+<style scoped>
+.tab-container {
+  width: 100%;
+  height: 100%;
+}
+.app-container{
+  padding: 15px !important;
+  margin: 0px !important;
+  background-color: #fff !important;
+}
+</style>

+ 1643 - 152
src/views/index.vue

@@ -1,181 +1,1672 @@
 <template>
-   <div class="dashboard-container">
-      <div class="dashboard-editor-container" v-hasPermi="['his:index']">
-        <panel-group />
-
-        <div class="divBox">
-            <el-card :bordered="false" dis-hover>
-              <div slot="header">
-                <div class="acea-row row-middle">
-                  <el-avatar
-                    icon="el-icon-s-operation"
-                    size="small"
-                    style="color: #1890ff; background: #e6f7ff; font-size: 13px"
-                  />
-                  <span class="ivu-pl-8" style="vertical-align: top;">订单统计</span>
-
-                   <el-link type="primary" :underline="false" class="ivu-pl-8" style="float: right; levertical-align: top;" @click="getDocT(0)">总排行</el-link>
-                    <el-link type="primary" :underline="false" class="ivu-pl-8" style="float: right; levertical-align: top;" @click="getDocT(1)">本年排行</el-link>
-                    <el-link type="primary" :underline="false" class="ivu-pl-8" style="float: right; levertical-align: top;" @click="getDocT(2)">本月排行</el-link>
-                    <el-link type="primary" :underline="false" class="ivu-pl-8" style="float: right; levertical-align: top;" @click="getDocT(3)">本周排行</el-link>
-                   <el-link type="primary" :underline="false" class="ivu-pl-8" style="float: right; levertical-align: top;"  @click="getDocT(4)">今日排行</el-link>
-                </div>
-              </div>
-              <el-row :gutter="24">
-                <el-col
-                  :xl="16"
-                  :lg="16"
-                  :md="24"
-                  :sm="24"
-                  :xs="24"
-                  class="ivu-mb mb10 dashboard-console-visit"
-                >
-                  <span >药品订单统计</span>
-                 <store-order-money />
-                 <span>问诊统计</span>
-                 <inquiry-order-money />
-                </el-col>
-                <el-col
-                  :xl="8"
-                  :lg="8"
-                  :md="24"
-                  :sm="24"
-                  :xs="24"
-                  class="ivu-mb mb10 dashboard-console-visit"
-                >
-                  <span class="ivu-pl-8" >医生问诊排行榜TOP10</span>
-                  <div style=" margin-bottom: 50px;"></div>
-                 <doctor-chart ref="getDoc" />
-                </el-col>
-              </el-row>
-            </el-card>
-        </div>
+  <div class="statistics-dashboard">
+    <!-- 数据概览 (Data Overview) -->
+    <el-card class="overview-section" shadow="never">
+      <div slot="header" class="header">
+        <span>数据概览</span>
       </div>
-      <div class="dashboard-editor-container">
-        {{greeting}}:{{user.nickName}}
-        <span style="margin-left: 20px;color: gray;">{{ currentTime }}</span>
-        <span style="margin-left: 20px;color: gray;">{{ currentDay }}</span>
+
+      <el-row :gutter="20">
+        <el-col :span="3">
+          <div class="data-card">
+            <div class="card-title">
+              <i class="el-icon-user-solid"></i>
+              分公司数量
+            </div>
+            <div class="card-value highlight">
+              <count-to :start-val="0" :end-val="dealderCount" :duration="3600" class="card-panel-num" /></div>
+          </div>
+        </el-col>
+
+        <el-col :span="3">
+          <div class="data-card">
+            <div class="card-title">
+              <i class="el-icon-user"></i>
+              销售数量
+            </div>
+            <div class="card-value highlight">
+              <count-to :start-val="0" :end-val="groupMgrCount" :duration="3600" class="card-panel-num" />
+            </div>
+          </div>
+        </el-col>
+
+        <el-col :span="3">
+          <div class="data-card">
+            <div class="card-title">
+              <i class="el-icon-shopping-cart-full"></i>
+              会员数量
+            </div>
+            <div class="card-value highlight">
+              <count-to :start-val="0" :end-val="memberCount" :duration="3600" class="card-panel-num" />
+              <span class="highlight-today-add">+{{todayIncreaseUserNum}}</span>
+            </div>
+            <div class="card-badge">
+            </div>
+          </div>
+        </el-col>
+        <el-col :span="3">
+          <div class="data-card">
+            <div class="card-title">
+              <i class="el-icon-shopping-cart-full"></i>
+              企微数量
+            </div>
+            <div class="card-value highlight">
+              <count-to :start-val="0" :end-val="memberCount" :duration="3600" class="card-panel-num" /></div>
+            <div class="card-badge">
+            </div>
+          </div>
+        </el-col>
+
+        <el-col :span="3">
+          <div class="data-card">
+            <div class="card-title">
+              <i class="el-icon-money"></i>
+              可用余额
+            </div>
+            <div class="card-value highlight">
+              <count-to :start-val="0" :end-val="balance" :duration="3600" class="card-panel-num" />
+            </div>
+          </div>
+        </el-col>
+
+        <el-col :span="3">
+          <div class="data-card">
+            <div class="card-title">
+              <span>今日消耗</span>
+            </div>
+            <div class="card-value highlight">
+              <count-to :start-val="0" :end-val="todayComsumption" :duration="3600" class="card-panel-num" />
+            </div>
+            <div class="card-sub">
+              <span>昨日消耗(元)</span>
+              <span class="sub-value">
+                <count-to :start-val="0" :end-val="yesterdayComsumption" :duration="3600" class="card-panel-num" />
+              </span>
+            </div>
+            <el-progress :percentage="percentage" :show-text="false" color="#409EFF"></el-progress>
+            <div class="card-desc">{{remainMessage}}</div>
+          </div>
+        </el-col>
+
+        <el-col :span="3">
+          <div class="data-card">
+            <div class="card-title">
+              <span class="cdn-label">CDN</span>
+              今日
+            </div>
+            <div class="card-value highlight">{{formatBytes(this.todayTraffic)}}
+            </div>
+            <div class="card-sub">
+              <span>本月</span>
+              <span class="sub-value">{{formatBytes(this.thisMonthTraffic)}}</span>
+            </div>
+          </div>
+        </el-col>
+
+        <el-col :span="3">
+          <div class="data-card">
+            <div class="card-title">
+              <i class="el-icon-message"></i>
+              短信剩余条数
+            </div>
+            <div class="card-value highlight">
+              <count-to :start-val="0" :end-val="smsRemainCount" :duration="3600" class="card-panel-num" />
+            </div>
+          </div>
+        </el-col>
+
+        <el-col :span="3">
+          <div class="data-card">
+            <div class="card-title">
+              平台今日看课人数
+            </div>
+            <div class="card-value highlight">
+              <count-to :start-val="0" :end-val="todayWatchUserCount" :duration="3600" class="card-panel-num" />
+            </div>
+            <div class="card-sub">
+              <span>配额上限</span>
+              <span class="sub-value">
+                <count-to :start-val="0" :end-val="todayWatchUserCount" :duration="3600" class="card-panel-num" />/<count-to :start-val="0" :end-val="versionLimit" :duration="3600" class="card-panel-num" /></span>
+            </div>
+            <el-progress :percentage="todayWatchUserCount/versionLimit" :show-text="false" color="#409EFF"></el-progress>
+          </div>
+        </el-col>
+        <el-col :span="3">
+          <div class="data-card">
+            <div class="card-title">
+              <i class="el-icon-shopping-cart-full"></i>
+              订单总数
+            </div>
+            <div class="card-value highlight">
+              <count-to :start-val="0" :end-val="orderTotalNum" :duration="3600" class="card-panel-num" />
+              <span class="highlight-today-add">+{{todayOrderNum}}</span>
+            </div>
+            <div class="card-badge">
+            </div>
+          </div>
+        </el-col>
+        <el-col :span="3">
+          <div class="data-card">
+            <div class="card-title">
+              <i class="el-icon-shopping-cart-full"></i>
+              收款总数
+            </div>
+            <div class="card-value highlight">
+              <count-to :start-val="0" :end-val="recvTotalNum" :duration="3600" class="card-panel-num" />
+              <span class="highlight-today-add">+{{recvTodayNum}}</span>
+            </div>
+            <div class="card-badge">
+            </div>
+          </div>
+        </el-col>
+        <el-col :span="3">
+          <div class="data-card">
+            <div class="card-title">
+              <i class="el-icon-shopping-cart-full"></i>
+              商品总数
+            </div>
+            <div class="card-value highlight">
+              <count-to :start-val="0" :end-val="goodsTotalNum" :duration="3600" class="card-panel-num" />
+              <span class="highlight-today-add">+{{todayGoodsNum}}</span>
+            </div>
+            <div class="card-badge">
+            </div>
+          </div>
+        </el-col>
+      </el-row>
+    </el-card>
+    <!-- 分析概览 (Analysis Overview) -->
+    <div class="analysis-section" shadow="never">
+      <div slot="header" class="header">
+        <span>分析概览</span>
+        <div class="tab-group">
+          <el-radio-group v-model="queryTime" size="medium" @change="handleAnalysis">
+            <el-radio-button label="今日"></el-radio-button>
+            <el-radio-button label="昨日"></el-radio-button>
+            <el-radio-button label="本周"></el-radio-button>
+            <el-radio-button label="本月"></el-radio-button>
+            <el-radio-button label="上月"></el-radio-button>
+          </el-radio-group>
+        </div>
+
+        <div class="action-group">
+          <el-radio-group v-model="userTypeText" @change="handleUserType">
+            <el-radio-button label="会员"></el-radio-button>
+            <el-radio-button label="企微"></el-radio-button>
+          </el-radio-group>
+
+          <el-dropdown @command="handleAutoRefresh" trigger="click">
+            <el-button size="small" plain>
+              自动刷新
+              <i class="el-icon-arrow-down el-icon--right"></i>
+            </el-button>
+            <el-dropdown-menu slot="dropdown">
+              <el-dropdown-item :command="0" :class="{ 'is-active': !autoRefreshInterval }">关闭</el-dropdown-item>
+              <el-dropdown-item :command="5" :class="{ 'is-active': autoRefreshInterval === 5 }">5分钟</el-dropdown-item>
+              <el-dropdown-item :command="10" :class="{ 'is-active': autoRefreshInterval === 10 }">10分钟</el-dropdown-item>
+              <el-dropdown-item :command="15" :class="{ 'is-active': autoRefreshInterval === 15 }">15分钟</el-dropdown-item>
+            </el-dropdown-menu>
+          </el-dropdown>
+          <el-button size="small" plain icon="el-icon-refresh" type="primary" @click="manualRefresh">手动刷新</el-button>
+          <!--          <el-button size="small" type="primary" @click="refresh">刷新</el-button>-->
+        </div>
       </div>
+    </div>
+    <div>
+      <el-row :gutter="20">
+        <el-col :span="12" style="position: relative">
+          <div class="analysis-card-check" :class="selectedDiv===0?'analysis-card-check-selected color':''" @click="handleToggleDiv(0)">
+            <div class="analysis-card">
+              <div class="card-icon"><i class="el-icon-monitor"></i></div>
+              <div class="card-content">
+                <div class="card-row">
+                  <span>观看人数</span>
+                  <span class="highlight">
+                    <count-to :start-val="0" :end-val="watchUserCount" :duration="3600" class="card-panel-num" />
+                  </span>
+                </div>
+                <div class="card-row">
+                  <span>完播人数</span>
+                  <span class="highlight">
+                    <count-to :start-val="0" :end-val="completedUserCount" :duration="3600" class="card-panel-num" />
+                  </span>
+                </div>
+                <div class="card-row">
+                  <span>完播率</span>
+                  <span class="highlight">{{completedRate}}%</span>
+                </div>
+              </div>
+            </div>
+            <div class="analysis-card">
+              <div class="card-icon"><i class="el-icon-video-play"></i></div>
+              <div class="card-content">
+                <div class="card-row">
+                  <span>观看次数</span>
+                  <span class="highlight">
+                    <count-to :start-val="0" :end-val="watchCount" :duration="3600" class="card-panel-num" /></span>
+                </div>
+                <div class="card-row">
+                  <span>完播次数</span>
+                  <span class="highlight">
+                    <count-to :start-val="0" :end-val="completedCount" :duration="3600" class="card-panel-num" />
+                  </span>
+                </div>
+                <div class="card-row">
+                  <span>视频完播率</span>
+                  <span class="highlight">{{watchRate}}%</span>
+                </div>
+              </div>
+            </div>
+          </div>
+        </el-col>
+
+        <el-col :span="6" style="position: relative">
+          <div class="analysis-card-check" :class="selectedDiv===1?'analysis-card-check-selected color':''"  @click="handleToggleDiv(1)">
+            <div class="analysis-card">
+              <div class="card-icon"><i class="el-icon-headset"></i></div>
+              <div class="card-content">
+                <div class="card-row">
+                  <span>答题人数</span>
+                  <span class="highlight">
+                    <count-to :start-val="0" :end-val="answerMemberCount" :duration="3600" class="card-panel-num" />
+                  </span>
+                </div>
+                <div class="card-row">
+                  <span>正确人数</span>
+                  <span class="highlight">
+                    <count-to :start-val="0" :end-val="correctUserCount" :duration="3600" class="card-panel-num" />
+                    </span>
+                </div>
+                <div class="card-row">
+                  <span>正确率</span>
+                  <span class="highlight">{{correctRate}}%</span>
+                </div>
+              </div>
+            </div>
+          </div>
+        </el-col>
+
+        <el-col :span="6" style="position: relative">
+          <div class="analysis-card-check" :class="selectedDiv===2?'analysis-card-check-selected color':''"  @click="handleToggleDiv(2)">
+            <div class="analysis-card">
+              <div class="card-icon"><i class="el-icon-present"></i></div>
+              <div class="card-content">
+                <div class="card-row">
+                  <span>答题红包个数</span>
+                  <span class="highlight">
+                    <count-to :start-val="0" :end-val="rewardCount" :duration="3600" class="card-panel-num" />
+                    </span>
+                </div>
+                <div class="card-row">
+                  <span>答题红包金额(元)</span>
+                  <span class="highlight">
+                    <count-to :start-val="0" :end-val="rewardMoney" :duration="3600" class="card-panel-num" /></span>
+                </div>
+              </div>
+            </div>
+          </div>
+        </el-col>
+      </el-row>
+    </div>
 
+    <!-- 图表区域 (Charts Area) -->
+    <transition name="fade">
+      <el-row :gutter="20" class="charts-section" v-show="selectedDiv===0">
+        <el-col :span="12">
+          <el-card shadow="never">
+            <div slot="header" class="chart-header">
+              <span>会员观看、完播人数趋势图</span>
+              <div class="legend">
+                <div class="legend-item">
+                  <span class="dot viewer-dot"></span>
+                  <span>观看人数</span>
+                </div>
+                <div class="legend-item">
+                  <span class="dot complete-dot"></span>
+                  <span>完播人数</span>
+                </div>
+              </div>
+              <!--              <el-button size="small" plain class="view-more">平台每日统计 <i class="el-icon-arrow-right"></i></el-button>-->
+            </div>
+            <div ref="viewerChart" class="chart-container"></div>
+          </el-card>
+        </el-col>
+
+        <el-col :span="12">
+          <el-card shadow="never">
+            <div slot="header" class="chart-header">
+              <span>经销商会员观看TOP10</span>
+              <div class="legend">
+                <el-radio-group v-model="viewerType" size="small" @change="handleDealerChartData">
+                  <el-radio-button label="0">按观看人数</el-radio-button>
+                  <el-radio-button label="1">按完播人数</el-radio-button>
+                </el-radio-group>
+              </div>
+              <!--              <el-button size="small" plain class="view-more">经销商统计 <i class="el-icon-arrow-right"></i></el-button>-->
+            </div>
+            <div ref="dealerChart" class="chart-container"></div>
+          </el-card>
+        </el-col>
+      </el-row>
+    </transition>
+    <transition name="fade">
+      <el-row :gutter="20" class="charts-section" v-show="selectedDiv===1">
+        <el-card shadow="never">
+          <div slot="header" class="chart-header">
+            <span>课程观看TOP10</span>
+            <div class="legend">
+              <el-radio-group v-model="viewerType" size="small" @change="handleCourseWatchChart">
+                <el-radio-button label="0">按观看人数</el-radio-button>
+                <el-radio-button label="1">按完播人数</el-radio-button>
+                <el-radio-button label="2">按答题人数</el-radio-button>
+                <el-radio-button label="3">按正确人数</el-radio-button>
+              </el-radio-group>
+            </div>
+            <div class="legend">
+              <el-radio-group v-model="delerSort" @change="handleCourseWatchChart">
+                <el-radio label="DESC">前10名</el-radio>
+                <el-radio label="ASC">倒数10名</el-radio>
+              </el-radio-group>
+            </div>
+            <div class="legend">
+              <div class="legend-item">
+                <span class="dot viewer-dot"></span>
+                <span>观看人数</span>
+              </div>
+              <div class="legend-item">
+                <span class="dot complete-dot"></span>
+                <span>完播人数</span>
+              </div>
+              <div class="legend-item">
+                <span class="dot" style="background-color: #E6A23C"></span>
+                <span>答题人数</span>
+              </div>
+              <div class="legend-item">
+                <span class="dot" style="background-color: #F56C6C"></span>
+                <span>正确人数</span>
+              </div>
+            </div>
+            <!--            <el-button size="small" plain class="view-more">经销商统计 <i class="el-icon-arrow-right"></i></el-button>-->
+          </div>
+          <div ref="courseWatchChart" class="chart-container"></div>
+        </el-card>
+      </el-row>
+    </transition>
+
+    <transition name="fade">
+      <el-row :gutter="20" class="charts-section" v-show="selectedDiv===2">
+        <el-col :span="12">
+          <el-card shadow="never">
+            <div slot="header" class="chart-header">
+              <span>答题红包金额TOP10</span>
+              <div class="legend">
+                <el-radio-group v-model="dataType" size="small" @change="handleAnswerRedPackViewerChart">
+                  <el-radio-button label="0">按经销商排行</el-radio-button>
+                  <el-radio-button label="1">按课程排行</el-radio-button>
+                </el-radio-group>
+              </div>
+              <!--              <el-button size="small" plain class="view-more">红包记录 <i class="el-icon-arrow-right"></i></el-button>-->
+            </div>
+            <div ref="answerRedPackViewerChart" class="chart-container"></div>
+          </el-card>
+        </el-col>
+        <el-col :span="12">
+          <el-card shadow="never">
+            <div slot="header" class="chart-header">
+              <span>答题红包金额趋势图</span>
+              <div class="legend">
+                <div class="legend-item">
+                  <span class="dot viewer-dot"></span>
+                  <span>答题红包金额</span>
+                </div>
+              </div>
+              <!--              <el-button size="small" plain class="view-more">红包记录 <i class="el-icon-arrow-right"></i></el-button>-->
+            </div>
+            <div ref="answerRedPackMoneyViewerChart" class="chart-container"></div>
+          </el-card>
+        </el-col>
+      </el-row>
+    </transition>
+    <el-row :gutter="20" class="charts-section">
+      <el-col :span="12">
+        <el-card shadow="never">
+          <div slot="header" class="chart-header">
+            <span>本月订单数</span>
+            <div class="legend">
+              <div class="legend-item">
+                <span class="dot viewer-dot"></span>
+                <span>订单数</span>
+              </div>
+              <div class="legend-item">
+                <span class="dot complete-dot"></span>
+                <span>订单金额</span>
+              </div>
+            </div>
+          </div>
+          <div ref="viewerOrderChart" class="chart-container"></div>
+        </el-card>
+      </el-col>
+      <el-col :span="12">
+        <el-card shadow="never">
+          <div slot="header" class="chart-header">
+            <span>本月收款数</span>
+            <div class="legend">
+              <div class="legend-item">
+                <span class="dot viewer-dot"></span>
+                <span>收款数</span>
+              </div>
+              <div class="legend-item">
+                <span class="dot complete-dot"></span>
+                <span>收款金额</span>
+              </div>
+            </div>
+          </div>
+          <div ref="viewerReceiveChart" class="chart-container"></div>
+        </el-card>
+      </el-col>
+    </el-row>
+    <br/>
   </div>
 </template>
 
 <script>
+import * as echarts from 'echarts'
+import CountTo from "vue-count-to";
+import {
+  analysisPreview,
+  authorizationInfo,
+  dealerAggregated, deaMemberTopTen, rechargeComsumption, rewardMoneyTopTen, rewardMoneyTrend,
+  smsBalance, thisMonthOrderCount, thisMonthRecvCount, trafficLog,
+  watchCourseTopTen, watchEndPlayTrend
+} from "@/api/statistics/statistics";
+import dayjs from 'dayjs';
 
-import doctorChart from "./components/his/doctorChart.vue";
-import PanelGroup from "./components/his/panelGroup.vue";
-import StoreOrderMoney from "./components/his/storeOrderMoney.vue";
-import inquiryOrderMoney from "./components/his/inquiryOrderMoney.vue";
-import user from "../store/modules/user";
-export default {
-  name: 'Index',
-  components: {
-    PanelGroup,StoreOrderMoney,doctorChart,inquiryOrderMoney
+
+const viewCharOption = {
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'shadow'
+    }
   },
-  data() {
-    return {
-       data:null,
-       currentTime: '',
-        currentDay: '',
-        greeting:'',
-      }
+  grid: {
+    left: '3%',
+    right: '4%',
+    bottom: '3%',
+    containLabel: true
   },
-  created() {
-    //this.getIndex();
-    console.log(this.user);
+  xAxis: {
+    type: 'category',
+    data: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23']
   },
-  computed: {
-    user(){
-      return this.$store.state.user.user;
-    }
+  yAxis: {
+    type: 'value'
   },
-  methods: {
-    // getIndex() {
-    //   this.loading = true;
-    //   getIndex().then(response => {
-    //     this.data = response.data;
-    //
-    //   });
-    // },
-    getDocT(row){
-      setTimeout(() => {
-           this.$refs.getDoc.getDoc(row);
-      }, 1);
-    },
-    updateTime() {
-      const now = new Date();
-      const year = now.getFullYear();
-      const month = String(now.getMonth() + 1).padStart(2, '0');
-      const day = String(now.getDate()).padStart(2, '0');
-      const hours = String(now.getHours()).padStart(2, '0');
-      const minutes = String(now.getMinutes()).padStart(2, '0');
-      const seconds = String(now.getSeconds()).padStart(2, '0');
-      this.currentTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
-      const ampm = hours >= 12 ? '下午' : '上午';
-            this.greeting = `${ampm}好`;
-        const daysOfWeek = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
-          this.currentDay = daysOfWeek[now.getDay()];
+  series: [
+    {
+      name: '观看人数',
+      type: 'bar',
+      data: [],
+      itemStyle: {
+        color: '#409EFF'
+      }
     },
+    {
+      name: '完播人数',
+      type: 'bar',
+      data: [],
+      itemStyle: {
+        color: '#67C23A'
+      }
+    }
+  ]
+}
+
+const thisMonthOrderCountOption = {
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'shadow'
+    }
   },
-  mounted() {
-      // 更新时间并设置定时器每秒更新一次
-      this.updateTime();
-      setInterval(() => {
-        this.updateTime();
-      }, 1000);
+  grid: {
+    left: '3%',
+    right: '4%',
+    bottom: '3%',
+    containLabel: true
+  },
+  xAxis: {
+    type: 'category',
+    data: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23']
+  },
+  yAxis: {
+    type: 'value'
+  },
+  series: [
+    {
+      name: '订单数',
+      type: 'line',
+      data: [],
+      itemStyle: {
+        color: '#409EFF'
+      }
     },
-  }
-
-</script>
+    {
+      name: '订单金额',
+      type: 'line',
+      data: [],
+      itemStyle: {
+        color: '#67C23A'
+      }
+    }
+  ]
+}
 
+const thisMonthRecvCountOption = {
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'shadow'
+    }
+  },
+  grid: {
+    left: '3%',
+    right: '4%',
+    bottom: '3%',
+    containLabel: true
+  },
+  xAxis: {
+    type: 'category',
+    data: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23']
+  },
+  yAxis: {
+    type: 'value'
+  },
+  series: [
+    {
+      name: '收款数',
+      type: 'line',
+      data: [],
+      itemStyle: {
+        color: '#409EFF'
+      }
+    },
+    {
+      name: '收款金额',
+      type: 'line',
+      data: [],
+      itemStyle: {
+        color: '#67C23A'
+      }
+    }
+  ]
+}
+const dealerOption = {
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'shadow'
+    }
+  },
+  grid: {
+    left: '3%',
+    right: '4%',
+    bottom: '3%',
+    containLabel: true
+  },
+  xAxis: {
+    type: 'value'
+  },
+  yAxis: {
+    type: 'category',
+    data: []
+  },
+  series: [
+    {
+      name: '观看人数',
+      type: 'bar',
+      data: [],
+      itemStyle: {
+        color: '#409EFF'
+      }
+    }
+  ]
+}
 
-<style rel="stylesheet/scss" lang="scss" scoped>
-  .count{
-        margin: 10px;
-        padding-bottom: 25px;
+const courseWatchOption = {
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'shadow'
+    }
+  },
+  grid: {
+    left: '3%',
+    right: '4%',
+    bottom: '8%',
+    top: '3%',
+    containLabel: true
+  },
+  xAxis: {
+    type: 'category',
+    data: [],
+    axisLabel: {
+      interval: 0,
+      rotate: 30,
+      fontSize: 10,
+      width: 100,
+      overflow: 'truncate'
+    }
+  },
+  yAxis: {
+    type: 'value',
+    splitLine: {
+      lineStyle: {
+        type: 'dashed'
       }
-  .dashboard-editor-container {
-    padding: 18px 22px 22px 22px;
-    background-color: rgb(240, 242, 245);
+    }
+  },
+  series: [
+    {
+      name: '观看人数',
+      type: 'bar',
+      data: [],
+      itemStyle: {
+        color: '#409EFF'
+      }
+    },
+    {
+      name: '完播人数',
+      type: 'bar',
+      data: [],
+      itemStyle: {
+        color: '#67C23A'
+      }
+    },
+    {
+      name: '答题人数',
+      type: 'bar',
+      data: [],
+      itemStyle: {
+        color: '#E6A23C'
+      }
+    },
+    {
+      name: '正确人数',
+      type: 'bar',
+      data: [],
+      itemStyle: {
+        color: '#F56C6C'
+      }
+    }
+  ]
+}
 
-    .chart-wrapper {
-      background: #fff;
-      padding: 16px 16px 0;
-      margin-bottom: 32px;
+const lineChartOption = {
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'cross' // 改为 'cross' 更适合折线图
     }
-  }
+  },
+  grid: {
+    left: '3%',
+    right: '4%',
+    bottom: '8%', // 如果x轴标签旋转,可能需要更大的 bottom
+    top: '5%',    // 增加一点顶部空间给可能的 Y 轴名称
+    containLabel: true
+  },
+  xAxis: {
+    type: 'time', // X轴类型改为 'time'
+    // data: [], // 时间轴不需要单独设置 data,数据在 series 中提供
+    axisLabel: {
+      // interval: 0, // 时间轴通常自动处理间隔,可以先移除或注释掉
+      rotate: 30,   // 保留旋转,如果标签可能重叠
+      fontSize: 10,
+      // width: 100, // width 和 overflow 对于时间轴可能行为不同,按需调整
+      // overflow: 'truncate',
+      formatter: null // ECharts 会自动格式化时间,如需特定格式可用 function 或字符串模板
+    }
+  },
+  yAxis: {
+    type: 'value',
+    name: '金额 (元)', // 添加 Y 轴名称
+    nameLocation: 'end', // 名称位置
+    nameTextStyle: {
+      align: 'right',
+      padding: [0, 10, 0, 0] // 调整名称与轴线的距离
+    },
+    splitLine: {
+      lineStyle: {
+        type: 'dashed'
+      }
+    },
+    axisLabel: {
+      formatter: '{value} 元' // 可选:给 Y 轴刻度添加单位
+    }
+  },
+  series: [
+    {
+      name: '答题红包金额',
+      type: 'line', // 系列类型改为 'line'
+      data: [
+      ],
+      itemStyle: { // 控制数据点(标记)的样式
+        color: '#409EFF'
+      },
+      lineStyle: { // 控制线的样式
+        color: '#409EFF'
+      },
+      smooth: false, // 是否平滑曲线,可设为 true
+      symbol: 'circle', // 数据点标记形状,'emptyCircle', 'rect', 'roundRect', 'triangle', 'diamond', 'pin', 'arrow', 'none'
+      symbolSize: 4   // 数据点标记大小
+    }
+  ]
+};
+
 
-  .acea-row {
-    ::v-deep.el-avatar--small {
-      width: 22px;
-      height: 22px;
-      line-height: 22px;
+const redPackageOption = {
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'shadow'
     }
-  }
-  .checkTime {
-    ::v-deep.el-radio__input {
-      display: none;
+  },
+  grid: {
+    left: '3%',
+    right: '4%',
+    bottom: '8%',
+    top: '3%',
+    containLabel: true
+  },
+  xAxis: {
+    type: 'category',
+    data: [],
+    axisLabel: {
+      interval: 0,
+      rotate: 30,
+      fontSize: 10,
+      width: 100,
+      overflow: 'truncate'
     }
-  }
-  .ivu-pl-8 {
-    margin-left: 8px;
-    font-size: 14px;
-  }
-  .divBox {
-    // padding: 0 20px !important;
-  }
-  .dashboard-console-visit {
-    ::v-deep.el-card__header {
-      padding: 14px 20px !important;
+  },
+  yAxis: {
+    type: 'value',
+    splitLine: {
+      lineStyle: {
+        type: 'dashed'
+      }
+    }
+  },
+  series: [
+    {
+      name: '答题红包金额',
+      type: 'bar',
+      data: [],
+      itemStyle: {
+        color: '#409EFF'
+      }
     }
-    ul {
-      li {
-        list-style-type: none;
-        margin-top: 12px;
+  ]
+}
+export default {
+  name: 'StatisticsDashboard',
+  components: {CountTo},
+  data() {
+    return {
+      percentage: 0,
+      // 预测message
+      remainMessage: '',
+      // 当天使用流量
+      todayTraffic: 0,
+      // 当月使用流量
+      thisMonthTraffic: 0,
+      dataType: '0',
+      delerSort: 'DESC',
+      smsRemainCount: 0,
+      viewerType: '0',
+      viewerChart: null,
+      userTypeText: '会员',
+      userType: 1,
+      dealerChart: null,
+      // 分公司数量
+      dealderCount: 0,
+      // 销售数量
+      groupMgrCount: 0,
+      // 会员总数量
+      memberCount: 0,
+      // 企微数量
+      qwMemberNum: 0,
+      // 正常会员数量
+      normalNum: 0,
+      // 黑名单会员数量
+      blackNum: 0,
+      // 观看人数
+      watchUserCount: 0,
+      // 完播人数
+      completedUserCount: 0,
+      // 完播率
+      completedRate: 0,
+      // 观看次数
+      watchCount:0,
+      // 完播次数
+      completedCount: 0,
+      // 视频完播率
+      watchRate: 0,
+      // 答题人数
+      answerMemberCount: 0,
+      // 正确人数
+      correctUserCount: 0,
+      correctRate: 0.0,
+      // 答题红包个数
+      rewardCount: 0,
+      // 答题红包金额
+      rewardMoney: 0.0,
+      queryTime: '今日',
+      todayWatchUserCount: 0,
+      versionLimit: 0,
+      /// 选中的分析概览
+      selectedDiv: 0,
+      filterType: 0,
+      answerRedPackViewerChart: null,
+      answerRedPackMoneyViewerChart: null,
+      todayComsumption: 0,
+      yesterdayComsumption: 0,
+      balance: 0,
+      autoRefreshInterval: null,
+      // 今日新增用户数
+      todayIncreaseUserNum: 0,
+      // 订单总数
+      orderTotalNum: 0,
+      // 今日新增订单数
+      todayOrderNum: 0,
+      // 收款总数
+      recvTotalNum: 0,
+      // 今日收款总数
+      recvTodayNum: 0,
+      // 商品总数
+      goodsTotalNum: 0,
+      // 今日商品总数
+      todayGoodsNum: 0
+    }
+  },
+  mounted() {
+    this.$nextTick(() => {
+      this.initViewerChart()
+      this.initDealerChart()
+      this.initCourseWatchChart();
+      this.initAnswerRedPackViewerChart();
+      this.initAnswerRedPackMoneyViewerChart();
+      this.initThisMonthOrderChart();
+      this.initThisMonthRecvChart()
+
+      // 监听窗口大小变化,重新渲染图表
+      window.addEventListener('resize', () => {
+        this.viewerChart && this.viewerChart.resize()
+        this.dealerChart && this.dealerChart.resize()
+      })
+    })
+  },
+  created() {
+    this.refresh();
+  },
+  methods: {
+    handleUserType(){
+      if(this.userTypeText === '会员'){
+        this.userType = 1
+      }else{
+        this.userType = 2
+      }
+
+      this.refresh()
+    },
+    /**
+     * 计算余额预计可持续的天数
+     * @param {number} balance - 当前账户余额
+     * @param {number} todayConsumption - 今日消耗金额
+     * @param {number} yesterdayConsumption - 昨日消耗金额
+     * @return {Object} 包含天数和进度百分比的对象
+     */
+    calculateRemainingDays(balance, todayConsumption, yesterdayConsumption) {
+      // 如果今日和昨日消耗都为0,则无法预测(避免除以0)
+      if (todayConsumption === 0 && yesterdayConsumption === 0) {
+        return {
+          days: Infinity,
+          percentage: 0,
+          message: '暂无消耗数据'
+        };
+      }
+
+      // 计算每日平均消耗量
+      const avgDailyConsumption = (todayConsumption + yesterdayConsumption) / 2;
+
+      // 如果平均消耗为0,则无法预测
+      if (avgDailyConsumption === 0) {
+        return {
+          days: Infinity,
+          percentage: 0,
+          message: '暂无消耗数据'
+        };
+      }
+
+      // 计算剩余天数(向下取整)
+      const remainingDays = Math.floor(balance / avgDailyConsumption);
+
+      // 计算进度条百分比,最大为100
+      // 这里假设100天是满值,可以根据需要调整
+      const maxDays = 100;
+      const percentage = Math.min(100, Math.max(0, Math.round((remainingDays / maxDays) * 100)));
+
+      let message = '';
+      if (remainingDays > 365) {
+        message = '预测余额充足';
+      } else {
+        message = `预测不足${remainingDays}天`;
+      }
+
+      return {
+        days: remainingDays,
+        percentage: 100 - percentage,
+        message: message
+      };
+    },
+    /**
+     * 将字节数转换为合适的单位表示(Byte、KB、MB、GB、TB)
+     * @param {number} bytes - 字节数
+     * @param {number} [decimals=2] - 小数点后保留的位数
+     * @returns {string} 格式化后的字符串,包含数值和单位
+     */
+    formatBytes(bytes, decimals = 2) {
+      if (bytes === 0) return '0 Byte';
+
+      const k = 1024;
+      const sizes = ['Byte', 'KB', 'MB', 'GB', 'TB'];
+
+      // 计算合适的单位级别
+      const i = Math.floor(Math.log(bytes) / Math.log(k));
+
+      // 转换为对应单位的值
+      const value = bytes / Math.pow(k, i);
+
+      // 格式化为指定小数位的字符串
+      return parseFloat(value.toFixed(decimals)) + ' ' + sizes[Math.min(i, sizes.length - 1)];
+    },
+    // 手动刷新
+    manualRefresh() {
+      this.refresh();
+    },
+    // 处理自动刷新选项
+    handleAutoRefresh(command) {
+      // 清除之前的定时器
+      if (this.timer) {
+        clearInterval(this.timer);
+        this.timer = null;
+      }
+
+      // 设置新的刷新间隔
+      this.autoRefreshInterval = parseInt(command);
+
+      // 如果间隔大于0,设置新的定时器
+      if (this.autoRefreshInterval > 0) {
+        this.timer = setInterval(() => {
+          this.refresh();
+        }, this.autoRefreshInterval * 60 * 1000); // 转换为毫秒
+
+        this.$message.success(`已设置${this.autoRefreshInterval}分钟自动刷新`);
+      } else {
+        this.$message.info('已关闭自动刷新');
+      }
+    },
+    refresh() {
+      rechargeComsumption().then(res=>{
+        if(res.code === 200){
+          this.balance = res.data.balance;
+          this.todayComsumption = res.data.todayComsumption;
+          this.yesterdayComsumption = res.data.yesterdayComsumption;
+          let calculateRemainingDays1 = this.calculateRemainingDays(this.balance,this.todayComsumption,this.yesterdayComsumption);
+          this.percentage = calculateRemainingDays1.percentage;
+          this.remainMessage = calculateRemainingDays1.message;
+        }
+      });
+
+      trafficLog().then(res=>{
+        if(res.code === 200) {
+          this.todayTraffic = res.data.today;
+          this.thisMonthTraffic = res.data.thisMonth;
+        }
+      })
+
+      dealerAggregated().then(res=>{
+        if(res.code === 200){
+          this.dealderCount = res.data.dealderCount??0;
+          this.groupMgrCount = res.data.groupMgrCount??0;
+          this.memberCount = res.data.memberCount??0;
+          this.qwMemberNum = res.data.qwMemberNum??0;
+          this.normalNum = res.data.normalNum??0;
+          this.blackNum = res.data.blackNum??0;
+          this.todayIncreaseUserNum = res.data.todayIncreaseUserNum??0;
+          this.orderTotalNum = res.data.orderTotalNum??0;
+          this.todayOrderNum = res.data.todayOrderNum??0;
+          this.recvTotalNum = res.data.recvTotalNum??0;
+          this.recvTodayNum = res.data.recvTodayNum??0;
+          this.goodsTotalNum = res.data.goodsTotalNum??0;
+          this.todayGoodsNum = res.data.todayGoodsNum??0;
+        }
+      })
+      let param = this.getParam();
+
+      // 获取当前日期时间
+      const today = dayjs();
+      param.startTime = this.formatDate(today);
+      param.endTime = this.formatDate(today);
+      analysisPreview(param).then(res=>{
+        if(res.code === 200){
+          this.watchUserCount = res.data.watchUserCount;
+          this.completedUserCount = res.data.completedUserCount;
+          this.completedRate = res.data.completedRate;
+          this.watchCount = res.data.watchCount;
+          this.completedCount = res.data.completedCount;
+          this.answerMemberCount = res.data.answerMemberCount;
+          this.correctUserCount = res.data.correctUserCount;
+          this.correctRate = res.data.correctRate;
+          this.rewardCount = res.data.rewardCount;
+          this.rewardMoney = res.data.rewardMoney;
+          this.watchRate = res.data.watchRate;
+        }
+      })
+      smsBalance().then(res=>{
+        if(res.code === 200){
+          if(res.data == null) {
+            this.smsRemainCount = 0;
+          } else {
+            this.smsRemainCount = res.data;
+          }
+        }
+      })
+      authorizationInfo().then(res=>{
+        if(res.code === 200){
+          this.todayWatchUserCount = res.data.todayWatchUserCount;
+          this.versionLimit = res.data.versionLimit;
+        }
+      })
+
+      this.handleCourseWatchChart()
+      this.handleViewChartData()
+
+      // 经销商会员观看TOP10
+      this.handleDealerChartData()
+
+      this.handleAnswerRedPackViewerChart()
+
+      this.handleAnswerRedPackMoneyViewerChart()
+
+      this.handleThisMonthRecvCount();
+      this.handleThisMonthOrderCount();
+
+    },
+    /**
+     * 将数字添加千位分隔符
+     * @param {number|string} num - 需要格式化的数字
+     * @return {string} 添加千位分隔符后的字符串
+     */
+    formatNumberWithCommas(num) {
+      if (num === null || num === undefined || isNaN(Number(num))) {
+        return '0';
+      }
+
+      const numStr = String(num);
+
+      // 处理负数
+      const isNegative = numStr.startsWith('-');
+      const absNumStr = isNegative ? numStr.slice(1) : numStr;
+
+      // 分离整数部分和小数部分
+      const parts = absNumStr.split('.');
+      const integerPart = parts[0];
+      const decimalPart = parts.length > 1 ? '.' + parts[1] : '';
+
+      const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
+
+      return (isNegative ? '-' : '') + formattedInteger + decimalPart;
+    },
+    handleToggleDiv(selected){
+      this.selectedDiv = selected;
+
+      if (selected === 1) {
+        this.$nextTick(() => {
+          if (this.courseWatchChart) {
+            this.courseWatchChart.resize();
+          } else {
+          }
+        });
+      }
+      else if (selected === 0) {
+        this.$nextTick(() => {
+          if (this.viewerChart) this.viewerChart.resize();
+          if (this.dealerChart) this.dealerChart.resize();
+        });
+      } else if (selected === 2) {
+        this.$nextTick(() => {
+          if (this.answerRedPackViewerChart) this.answerRedPackViewerChart.resize();
+          if (this.answerRedPackMoneyViewerChart) this.answerRedPackMoneyViewerChart.resize();
+        });
+      }
+      if(this.selectedDiv === 0){
+        this.handleViewChartData()
+        this.handleDealerChartData()
+      } else if(this.selectedDiv === 1) {
+        this.handleCourseWatchChart()
+      } else if(this.selectedDiv === 2) {
+        this.handleAnswerRedPackViewerChart()
+        this.handleAnswerRedPackMoneyViewerChart()
       }
+    },
+    formatDate(date) {
+      return dayjs(date).format('YYYY-MM-DD');
+    },
+
+    getParam(){
+      let param = {
+        startTime: '',
+        endTime: '',
+        userType: this.userType
+      };
+      // 获取当前日期时间
+      const today = dayjs();
+
+      let type = 0;
+      if (this.queryTime === '今日') {
+        param.startTime = this.formatDate(today);
+        param.endTime = this.formatDate(today);
+        type = 0;
+      } else if (this.queryTime === '昨日') {
+        const yesterday = today.subtract(1, 'day');
+        param.startTime = this.formatDate(yesterday);
+        param.endTime = this.formatDate(yesterday);
+        type = 1;
+      } else if (this.queryTime === '本周') {
+        param.startTime = this.formatDate(today.startOf('week'));
+        param.endTime = this.formatDate(today.endOf('week'));
+        type = 2;
+      } else if (this.queryTime === '本月') {
+        param.startTime = this.formatDate(today.startOf('month'));
+        param.endTime = this.formatDate(today.endOf('month'));
+        type = 3;
+      } else if (this.queryTime === '上月') {
+        const lastMonth = today.subtract(1, 'month');
+        param.startTime = this.formatDate(lastMonth.startOf('month'));
+        param.endTime = this.formatDate(lastMonth.endOf('month'));
+        type = 4;
+      } else {
+        console.warn(`未知的 queryTime: ${this.queryTime}, 默认使用今日`);
+        param.startTime = this.formatDate(today);
+        param.endTime = this.formatDate(today);
+      }
+      param.type = type;
+      param.sort = this.delerSort;
+      return param;
+    },
+    // 分析概览
+    handleAnalysis(e){
+
+      let param = this.getParam();
+      analysisPreview(param).then(res=>{
+        if(res.code === 200){
+          this.watchUserCount = res.data.watchUserCount;
+          this.completedUserCount = res.data.completedUserCount;
+          this.completedRate = res.data.completedRate;
+          this.watchCount = res.data.watchCount;
+          this.completedCount = res.data.completedCount;
+          this.answerMemberCount = res.data.answerMemberCount;
+          this.correctUserCount = res.data.correctUserCount;
+          this.correctRate = res.data.correctRate;
+          this.rewardCount = res.data.rewardCount;
+          this.rewardMoney = res.data.rewardMoney;
+          this.watchRate = res.data.watchRate;
+        }
+      })
+
+      if(this.selectedDiv === 0){
+        this.handleViewChartData()
+        this.handleDealerChartData()
+      } else if(this.selectedDiv === 1) {
+        this.handleCourseWatchChart()
+      } else if(this.selectedDiv === 2) {
+        this.handleAnswerRedPackViewerChart()
+        this.handleAnswerRedPackMoneyViewerChart()
+      }
+    },
+    handleAnswerRedPackViewerChart(){
+      let param = this.getParam();
+      param = {...param,statisticalType:this.viewerType,dataType: this.dataType};
+      rewardMoneyTopTen(param).then(res=>{
+        if(res.code === 200){
+          let data = res.data;
+          let companyNameList = data.map(e=>e.companyName)
+          let courseNameList = data.map(e=>e.courseName)
+          let rewardMoneyList = data.map(e=>e.rewardMoney)
+          if(this.dataType === '0'){
+            redPackageOption.xAxis.data = companyNameList;
+          }else{
+            redPackageOption.xAxis.data = courseNameList;
+          }
+          redPackageOption.series[0].data = rewardMoneyList;
+
+          this.answerRedPackViewerChart.setOption(redPackageOption)
+        }
+      })
+    },
+    handleAnswerRedPackMoneyViewerChart(){
+      let param = this.getParam();
+      param = {...param,statisticalType:this.viewerType,dataType: this.dataType};
+      rewardMoneyTrend(param).then(res=>{
+        if(res.code === 200){
+          let data = res.data;
+          let option = data.map(e=>[e.x,e.rewardMoney])
+          lineChartOption.series[0].data = option;
+
+          this.answerRedPackMoneyViewerChart.setOption(lineChartOption)
+        }
+      })
+    },
+    handleCourseWatchChart() {
+      let param = this.getParam();
+      param = {...param,statisticalType:this.viewerType};
+      watchCourseTopTen(param).then(res=>{
+        if(res.code === 200){
+          let data = res.data;
+          let watchUserCountList = data.map(e=>e.watchUserCount);
+          let completedUserCountList = data.map(e=>e.completedUserCount);
+          let answerUserCountList = data.map(e=>e.answerUserCount);
+          let correctUserCountList = data.map(e=>e.correctUserCount);
+          let courseNameList = data.map(e=>e.courseName);
+          courseWatchOption.xAxis.data = courseNameList;
+          courseWatchOption.series[0].data = watchUserCountList;
+          courseWatchOption.series[1].data = completedUserCountList;
+          courseWatchOption.series[2].data = answerUserCountList;
+          courseWatchOption.series[3].data = correctUserCountList;
+          this.courseWatchChart.setOption(courseWatchOption)
+        }
+      })
+    },
+    handleDealerChartData(){
+      let param = this.getParam();
+
+      // 经销商会员观看TOP10
+      deaMemberTopTen({...param,statisticalType: this.viewerType}).then(res=>{
+        if(res.code === 200){
+          let data = res.data;
+          let companyNameList = data.map(e=>e.companyName);
+          let watchUserList = data.map(e=>e.watchUserCount);
+          dealerOption.yAxis.data = companyNameList;
+          dealerOption.series[0].data = watchUserList;
+
+          this.dealerChart.setOption(dealerOption)
+        }
+      })
+
+    },
+    handleThisMonthOrderCount(){
+      thisMonthOrderCount().then(res=>{
+        if(res.code === 200){
+          let dates = res.dates;
+          let orderCount = res.orderCount;
+          let payPrice = res.payPrice;
+
+          thisMonthOrderCountOption.series[0].data = orderCount;
+          thisMonthOrderCountOption.series[1].data = payPrice;
+          thisMonthOrderCountOption.xAxis.data = dates;
+
+          this.thisMonthOrderChart.setOption(thisMonthOrderCountOption)
+        }
+      })
+    },
+    handleThisMonthRecvCount(){
+      thisMonthRecvCount().then(res=>{
+        if(res.code === 200){
+          let dates = res.dates;
+          let orderCount = res.orderCount;
+          let payMoney = res.payMoney;
+
+          thisMonthRecvCountOption.series[0].data = orderCount;
+          thisMonthRecvCountOption.series[1].data = payMoney;
+          thisMonthRecvCountOption.xAxis.data = dates;
+          this.thisMonthRecvChart.setOption(thisMonthRecvCountOption)
+        }
+      })
+    },
+    handleViewChartData(){
+      let param = this.getParam();
+
+      watchEndPlayTrend({...param}).then(res=>{
+        if(res.code === 200){
+          let data = res.data;
+          let watchUserCountList = data.map(e=>e.watchUserCount);
+          let completedUserCountList = data.map(e=>e.completedUserCount);
+          let xAxis = data.map(e=>e.x);
+          viewCharOption.series[0].data = watchUserCountList;
+          viewCharOption.series[1].data = completedUserCountList;
+          viewCharOption.xAxis.data = xAxis;
+
+          this.viewerChart.setOption(viewCharOption);
+        }
+      })
+
+    },
+    initThisMonthOrderChart(){
+      this.thisMonthOrderChart = echarts.init(this.$refs.viewerOrderChart)
+      this.thisMonthOrderChart.setOption(thisMonthOrderCountOption)
+    },
+    initThisMonthRecvChart(){
+      this.thisMonthRecvChart = echarts.init(this.$refs.viewerReceiveChart)
+      this.thisMonthRecvChart.setOption(thisMonthOrderCountOption)
+    },
+    initViewerChart() {
+      this.viewerChart = echarts.init(this.$refs.viewerChart)
+      this.viewerChart.setOption(viewCharOption)
+    },
+    initDealerChart() {
+      this.dealerChart = echarts.init(this.$refs.dealerChart)
+
+      this.dealerChart.setOption(dealerOption)
+    },
+    initCourseWatchChart() {
+      this.courseWatchChart = echarts.init(this.$refs.courseWatchChart)
+
+      this.courseWatchChart.setOption(courseWatchOption)
+    },
+    initAnswerRedPackViewerChart(){
+      this.answerRedPackViewerChart = echarts.init(this.$refs.answerRedPackViewerChart)
+
+      this.answerRedPackViewerChart.setOption(redPackageOption)
+    },
+    initAnswerRedPackMoneyViewerChart(){
+      this.answerRedPackMoneyViewerChart = echarts.init(this.$refs.answerRedPackMoneyViewerChart)
+
+      this.answerRedPackMoneyViewerChart.setOption(lineChartOption)
     }
+  },
+
+  beforeDestroy() {
+    // 组件销毁时清除定时器
+    if (this.timer) {
+      clearInterval(this.timer);
+      this.timer = null;
+    }
+    // window.removeEventListener('resize', this.resizeHandler)
+    this.viewerChart && this.viewerChart.dispose()
+    this.dealerChart && this.dealerChart.dispose()
   }
-  .ivu-mb {
-    margin-bottom: 10px;
+}
+</script>
+
+<style scoped>
+.highlight-today-add{
+  color:green;font-size:17px;font-weight: normal;
+}
+.action-group .el-button + .el-button,
+.action-group .el-dropdown {
+  margin-left: 10px;
+}
+.is-active {
+  color: #409EFF;
+  font-weight: bold;
+}
+::v-deep .el-radio-button__inner:hover {
+  color: #409EFF; /* 鼠标悬浮时的文字颜色,可以根据需要调整 */
+}
+::v-deep .el-radio-button.is-active .el-radio-button__inner {
+  background-color: #409EFF; /* 选中时的背景色 */
+  border-color: #409EFF;     /* 选中时的边框色 */
+  color: #FFFFFF;           /* 选中时的文字颜色 (通常是白色) */
+  box-shadow: -1px 0 0 0 #409EFF; /* 处理按钮间的连接缝隙 */
+}
+/* 如果需要,也可以修改非选中状态下的聚焦(focus)或悬浮(hover)样式 */
+/* 例如,让非选中按钮悬浮时边框和文字也变蓝 */
+::v-deep .el-radio-button:not(.is-active) .el-radio-button__inner:hover {
+  color: #409EFF;
+  /* border-color: #b3d8ff;  Element UI 默认悬浮边框色,可以按需修改 */
+}
+/* 聚焦时的外框,如果需要的话 */
+::v-deep .el-radio-button:focus:not(.is-checked) .el-radio-button__inner {
+  /* border-color: #409EFF; */ /* Element UI 默认的 focus 颜色通常关联主题色 */
+  /* box-shadow: 0 0 2px 2px rgba(64, 158, 255, 0.2); */ /* 示例 focus 光晕 */
+}
+.statistics-dashboard {
+  padding: 20px;
+  background-color: #f5f7fa;
+}
+
+.overview-section,
+.analysis-section {
+  margin-bottom: 20px;
+  border-radius: 4px;
+}
+
+.header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  font-size: 16px;
+  font-weight: 500;
+}
+
+.data-card {
+  background-color: #fff;
+  border-radius: 4px;
+  padding: 15px;
+  height: 120px;
+  display: flex;
+  flex-direction: column;
+  position: relative;
+  transition: background-color 0.3s ease-in-out;
+}
+.data-card:hover{
+  border: 1px solid #4592ff;
+  background-color: #e7f1ff;
+}
+
+.card-title {
+  color: #606266;
+  font-size: 14px;
+  margin-bottom: 10px;
+}
+
+.card-value {
+  font-size: 24px;
+  font-weight: bold;
+  margin-top: auto;
+}
+
+.highlight {
+  color: #409EFF;
+}
+
+.card-sub {
+  display: flex;
+  justify-content: space-between;
+  font-size: 12px;
+  color: #909399;
+  margin-top: 5px;
+}
+
+.card-desc {
+  font-size: 12px;
+  color: #909399;
+  margin-top: 5px;
+}
+
+.card-badge {
+  position: absolute;
+  top: 15px;
+  right: 15px;
+  background: #f0f9eb;
+  color: #67c23a;
+  padding: 2px 5px;
+  border-radius: 4px;
+}
+
+.cdn-label {
+  background-color: #409EFF;
+  color: white;
+  padding: 2px 5px;
+  border-radius: 4px;
+  margin-right: 5px;
+  font-size: 12px;
+}
+
+.tab-group {
+  display: flex;
+  gap: 10px;
+}
+
+.action-group {
+  display: flex;
+  gap: 10px;
+}
+
+.analysis-card {
+  border-radius: 4px;
+  padding: 20px;
+  display: flex;
+  align-items: center;
+}
+
+.card-icon {
+  width: 50px;
+  height: 50px;
+  background-color: rgba(64, 158, 255, 0.1);
+  border-radius: 8px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  font-size: 24px;
+  color: #409EFF;
+  margin-right: 20px;
+}
+
+.card-content {
+  display: flex;
+}
+
+.card-row {
+  display: flex;
+  justify-content: center;
+  justify-items: center;
+  flex-direction: column;
+  padding: 10px;
+  .highlight{
+    text-align: center;
+    margin-top: 1em;
+
+    font-family: BebasNeue;
+    color: #1677ff;
+    font-size: 26px;
+    line-height: 42px;
+    font-weight: 400;
+    margin-top: 8px;
   }
-  </style>
+  font-size: 15px;
+  color: #000;
+
+}
+
+.charts-section {
+  margin-top: 20px;
+}
+
+.chart-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.view-more {
+  font-size: 12px;
+}
+
+.legend {
+  display: flex;
+  gap: 15px;
+}
+
+.legend-item {
+  display: flex;
+  align-items: center;
+  font-size: 12px;
+}
+
+.dot {
+  width: 10px;
+  height: 10px;
+  border-radius: 50%;
+  margin-right: 5px;
+}
+
+.viewer-dot {
+  background-color: #409EFF;
+}
+
+.complete-dot {
+  background-color: #67C23A;
+}
+
+.chart-container {
+  height: 350px;
+  width: 100%;
+}
+.analysis-card-check{
+  display: flex;
+  flex-direction: row;
+  border: 1px solid transparent;
+  background-color: #fff;
+  border-radius: 4px;
+}
+.analysis-card-check:hover{
+  cursor: pointer;
+}
+.analysis-card-check-selected:after{
+  content: "";
+  display: block;
+  border-width: 15px;
+  position: absolute;
+  bottom: -30px;
+  left: 50%;
+  margin-left: -32px;
+  border-style: solid dashed dashed solid;
+  border-color: #4592FF transparent transparent transparent;
+  font-size: 0;
+  line-height: 0;
+  z-index:1;
+}
+.analysis-card-check-selected:before{
+  content: "";
+  display: block;
+  border-width: 15px;
+  position: absolute;
+  bottom: -30px;
+  left: 50%;
+  margin-left: -32px;
+  border-style: solid dashed dashed solid;
+  border-color: #4592FF transparent transparent transparent;
+  font-size: 0;
+  line-height: 0;
+  z-index:1;
+}
+.analysis-card-check-selected{
+  border: 1px solid #4592FF;
+  background-color: #e7f1ff;
+}
+.color{
+  position: relative;
+  border: 1px solid #4592FF;
+  background-color: #e7f1ff;
+}
+.color:after {
+  bottom: -27px;
+  border-color: #E7F1FF transparent transparent transparent;
+}
+.legend-group{
+
+}
+</style>

+ 438 - 0
src/views/user/transfer/index.vue

@@ -0,0 +1,438 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="80px">
+      <el-form-item label="公司名" prop="companyId">
+        <el-select filterable style="width: 220px" v-model="queryParams.corpId" placeholder="请选择公司名" clearable size="small">
+          <el-option
+            v-for="item in companys"
+            :key="item.companyId"
+            :label="item.companyName"
+            :value="item.companyId"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="转移类型" prop="transferType">
+        <el-select v-model="queryParams.transferType" placeholder="请选择转移类型" clearable size="small" style="width: 220px">
+          <el-option
+            v-for="item in transferTypeList"
+            :key="item.dictValue"
+            :label="item.dictLabel"
+            :value="item.dictValue"/>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="目标销售" prop="targetUserId">
+        <el-select v-model="queryParams.targetUserId" remote
+                   placeholder="请选择" filterable clearable
+                   style="width: 220px;" @keyup.enter.native="handleQuery" :remote-method="getAllUserListLimitQuery">
+          <el-option
+            v-for="dict in companyUserList"
+            :key="`${dict.nickName} - ${dict.userName}`"
+            :label="`${dict.nickName} - ${dict.userName}`"
+            :value="dict.userId">
+          </el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="发起人" prop="initiatorUserId">
+        <el-select v-model="queryParams.initiatorUserId" remote placeholder="请选择" filterable clearable
+                   style="width: 220px;" @keyup.enter.native="handleQuery"
+                   :remote-method="getAllUserListLimitQuery3"
+        >
+          <el-option
+            v-for="dict in companyUserList3"
+            :key="`${dict.nickName} - ${dict.userName}`"
+            :label="`${dict.nickName} - ${dict.userName}`"
+            :value="dict.userId">
+          </el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="审批状态" prop="approvalStatus">
+        <el-select v-model="queryParams.approvalStatus" placeholder="请选择审批状态" clearable size="small" style="width: 220px">
+          <el-option
+            v-for="item in approvalStatusList"
+            :key="item.dictValue"
+            :label="item.dictLabel"
+            :value="item.dictValue"/>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="审批时间" prop="processedAt">
+        <el-date-picker clearable size="small" style="width: 220px"
+                        v-model="queryParams.processedAt"
+                        type="date"
+                        value-format="yyyy-MM-dd"
+                        placeholder="选择审批处理时间">
+        </el-date-picker>
+      </el-form-item>
+      <el-form-item label="创建时间" prop="createdAt">
+        <el-date-picker clearable size="small" style="width: 220px"
+                        v-model="queryParams.createdAt"
+                        type="date"
+                        value-format="yyyy-MM-dd"
+                        placeholder="选择记录创建时间">
+        </el-date-picker>
+      </el-form-item>
+      <el-form-item label="更新时间" prop="updatedAt">
+        <el-date-picker clearable size="small" style="width: 220px"
+                        v-model="queryParams.updatedAt"
+                        type="date"
+                        value-format="yyyy-MM-dd"
+                        placeholder="选择记录更新时间">
+        </el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          icon="el-icon-download"
+          size="mini"
+          @click="handleExport"
+          v-hasPermi="['system:approval:export']"
+        >导出</el-button>
+      </el-col>
+	  <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="approvalList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="ID" align="center" prop="id" />
+      <el-table-column label="所属公司" align="center" prop="companyName" />
+      <el-table-column label="转移类型" align="center" prop="transferTypeText" />
+      <el-table-column label="目标销售" align="center" prop="targetUserName" />
+      <el-table-column label="发起用户" align="center" prop="initiatorUserName" />
+      <el-table-column label="转移内容/原因" align="center" prop="content" />
+      <el-table-column label="审批状态" align="center" prop="approvalStatus" >
+        <template slot-scope="scope">
+          <el-tag :type="scope.row.approvalStatus == 1 ? 'success' : scope.row.approvalStatus == 2 ? 'danger' : ''">{{ scope.row.approvalStatusText }}</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="审批意见/备注" align="center" prop="approvalRemark" />
+      <el-table-column label="创建时间" align="center" prop="createdAt" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createdAt, '{y}-{m}-{d}') }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="更新时间" align="center" prop="updatedAt" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.updatedAt, '{y}-{m}-{d}') }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            v-if="scope.row.approvalStatus == 0"
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="viewMode=false;handleUpdate(scope.row)"
+            v-hasPermi="['system:approval:edit']"
+          >审批</el-button>
+          <el-button
+            v-if="scope.row.approvalStatus != 0"
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="viewMode=true;handleUpdate(scope.row)"
+            v-hasPermi="['system:approval:edit']"
+          >查看</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改客户转移审批对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="900px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="企业" prop="corpId">
+          <el-input v-model="form.companyName" placeholder="企业" disabled="disabled"/>
+        </el-form-item>
+        <el-form-item label="转移类型" prop="transferType">
+          <el-input v-model="form.transferTypeText" placeholder="转移类型" disabled="disabled"/>
+        </el-form-item>
+        <el-form-item label="转移矩阵" prop="customerIds">
+          <el-table :data="form.customerList" border>
+            <el-table-column label="客户" align="center" prop="userName" />
+            <el-table-column label="转移前销售" align="center" prop="beforeCompanyUserName" />
+            <el-table-column label="转移后销售" align="center" prop="afterCompanyUserName" />
+          </el-table>
+        </el-form-item>
+        <el-form-item label="接收销售" prop="targetUserId">
+          <el-input v-model="form.targetUserName" placeholder="请输入目标接收销售用户 ID" disabled="disabled"/>
+        </el-form-item>
+        <el-form-item label="请求用户" prop="initiatorUserId">
+          <el-input v-model="form.initiatorUserName" placeholder="请输入发起此转移请求的用户 ID" disabled="disabled"/>
+        </el-form-item>
+        <el-form-item label="转移提示内容">
+          <el-input v-model="form.content" :min-height="192" disabled="disabled"/>
+        </el-form-item>
+        <el-form-item label="审批状态">
+          <el-radio-group v-model="form.approvalStatus">
+            <el-tag>{{form.approvalStatusText}}</el-tag>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="审批意见" prop="approvalRemark">
+          <el-input v-model="form.approvalRemark" type="textarea" placeholder="请输入内容"  disabled="disabled"/>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="success" @click="submitForm(1)" v-if="!viewMode">通过</el-button>
+        <el-button type="danger" @click="submitForm(2)" v-if="!viewMode">拒绝</el-button>
+        <el-button @click="cancel">关 闭</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listApproval, getApproval, delApproval, addApproval, updateApproval, exportApproval } from "@/api/user/approval";
+import Editor from '@/components/Editor';
+import {getDicts} from "@/api/system/dict/data";
+import {getCompanyList} from "@/api/company/company";
+import {getAllUserListLimit} from "@/api/store/user";
+
+export default {
+  name: "Approval",
+  components: { Editor },
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 客户转移审批表格数据
+      approvalList: [],
+      companys:[],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 转移类型
+      transferTypeList: [],
+      companyUserList: [],
+      companyUserList3: [],
+      // 审批状态
+      approvalStatusList: [],
+      viewMode: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        corpId: null,
+        transferType: null,
+        customerIds: null,
+        originalUserId: null,
+        targetUserId: null,
+        initiatorUserId: null,
+        content: null,
+        approvalStatus: null,
+        approverUserId: null,
+        approvalRemark: null,
+        processedAt: null,
+        createdAt: null,
+        updatedAt: null
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        corpId: [
+          { required: true, message: "企业不能为空", trigger: "blur" }
+        ],
+        transferType: [
+          { required: true, message: "转移类型不能为空", trigger: "change" }
+        ],
+        customerIds: [
+          { required: true, message: "客户 ID 列表不能为空", trigger: "blur" }
+        ],
+        targetUserId: [
+          { required: true, message: "目标接收销售用户不能为空", trigger: "blur" }
+        ],
+        initiatorUserId: [
+          { required: true, message: "发起此转移请求的用户不能为空", trigger: "blur" }
+        ],
+        approvalStatus: [
+          { required: true, message: "审批状态不能为空", trigger: "blur" }
+        ],
+        createdAt: [
+          { required: true, message: "记录创建时间不能为空", trigger: "blur" }
+        ],
+        updatedAt: [
+          { required: true, message: "记录最后更新时间不能为空", trigger: "blur" }
+        ],
+        approvalRemark: [
+          { required: true, message: "审批意见不能为空!", trigger: "blur" }
+        ]
+      }
+    };
+  },
+  created() {
+    getDicts('transfer_type').then(response => {
+      this.transferTypeList = response.data;
+    })
+    getDicts('transfer_approval_status').then(response => {
+      this.approvalStatusList = response.data;
+    })
+    getAllUserListLimit().then(res=>{
+      if(res.code === 200) {
+        this.companyUserList = res.data
+      }
+    });
+    getAllUserListLimit().then(res=>{
+      if(res.code === 200) {
+        this.companyUserList3 = res.data
+      }
+    });
+    getCompanyList().then(response => {
+      this.companys = response.data;
+    });
+    this.getList();
+  },
+  methods: {
+    getAllUserListLimitQuery(keywords){
+      getAllUserListLimit({
+        keywords: keywords
+      }).then(res=>{
+        if(res.code === 200) {
+          this.companyUserList = res.data
+        }
+      });
+    },
+    getAllUserListLimitQuery3(keywords){
+      getAllUserListLimit({
+        keywords: keywords
+      }).then(res=>{
+        if(res.code === 200) {
+          this.companyUserList3 = res.data
+        }
+      });
+    },
+    /** 查询客户转移审批列表 */
+    getList() {
+      this.loading = true;
+      listApproval(this.queryParams).then(response => {
+        this.approvalList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        corpId: null,
+        transferType: null,
+        customerIds: null,
+        originalUserId: null,
+        targetUserId: null,
+        initiatorUserId: null,
+        content: null,
+        approvalStatus: 0,
+        approverUserId: null,
+        approvalRemark: null,
+        processedAt: null,
+        createdAt: null,
+        updatedAt: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加客户转移审批";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id || this.ids
+      getApproval(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改客户转移审批";
+      });
+    },
+    /** 提交按钮 */
+    submitForm(type) {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != null) {
+            updateApproval({...this.form,approvalStatus: type}).then(response => {
+              if (response.code === 200) {
+                this.msgSuccess(response.msg);
+                this.open = false;
+                this.getList();
+              }
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除客户转移审批编号为"' + ids + '"的数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return delApproval(ids);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(function() {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有客户转移审批数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return exportApproval(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+        }).catch(function() {});
+    }
+  }
+};
+</script>