xdd 1 viikko sitten
vanhempi
commit
c87f82f588
33 muutettua tiedostoa jossa 7582 lisäystä ja 270 poistoa
  1. 62 0
      src/api/course/qw/courseAnswerlogs.js
  2. 114 0
      src/api/course/qw/courseWatchLog.js
  3. 53 0
      src/api/qw/qw/QwWorkTask.js
  4. 53 0
      src/api/user/approval.js
  5. 53 0
      src/api/users/approval.js
  6. 17 1
      src/api/users/user.js
  7. 43 0
      src/views/course/courseUserStatistics/courseUserStatisticsTabIndex.vue
  8. 73 29
      src/views/course/courseUserStatistics/index.vue
  9. 38 10
      src/views/course/courseUserStatistics/my.vue
  10. 50 19
      src/views/course/courseUserStatistics/myStatistics.vue
  11. 43 0
      src/views/course/courseUserStatistics/myStatisticsTabIndex.vue
  12. 43 0
      src/views/course/courseUserStatistics/myTabIndex.vue
  13. 366 0
      src/views/course/courseUserStatistics/qw/index.vue
  14. 366 0
      src/views/course/courseUserStatistics/qw/my.vue
  15. 680 0
      src/views/course/courseUserStatistics/qw/myStatistics.vue
  16. 680 0
      src/views/course/courseUserStatistics/qw/statistics.vue
  17. 61 16
      src/views/course/courseUserStatistics/statistics.vue
  18. 43 0
      src/views/course/courseUserStatistics/statisticsTabIndex.vue
  19. 489 0
      src/views/course/courseWatchLog/qw/index.vue
  20. 396 0
      src/views/course/courseWatchLog/qw/myCourseWatchLog.vue
  21. 294 0
      src/views/course/courseWatchLog/qw/statistics.vue
  22. 602 0
      src/views/course/courseWatchLog/qw/watchLog.vue
  23. 314 0
      src/views/course/courseWatchLog/qw/watchLogStatistics.vue
  24. 43 0
      src/views/qw/QwWorkTask/QwWorkTaskTabIndex.vue
  25. 93 195
      src/views/qw/QwWorkTask/index.vue
  26. 276 0
      src/views/qw/QwWorkTask/qw/allTask.vue
  27. 421 0
      src/views/qw/QwWorkTask/qw/index.vue
  28. 367 0
      src/views/qw/QwWorkTask/qw/qwWorkTask.vue
  29. 179 0
      src/views/user/darkRoom/index.vue
  30. 438 0
      src/views/user/transfer/index.vue
  31. 179 0
      src/views/users/user/darkRoom.vue
  32. 254 0
      src/views/users/user/transfer.vue
  33. 399 0
      src/views/users/user/transferLog.vue

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

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

