Browse Source

总后台看课记录同步销售后端

xw 1 week ago
parent
commit
4310c320e7
3 changed files with 437 additions and 39 deletions
  1. 26 0
      src/api/course/courseRedPacketLog.js
  2. 9 0
      src/api/qw/sop.js
  3. 402 39
      src/views/course/courseWatchLog/index.vue

+ 26 - 0
src/api/course/courseRedPacketLog.js

@@ -22,12 +22,38 @@ export function courseList() {
     method: 'get',
   })
 }
+
+// 企微课程列表
+export function qwCourseList() {
+  return request({
+    url: '/course/courseRedPacketLog/qwcourseList',
+    method: 'get',
+  })
+}
+
 export function videoList(id) {
   return request({
     url: '/course/courseRedPacketLog/videoList/' + id,
     method: 'get'
   })
 }
+
+// 企微视频列表
+export function qwVideoList(courseId) {
+  return request({
+    url: '/course/courseRedPacketLog/qwvideoList/' + courseId,
+    method: 'get'
+  })
+}
+
+// 营期列表(支持营期名称模糊查询)
+export function periodList(params) {
+  return request({
+    url: '/course/courseRedPacketLog/period',
+    method: 'get',
+    params: params
+  })
+}
 // 新增短链课程看课记录
 export function addCourseRedPacketLog(data) {
   return request({

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

@@ -121,3 +121,12 @@ export function updateSopQwUser(data) {
     data: data
   })
 }
+
+// 根据名称查询SOP列表
+export function infoSop(params) {
+  return request({
+    url: '/qw/sop/info',
+    method: 'get',
+    params: params
+  })
+}

+ 402 - 39
src/views/course/courseWatchLog/index.vue

@@ -1,8 +1,45 @@
 <template>
   <div class="app-container">
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
+      <el-form-item label="看课方式" prop="sendType">
+        <el-select v-model="queryParams.sendType" placeholder="选择看课方式"  clearable size="small" @change="handleSendTypeChange">
+          <el-option
+            v-for="dict in sendTypeOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="会员昵称" prop="nickName" v-if="queryParams.sendType == 1">
+        <el-input
+          v-model="queryParams.nickName"
+          placeholder="请输入会员昵称"
+          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="externalUserName" v-if="queryParams.sendType == 2">
+        <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-select filterable  v-model="queryParams.courseId" placeholder="请选择课程"  clearable size="small" @change="handleCourseChange">
           <el-option
             v-for="dict in courseLists"
             :key="dict.dictValue"
@@ -11,8 +48,21 @@
           />
         </el-select>
       </el-form-item>
+      <!-- 营期:仅在会员方式(sendType=1)时显示 -->
+      <el-form-item label="营期" prop="periodId" v-if="queryParams.sendType == 1">
+        <el-select filterable remote :remote-method="remotePeriodSearch" v-model="queryParams.periodId" placeholder="请选择营期"  clearable size="small" @change="handlePeriodChange" :disabled="!queryParams.courseId">
+          <el-option
+            v-for="dict in periodList"
+            :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-select filterable  v-model="queryParams.videoId" placeholder="请选择小节"  clearable size="small" 
+          :disabled="queryParams.sendType == 1 ? !queryParams.periodId : !queryParams.courseId">
           <el-option
             v-for="dict in videoList"
             :key="dict.dictValue"
@@ -21,6 +71,27 @@
           />
         </el-select>
       </el-form-item>
+      <!-- SOP名称:仅在企微方式(sendType=2)时显示 -->
+      <el-form-item label="SOP名称" prop="sopId" v-if="queryParams.sendType == 2">
+        <el-autocomplete
+          v-model="sopSearchText"
+          :fetch-suggestions="querySopAsync"
+          placeholder="请输入SOP名称"
+          clearable
+          size="small"
+          style="width: 200px"
+          @select="handleSopSelect"
+          @clear="handleSopClear"
+          :trigger-on-focus="false"
+        >
+          <template slot-scope="{ item }">
+            <div class="sop-item">
+              <span class="sop-name">{{ item.name }}</span>
+            </div>
+          </template>
+        </el-autocomplete>
+      </el-form-item>
+      <!-- 营期时间 -->
       <el-form-item label="营期时间" prop="scheduleTime">
         <el-date-picker
           v-model="scheduleTime"
@@ -42,7 +113,19 @@
         <el-date-picker v-model="updateTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange"
                         range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="updateChange"></el-date-picker>
       </el-form-item>
-
+      <!-- 进线时间 -->
+      <el-form-item label="进线时间" prop="qecCreateTime">
+        <el-date-picker
+          v-model="qecCreateTime"
+          type="daterange"
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          value-format="yyyy-MM-dd"
+          style="width: 240px"
+          @change="qecCreateTimeChange"
+        />
+      </el-form-item>
 
       <el-form-item>
         <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
@@ -72,43 +155,40 @@
     <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="qwUserName" />
-      <el-table-column label="企微客户" align="center" prop="externalUserName">
+      <el-table-column label="客户ID" align="center" prop="userId"  />
+      <el-table-column :label="queryParams.sendType == 1 ? '会员昵称' : '企微客户'" align="center">
         <template slot-scope="scope">
-          <div style="display: flex;white-space: nowrap">
+          <div style="display: flex;white-space: nowrap" v-if="queryParams.sendType == 1">
             <div style="margin: auto">
-              {{scope.row.externalUserName}}
+              {{scope.row.fsNickName}}
             </div>
             <el-popover
               placement="right"
               title=""
               trigger="hover">
-              <img slot="reference" :src="scope.row.externalUserAvatar" style="width: 30px;height: 30px">
-              <img :src="scope.row.externalUserAvatar" style="max-width: 200px;max-height: 200px">
+              <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="fsNickName">
-        <template slot-scope="scope">
-          <div style="display: flex;white-space: nowrap">
+          <div style="display: flex;white-space: nowrap" v-if="queryParams.sendType == 2">
             <div style="margin: auto">
-              {{scope.row.fsNickName}}
+              {{scope.row.externalUserName}}
             </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">
+              <img slot="reference" :src="scope.row.externalUserAvatar" style="width: 30px;height: 30px">
+              <img :src="scope.row.externalUserAvatar" style="max-width: 200px;max-height: 200px">
             </el-popover>
           </div>
         </template>
       </el-table-column>
       <el-table-column label="项目" align="center" prop="projectName" />
       <el-table-column label="课程名称" align="center" prop="courseName" />
-      <el-table-column label="小节名称" align="center" prop="videoName" />
-      <el-table-column label="企微员工名称" align="center" prop="qwUserName" v-if="queryParams.sourceType == 2"/>
+      <el-table-column label="营期名称" align="center" prop="periodIdName" v-if="queryParams.sendType == 1" />
+      <el-table-column label="小节名称" align="center" prop="videoName"  />
+      <el-table-column label="企微员工名称" align="center" prop="qwUserName" v-if="queryParams.sendType == 2"/>
       <el-table-column label="记录类型" align="center" prop="logType">
         <template slot-scope="scope">
           <dict-tag :options="logTypeOptions" :value="scope.row.logType"/>
@@ -116,13 +196,17 @@
       </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-column label="所属发送方式" align="center" prop="sendType">
+        <template slot-scope="scope">
+          <el-tag :type="scope.row.sendType == 1 ? 'success' : 'primary'">
+            {{ scope.row.sendType == 1 ? '会员' : '企微' }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180" />
+      <el-table-column label="更新时间" align="center" prop="updateTime" width="180" />
+      <el-table-column label="完课时间" align="center" prop="finishTime" width="180" />
+      <el-table-column label="营期时间" align="center" prop="campPeriodTime" width="180" />
     </el-table>
 
     <pagination
@@ -138,10 +222,8 @@
 
 <script>
 import { listCourseWatchLog, getCourseWatchLog, delCourseWatchLog, addCourseWatchLog, updateCourseWatchLog, exportCourseWatchLog } from "@/api/course/courseWatchLog";
-import {allList}from "@/api/company/company";
-import { courseList,videoList } from '@/api/course/courseRedPacketLog'
-import {getUserList} from "@/api/company/companyUser";
-import {getFsUserList} from "@/api/users/user";
+import {courseList, qwCourseList, videoList, qwVideoList, periodList} from '@/api/course/courseRedPacketLog'
+import {infoSop} from "@/api/qw/sop";
 export default {
   name: "CourseWatchLog",
   data() {
@@ -150,9 +232,19 @@ export default {
       activeName:"00",
       createTime:null,
       updateTime:null,
+      scheduleTime:null,
+      qecCreateTime: null,
       courseLists:[],
+      periodList:[],  // 营期列表
       videoList:[],
       logTypeOptions:[],
+      sendTypeOptions:[{
+        dictLabel:"会员",dictValue:1
+      },
+        {
+          dictLabel:"企微",dictValue:2
+        }
+      ],
       queryUserLoading: false,
       // 遮罩层
       loading: true,
@@ -177,6 +269,10 @@ export default {
       // 是否显示弹出层
       open: false,
 
+      // SOP搜索相关
+      sopSearchText: '', // SOP搜索框显示的文本
+      selectedSopId: null, // 选中的SOP ID
+
       // 查询参数
       queryParams: {
         pageNum: 1,
@@ -192,26 +288,28 @@ export default {
         companyUserId: null,
         companyId: null,
         courseId: null,
+        periodId: null,  // 营期ID
         sTime:null,
         eTime:null,
         upSTime:null,
         upETime:null,
+        qecSTime:null,
+        qecETime:null,
         scheduleStartTime: null,
         scheduleEndTime: null,
-        sourceType: 1
+        sendType: 1,  // 默认会员方式
+        sopId: null, // sopId
       },
       // 表单参数
       form: {},
       // 表单校验
       rules: {
       },
-      scheduleTime: null,
     };
   },
   created() {
-    courseList().then(response => {
-      this.courseLists = response.list;
-    });
+    // 初始化时根据默认的sendType加载课程列表
+    this.loadCourseList();
     this.getList();
     this.getDicts("sys_course_watch_log_type").then(response => {
       this.logTypeOptions = response.data;
@@ -234,8 +332,7 @@ export default {
     }
   },
   methods: {
-    handleClear(){
-      this.queryUserLoading = false;
+    handleClear(){ this.queryUserLoading = false;
       this.fsUserList = [];
     },
     remoteGetFsUserList(query){
@@ -262,6 +359,208 @@ export default {
         })
       }
     },
+    /**
+     * 课程变更处理
+     * sendType=1(会员): 加载营期列表,清空营期和小节
+     * sendType=2(企微): 直接加载小节列表,清空小节
+     */
+    handleCourseChange(courseId) {
+      // 清空下级选择和列表
+      this.queryParams.periodId = null;
+      this.queryParams.videoId = null;
+      this.periodList = [];
+      this.videoList = [];
+
+      if (courseId) {
+        if (this.queryParams.sendType == 1) {
+          // 会员方式:加载营期列表(三级联动:课程->营期->小节)
+          this.loadPeriodList(courseId);
+        } else if (this.queryParams.sendType == 2) {
+          // 企微方式:直接加载小节列表(二级联动:课程->小节)
+          this.loadQwVideoList(courseId);
+        }
+      }
+    },
+
+    /**
+     * 营期变更处理
+     * 当营期改变时,清空小节的选择和列表
+     */
+    handlePeriodChange(periodId) {
+      // 清空下级选择和列表
+      this.queryParams.videoId = null;
+      this.videoList = [];
+
+      // 如果选择了营期,加载视频列表
+      if (periodId) {
+        this.loadVideoList(periodId);
+      }
+    },
+
+    /**
+     * 加载营期列表
+     * @param {Number} courseId - 课程ID
+     * @param {String} periodName - 营期名称(可选,用于模糊查询)
+     */
+    loadPeriodList(courseId, periodName = null) {
+      const params = { courseId: courseId };
+      if (periodName) {
+        params.periodName = periodName;
+      }
+      periodList(params).then(response => {
+        this.periodList = response.data || [];
+      }).catch(error => {
+        console.error('加载营期列表失败:', error);
+        this.$message.error('加载营期列表失败');
+        this.periodList = [];
+      });
+    },
+
+    /**
+     * 加载视频列表(通过营期ID)
+     * @param {Number} periodId - 营期ID
+     */
+    loadVideoList(periodId) {
+      videoList(periodId).then(response => {
+        this.videoList = response.list || [];
+      }).catch(error => {
+        console.error('加载视频列表失败:', error);
+        this.$message.error('加载视频列表失败');
+        this.videoList = [];
+      });
+    },
+
+    /**
+     * 加载视频列表(通过课程ID)- 企微方式使用
+     * @param {Number} courseId - 课程ID
+     */
+    loadQwVideoList(courseId) {
+      qwVideoList(courseId).then(response => {
+        this.videoList = response.list || [];
+      }).catch(error => {
+        console.error('加载视频列表失败:', error);
+        this.$message.error('加载视频列表失败');
+        this.videoList = [];
+      });
+    },
+    
+    /**
+     * 加载课程列表
+     * 根据sendType加载不同的课程列表
+     */
+    loadCourseList() {
+      if (this.queryParams.sendType == 2) {
+        // 企微方式:调用qwCourseList
+        qwCourseList().then(response => {
+          this.courseLists = response.list || [];
+        }).catch(error => {
+          console.error('加载企微课程列表失败:', error);
+          this.$message.error('加载课程列表失败');
+          this.courseLists = [];
+        });
+      } else {
+        // 会员方式:调用courseList
+        courseList().then(response => {
+          this.courseLists = response.list || [];
+        }).catch(error => {
+          console.error('加载课程列表失败:', error);
+          this.$message.error('加载课程列表失败');
+          this.courseLists = [];
+        });
+      }
+    },
+
+    /**
+     * 处理看课方式改变
+     */
+    handleSendTypeChange() {
+      // 清空课程、营期、小节相关数据
+      this.queryParams.courseId = null;
+      this.queryParams.periodId = null;
+      this.queryParams.videoId = null;
+      this.periodList = [];
+      this.videoList = [];
+      this.courseLists = [];
+      
+      // 重新加载课程列表
+      this.loadCourseList();
+      
+      this.handleQuery(); // 重新查询列表
+    },
+
+    /**
+     * 营期远程搜索
+     * @param {String} query - 搜索关键词
+     */
+    remotePeriodSearch(query) {
+      if (!this.queryParams.courseId) {
+        this.$message.warning('请先选择课程');
+        return;
+      }
+      // 根据营期名称模糊查询
+      this.loadPeriodList(this.queryParams.courseId, query);
+    },
+
+    /**
+     * 异步查询SOP列表
+     * @param {string} queryString - 查询字符串
+     * @param {function} callback - 回调函数
+     */
+    querySopAsync(queryString, callback) {
+      if (!queryString) {
+        callback([]);
+        return;
+      }
+
+      infoSop({ name: queryString }).then(response => {
+        if (response && response.rows) {
+          const suggestions = response.rows.map(item => ({
+            value: item.name,
+            id: item.id,
+            name: item.name
+          }));
+          callback(suggestions);
+        } else {
+          callback([]);
+        }
+      }).catch(error => {
+        console.error('通过sop查询失败:', error);
+        callback([]);
+      });
+    },
+
+    /**
+     * 选择SOP
+     * @param {object} item - 选中的SOP项
+     */
+    handleSopSelect(item) {
+      this.selectedSopId = item.id;
+      this.queryParams.sopId = item.id;
+      this.sopSearchText = item.name;
+    },
+
+    /**
+     * 清空SOP选择
+     */
+    handleSopClear() {
+      this.selectedSopId = null;
+      this.queryParams.sopId = null;
+      this.sopSearchText = '';
+    },
+
+    /**
+     * 营期时间变更
+     */
+    handleScheduleTimeChange(val) {
+      if (val) {
+        this.queryParams.scheduleStartTime = val[0];
+        this.queryParams.scheduleEndTime = val[1];
+      } else {
+        this.queryParams.scheduleStartTime = null;
+        this.queryParams.scheduleEndTime = null;
+      }
+    },
+
     courseChange(row){
       this.queryParams.videoId=null;
       if(row === ''){
@@ -291,6 +590,40 @@ export default {
         this.queryParams.upETime = null;
       }
     },
+
+    /**
+     * 进线时间变更
+     */
+    qecCreateTimeChange(qecCreateTime) {
+      if (qecCreateTime && qecCreateTime.length >= 2) {
+        // 检查选择的日期范围是否超过7天(包括起始和结束日期)
+        const startDate = new Date(qecCreateTime[0]);
+        const endDate = new Date(qecCreateTime[1]);
+
+        // 设置时间为当天开始,避免时间部分影响计算
+        startDate.setHours(0, 0, 0, 0);
+        endDate.setHours(0, 0, 0, 0);
+
+        const timeDiff = Math.abs(endDate - startDate);
+        const diffDays = Math.ceil(timeDiff / (1000 * 60 * 60 * 24));
+
+        // 如果超过6天的范围(总共7天,包括起始日)
+        if (diffDays > 6) {
+          this.$message.error('进线时间选择范围不能超过7天');
+          // 清空选择
+          this.qecCreateTime = null;
+          this.queryParams.qecSTime = null;
+          this.queryParams.qecETime = null;
+          return;
+        }
+
+        this.queryParams.qecSTime = qecCreateTime[0] || null;
+        this.queryParams.qecETime = qecCreateTime[1] || null;
+      } else {
+        this.queryParams.qecSTime = null;
+        this.queryParams.qecETime = null;
+      }
+    },
     handleClickX(tab,event){
       this.activeName=tab.name;
       if(tab.name=="00"){
@@ -334,8 +667,10 @@ export default {
         companyUserId: null,
         companyId: null,
         courseId: null,
+        periodId: null,
         scheduleStartTime: null,
         scheduleEndTime: null,
+        sopId: null,
       };
       this.scheduleTime=null;
       this.resetForm("form");
@@ -350,14 +685,29 @@ export default {
       this.resetForm("queryForm");
       this.createTime = null;
       this.scheduleTime = null;
+      this.updateTime = null;
+      this.qecCreateTime = null;
       this.queryParams.sTime = null;
       this.queryParams.eTime = null;
       this.queryParams.upSTime = null;
       this.queryParams.upETime = null;
+      this.queryParams.qecSTime = null;
+      this.queryParams.qecETime = null;
       this.queryParams.scheduleStartTime = null;
       this.queryParams.scheduleEndTime = null;
-      this.scheduleTime=null;
-      this.updateTime=null;
+      this.queryParams.sopId = null; // 重置SOP ID
+      this.queryParams.externalUserName = null;
+
+      // 重置三级联动相关数据
+      this.queryParams.courseId = null;
+      this.queryParams.periodId = null;
+      this.queryParams.videoId = null;
+      this.periodList = [];
+      this.videoList = [];
+
+      // 重置SOP搜索
+      this.handleSopClear();
+
       this.handleQuery();
     },
     // 多选框选中数据
@@ -443,3 +793,16 @@ export default {
   }
 };
 </script>
+
+<style scoped>
+/* SOP搜索框样式 */
+.sop-item {
+  display: flex;
+  align-items: center;
+}
+
+.sop-name {
+  font-size: 14px;
+  color: #606266;
+}
+</style>