@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 查询企微任务看板列表
+export function listQwWorkTask(query) {
+  return request({
+    url: '/qw/qw/QwWorkTask/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询企微任务看板详细
+export function getQwWorkTask(id) {
+  return request({
+    url: '/qw/qw/QwWorkTask/' + id,
+    method: 'get'
+  })
+}
+
+// 新增企微任务看板
+export function addQwWorkTask(data) {
+  return request({
+    url: '/qw/qw/QwWorkTask',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改企微任务看板
+export function updateQwWorkTask(data) {
+  return request({
+    url: '/qw/qw/QwWorkTask',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除企微任务看板
+export function delQwWorkTask(id) {
+  return request({
+    url: '/qw/qw/QwWorkTask/' + id,
+    method: 'delete'
+  })
+}
+
+// 导出企微任务看板
+export function exportQwWorkTask(query) {
+  return request({
+    url: '/qw/qw/QwWorkTask/export',
+    method: 'get',
+    params: query
+  })
+}

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

+ 53 - 0
src/api/users/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
+  })
+}

+ 17 - 1
src/api/users/user.js

@@ -21,6 +21,23 @@ export function getUser(userId) {
   })
 }
 
+// 列出当前公司的客户
+export function listUser(data) {
+  return request({
+    url: '/user/fsUser/list',
+    method: 'post',
+    data: data
+  })
+}
+
+// 转移客户
+export function transferUser(data) {
+  return request({
+    url: '/fsUser/user/transfer',
+    method: 'post',
+    data: data
+  })
+}
 export function getUserList(query) {
   return request({
     url: '/users/user/getUserList',
@@ -46,4 +63,3 @@ export function updateUser(data) {
     data: data
   })
 }
- 

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

+ 73 - 29
src/views/course/courseUserStatistics/index.vue

@@ -1,14 +1,45 @@
 <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 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="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="change"></el-date-picker>
@@ -21,8 +52,10 @@
 
     <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"/>
             <!-- 进线数 -->
@@ -61,12 +94,12 @@
             </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="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">
@@ -109,23 +142,23 @@
             </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="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-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-more
+    <pagination
       v-show="total>0"
       :total="total"
       :page.sync="queryParams.pageNum"
@@ -139,6 +172,7 @@
 <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";
 export default {
   name: "CourseWatchLog",
   data() {
@@ -148,6 +182,8 @@ export default {
       courseLists:[],
       videoList:[],
       logTypeOptions:[],
+      companyUserList: [],
+      projectLists: [],
       // 遮罩层
       loading: true,
       // 导出遮罩层
@@ -203,6 +239,14 @@ export default {
     this.getDicts("sys_course_watch_log_type").then(response => {
       this.logTypeOptions = response.data;
     });
+    this.getDicts("sys_course_project").then(response => {
+      this.projectLists = response.data;
+    })
+    getUserList().then(res=>{
+      if(res.code === 200) {
+        this.companyUserList = res.data
+      }
+    })
   },
   methods: {
     courseChange(row){

+ 38 - 10
src/views/course/courseUserStatistics/my.vue

@@ -1,14 +1,35 @@
 <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 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>
@@ -21,7 +42,10 @@
 
     <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="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"/>
@@ -125,7 +149,7 @@
             </el-table-column>
     </el-table>
 
-    <pagination-more
+    <pagination
       v-show="total>0"
       :total="total"
       :page.sync="queryParams.pageNum"
@@ -164,6 +188,7 @@ export default {
       total: 0,
       // 短链课程看课记录表格数据
       courseWatchLogList: [],
+      projectLists: [],
       // 弹出层标题
       title: "",
       // 是否显示弹出层
@@ -203,6 +228,9 @@ export default {
     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){

+ 50 - 19
src/views/course/courseUserStatistics/myStatistics.vue

@@ -1,14 +1,35 @@
 <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 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>
@@ -22,12 +43,16 @@
     <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">
@@ -35,7 +60,7 @@
                     <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">
@@ -43,8 +68,8 @@
                     <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>
@@ -435,11 +460,11 @@
                     <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-more
+    <pagination
       v-show="total>0"
       :total="total"
       :page.sync="queryParams.pageNum"
@@ -478,6 +503,7 @@ export default {
       total: 0,
       // 短链课程看课记录表格数据
       courseWatchLogList: [],
+      projectLists: [],
       // 弹出层标题
       title: "",
       // 是否显示弹出层
@@ -510,13 +536,18 @@ export default {
     };
   },
   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();
-    this.getDicts("sys_course_watch_log_type").then(response => {
-      this.logTypeOptions = response.data;
-    });
+
   },
   methods: {
     courseChange(row){

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

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

@@ -0,0 +1,366 @@
+<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="首日上线" 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'
+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;
+      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>

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

@@ -0,0 +1,366 @@
+<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="首日上线" 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'
+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;
+      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>

+ 680 - 0
src/views/course/courseUserStatistics/qw/statistics.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 } 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: [],
+      // 弹出层标题
+      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;
+      qwWatchLogAllStatisticsList(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>

+ 61 - 16
src/views/course/courseUserStatistics/statistics.vue

@@ -1,14 +1,45 @@
 <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 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="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="change"></el-date-picker>
@@ -22,12 +53,15 @@
     <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">
@@ -35,7 +69,7 @@
                     <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">
@@ -43,8 +77,8 @@
                     <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>
@@ -435,11 +469,11 @@
                     <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-more
+    <pagination
       v-show="total>0"
       :total="total"
       :page.sync="queryParams.pageNum"
@@ -453,6 +487,7 @@
 <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";
 export default {
   name: "CourseWatchLog",
   data() {
@@ -482,6 +517,8 @@ export default {
       title: "",
       // 是否显示弹出层
       open: false,
+      companyUserList: [],
+      projectLists: [],
       // 查询参数
       queryParams: {
         pageNum: 1,
@@ -517,6 +554,14 @@ export default {
     this.getDicts("sys_course_watch_log_type").then(response => {
       this.logTypeOptions = response.data;
     });
+    this.getDicts("sys_course_project").then(response => {
+      this.projectLists = response.data;
+    })
+    getUserList().then(res=>{
+      if(res.code === 200) {
+        this.companyUserList = res.data
+      }
+    })
   },
   methods: {
     courseChange(row){

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

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

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

@@ -0,0 +1,294 @@
+<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.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'
+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;
+      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() {
+      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>

+ 43 - 0
src/views/qw/QwWorkTask/QwWorkTaskTabIndex.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>

+ 93 - 195
src/views/qw/QwWorkTask/index.vue

@@ -1,36 +1,18 @@
 <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="extId">
-        <el-input
-          v-model="queryParams.extId"
-          placeholder="请输入外部联系人id"
-          clearable
-          size="small"
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="企微用户id" prop="qwUserId">
-        <el-input
-          v-model="queryParams.qwUserId"
-          placeholder="请输入企微用户id"
-          clearable
-          size="small"
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="状态 0 待处理 1 已处理 3 过期" prop="status">
-        <el-select v-model="queryParams.status" placeholder="请选择状态 0 待处理 1 已处理 3 过期" clearable size="small">
+      <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 statusOptions"
-            :key="dict.dictValue"
-            :label="dict.dictLabel"
-            :value="dict.dictValue"
-          />
+            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="类别 1先导 2 课程 3 大小转 4 转人工" prop="type">
-        <el-select v-model="queryParams.type" placeholder="请选择类别 1先导 2 课程 3 大小转 4 转人工" clearable size="small">
+      <el-form-item label="类别" prop="type">
+        <el-select v-model="queryParams.type" placeholder="请选择类别" clearable size="small">
           <el-option
             v-for="dict in typeOptions"
             :key="dict.dictValue"
@@ -39,141 +21,48 @@
           />
         </el-select>
       </el-form-item>
-      <el-form-item label="标题" prop="title">
-        <el-input
-          v-model="queryParams.title"
-          placeholder="请输入标题"
-          clearable
-          size="small"
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="分值" prop="score">
-        <el-input
-          v-model="queryParams.score"
-          placeholder="请输入分值"
-          clearable
-          size="small"
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="sopid" prop="sopId">
-        <el-input
-          v-model="queryParams.sopId"
-          placeholder="请输入sopid"
-          clearable
-          size="small"
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="公司id" prop="companyId">
-        <el-input
-          v-model="queryParams.companyId"
-          placeholder="请输入公司id"
-          clearable
-          size="small"
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="用户id" prop="companyUserId">
-        <el-input
-          v-model="queryParams.companyUserId"
-          placeholder="请输入用户id"
-          clearable
-          size="small"
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
       <el-form-item>
         <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
       </el-form-item>
     </el-form>
 
-    <el-row :gutter="10" class="mb8">
-      <el-col :span="1.5">
-        <el-button
-          type="primary"
-          plain
-          icon="el-icon-plus"
-          size="mini"
-          @click="handleAdd"
-          v-hasPermi="['qw:QwWorkTask:add']"
-        >新增</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="success"
-          plain
-          icon="el-icon-edit"
-          size="mini"
-          :disabled="single"
-          @click="handleUpdate"
-          v-hasPermi="['qw:QwWorkTask:edit']"
-        >修改</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="danger"
-          plain
-          icon="el-icon-delete"
-          size="mini"
-          :disabled="multiple"
-          @click="handleDelete"
-          v-hasPermi="['qw:QwWorkTask:remove']"
-        >删除</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="warning"
-          plain
-          icon="el-icon-download"
-          size="mini"
-          :loading="exportLoading"
-          @click="handleExport"
-          v-hasPermi="['qw:QwWorkTask:export']"
-        >导出</el-button>
-      </el-col>
-      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
-    </el-row>
-
+  <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+	<el-tabs type="card" v-model="actName" @tab-click="handleClickX">
+	  <el-tab-pane v-for="(item,index) in statusOptions" :label="item.dictLabel" :name="item.dictValue"></el-tab-pane>
+	</el-tabs>
     <el-table border v-loading="loading" :data="QwWorkTaskList" @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="外部联系人id" align="center" prop="extId" />
-      <el-table-column label="企微用户id" align="center" prop="qwUserId" />
-      <el-table-column label="状态 0 待处理 1 已处理 3 过期" align="center" prop="status">
-        <template slot-scope="scope">
-          <dict-tag :options="statusOptions" :value="scope.row.status"/>
-        </template>
+<!--      <el-table-column label="客户昵称" align="center" prop="name" />-->
+<!--      <el-table-column label="企微账号" align="center" prop="qwUserName" />-->
+      <el-table-column label="销售名称" align="center" prop="companyUserName" />
+      <el-table-column label="状态" align="center" prop="status">
+		 <template slot-scope="scope">
+		   <dict-tag :options="statusOptions" :value="scope.row.status"/>
+		 </template>
       </el-table-column>
-      <el-table-column label="类别 1先导 2 课程 3 大小转 4 转人工" align="center" prop="type">
+      <el-table-column label="类别" align="center" prop="typeText">
         <template slot-scope="scope">
           <dict-tag :options="typeOptions" :value="scope.row.type"/>
         </template>
       </el-table-column>
       <el-table-column label="标题" align="center" prop="title" />
       <el-table-column label="备注" align="center" prop="remark" />
-      <el-table-column label="分值" align="center" prop="score" />
-      <el-table-column label="sopid" align="center" prop="sopId" />
-      <el-table-column label="公司id" align="center" prop="companyId" />
-      <el-table-column label="用户id" align="center" prop="companyUserId" />
+      <el-table-column label="分值" align="center" prop="score">
+        <template slot-scope="scope">
+          <span :style="getScoreStyle(scope.row.score)">{{ scope.row.score }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="sop编号" align="center" prop="sopId" />
+      <el-table-column label="公司id" align="center" prop="companyName" />
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
         <template slot-scope="scope">
-          <el-button
+          <el-button v-if="scope.row.status==0"
             size="mini"
             type="text"
             icon="el-icon-edit"
             @click="handleUpdate(scope.row)"
-            v-hasPermi="['qw:QwWorkTask:edit']"
-          >修改</el-button>
-          <el-button
-            size="mini"
-            type="text"
-            icon="el-icon-delete"
-            @click="handleDelete(scope.row)"
-            v-hasPermi="['qw:QwWorkTask:remove']"
-          >删除</el-button>
+          >处理</el-button>
         </template>
       </el-table-column>
     </el-table>
@@ -189,49 +78,8 @@
     <!-- 添加或修改企微任务看板对话框 -->
     <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
       <el-form ref="form" :model="form" :rules="rules" label-width="80px">
-        <el-form-item label="外部联系人id" prop="extId">
-          <el-input v-model="form.extId" placeholder="请输入外部联系人id" />
-        </el-form-item>
-        <el-form-item label="企微用户id" prop="qwUserId">
-          <el-input v-model="form.qwUserId" placeholder="请输入企微用户id" />
-        </el-form-item>
-        <el-form-item label="状态 0 待处理 1 已处理 3 过期" prop="status">
-          <el-select v-model="form.status" placeholder="请选择状态 0 待处理 1 已处理 3 过期">
-            <el-option
-              v-for="dict in statusOptions"
-              :key="dict.dictValue"
-              :label="dict.dictLabel"
-              :value="parseInt(dict.dictValue)"
-            ></el-option>
-          </el-select>
-        </el-form-item>
-        <el-form-item label="类别 1先导 2 课程 3 大小转 4 转人工" prop="type">
-          <el-select v-model="form.type" placeholder="请选择类别 1先导 2 课程 3 大小转 4 转人工">
-            <el-option
-              v-for="dict in typeOptions"
-              :key="dict.dictValue"
-              :label="dict.dictLabel"
-              :value="parseInt(dict.dictValue)"
-            ></el-option>
-          </el-select>
-        </el-form-item>
-        <el-form-item label="标题" prop="title">
-          <el-input v-model="form.title" placeholder="请输入标题" />
-        </el-form-item>
         <el-form-item label="备注" prop="remark">
-          <el-input v-model="form.remark" placeholder="请输入备注" />
-        </el-form-item>
-        <el-form-item label="分值" prop="score">
-          <el-input v-model="form.score" placeholder="请输入分值" />
-        </el-form-item>
-        <el-form-item label="sopid" prop="sopId">
-          <el-input v-model="form.sopId" placeholder="请输入sopid" />
-        </el-form-item>
-        <el-form-item label="公司id" prop="companyId">
-          <el-input v-model="form.companyId" placeholder="请输入公司id" />
-        </el-form-item>
-        <el-form-item label="用户id" prop="companyUserId">
-          <el-input v-model="form.companyUserId" placeholder="请输入用户id" />
+          <el-input  v-model="form.remark" placeholder="请输入备注"  type="textarea" :rows="3"/>
         </el-form-item>
       </el-form>
       <div slot="footer" class="dialog-footer">
@@ -244,17 +92,21 @@
 
 <script>
 import { listQwWorkTask, getQwWorkTask, delQwWorkTask, addQwWorkTask, updateQwWorkTask, exportQwWorkTask } from "@/api/qw/QwWorkTask";
-
+import {getMyQwUserList, getMyQwCompanyList, handleInputAuthAppKey, updateUser} from "@/api/qw/user";
+import {getUserList} from "@/api/company/companyUser";
 export default {
   name: "QwWorkTask",
   data() {
     return {
+	  actName:"0",
       // 遮罩层
-      loading: true,
+      loading: false,
       // 导出遮罩层
       exportLoading: false,
       // 选中数组
       ids: [],
+	  myQwUserList:[],
+      companyUserList: [],
       // 非单个禁用
       single: true,
       // 非多个禁用
@@ -278,8 +130,8 @@ export default {
         pageNum: 1,
         pageSize: 10,
         extId: null,
+        status: 0,
         qwUserId: null,
-        status: null,
         type: null,
         title: null,
         score: null,
@@ -295,24 +147,56 @@ export default {
     };
   },
   created() {
-    this.getList();
-    this.getDicts("sys_company_status").then(response => {
+
+    this.getDicts("sys_qw_work_task_status").then(response => {
       this.statusOptions = response.data;
     });
-    this.getDicts("sys_company_status").then(response => {
+
+    this.getDicts("sys_qw_work_task_type").then(response => {
       this.typeOptions = response.data;
     });
+
+    getUserList().then(res=>{
+      if(res.code === 200) {
+        this.companyUserList = res.data
+      }
+    })
+    this.handleGetMyQwUserList();
+
+    this.getList();
   },
   methods: {
+	getScoreStyle(score) {
+	let backgroundColor = '';
+	  if (score >= 15) {
+		backgroundColor = '#ff4d4f';    // 红色
+	  } else if (score >= 9) {
+		backgroundColor = '#ff7d45';    // 橘红
+	  } else if (score >= 4) {
+		backgroundColor = '#ffec3d';    // 黄色
+	  } else {
+		backgroundColor = '#ffffff';    // 白色
+	  }
+	  return {
+		'background-color': backgroundColor,
+		'padding': '5px 10px',
+		'border-radius': '4px'
+	  };
+	},
     /** 查询企微任务看板列表 */
     getList() {
       this.loading = true;
       listQwWorkTask(this.queryParams).then(response => {
         this.QwWorkTaskList = response.rows;
         this.total = response.total;
+      }).finally(() => {
         this.loading = false;
       });
     },
+	handleClickX(tab, event) {
+	  this.queryParams.status=tab.name;
+	  this.handleQuery();
+	},
     // 取消按钮
     cancel() {
       this.open = false;
@@ -324,7 +208,7 @@ export default {
         id: null,
         extId: null,
         qwUserId: null,
-        status: null,
+        status: 0,
         type: null,
         title: null,
         remark: null,
@@ -342,6 +226,18 @@ export default {
       this.queryParams.pageNum = 1;
       this.getList();
     },
+	handleGetMyQwUserList(){
+
+	  getMyQwUserList().then(response => {
+	    this.myQwUserList = response.data;
+	    if(this.myQwUserList!=null){
+	      // this.queryParams.qwUserId=this.myQwUserList[0].dictValue
+	      this.queryParams.corpId=this.myQwUserList[0].corpId
+	      this.getList();
+	    }
+	  });
+
+	},
     /** 重置按钮操作 */
     resetQuery() {
       this.resetForm("queryForm");
@@ -363,11 +259,10 @@ export default {
     handleUpdate(row) {
       this.reset();
       const id = row.id || this.ids
-      getQwWorkTask(id).then(response => {
-        this.form = response.data;
+        this.form = row;
         this.open = true;
-        this.title = "修改企微任务看板";
-      });
+        this.title = "处理任务";
+
     },
     /** 提交按钮 */
     submitForm() {
@@ -415,8 +310,11 @@ export default {
           return exportQwWorkTask(queryParams);
         }).then(response => {
           this.download(response.msg);
+
+        }).catch(() => {})
+        .finally(()=>{
           this.exportLoading = false;
-        }).catch(() => {});
+        })
     }
   }
 };

+ 276 - 0
src/views/qw/QwWorkTask/qw/allTask.vue

@@ -0,0 +1,276 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="企微账号" prop="qwUserName">
+             <el-input
+               v-model="queryParams.qwUserName"
+               placeholder="请输入企微昵称"
+               clearable
+               size="small"
+               @keyup.enter.native="handleQuery"
+             />
+      </el-form-item>
+
+
+      <el-form-item>
+        <el-button type="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>
+
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table border v-loading="loading" :data="QwWorkTaskList" @selection-change="handleSelectionChange">
+    <el-table-column type="selection" width="55" align="center" />
+    <el-table-column label="企微账号" align="center" prop="qwUserName" />
+	<el-table-column label="销售昵称" align="center" prop="companyUserName" />
+	<el-table-column label="待处理" align="center" prop="status0" />
+	<el-table-column label="已处理" align="center" prop="status1" />
+	<el-table-column label="待处理完课" align="center" prop="status2" />
+	<el-table-column label="已处理完课" align="center" prop="status3" />
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改企微任务看板对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="备注" prop="remark">
+          <el-input  v-model="form.remark" placeholder="请输入备注"  type="textarea" :rows="3"/>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listQwWorkTask, getQwWorkTask, delQwWorkTask, addQwWorkTask, updateQwWorkTask, exportQwWorkTask,allListQwWorkTask } from "@/api/qw/QwWorkTask";
+import {getMyQwUserList, getMyQwCompanyList, handleInputAuthAppKey, updateUser} from "@/api/qw/user";
+export default {
+  name: "QwWorkTask",
+  data() {
+    return {
+	  actName:"0",
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+	  myQwUserList:[],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 企微任务看板表格数据
+      QwWorkTaskList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 状态 0 待处理 1 已处理 3 过期字典
+      statusOptions: [],
+      // 类别 1先导 2 课程 3 大小转 4 转人工字典
+      typeOptions: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        extId: null,
+        qwUserId: null,
+        status: 0,
+        type: null,
+        title: null,
+        score: null,
+        sopId: null,
+        companyId: null,
+        companyUserId: null,
+		qwUserId: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      }
+    };
+  },
+  created() {
+	this.handleGetMyQwUserList();
+    this.getDicts("sys_qw_work_task_status").then(response => {
+      this.statusOptions = response.data;
+    });
+    this.getDicts("sys_qw_work_task_type").then(response => {
+      this.typeOptions = response.data;
+    });
+  },
+  methods: {
+	getScoreStyle(score) {
+	let backgroundColor = '';
+	  if (score >= 15) {
+		backgroundColor = '#ff4d4f';    // 红色
+	  } else if (score >= 9) {
+		backgroundColor = '#ff7d45';    // 橘红
+	  } else if (score >= 4) {
+		backgroundColor = '#ffec3d';    // 黄色
+	  } else {
+		backgroundColor = '#ffffff';    // 白色
+	  }
+	  return { 
+		'background-color': backgroundColor,
+		'padding': '5px 10px',
+		'border-radius': '4px'
+	  };
+	},
+	formatTime(timeStr) {
+	      if (!timeStr && timeStr !== 0) return '';
+	      
+	      // 处理数字和字符串输入
+	      const str = String(timeStr).padStart(4, '0');
+	      
+	      // 提取有效部分
+	      const hours = str.substring(0, 2);
+	      const minutes = str.substring(2, 4);
+	      
+	      // 简单验证
+	      if (hours > 23 || minutes > 59) return '无效时间';
+	      
+	      return `${hours}:${minutes}`;
+	    },
+    /** 查询企微任务看板列表 */
+    getList() {
+      this.loading = true;
+      allListQwWorkTask(this.queryParams).then(response => {
+        this.QwWorkTaskList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+	handleClickX(tab, event) {
+	  this.queryParams.status=tab.name;
+	  this.handleQuery();
+	},
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        extId: null,
+        qwUserId: null,
+        status: 0,
+        type: null,
+        title: null,
+        remark: null,
+        score: null,
+        sopId: null,
+        companyId: null,
+        companyUserId: null,
+        createTime: null,
+        updateTime: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+	handleGetMyQwUserList(){
+	 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
+        this.form = row;
+        this.open = true;
+        this.title = "处理任务";
+
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != null) {
+            updateQwWorkTask(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addQwWorkTask(this.form).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除企微任务看板编号为"' + ids + '"的数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return delQwWorkTask(ids);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有企微任务看板数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportQwWorkTask(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    }
+  }
+};
+</script>

+ 421 - 0
src/views/qw/QwWorkTask/qw/index.vue

@@ -0,0 +1,421 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="企微账号" prop="qwUserId">
+              <el-select v-model="queryParams.qwUserId" placeholder="企微账号"  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="类别" prop="type">
+        <el-select v-model="queryParams.type" placeholder="请选择类别" clearable size="small">
+          <el-option
+            v-for="dict in typeOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+
+	  <el-form-item label="处理状态" prop="trackType">
+	    <el-select v-model="queryParams.trackType" placeholder="处理状态" clearable size="small">
+	      <el-option
+	        v-for="dict in trackTypeOptions"
+	        :key="dict.dictValue"
+	        :label="dict.dictLabel"
+	        :value="dict.dictValue"
+	      />
+	    </el-select>
+	  </el-form-item>
+
+      <el-form-item label="sop编号" prop="sopId">
+        <el-input
+          v-model="queryParams.sopId"
+          placeholder="请输入sopid"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+	<el-tabs type="card" v-model="actName" @tab-click="handleClickX">
+	  <el-tab-pane v-for="(item,index) in statusOptions" :label="item.dictLabel" :name="item.dictValue"></el-tab-pane>
+	</el-tabs>
+    <el-table border v-loading="loading" :data="QwWorkTaskList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="客户昵称" align="center" prop="name" />
+      <el-table-column label="企微账号" align="center" prop="qwUserName" />
+      <el-table-column label="状态" align="center" prop="status">
+		 <template slot-scope="scope">
+		   <dict-tag :options="statusOptions" :value="scope.row.status"/>
+		 </template>
+      </el-table-column>
+      <el-table-column label="类别" align="center" prop="type">
+        <template slot-scope="scope">
+          <dict-tag :options="typeOptions" :value="scope.row.type"/>
+        </template>
+      </el-table-column>
+	  <el-table-column label="处理状态" align="center" prop="trackType">
+	    <template slot-scope="scope">
+	      <dict-tag :options="trackTypeOptions" :value="scope.row.trackType"/>
+	    </template>
+	  </el-table-column>
+      <el-table-column label="标题" align="center" prop="title" />
+      <el-table-column label="描述" align="center" prop="description" />
+      <el-table-column label="分值" align="center" prop="score">
+        <template slot-scope="scope">
+          <span :style="getScoreStyle(scope.row.score)">{{ scope.row.score }}</span>
+        </template>
+      </el-table-column>
+	  <el-table-column label="用户过往看课记录" align="center" width="200px">
+	    <template slot-scope="scope">
+	      <div style="display: flex; gap: 4px; justify-content: center;">
+	        <span
+	          v-for="(log, index) in scope.row.logs"
+	          :key="index"
+	          :style="{
+	            display: 'inline-block',
+	            width: '16px',
+	            height: '16px',
+	            borderRadius: '2px',
+	            backgroundColor:
+	              log === 2 ? '#67C23A' :
+				  log === 3 ? '#f55a4f' :
+				  log === 4 ? '#FFD700' :
+				  '#909399'
+	          }"
+	        ></span>
+	      </div>
+	    </template>
+	  </el-table-column>
+	    <el-table-column label="最晚看课时间" align="center">
+	        <template slot-scope="scope">
+	          {{ formatTime(scope.row.lastWatchTime) }}
+	        </template>
+	      </el-table-column>
+
+      <el-table-column label="sopId" align="center" prop="sopId" />
+      <el-table-column label="创建时间" align="center" prop="createTime" />
+      <el-table-column label="修改时间" align="center" prop="updateTime" />
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button v-if="scope.row.status==0"
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+          >处理</el-button>
+		  <el-button v-if="scope.row.status==0"
+		    size="mini"
+		    type="text"
+		    @click="handleUpdate2(scope.row)"
+		  >未接通</el-button>
+		  <el-button v-if="scope.row.status==0"
+		    size="mini"
+		    type="text"
+		    @click="handleUpdate3(scope.row)"
+		  >接通</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改企微任务看板对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+		<el-form-item label="处理类型" prop="trackType">
+		  <el-select v-model="form.trackType" placeholder="状态" clearable size="small">
+			  <el-option
+				v-for="dict in trackTypeOptions"
+				:key="dict.dictValue"
+				:label="dict.dictLabel"
+				:value="dict.dictValue"
+			  />
+		  </el-select>
+		</el-form-item>
+        <el-form-item label="描述" prop="description">
+          <el-input  v-model="form.description" placeholder="请输入描述"  type="textarea" :rows="3"/>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listQwWorkTask, getQwWorkTask, delQwWorkTask, addQwWorkTask, updateQwWorkTask, exportQwWorkTask,updateQwWorkTask2,updateQwWorkTask3 } from "@/api/qw/qw/QwWorkTask";
+import {getMyQwUserList, getMyQwCompanyList, handleInputAuthAppKey, updateUser} from "@/api/qw/user";
+export default {
+  name: "QwWorkTask",
+  data() {
+    return {
+	  actName:"0",
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+	  myQwUserList:[],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 企微任务看板表格数据
+      QwWorkTaskList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 状态 0 待处理 1 已处理 3 过期字典
+      statusOptions: [],
+	  trackTypeOptions:[],
+      // 类别 1先导 2 课程 3 大小转 4 转人工字典
+      typeOptions: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        extId: null,
+        qwUserId: null,
+        status: 0,
+        type: null,
+        title: null,
+        score: null,
+        sopId: null,
+        companyId: null,
+        companyUserId: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+		   trackType: [
+		      { required: true, message: '请选择处理类型', trigger: 'change' }
+		    ]
+      }
+    };
+  },
+  created() {
+	this.handleGetMyQwUserList();
+    this.getDicts("sys_qw_work_task_status").then(response => {
+      this.statusOptions = response.data;
+    });
+    this.getDicts("sys_qw_work_task_type").then(response => {
+      this.typeOptions = response.data;
+    });
+	this.getDicts("sys_qw_work_task_track_type").then(response => {
+	  this.trackTypeOptions = response.data;
+	});
+
+
+  },
+  methods: {
+	getScoreStyle(score) {
+	let backgroundColor = '';
+	  if (score >= 15) {
+		backgroundColor = '#ff4d4f';    // 红色
+	  } else if (score >= 9) {
+		backgroundColor = '#ff7d45';    // 橘红
+	  } else if (score >= 4) {
+		backgroundColor = '#ffec3d';    // 黄色
+	  } else {
+		backgroundColor = '#ffffff';    // 白色
+	  }
+	  return {
+		'background-color': backgroundColor,
+		'padding': '5px 10px',
+		'border-radius': '4px'
+	  };
+	},
+	formatTime(timeStr) {
+	      if (!timeStr && timeStr !== 0) return '';
+
+	      // 处理数字和字符串输入
+	      const str = String(timeStr).padStart(4, '0');
+
+	      // 提取有效部分
+	      const hours = str.substring(0, 2);
+	      const minutes = str.substring(2, 4);
+
+	      // 简单验证
+	      if (hours > 23 || minutes > 59) return '无效时间';
+
+	      return `${hours}:${minutes}`;
+	    },
+    /** 查询企微任务看板列表 */
+    getList() {
+      this.loading = true;
+      listQwWorkTask(this.queryParams).then(response => {
+        this.QwWorkTaskList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+	handleClickX(tab, event) {
+	  this.queryParams.status=tab.name;
+	  this.handleQuery();
+	},
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        extId: null,
+        qwUserId: null,
+        status: 0,
+        type: null,
+        title: null,
+        remark: null,
+        score: null,
+        sopId: null,
+        companyId: null,
+        companyUserId: null,
+        createTime: null,
+        updateTime: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+	handleGetMyQwUserList(){
+
+	  getMyQwUserList().then(response => {
+	    this.myQwUserList = response.data;
+	    if(this.myQwUserList!=null){
+	      this.queryParams.qwUserId=this.myQwUserList[0].dictValue
+	      this.queryParams.corpId=this.myQwUserList[0].corpId
+	      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
+        this.form = row;
+        this.open = true;
+        this.title = "处理任务";
+
+    },
+
+	handleUpdate2(row) {
+	  var from={id:row.id }
+	  	updateQwWorkTask2(from).then(response => {
+	    this.msgSuccess("修改成功");
+
+	    this.getList();
+	  });
+
+	},
+	handleUpdate3(row) {
+	  var from={id:row.id }
+		updateQwWorkTask3(from).then(response => {
+	  this.msgSuccess("修改成功");
+
+	  this.getList();
+	});
+	},
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != null) {
+            updateQwWorkTask(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addQwWorkTask(this.form).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除企微任务看板编号为"' + ids + '"的数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return delQwWorkTask(ids);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有企微任务看板数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportQwWorkTask(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    }
+  }
+};
+</script>

+ 367 - 0
src/views/qw/QwWorkTask/qw/qwWorkTask.vue

@@ -0,0 +1,367 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+     <el-form-item label="企微账号" prop="qwUserName">
+             <el-input
+               v-model="queryParams.qwUserName"
+               placeholder="请输入企微昵称"
+               clearable
+               size="small"
+               @keyup.enter.native="handleQuery"
+             />
+      </el-form-item>
+      <el-form-item label="类别" prop="type">
+        <el-select v-model="queryParams.type" placeholder="请选择类别" clearable size="small">
+          <el-option
+            v-for="dict in typeOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+	  <el-form-item label="处理状态" prop="trackType">
+	    <el-select v-model="queryParams.trackType" placeholder="处理状态" clearable size="small">
+	      <el-option
+	        v-for="dict in trackTypeOptions"
+	        :key="dict.dictValue"
+	        :label="dict.dictLabel"
+	        :value="dict.dictValue"
+	      />
+	    </el-select>
+	  </el-form-item>
+      <el-form-item label="sop编号" prop="sopId">
+        <el-input
+          v-model="queryParams.sopId"
+          placeholder="请输入sopid"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+	<el-tabs type="card" v-model="actName" @tab-click="handleClickX">
+	  <el-tab-pane v-for="(item,index) in statusOptions" :label="item.dictLabel" :name="item.dictValue"></el-tab-pane>
+	</el-tabs>
+    <el-table border v-loading="loading" :data="QwWorkTaskList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="客户昵称" align="center" prop="name" />
+      <el-table-column label="企微账号" align="center" prop="qwUserName" />
+      <el-table-column label="状态" align="center" prop="status">
+		 <template slot-scope="scope">
+		   <dict-tag :options="statusOptions" :value="scope.row.status"/>
+		 </template>
+      </el-table-column>
+      <el-table-column label="类别" align="center" prop="type">
+        <template slot-scope="scope">
+          <dict-tag :options="typeOptions" :value="scope.row.type"/>
+        </template>
+      </el-table-column>
+	  <el-table-column label="处理状态" align="center" prop="trackType">
+	    <template slot-scope="scope">
+	      <dict-tag :options="trackTypeOptions" :value="scope.row.trackType"/>
+	    </template>
+	  </el-table-column>
+      <el-table-column label="标题" align="center" prop="title" />
+      <el-table-column label="备注" align="center" prop="remark" />
+      <el-table-column label="分值" align="center" prop="score">
+        <template slot-scope="scope">
+          <span :style="getScoreStyle(scope.row.score)">{{ scope.row.score }}</span>
+        </template>
+      </el-table-column>
+	  <el-table-column label="用户过往看课记录" align="center" width="200px">
+	    <template slot-scope="scope">
+	      <div style="display: flex; gap: 4px; justify-content: center;">
+	        <span 
+	          v-for="(log, index) in scope.row.logs" 
+	          :key="index"
+	          :style="{
+	            display: 'inline-block',
+	            width: '16px',
+	            height: '16px',
+	            borderRadius: '2px',
+	            backgroundColor: 
+	              log === 2 ? '#67C23A' : 
+				  log === 3 ? '#f55a4f' : 
+				  log === 4 ? '#FFD700' : 
+				  '#909399'
+	          }"
+	        ></span>
+	      </div>
+	    </template>
+	  </el-table-column>
+	    <el-table-column label="最晚看课时间" align="center">
+	        <template slot-scope="scope">
+	          {{ formatTime(scope.row.lastWatchTime) }}
+	        </template>
+	      </el-table-column>
+	  
+      <el-table-column label="sopId" align="center" prop="sopId" />
+      <el-table-column label="创建时间" align="center" prop="createTime" />
+      <el-table-column label="修改时间" align="center" prop="updateTime" />
+<!--      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button v-if="scope.row.status==0"
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+          >处理</el-button>
+        </template>
+      </el-table-column> -->
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改企微任务看板对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="备注" prop="remark">
+          <el-input  v-model="form.remark" placeholder="请输入备注"  type="textarea" :rows="3"/>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listQwWorkTask, getQwWorkTask, delQwWorkTask, addQwWorkTask, updateQwWorkTask, exportQwWorkTask,glList} from "@/api/qw/QwWorkTask";
+import {getMyQwUserList, getMyQwCompanyList, handleInputAuthAppKey, updateUser} from "@/api/qw/user";
+export default {
+  name: "QwWorkTask",
+  data() {
+    return {
+	  actName:"0",
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+	  myQwUserList:[],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 企微任务看板表格数据
+      QwWorkTaskList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 状态 0 待处理 1 已处理 3 过期字典
+      statusOptions: [],
+      // 类别 1先导 2 课程 3 大小转 4 转人工字典
+      typeOptions: [],
+	  trackTypeOptions:[],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        extId: null,
+        qwUserId: null,
+        status: 0,
+        type: null,
+        title: null,
+        score: null,
+        sopId: null,
+        companyId: null,
+        companyUserId: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      }
+    };
+  },
+  created() {
+	this.handleGetMyQwUserList();
+    this.getDicts("sys_qw_work_task_status").then(response => {
+      this.statusOptions = response.data;
+    });
+    this.getDicts("sys_qw_work_task_type").then(response => {
+      this.typeOptions = response.data;
+    });
+	this.getDicts("sys_qw_work_task_track_type").then(response => {
+	  this.trackTypeOptions = response.data;
+	});
+  },
+  methods: {
+	getScoreStyle(score) {
+	let backgroundColor = '';
+	  if (score >= 15) {
+		backgroundColor = '#ff4d4f';    // 红色
+	  } else if (score >= 9) {
+		backgroundColor = '#ff7d45';    // 橘红
+	  } else if (score >= 4) {
+		backgroundColor = '#ffec3d';    // 黄色
+	  } else {
+		backgroundColor = '#ffffff';    // 白色
+	  }
+	  return { 
+		'background-color': backgroundColor,
+		'padding': '5px 10px',
+		'border-radius': '4px'
+	  };
+	},
+	formatTime(timeStr) {
+	      if (!timeStr && timeStr !== 0) return '';
+	      
+	      // 处理数字和字符串输入
+	      const str = String(timeStr).padStart(4, '0');
+	      
+	      // 提取有效部分
+	      const hours = str.substring(0, 2);
+	      const minutes = str.substring(2, 4);
+	      
+	      // 简单验证
+	      if (hours > 23 || minutes > 59) return '无效时间';
+	      
+	      return `${hours}:${minutes}`;
+	    },
+    /** 查询企微任务看板列表 */
+    getList() {
+      this.loading = true;
+      glList(this.queryParams).then(response => {
+        this.QwWorkTaskList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+	handleClickX(tab, event) {
+	  this.queryParams.status=tab.name;
+	  this.handleQuery();
+	},
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        extId: null,
+        qwUserId: null,
+        status: 0,
+        type: null,
+        title: null,
+        remark: null,
+        score: null,
+        sopId: null,
+        companyId: null,
+        companyUserId: null,
+        createTime: null,
+        updateTime: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+	handleGetMyQwUserList(){
+
+		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
+        this.form = row;
+        this.open = true;
+        this.title = "处理任务";
+
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != null) {
+            updateQwWorkTask(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addQwWorkTask(this.form).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除企微任务看板编号为"' + ids + '"的数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return delQwWorkTask(ids);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有企微任务看板数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportQwWorkTask(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    }
+  }
+};
+</script>

+ 179 - 0
src/views/user/darkRoom/index.vue

@@ -0,0 +1,179 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px" @submit.native.prevent>
+      <el-form-item label="关键词" prop="keyword">
+        <el-input
+          style="width:220px"
+          v-model="queryParams.keyword"
+          placeholder="请输入微信名称/手机号"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-button
+        size="mini"
+        type="primary"
+        style="margin-left: 5px"
+        :disabled="ids.length === 0"
+        @click="handleUpdateBatch"
+        v-hasPermi="['store:user:enabledUsers']"
+      >批量启用</el-button>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table  height="500" border v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="用户id" align="center" prop="userId" />
+      <el-table-column label="用户昵称" align="center" prop="nickname" />
+      <el-table-column label="用户头像" align="center" prop="avatar">
+        <template slot-scope="scope">
+          <el-popover trigger="hover" placement="top">
+            <el-image :src="scope.row.avatar" style="width: 200px;height: 200px"/>
+            <div slot="reference" class="name-wrapper">
+              <el-avatar shape="square" size="large" :src="scope.row.avatar"/>
+            </div>
+          </el-popover>
+        </template>
+      </el-table-column>
+      <el-table-column label="注册时间" align="center" prop="createTime" width="160px"/>
+      <el-table-column label="看课数量" align="center" prop="watchCourseCount" />
+      <el-table-column label="缺课数量" align="center" prop="missCourseCount" />
+      <el-table-column label="缺课状态" align="center" prop="missCourseStatus">
+        <template slot-scope="scope">
+          <el-tag effect="dark" type="danger"  v-if="scope.row.missCourseStatus === 1">已缺课</el-tag>
+          <el-tag effect="dark" type="success" v-else>未缺课</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="参与营期数量" align="center" prop="partCourseCount" width="100px"/>
+      <el-table-column label="最后一次看课时间" align="center" prop="lastWatchDate"  width="160px"/>
+      <el-table-column label="用户状态" align="center" prop="courseCountStatus">
+        <template slot-scope="scope">
+          <el-tag effect="dark" type="success" v-if="scope.row.courseCountStatus === 1">正常</el-tag>
+          <el-tag effect="dark" type="info"    v-else-if="scope.row.courseCountStatus === 2">停止</el-tag>
+          <el-tag effect="dark" type="danger"  v-else>未看</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="停课天数" align="center" prop="stopWatchDays" />
+      <el-table-column label="完播时间" align="center" prop="completeWatchDate" width="160px"/>
+      <el-table-column label="标签名称" align="center" prop="tag" />
+      <el-table-column label="所属销售名称" align="center" prop="companyUserNickName" width="120px"/>
+      <el-table-column label="备注" align="center" prop="remark" />
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['store:user:darkRoomList']"
+          >启用</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </div>
+</template>
+
+<script>
+import { darkRoomList,enabledUsers } from "@/api/store/user";
+
+export default {
+  name: "darkRoom",
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 用户列表数据
+      userList: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        keyword: null,
+        isBlack: true
+      },
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询客户列表 */
+    getList() {
+      this.loading = true;
+      darkRoomList(this.queryParams).then(response => {
+        this.userList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.userId)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    // 启用
+    handleUpdate(row) {
+      enabledUsers([row.userId]).then(response => {
+        if (response.code === 200) {
+          this.msgSuccess("取消禁用成功");
+          this.getList();
+        } else {
+          this.msgError(response.msg);
+        }
+      });
+    },
+    // 启用
+    handleUpdateBatch() {
+      enabledUsers(this.ids).then(response => {
+        if (response.code === 200) {
+          this.msgSuccess("取消禁用成功");
+          this.getList();
+        } else {
+          this.msgError(response.msg);
+        }
+      });
+    }
+  }
+};
+</script>
+<style scoped>
+.app-container {
+  height: 87.5vh;
+}
+</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" border>
+      <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>

+ 179 - 0
src/views/users/user/darkRoom.vue

@@ -0,0 +1,179 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px" @submit.native.prevent>
+      <el-form-item label="关键词" prop="keyword">
+        <el-input
+          style="width:220px"
+          v-model="queryParams.keyword"
+          placeholder="请输入微信名称/手机号"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-button
+        size="mini"
+        type="primary"
+        style="margin-left: 5px"
+        :disabled="ids.length === 0"
+        @click="handleUpdateBatch"
+        v-hasPermi="['users:user:darkRoomList']"
+      >批量启用</el-button>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table  height="500" border v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="用户id" align="center" prop="userId" />
+      <el-table-column label="用户昵称" align="center" prop="nickname" />
+      <el-table-column label="用户头像" align="center" prop="avatar">
+        <template slot-scope="scope">
+          <el-popover trigger="hover" placement="top">
+            <el-image :src="scope.row.avatar" style="width: 200px;height: 200px"/>
+            <div slot="reference" class="name-wrapper">
+              <el-avatar shape="square" size="large" :src="scope.row.avatar"/>
+            </div>
+          </el-popover>
+        </template>
+      </el-table-column>
+      <el-table-column label="注册时间" align="center" prop="createTime" width="160px"/>
+      <el-table-column label="看课数量" align="center" prop="watchCourseCount" />
+      <el-table-column label="缺课数量" align="center" prop="missCourseCount" />
+      <el-table-column label="缺课状态" align="center" prop="missCourseStatus">
+        <template slot-scope="scope">
+          <el-tag effect="dark" type="danger"  v-if="scope.row.missCourseStatus === 1">已缺课</el-tag>
+          <el-tag effect="dark" type="success" v-else>未缺课</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="参与营期数量" align="center" prop="partCourseCount" width="100px"/>
+      <el-table-column label="最后一次看课时间" align="center" prop="lastWatchDate"  width="160px"/>
+      <el-table-column label="用户状态" align="center" prop="courseCountStatus">
+        <template slot-scope="scope">
+          <el-tag effect="dark" type="success" v-if="scope.row.courseCountStatus === 1">正常</el-tag>
+          <el-tag effect="dark" type="info"    v-else-if="scope.row.courseCountStatus === 2">停止</el-tag>
+          <el-tag effect="dark" type="danger"  v-else>未看</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="停课天数" align="center" prop="stopWatchDays" />
+      <el-table-column label="完播时间" align="center" prop="completeWatchDate" width="160px"/>
+      <el-table-column label="标签名称" align="center" prop="tag" />
+      <el-table-column label="所属销售名称" align="center" prop="companyUserNickName" width="120px"/>
+      <el-table-column label="备注" align="center" prop="remark" />
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['users:user:darkRoomList']"
+          >启用</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </div>
+</template>
+
+<script>
+import { darkRoomList,enabledUsers } from "@/api/users/user";
+
+export default {
+  name: "darkRoom",
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 用户列表数据
+      userList: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        keyword: null,
+        isBlack: true
+      },
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询客户列表 */
+    getList() {
+      this.loading = true;
+      darkRoomList(this.queryParams).then(response => {
+        this.userList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.userId)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    // 启用
+    handleUpdate(row) {
+      enabledUsers([row.userId]).then(response => {
+        if (response.code === 200) {
+          this.msgSuccess("取消禁用成功");
+          this.getList();
+        } else {
+          this.msgError(response.msg);
+        }
+      });
+    },
+    // 启用
+    handleUpdateBatch() {
+      enabledUsers(this.ids).then(response => {
+        if (response.code === 200) {
+          this.msgSuccess("取消禁用成功");
+          this.getList();
+        } else {
+          this.msgError(response.msg);
+        }
+      });
+    }
+  }
+};
+</script>
+<style scoped>
+.app-container {
+  height: 87.5vh;
+}
+</style>

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

@@ -0,0 +1,254 @@
+<template>
+  <div class="app-container">
+    <!-- 搜索区域 -->
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="销售" prop="salesName">
+        <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="phone">
+        <el-input
+          style="width: 220px"
+          v-model="queryParams.phone"
+          placeholder="请输入手机号码"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <!-- 操作按钮区域 -->
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          icon="el-icon-d-arrow-right"
+          size="mini"
+          :disabled="multiple"
+          @click="handleTransfer"
+          v-hasPermi="['company:user:transfer']"
+        >转移</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <!-- 表格数据 -->
+    <el-table height="500" border v-loading="loading" :data="customerList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="客户ID" align="center" prop="userId" />
+      <el-table-column label="昵称" align="center" prop="nickname" />
+      <el-table-column label="所属销售" align="center" prop="companyUserName" />
+      <el-table-column label="手机号码" align="center" prop="phone" />
+      <el-table-column label="状态" align="center" prop="statusText" >
+        <template slot-scope="scope">
+          <el-tag :type="scope.row.statusText === '正常' ? 'success' : 'danger'">{{ scope.row.statusText }}</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ scope.row.createTime }}</span>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 分页 -->
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 客户转移对话框 (可选,更复杂的转移逻辑可能需要) -->
+
+    <el-dialog title="客户转移" :visible.sync="openTransferDialog" width="500px" append-to-body>
+      <el-form ref="transferForm" :model="transferForm" label-width="100px" :rules="cusTransfer">
+        <el-alert
+          title="会经过总后台审核后才进行转移"
+          type="warning">
+        </el-alert>
+        <el-form-item label="转移至销售" prop="targetUserId">
+          <el-select v-model="transferForm.targetUserId" remote placeholder="请选择" filterable clearable>
+            <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="content">
+          <el-input type="textarea" v-model="transferForm.content" placeholder="转移原因"></el-input>
+        </el-form-item>
+        <p>确定要转移选中的 <strong>{{ ids.length }}</strong> 个客户吗?</p>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="danger" @click="submitTransfer">提交申请</el-button>
+        <el-button @click="cancelTransfer">取 消</el-button>
+      </div>
+    </el-dialog>
+
+  </div>
+</template>
+
+<script>
+import { listUser, transferUser} from "@/api/users/user";
+import {parseTime} from "../../../utils/common";
+import {getUserList} from "@/api/company/companyUser"; // 假设API文件路径
+
+export default {
+  name: "CustomerTransfer",
+  data() {
+    return {
+      loading: true,
+      ids: [],
+      single: true,
+      multiple: true,
+      showSearch: true,
+      openTransferDialog: false,
+      total: 0,
+      customerList: [],
+      companyUserList: [],
+      transferForm: {
+        targetUserId: null,
+        content: null
+      },
+      cusTransfer: {
+        targetUserId: [{required: true, message: '请选择转移至销售', trigger: 'change'}],
+        content: [{required: true, message: '请选择转移至销售', trigger: 'change'}]
+      },
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        salesName: null,
+        phoneNumber: null,
+      },
+    };
+  },
+  created() {
+    getUserList().then(res=>{
+      if(res.code === 200) {
+        this.companyUserList = res.data
+      }
+    });
+
+    this.getList();
+  },
+  methods: {
+    parseTime,
+    /** 查询客户列表 */
+    getList() {
+      this.loading = true;
+      listUser(this.queryParams).then(response => {
+        this.customerList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      }).catch(() => {
+        this.loading = false;
+      });
+    },
+    cancelTransfer() {
+      this.openTransferDialog = false;
+      this.resetTransferForm();
+    },
+    resetTransferForm() {
+      this.transferForm = {
+        targetUserId: null,
+        content: null
+      };
+      this.resetForm("transferForm"); // 假设 transferForm 是 el-form 的 ref
+    },
+
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.userId)
+      this.single = selection.length !== 1;
+      this.multiple = !selection.length;
+    },
+
+    /** 转移按钮操作 */
+    handleTransfer(row) {
+      const userIds = row.userId ? [row.userId] : this.ids;
+      if (userIds.length === 0) {
+        this.$message.warning("请至少选择一个客户进行转移");
+        return;
+      }
+
+      this.resetTransferForm();
+      this.openTransferDialog = true;
+    },
+
+    /** 提交转移按钮 (如果使用对话框) */
+
+    submitTransfer() {
+      this.$refs["transferForm"].validate(valid => {
+        if (valid) {
+          transferUser({
+            userIds: this.ids,
+            targetCompanyUserId: this.transferForm.targetUserId,
+            content: this.transferForm.content
+          }).then(response => {
+            if (response.code === 200) {
+              this.msgSuccess(response.msg);
+              this.openTransferDialog = false;
+              this.getList();
+            } else {
+              this.msgError(response.msg || "转移失败");
+            }
+          }).catch(() => {
+             this.msgError("转移请求失败");
+          });
+        }
+      });
+    },
+
+
+    /** 导出按钮操作 */
+
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有符合条件的客户数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          // 调用 exportCustomer API
+          return exportCustomer(queryParams);
+        }).then(response => {
+          // 处理下载
+          this.download(response.msg); // RuoYi 提供的下载方法
+        }).catch(function() {});
+    }
+
+  }
+};
+</script>
+
+<style scoped>
+/* 可以添加一些自定义样式 */
+.mb8 {
+  margin-bottom: 8px;
+}
+</style>

+ 399 - 0
src/views/users/user/transferLog.vue

@@ -0,0 +1,399 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="转移类型" prop="transferType">
+        <el-select v-model="queryParams.transferType" placeholder="请选择转移类型" clearable size="small">
+          <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: 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="initiatorUserId">
+        <el-select v-model="queryParams.initiatorUserId" 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="approvalStatus">
+        <el-select v-model="queryParams.approvalStatus" placeholder="请选择审批状态" clearable size="small">
+          <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: 200px"
+          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: 200px"
+          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: 200px"
+          v-model="queryParams.updatedAt"
+          type="date"
+          value-format="yyyy-MM-dd"
+          placeholder="选择记录最后更新时间">
+        </el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </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="originalUserName" />
+      <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="请输入目标销售" disabled="disabled"/>
+        </el-form-item>
+        <el-form-item label="发起用户" prop="initiatorUserId">
+          <el-input v-model="form.initiatorUserName" placeholder="请输入发起用户" 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="请输入内容" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="success" @click="submitForm(3)" 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/users/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";
+import {getUserList} from "@/api/company/companyUser";
+
+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" }
+        ]
+      }
+    };
+  },
+  created() {
+    getDicts('transfer_type').then(response => {
+      this.transferTypeList = response.data;
+    })
+    getDicts('transfer_approval_status').then(response => {
+      this.approvalStatusList = response.data;
+    })
+    getUserList().then(res=>{
+      if(res.code === 200) {
+        this.companyUserList = res.data
+      }
+    });
+    this.getList();
+  },
+  methods: {
+    /** 查询客户转移审批列表 */
+    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>