Browse Source

Merge remote-tracking branch 'origin/master'

yjwang 3 days ago
parent
commit
575eace654

+ 19 - 0
src/api/qw/externalContact.js

@@ -92,6 +92,16 @@ export function addTag(data) {
     data: data
   })
 }
+
+export function addTagByWatch(data) {
+  return request({
+    url: '/qw/externalContact/addTagByWatch',
+    method: 'post',
+    data: data
+  })
+}
+
+
 export function delTag(data) {
   return request({
     url: '/qw/externalContact/delTag',
@@ -99,6 +109,15 @@ export function delTag(data) {
     data: data
   })
 }
+
+export function delTagByWatch(data) {
+  return request({
+    url: '/qw/externalContact/delTagByWatch',
+    method: 'post',
+    data: data
+  })
+}
+
 export function resignedTransfer(data) {
   return request({
     url: '/qw/externalContact/resignedTransfer',

+ 7 - 14
src/views/course/courseRedPacketLog/index.vue

@@ -1,16 +1,7 @@
 <template>
   <div class="app-container">
     <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
-		<el-form-item label="公司名" prop="companyId">
-		     <el-select filterable  v-model="queryParams.companyId" placeholder="请选择公司名"  clearable size="small">
-		         <el-option
-		           v-for="item in companys"
-		           :key="item.companyId"
-		           :label="item.companyName"
-		           :value="item.companyId"
-		         />
-		   </el-select>
-		 </el-form-item>
+
 		<el-form-item label="员工" prop="companyUserName">
 		  <el-input
 		    v-model="queryParams.companyUserName"
@@ -148,14 +139,14 @@
       <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>
+          <el-tag>{{ scope.row.status === 0 ? "发送中" : scope.row.status === 1 ? "已完成" : "待补发" }}</el-tag>
         </template>
       </el-table-column>
       <el-table-column label="企微账号" align="center" prop="qwUserName" />
       <el-table-column label="创建时间" align="center" prop="createTime" />
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
           <template slot-scope="scope">
-            <el-button v-if=""
+            <el-button v-if="scope.row.status==2"
               size="mini"
               type="text"
               @click="handleRetry(scope.row)"
@@ -397,9 +388,11 @@ export default {
           type: "warning"
         }).then(function() {
           return retryCourseRedPacketLog(logIds);
-        }).then(() => {
+        }).then((response) => {
           this.getList();
-          this.msgSuccess("删除成功");
+
+          this.msgSuccess(response.msg); // Fallback message
+
         }).catch(() => {});
     },
 	getSubCateList(pid){

+ 23 - 14
src/views/course/courseRedPacketLog/myCourseRedPacketLog.vue

@@ -129,12 +129,20 @@
       <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>
+          <el-tag>{{ scope.row.status === 0 ? "发送中" : scope.row.status === 1 ? "已完成" : "待补发" }}</el-tag>
         </template>
       </el-table-column>
       <el-table-column label="企微员工名称" align="center" prop="qwUserName" />
       <el-table-column label="创建时间" align="center" prop="createTime" />
-
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+          <template slot-scope="scope">
+            <el-button v-if="scope.row.status==2"
+              size="mini"
+              type="text"
+              @click="handleRetry(scope.row)"
+            >重新发送</el-button>
+          </template>
+        </el-table-column>
     </el-table>
 
     <pagination
@@ -373,19 +381,20 @@ export default {
         }
       });
     },
-    /** 删除按钮操作 */
-    handleDelete(row) {
+    handleRetry(row) {
       const logIds = row.logId || this.ids;
-      this.$confirm('是否确认删除短链课程看课记录编号为"' + logIds + '"的数据项?', "警告", {
-        confirmButtonText: "确定",
-        cancelButtonText: "取消",
-        type: "warning"
-      }).then(function() {
-        return delCourseRedPacketLog(logIds);
-      }).then(() => {
-        this.getList();
-        this.msgSuccess("删除成功");
-      }).catch(() => {});
+      this.$confirm('是否确认重新发送红包?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return retryCourseRedPacketLog(logIds);
+        }).then((response) => {
+          this.getList();
+
+          this.msgSuccess(response.msg); // Fallback message
+
+        }).catch(() => {});
     },
     getSubCateList(pid){
       this.form.subCateId=null;

+ 381 - 2
src/views/course/courseWatchLog/index.vue

@@ -96,6 +96,24 @@
           @click="handleExport"
           v-hasPermi="['course:courseWatchLog:export']"
         >导出</el-button>
+        <el-col :span="1.5">
+          <el-button
+            type="primary"
+            plain
+            size="mini"
+            @click="addUserTag"
+            v-hasPermi="['qw:externalContact:addTag']"
+          >批量添加标签</el-button>
+        </el-col>
+        <el-col :span="1.5">
+          <el-button
+            type="primary"
+            plain
+            size="mini"
+            @click="delUserTag"
+            v-hasPermi="['qw:externalContact:delTag']"
+          >批量移除标签</el-button>
+        </el-col>
       </el-col>
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
@@ -144,17 +162,109 @@
       @pagination="getList"
     />
 
+    <el-dialog title="批量添加标签" :visible.sync="tagOpen" width="800px" append-to-body>
+      <div>搜索标签:
+        <el-input v-model="tagChange.tagName" placeholder="请输入标签名称" clearable size="small" style="width: 200px;margin-right: 10px" />
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleSearchTags(tagChange.tagName)">搜索</el-button>
+        <el-button type="primary" icon="el-icon-plus" size="mini" @click="cancelSearchTags">重置</el-button>
+      </div>
+      <el-form ref="form" :model="addTagFormByWatch"  label-width="80px">
+        <div v-for="item in tagGroupList" :key="item.id" >
+          <div style="font-size: 20px;margin-top: 20px;margin-bottom: 20px;">
+            <span class="name-background">{{ item.name }}</span>
+          </div>
+          <div class="tag-container">
+            <a
+              v-for="tagItem in item.tag"
+              class="tag-box"
+              @click="tagSelection(tagItem)"
+              :class="{ 'tag-selected': tagItem.isSelected }"
+            >
+              {{ tagItem.name }}
+            </a>
+          </div>
+        </div>
+      </el-form>
+      <pagination
+        v-show="tagTotal>0"
+        :total="tagTotal"
+        :page.sync="queryTagParams.pageNum"
+        :limit.sync="queryTagParams.pageSize"
+        @pagination="getPageListTagGroup"
+      />
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="addTagSubmitForm()">确 定</el-button>
+        <el-button @click="addTagCancel">取 消</el-button>
+      </div>
+    </el-dialog>
+
+    <el-dialog title="批量移除标签" :visible.sync="tagDelOpen" width="800px" append-to-body>
+      <div>搜索标签:
+        <el-input v-model="tagChange.tagName" placeholder="请输入标签名称" clearable size="small" style="width: 200px;margin-right: 10px" />
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleSearchTags(tagChange.tagName)">搜索</el-button>
+        <el-button type="primary" icon="el-icon-plus" size="mini" @click="cancelSearchTags">重置</el-button>
+      </div>
+      <el-form ref="form" :model="addTagFormByWatch"  label-width="80px">
+        <div v-for="item in tagGroupList" :key="item.id" >
+          <div style="font-size: 20px;margin-top: 20px;margin-bottom: 20px;">
+            <span class="name-background">{{ item.name }}</span>
+          </div>
+          <div class="tag-container">
+            <a
+              v-for="tagItem in item.tag"
+              class="tag-box"
+              @click="tagSelection(tagItem)"
+              :class="{ 'tag-selected': tagItem.isSelected }"
+            >
+              {{ tagItem.name }}
+            </a>
+          </div>
+        </div>
+      </el-form>
+      <pagination
+        v-show="tagTotal>0"
+        :total="tagTotal"
+        :page.sync="queryTagParams.pageNum"
+        :limit.sync="queryTagParams.pageSize"
+        @pagination="getPageListTagGroup"
+      />
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="tagDelSubmitForm()">确 定</el-button>
+        <el-button @click="DelTagCancel">取 消</el-button>
+      </div>
+    </el-dialog>
+
+
+    <el-dialog
+      :title="resultTitle"
+      :visible.sync="resultDialogVisible"
+      width="50%"
+      custom-class="feedback-dialog"
+    >
+      <pre style="white-space: pre-wrap; font-family: inherit;">{{ resultMessage }}</pre>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="resultDialogVisible = false">关闭</el-button>
+      </span>
+    </el-dialog>
+
   </div>
 </template>
 
 <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 {allListTagGroup} from "../../../api/qw/tagGroup";
+import {searchTags} from "../../../api/qw/tag";
+import {addTagByWatch, delTagByWatch} from "../../../api/qw/externalContact";
 export default {
   name: "CourseWatchLog",
   data() {
     return {
+
+      resultDialogVisible: false,
+      resultMessage: '',
+      resultTitle:'',
+
       activeName:"2",
       createTime:null,
       updateTime:null,
@@ -188,6 +298,32 @@ export default {
       title: "",
       // 是否显示弹出层
       open: false,
+
+      tagOpen:false,
+      //标签弹窗选择
+      tagChange:{
+        open:false,
+        index:null,
+      },
+      addTagFormByWatch:{
+        logId:[],
+        tagIds:[]
+      },
+      tagGroupList: [],
+      tagTotal:0,
+
+      tagDelOpen:false,
+
+      queryTagParams:{
+        pageNum: 1,
+        pageSize: 10,
+        total:0,
+        name:null,
+        corpId:null,
+      },
+
+
+
       // 查询参数
       queryParams: {
         pageNum: 1,
@@ -409,7 +545,250 @@ export default {
     },
     handleSendTypeChange() {
       this.handleQuery(); // 重新查询列表
-    }
+    },
+
+
+    addUserTag(){
+
+      if(this.ids==null||this.ids==""){
+        return  this.$message('请选择需要添加标签的客户');
+      }
+
+      this.getPageListTagGroup();
+
+      setTimeout(() => {
+
+        for (let i = 0; i < this.tagGroupList.length; i++) {
+          for (let x = 0; x < this.tagGroupList[i].tag.length; x++) {
+            this.tagGroupList[i].tag[x].isSelected=false;
+          }
+        }
+      }, 200);
+
+
+      this.tagOpen = true;
+
+    },
+
+    delUserTag(){
+
+      if(this.ids==null||this.ids==""){
+        return  this.$message('请选择需要移除标签的客户');
+      }
+      this.getPageListTagGroup();
+
+      setTimeout(() => {
+        for (let i = 0; i < this.tagGroupList.length; i++) {
+          for (let x = 0; x < this.tagGroupList[i].tag.length; x++) {
+            this.tagGroupList[i].tag[x].isSelected=false;
+          }
+        }
+      }, 200);
+
+      this.tagDelOpen = true;
+
+    },
+
+    getPageListTagGroup(){
+      this.queryTagParams.corpId=this.queryParams.corpId
+      allListTagGroup(this.queryTagParams).then(response => {
+        this.tagGroupList = response.rows;
+        this.tagTotal = response.total;
+      });
+    },
+
+    tagSelection(row){
+      row.isSelected= !row.isSelected;
+      this.$forceUpdate();
+    },
+
+    handleSearchTags(name){
+
+      searchTags({name:name,corpId:this.queryParams.corpId}).then(response => {
+        this.tagGroupList = response.rows;
+      });
+
+    },
+
+    cancelSearchTags(){
+      this.resetSearchQueryTag()
+
+      this.getPageListTagGroup();
+    },
+
+    resetSearchQueryTag(){
+
+      this.queryTagParams= {
+        pageNum: 1,
+        pageSize: 10,
+        total:0,
+        name:null,
+      };
+    },
+
+    addTagSubmitForm(){
+
+      for (let i = 0; i < this.tagGroupList.length; i++) {
+        for (let x = 0; x < this.tagGroupList[i].tag.length; x++) {
+          if(this.tagGroupList[i].tag[x].isSelected==true){
+            this.addTagFormByWatch.tagIds.push(this.tagGroupList[i].tag[x].tagId)
+          }
+
+        }
+      }
+      if(this.addTagFormByWatch.tagIds==[]||this.addTagFormByWatch.tagIds==null||this.addTagFormByWatch.tagIds==""){
+        return  this.$message('请选择标签');
+      }
+
+      this.addTagFormByWatch.logIds=this.ids;
+
+
+      let loadingRock = this.$loading({
+        lock: true,
+        text: '正在执行中请稍后~~请不要刷新页面!!',
+        spinner: 'el-icon-loading',
+        background: 'rgba(0, 0, 0, 0.7)'
+      });
+
+
+      addTagByWatch(this.addTagFormByWatch).then(response => {
+        // this.msgSuccess(response.msg);
+
+        this.resultMessage = response.msg;
+        this.resultDialogVisible = true; // 显示弹窗
+        this.resultTitle = '批量添加标签结果';
+
+        this.tagOpen = false;
+        loadingRock.close();
+        this.addTagFormByWatch={
+          logIds:[],
+          tagIds:[]
+        };
+        this.getList()
+      }).finally(res=>{
+        loadingRock.close();
+      });
+
+    },
+
+    tagDelSubmitForm(){
+      for (let i = 0; i < this.tagGroupList.length; i++) {
+        for (let x = 0; x < this.tagGroupList[i].tag.length; x++) {
+          if(this.tagGroupList[i].tag[x].isSelected==true){
+            this.addTagFormByWatch.tagIds.push(this.tagGroupList[i].tag[x].tagId)
+          }
+
+        }
+      }
+      if(this.addTagFormByWatch.tagIds==[]||this.addTagFormByWatch.tagIds==null||this.addTagFormByWatch.tagIds==""){
+        return  this.$message('请选择标签');
+      }
+      this.addTagFormByWatch.corpId=this.queryParams.corpId
+      this.addTagFormByWatch.logIds=this.ids;
+
+      let loadingRock = this.$loading({
+        lock: true,
+        text: '正在执行中请稍后~~请不要刷新页面!!',
+        spinner: 'el-icon-loading',
+        background: 'rgba(0, 0, 0, 0.7)'
+      });
+
+
+      delTagByWatch(this.addTagFormByWatch).then(response => {
+        // this.msgSuccess(response.msg);
+
+        this.resultMessage = response.msg;
+        this.resultDialogVisible = true; // 显示弹窗
+        this.resultTitle = '批量移除标签结果';
+
+        this.tagDelOpen = false;
+        loadingRock.close();
+        this.addTagFormByWatch={
+          userIds:[],
+          tagIds:[]
+        };
+        this.getList()
+      }).finally(res=>{
+        loadingRock.close();
+      });
+    },
+
+    addTagCancel() {
+
+      this.tagOpen = false;
+
+      this.addTagFormByWatch={
+        logIds:[],
+        tagIds:[]
+      };
+    },
+    DelTagCancel() {
+      this.tagDelOpen = false;
+
+      this.addTagFormByWatch={
+        logIds:[],
+        tagIds:[]
+      };
+    },
+
   }
 };
 </script>
+<style scoped>
+/* CSS 样式 */
+.tag-container {
+  display: flex;
+  flex-wrap: wrap; /* 超出宽度时自动换行 */
+  gap: 8px; /* 设置标签之间的间距 */
+}
+.name-background {
+  display: inline-block;
+  background-color: #abece6; /* 背景颜色 */
+  padding: 4px 8px; /* 调整内边距,让背景包裹文字 */
+  border-radius: 4px; /* 可选:设置圆角 */
+}
+/* CSS 样式 */
+.tag-container {
+  display: flex;
+  flex-wrap: wrap; /* 超出宽度时自动换行 */
+  gap: 8px; /* 设置标签之间的间距 */
+}
+.name-background {
+  display: inline-block;
+  background-color: #abece6; /* 背景颜色 */
+  padding: 4px 8px; /* 调整内边距,让背景包裹文字 */
+  border-radius: 4px; /* 可选:设置圆角 */
+}
+.tag-box {
+  padding: 8px 12px;
+  border: 1px solid #989797;
+  border-radius: 4px;
+  cursor: pointer;
+  display: inline-block;
+}
+
+.tag-selected {
+  background-color: #00bc98;
+  color: #fff;
+  border-color: #00bc98;
+}
+
+.el-tag + .el-tag {
+  margin-left: 10px;
+}
+
+
+
+.button-new-tag {
+  margin-left: 10px;
+  height: 32px;
+  line-height: 30px;
+  padding-top: 0;
+  padding-bottom: 0;
+}
+.input-new-tag {
+  width: 90px;
+  margin-left: 10px;
+  vertical-align: bottom;
+}
+</style>

+ 380 - 3
src/views/course/courseWatchLog/myCourseWatchLog.vue

@@ -90,6 +90,24 @@
           v-hasPermi="['course:courseWatchLog:myExport']"
         >导出</el-button>
       </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          size="mini"
+          @click="addUserTag"
+          v-hasPermi="['qw:externalContact:addTag']"
+        >批量添加标签</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          size="mini"
+          @click="delUserTag"
+          v-hasPermi="['qw:externalContact:delTag']"
+        >批量移除标签</el-button>
+      </el-col>
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
@@ -144,6 +162,91 @@
       @pagination="getList"
     />
 
+
+    <el-dialog title="批量添加标签" :visible.sync="tagOpen" width="800px" append-to-body>
+      <div>搜索标签:
+        <el-input v-model="tagChange.tagName" placeholder="请输入标签名称" clearable size="small" style="width: 200px;margin-right: 10px" />
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleSearchTags(tagChange.tagName)">搜索</el-button>
+        <el-button type="primary" icon="el-icon-plus" size="mini" @click="cancelSearchTags">重置</el-button>
+      </div>
+      <el-form ref="form" :model="addTagFormByWatch"  label-width="80px">
+        <div v-for="item in tagGroupList" :key="item.id" >
+          <div style="font-size: 20px;margin-top: 20px;margin-bottom: 20px;">
+            <span class="name-background">{{ item.name }}</span>
+          </div>
+          <div class="tag-container">
+            <a
+              v-for="tagItem in item.tag"
+              class="tag-box"
+              @click="tagSelection(tagItem)"
+              :class="{ 'tag-selected': tagItem.isSelected }"
+            >
+              {{ tagItem.name }}
+            </a>
+          </div>
+        </div>
+      </el-form>
+      <pagination
+        v-show="tagTotal>0"
+        :total="tagTotal"
+        :page.sync="queryTagParams.pageNum"
+        :limit.sync="queryTagParams.pageSize"
+        @pagination="getPageListTagGroup"
+      />
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="addTagSubmitForm()">确 定</el-button>
+        <el-button @click="addTagCancel">取 消</el-button>
+      </div>
+    </el-dialog>
+
+    <el-dialog title="批量移除标签" :visible.sync="tagDelOpen" width="800px" append-to-body>
+      <div>搜索标签:
+        <el-input v-model="tagChange.tagName" placeholder="请输入标签名称" clearable size="small" style="width: 200px;margin-right: 10px" />
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleSearchTags(tagChange.tagName)">搜索</el-button>
+        <el-button type="primary" icon="el-icon-plus" size="mini" @click="cancelSearchTags">重置</el-button>
+      </div>
+      <el-form ref="form" :model="addTagFormByWatch"  label-width="80px">
+        <div v-for="item in tagGroupList" :key="item.id" >
+          <div style="font-size: 20px;margin-top: 20px;margin-bottom: 20px;">
+            <span class="name-background">{{ item.name }}</span>
+          </div>
+          <div class="tag-container">
+            <a
+              v-for="tagItem in item.tag"
+              class="tag-box"
+              @click="tagSelection(tagItem)"
+              :class="{ 'tag-selected': tagItem.isSelected }"
+            >
+              {{ tagItem.name }}
+            </a>
+          </div>
+        </div>
+      </el-form>
+      <pagination
+        v-show="tagTotal>0"
+        :total="tagTotal"
+        :page.sync="queryTagParams.pageNum"
+        :limit.sync="queryTagParams.pageSize"
+        @pagination="getPageListTagGroup"
+      />
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="tagDelSubmitForm()">确 定</el-button>
+        <el-button @click="DelTagCancel">取 消</el-button>
+      </div>
+    </el-dialog>
+
+    <el-dialog
+      :title="resultTitle"
+      :visible.sync="resultDialogVisible"
+      width="50%"
+      custom-class="feedback-dialog"
+    >
+      <pre style="white-space: pre-wrap; font-family: inherit;">{{ resultMessage }}</pre>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="resultDialogVisible = false">关闭</el-button>
+      </span>
+    </el-dialog>
+
   </div>
 </template>
 
@@ -151,12 +254,18 @@
 import { myListCourseWatchLog, getCourseWatchLog, delCourseWatchLog, addCourseWatchLog, updateCourseWatchLog, exportCourseWatchLog } from "@/api/course/courseWatchLog";
 import { courseList,videoList } from '@/api/course/courseRedPacketLog'
 import {getMyQwUserList} from "@/api/qw/user";
-import {allListTagGroup} from "@/api/qw/tagGroup";
-import {listTag} from "@/api/qw/tag";
+import {allListTagGroup} from "../../../api/qw/tagGroup";
+import {searchTags} from "../../../api/qw/tag";
+import {addTagByWatch, delTagByWatch} from "../../../api/qw/externalContact";
 export default {
   name: "CourseWatchLog",
   data() {
     return {
+
+      resultDialogVisible: false,
+      resultMessage: '',
+      resultTitle:'',
+
       createTime:null,
       courseLists:[],
       videoList:[],
@@ -182,6 +291,32 @@ export default {
       title: "",
       // 是否显示弹出层
       open: false,
+
+
+      tagOpen:false,
+      //标签弹窗选择
+      tagChange:{
+        open:false,
+        index:null,
+      },
+      addTagFormByWatch:{
+        logId:[],
+        tagIds:[]
+      },
+      tagGroupList: [],
+      tagTotal:0,
+
+      tagDelOpen:false,
+
+      queryTagParams:{
+        pageNum: 1,
+        pageSize: 10,
+        total:0,
+        name:null,
+        corpId:null,
+      },
+
+
       // 查询参数
       queryParams: {
         pageNum: 1,
@@ -382,7 +517,249 @@ export default {
           this.download(response.msg);
           this.exportLoading = false;
         }).catch(() => {});
-    }
+    },
+
+    addUserTag(){
+
+      if(this.ids==null||this.ids==""){
+        return  this.$message('请选择需要添加标签的客户');
+      }
+
+      this.getPageListTagGroup();
+
+      setTimeout(() => {
+
+        for (let i = 0; i < this.tagGroupList.length; i++) {
+          for (let x = 0; x < this.tagGroupList[i].tag.length; x++) {
+            this.tagGroupList[i].tag[x].isSelected=false;
+          }
+        }
+      }, 200);
+
+
+      this.tagOpen = true;
+
+    },
+
+    delUserTag(){
+
+      if(this.ids==null||this.ids==""){
+        return  this.$message('请选择需要移除标签的客户');
+      }
+      this.getPageListTagGroup();
+
+      setTimeout(() => {
+        for (let i = 0; i < this.tagGroupList.length; i++) {
+          for (let x = 0; x < this.tagGroupList[i].tag.length; x++) {
+            this.tagGroupList[i].tag[x].isSelected=false;
+          }
+        }
+      }, 200);
+
+      this.tagDelOpen = true;
+
+    },
+
+    getPageListTagGroup(){
+      this.queryTagParams.corpId=this.queryParams.corpId
+      allListTagGroup(this.queryTagParams).then(response => {
+        this.tagGroupList = response.rows;
+        this.tagTotal = response.total;
+      });
+    },
+
+    tagSelection(row){
+      row.isSelected= !row.isSelected;
+      this.$forceUpdate();
+    },
+
+    handleSearchTags(name){
+
+      searchTags({name:name,corpId:this.queryParams.corpId}).then(response => {
+        this.tagGroupList = response.rows;
+      });
+
+    },
+
+    cancelSearchTags(){
+      this.resetSearchQueryTag()
+
+      this.getPageListTagGroup();
+    },
+
+    resetSearchQueryTag(){
+
+      this.queryTagParams= {
+        pageNum: 1,
+        pageSize: 10,
+        total:0,
+        name:null,
+      };
+    },
+
+    addTagSubmitForm(){
+
+      for (let i = 0; i < this.tagGroupList.length; i++) {
+        for (let x = 0; x < this.tagGroupList[i].tag.length; x++) {
+          if(this.tagGroupList[i].tag[x].isSelected==true){
+            this.addTagFormByWatch.tagIds.push(this.tagGroupList[i].tag[x].tagId)
+          }
+
+        }
+      }
+      if(this.addTagFormByWatch.tagIds==[]||this.addTagFormByWatch.tagIds==null||this.addTagFormByWatch.tagIds==""){
+        return  this.$message('请选择标签');
+      }
+
+      this.addTagFormByWatch.logIds=this.ids;
+
+
+      let loadingRock = this.$loading({
+        lock: true,
+        text: '正在执行中请稍后~~请不要刷新页面!!',
+        spinner: 'el-icon-loading',
+        background: 'rgba(0, 0, 0, 0.7)'
+      });
+
+
+      addTagByWatch(this.addTagFormByWatch).then(response => {
+        // this.msgSuccess(response.msg);
+
+        this.resultMessage = response.msg;
+        this.resultDialogVisible = true; // 显示弹窗
+        this.resultTitle = '批量添加标签结果';
+
+
+        this.tagOpen = false;
+        loadingRock.close();
+        this.addTagFormByWatch={
+          logIds:[],
+          tagIds:[]
+        };
+        this.getList()
+      }).finally(res=>{
+        loadingRock.close();
+      });
+
+    },
+
+    tagDelSubmitForm(){
+      for (let i = 0; i < this.tagGroupList.length; i++) {
+        for (let x = 0; x < this.tagGroupList[i].tag.length; x++) {
+          if(this.tagGroupList[i].tag[x].isSelected==true){
+            this.addTagFormByWatch.tagIds.push(this.tagGroupList[i].tag[x].tagId)
+          }
+
+        }
+      }
+      if(this.addTagFormByWatch.tagIds==[]||this.addTagFormByWatch.tagIds==null||this.addTagFormByWatch.tagIds==""){
+        return  this.$message('请选择标签');
+      }
+      this.addTagFormByWatch.corpId=this.queryParams.corpId
+      this.addTagFormByWatch.logIds=this.ids;
+
+      let loadingRock = this.$loading({
+        lock: true,
+        text: '正在执行中请稍后~~请不要刷新页面!!',
+        spinner: 'el-icon-loading',
+        background: 'rgba(0, 0, 0, 0.7)'
+      });
+
+
+      delTagByWatch(this.addTagFormByWatch).then(response => {
+        // this.msgSuccess(response.msg);
+
+        this.resultMessage = response.msg;
+        this.resultDialogVisible = true; // 显示弹窗
+        this.resultTitle = '批量移除标签结果';
+
+        this.tagDelOpen = false;
+        loadingRock.close();
+        this.addTagFormByWatch={
+          userIds:[],
+          tagIds:[]
+        };
+        this.getList()
+      }).finally(res=>{
+        loadingRock.close();
+      });
+    },
+
+    addTagCancel() {
+
+      this.tagOpen = false;
+
+      this.addTagFormByWatch={
+        logIds:[],
+        tagIds:[]
+      };
+    },
+    DelTagCancel() {
+      this.tagDelOpen = false;
+
+      this.addTagFormByWatch={
+        logIds:[],
+        tagIds:[]
+      };
+    },
   }
 };
 </script>
+<style scoped>
+/* CSS 样式 */
+.tag-container {
+  display: flex;
+  flex-wrap: wrap; /* 超出宽度时自动换行 */
+  gap: 8px; /* 设置标签之间的间距 */
+}
+.name-background {
+  display: inline-block;
+  background-color: #abece6; /* 背景颜色 */
+  padding: 4px 8px; /* 调整内边距,让背景包裹文字 */
+  border-radius: 4px; /* 可选:设置圆角 */
+}
+/* CSS 样式 */
+.tag-container {
+  display: flex;
+  flex-wrap: wrap; /* 超出宽度时自动换行 */
+  gap: 8px; /* 设置标签之间的间距 */
+}
+.name-background {
+  display: inline-block;
+  background-color: #abece6; /* 背景颜色 */
+  padding: 4px 8px; /* 调整内边距,让背景包裹文字 */
+  border-radius: 4px; /* 可选:设置圆角 */
+}
+.tag-box {
+  padding: 8px 12px;
+  border: 1px solid #989797;
+  border-radius: 4px;
+  cursor: pointer;
+  display: inline-block;
+}
+
+.tag-selected {
+  background-color: #00bc98;
+  color: #fff;
+  border-color: #00bc98;
+}
+
+.el-tag + .el-tag {
+  margin-left: 10px;
+}
+
+
+
+.button-new-tag {
+  margin-left: 10px;
+  height: 32px;
+  line-height: 30px;
+  padding-top: 0;
+  padding-bottom: 0;
+}
+.input-new-tag {
+  width: 90px;
+  margin-left: 10px;
+  vertical-align: bottom;
+}
+</style>

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

@@ -120,6 +120,24 @@
           v-hasPermi="['course:courseWatchLog:myExport']"
         >导出
         </el-button>
+        <el-col :span="1.5">
+          <el-button
+            type="primary"
+            plain
+            size="mini"
+            @click="addUserTag"
+            v-hasPermi="['qw:externalContact:addTag']"
+          >批量添加标签</el-button>
+        </el-col>
+        <el-col :span="1.5">
+          <el-button
+            type="primary"
+            plain
+            size="mini"
+            @click="delUserTag"
+            v-hasPermi="['qw:externalContact:delTag']"
+          >批量移除标签</el-button>
+        </el-col>
       </el-col>
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
@@ -289,6 +307,92 @@
       />
     </el-drawer>
 
+
+    <el-dialog title="批量添加标签" :visible.sync="tagOpen" width="800px" append-to-body>
+      <div>搜索标签:
+        <el-input v-model="tagChange.tagName" placeholder="请输入标签名称" clearable size="small" style="width: 200px;margin-right: 10px" />
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleSearchTags(tagChange.tagName)">搜索</el-button>
+        <el-button type="primary" icon="el-icon-plus" size="mini" @click="cancelSearchTags">重置</el-button>
+      </div>
+      <el-form ref="form" :model="addTagFormByWatch"  label-width="80px">
+        <div v-for="item in tagGroupList" :key="item.id" >
+          <div style="font-size: 20px;margin-top: 20px;margin-bottom: 20px;">
+            <span class="name-background">{{ item.name }}</span>
+          </div>
+          <div class="tag-container">
+            <a
+              v-for="tagItem in item.tag"
+              class="tag-box"
+              @click="tagSelection(tagItem)"
+              :class="{ 'tag-selected': tagItem.isSelected }"
+            >
+              {{ tagItem.name }}
+            </a>
+          </div>
+        </div>
+      </el-form>
+      <pagination
+        v-show="tagTotal>0"
+        :total="tagTotal"
+        :page.sync="queryTagParams.pageNum"
+        :limit.sync="queryTagParams.pageSize"
+        @pagination="getPageListTagGroup"
+      />
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="addTagSubmitForm()">确 定</el-button>
+        <el-button @click="addTagCancel">取 消</el-button>
+      </div>
+    </el-dialog>
+
+    <el-dialog title="批量移除标签" :visible.sync="tagDelOpen" width="800px" append-to-body>
+      <div>搜索标签:
+        <el-input v-model="tagChange.tagName" placeholder="请输入标签名称" clearable size="small" style="width: 200px;margin-right: 10px" />
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleSearchTags(tagChange.tagName)">搜索</el-button>
+        <el-button type="primary" icon="el-icon-plus" size="mini" @click="cancelSearchTags">重置</el-button>
+      </div>
+      <el-form ref="form" :model="addTagFormByWatch"  label-width="80px">
+        <div v-for="item in tagGroupList" :key="item.id" >
+          <div style="font-size: 20px;margin-top: 20px;margin-bottom: 20px;">
+            <span class="name-background">{{ item.name }}</span>
+          </div>
+          <div class="tag-container">
+            <a
+              v-for="tagItem in item.tag"
+              class="tag-box"
+              @click="tagSelection(tagItem)"
+              :class="{ 'tag-selected': tagItem.isSelected }"
+            >
+              {{ tagItem.name }}
+            </a>
+          </div>
+        </div>
+      </el-form>
+      <pagination
+        v-show="tagTotal>0"
+        :total="tagTotal"
+        :page.sync="queryTagParams.pageNum"
+        :limit.sync="queryTagParams.pageSize"
+        @pagination="getPageListTagGroup"
+      />
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="tagDelSubmitForm()">确 定</el-button>
+        <el-button @click="DelTagCancel">取 消</el-button>
+      </div>
+    </el-dialog>
+
+    <el-dialog
+      :title="resultTitle"
+      :visible.sync="resultDialogVisible"
+      width="50%"
+      custom-class="feedback-dialog"
+    >
+      <pre style="white-space: pre-wrap; font-family: inherit;">{{ resultMessage }}</pre>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="resultDialogVisible = false">关闭</el-button>
+      </span>
+    </el-dialog>
+
+
   </div>
 </template>
 
@@ -304,12 +408,20 @@ import {
 import {courseList, myListCourseRedPacketLog, videoList} from '@/api/course/courseRedPacketLog'
 import {myListLogs} from "@/api/course/courseAnswerlogs";
 import {getMyQwUserList} from "@/api/qw/user";
+import {searchTags} from "../../../api/qw/tag";
+import {addTagByWatch, delTagByWatch} from "../../../api/qw/externalContact";
+import {allListTagGroup} from "../../../api/qw/tagGroup";
 
 
 export default {
   name: "CourseWatchLog",
   data() {
     return {
+
+      resultDialogVisible: false,
+      resultMessage: '',
+      resultTitle:'',
+
       activeName:"2",
       createTime: null,
       updateTime:null,
@@ -367,6 +479,30 @@ export default {
       title: "",
       // 是否显示弹出层
       open: false,
+
+      tagOpen:false,
+      //标签弹窗选择
+      tagChange:{
+        open:false,
+        index:null,
+      },
+      addTagFormByWatch:{
+        logId:[],
+        tagIds:[]
+      },
+      tagGroupList: [],
+      tagTotal:0,
+
+      tagDelOpen:false,
+
+      queryTagParams:{
+        pageNum: 1,
+        pageSize: 10,
+        total:0,
+        name:null,
+        corpId:null,
+      },
+
       // 查询参数
       queryParams: {
         pageNum: 1,
@@ -628,6 +764,248 @@ export default {
         this.loadingRedLog = false;
       })
     },
+
+    addUserTag(){
+
+      if(this.ids==null||this.ids==""){
+        return  this.$message('请选择需要添加标签的客户');
+      }
+
+      this.getPageListTagGroup();
+
+      setTimeout(() => {
+
+        for (let i = 0; i < this.tagGroupList.length; i++) {
+          for (let x = 0; x < this.tagGroupList[i].tag.length; x++) {
+            this.tagGroupList[i].tag[x].isSelected=false;
+          }
+        }
+      }, 200);
+
+
+      this.tagOpen = true;
+
+    },
+
+    delUserTag(){
+
+      if(this.ids==null||this.ids==""){
+        return  this.$message('请选择需要移除标签的客户');
+      }
+      this.getPageListTagGroup();
+
+      setTimeout(() => {
+        for (let i = 0; i < this.tagGroupList.length; i++) {
+          for (let x = 0; x < this.tagGroupList[i].tag.length; x++) {
+            this.tagGroupList[i].tag[x].isSelected=false;
+          }
+        }
+      }, 200);
+
+      this.tagDelOpen = true;
+
+    },
+
+    getPageListTagGroup(){
+      this.queryTagParams.corpId=this.queryParams.corpId
+      allListTagGroup(this.queryTagParams).then(response => {
+        this.tagGroupList = response.rows;
+        this.tagTotal = response.total;
+      });
+    },
+
+    tagSelection(row){
+      row.isSelected= !row.isSelected;
+      this.$forceUpdate();
+    },
+
+    handleSearchTags(name){
+
+      searchTags({name:name,corpId:this.queryParams.corpId}).then(response => {
+        this.tagGroupList = response.rows;
+      });
+
+    },
+
+    cancelSearchTags(){
+      this.resetSearchQueryTag()
+
+      this.getPageListTagGroup();
+    },
+
+    resetSearchQueryTag(){
+
+      this.queryTagParams= {
+        pageNum: 1,
+        pageSize: 10,
+        total:0,
+        name:null,
+      };
+    },
+
+    addTagSubmitForm(){
+
+      for (let i = 0; i < this.tagGroupList.length; i++) {
+        for (let x = 0; x < this.tagGroupList[i].tag.length; x++) {
+          if(this.tagGroupList[i].tag[x].isSelected==true){
+            this.addTagFormByWatch.tagIds.push(this.tagGroupList[i].tag[x].tagId)
+          }
+
+        }
+      }
+      if(this.addTagFormByWatch.tagIds==[]||this.addTagFormByWatch.tagIds==null||this.addTagFormByWatch.tagIds==""){
+        return  this.$message('请选择标签');
+      }
+
+      this.addTagFormByWatch.logIds=this.ids;
+
+
+      let loadingRock = this.$loading({
+        lock: true,
+        text: '正在执行中请稍后~~请不要刷新页面!!',
+        spinner: 'el-icon-loading',
+        background: 'rgba(0, 0, 0, 0.7)'
+      });
+
+
+      addTagByWatch(this.addTagFormByWatch).then(response => {
+        // this.msgSuccess(response.msg);
+
+        this.resultMessage = response.msg;
+        this.resultDialogVisible = true; // 显示弹窗
+        this.resultTitle = '批量添加标签结果';
+
+
+        this.tagOpen = false;
+        loadingRock.close();
+        this.addTagFormByWatch={
+          logIds:[],
+          tagIds:[]
+        };
+        this.getList()
+      }).finally(res=>{
+        loadingRock.close();
+      });
+
+    },
+
+    tagDelSubmitForm(){
+      for (let i = 0; i < this.tagGroupList.length; i++) {
+        for (let x = 0; x < this.tagGroupList[i].tag.length; x++) {
+          if(this.tagGroupList[i].tag[x].isSelected==true){
+            this.addTagFormByWatch.tagIds.push(this.tagGroupList[i].tag[x].tagId)
+          }
+
+        }
+      }
+      if(this.addTagFormByWatch.tagIds==[]||this.addTagFormByWatch.tagIds==null||this.addTagFormByWatch.tagIds==""){
+        return  this.$message('请选择标签');
+      }
+      this.addTagFormByWatch.corpId=this.queryParams.corpId
+      this.addTagFormByWatch.logIds=this.ids;
+
+      let loadingRock = this.$loading({
+        lock: true,
+        text: '正在执行中请稍后~~请不要刷新页面!!',
+        spinner: 'el-icon-loading',
+        background: 'rgba(0, 0, 0, 0.7)'
+      });
+
+
+      delTagByWatch(this.addTagFormByWatch).then(response => {
+        // this.msgSuccess(response.msg);
+
+        this.resultMessage = response.msg;
+        this.resultDialogVisible = true; // 显示弹窗
+        this.resultTitle = '批量移除标签结果';
+
+        this.tagDelOpen = false;
+        loadingRock.close();
+        this.addTagFormByWatch={
+          userIds:[],
+          tagIds:[]
+        };
+        this.getList()
+      }).finally(res=>{
+        loadingRock.close();
+      });
+    },
+
+    addTagCancel() {
+
+      this.tagOpen = false;
+
+      this.addTagFormByWatch={
+        logIds:[],
+        tagIds:[]
+      };
+    },
+    DelTagCancel() {
+      this.tagDelOpen = false;
+
+      this.addTagFormByWatch={
+        logIds:[],
+        tagIds:[]
+      };
+    },
   }
 };
 </script>
+<style scoped>
+/* CSS 样式 */
+.tag-container {
+  display: flex;
+  flex-wrap: wrap; /* 超出宽度时自动换行 */
+  gap: 8px; /* 设置标签之间的间距 */
+}
+.name-background {
+  display: inline-block;
+  background-color: #abece6; /* 背景颜色 */
+  padding: 4px 8px; /* 调整内边距,让背景包裹文字 */
+  border-radius: 4px; /* 可选:设置圆角 */
+}
+/* CSS 样式 */
+.tag-container {
+  display: flex;
+  flex-wrap: wrap; /* 超出宽度时自动换行 */
+  gap: 8px; /* 设置标签之间的间距 */
+}
+.name-background {
+  display: inline-block;
+  background-color: #abece6; /* 背景颜色 */
+  padding: 4px 8px; /* 调整内边距,让背景包裹文字 */
+  border-radius: 4px; /* 可选:设置圆角 */
+}
+.tag-box {
+  padding: 8px 12px;
+  border: 1px solid #989797;
+  border-radius: 4px;
+  cursor: pointer;
+  display: inline-block;
+}
+
+.tag-selected {
+  background-color: #00bc98;
+  color: #fff;
+  border-color: #00bc98;
+}
+
+.el-tag + .el-tag {
+  margin-left: 10px;
+}
+
+
+
+.button-new-tag {
+  margin-left: 10px;
+  height: 32px;
+  line-height: 30px;
+  padding-top: 0;
+  padding-bottom: 0;
+}
+.input-new-tag {
+  width: 90px;
+  margin-left: 10px;
+  vertical-align: bottom;
+}
+</style>

+ 10 - 10
src/views/qw/externalContact/index.vue

@@ -40,7 +40,15 @@
           />
         </el-select>
       </el-form-item>
-
+      <el-form-item label="所属销售" prop="companyUser">
+        <el-input
+          v-model="queryParams.companyUser"
+          placeholder="请输入昵称或者手机号"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
       <el-form-item label="性别" prop="gender">
         <el-select v-model="queryParams.gender" placeholder="状态" clearable size="small">
           <el-option
@@ -160,15 +168,7 @@
           @keyup.enter.native="handleQuery"
         />
       </el-form-item>
-      <el-form-item label="所属销售" prop="companyUser">
-        <el-input
-          v-model="queryParams.companyUser"
-          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>

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

@@ -380,6 +380,13 @@
             </a>
           </div>
         </div>
+        <pagination
+          v-show="tagTotal>0"
+          :total="tagTotal"
+          :page.sync="queryTagParams.pageNum"
+          :limit.sync="queryTagParams.pageSize"
+          @pagination="getPageListTagGroup"
+        />
         <div slot="footer" class="dialog-footer">
           <el-button type="primary" @click="tagSubmitForm(changeTagDialog.type)">确 定</el-button>
           <el-button @click="tagCancel(changeTagDialog.type)">取消</el-button>
@@ -660,12 +667,21 @@ export default {
         title:"",
         open:false,
       },
-
+      tagTotal:0,
       //标签弹窗选择
       tagChange:{
         open:false,
         index:null,
       },
+
+      queryTagParams:{
+        pageNum: 1,
+        pageSize: 10,
+        total:0,
+        name:null,
+        corpId:null,
+      },
+
       //选择标签
       changeTagDialog:{
         title:"",
@@ -880,6 +896,14 @@ export default {
         }
       })
     },
+
+    getPageListTagGroup(){
+      this.queryTagParams.corpId=this.queryParams.corpId
+      allListTagGroup(this.queryTagParams).then(response => {
+        this.tagGroupList = response.rows;
+        this.tagTotal = response.total;
+      });
+    },
     handleSearchTags(name){
 
       searchTags({name:name,corpId:this.queryParams.corpId}).then(response => {

+ 29 - 0
src/views/qw/sop/updateSop.vue

@@ -159,6 +159,20 @@
           </el-form-item>
         </div>
 
+        <el-form-item label="是否固定营期" prop="isFixed" v-if="form.type != 3">
+          <el-radio-group v-model="form.isFixed">
+            <el-radio
+              :label="1"
+            >是
+            </el-radio>
+            <el-radio
+              :label="0"
+            >否
+            </el-radio>
+          </el-radio-group>
+          <Tip title="如果为固定营期,不管什么时候进入SOP的客户,营期时间都为SOP任务开始时间" />
+        </el-form-item>
+
         <el-form-item label="开始时间" prop="startTime">
           <el-date-picker clearable size="small"
                           v-model="form.startTime"
@@ -167,6 +181,21 @@
                           placeholder="选择开始时间">
           </el-date-picker>
         </el-form-item>
+
+        <el-form-item label="是否只发送注册用户" prop="isRegister" v-if="form.type != 3">
+          <el-radio-group v-model="form.isRegister">
+            <el-radio
+              :label="1"
+            >是
+            </el-radio>
+            <el-radio
+              :label="0"
+            >否
+            </el-radio>
+          </el-radio-group>
+          <Tip title="是否只发送在平台注册了会员的客户" />
+        </el-form-item>
+
         <el-form-item v-if="form.sendType==2 || form.sendType==4" label="自动添加SOP" prop="autoSopTime" >
           <el-radio-group v-model="form.autoSopTime.autoSopType">
             <el-radio

+ 4 - 0
src/views/qw/sopTemp/index.vue

@@ -248,6 +248,10 @@
         <el-form-item label="排序" prop="sort">
           <el-input-number v-model="form.sort" :min="0" label="排序"></el-input-number>
         </el-form-item>
+
+        <el-form-item label="内容" prop="modeContent">
+          <el-input v-model="form.modeContent" placeholder="请输入文字内容"  type="textarea" :rows="3"/>
+        </el-form-item>
         <el-form-item label="发课时间" prop="time" v-if="form.sendType == 11 && !form.id">
           <el-time-picker
             class="custom-input"

+ 6 - 0
src/views/qw/sopTemp/updateSopTemp.vue

@@ -1028,6 +1028,12 @@ export default {
         return;
       }
       for (let j = 0; j < data.content.length; j++) {
+
+        //如果不是每天第一条,全刷为非官方
+        if (j!==0){
+          data.content[j].isOfficial=0;
+        }
+
         if (data.content[j].type != 4 && data.content[j].type != 5) {
           for (let k = 0; k < data.content[j].setting.length; k++) {
             if (data.content[j].setting[k].contentType == 4 && (data.content[j].setting[k].miniprogramTitle != null || data.content[j].setting[k].miniprogramTitle != "")) {

+ 401 - 0
src/views/statistics/section/inline.vue

@@ -0,0 +1,401 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="公司" prop="companyId">
+        <select-tree
+          v-model="selectedCompanyList"
+          :raw-data="deptList"
+          :parentSelectable="true"
+          placeholder="请选择销售"
+          :multiple="true"
+          component-width="300px"
+          :max-display-tags="3"
+          :check-strictly="false"
+          :return-leaf-only="false"
+          @change="handleMultiChange"
+        ></select-tree>
+      </el-form-item>
+      <el-form-item label="时间范围" prop="dateRange">
+        <el-date-picker
+          v-model="dateRange"
+          type="daterange"
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          value-format="yyyy-MM-dd"
+          style="width: 240px"
+        />
+      </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"
+          @click="handleExport"
+          v-hasPermi="['system:stats:export']"
+        >导出</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <!-- 警告提示 -->
+    <el-alert
+      title="待激活人数依赖进粉时的自动标记和员工手动标记是可变的,相应的目标人数和进线完播率受此影响;为了不受进粉期人数变化影响,第二天新进报名产生的观后数据不做统计;辅助工作台人数不一致可能是受进粉影响,也有可能是员工删除了好友或者取消了客户频道报名。"
+      type="warning"
+      :closable="false"
+      show-icon
+      class="mb8">
+    </el-alert>
+
+    <el-table border v-loading="loading" :data="statsList" @selection-change="handleSelectionChange" show-summary :summary-method="getSummaries">
+      <!-- 未上线部分 -->
+      <el-table-column prop="deptName" label="部门" width="180" align="center"/>
+      <el-table-column prop="companyUserName" label="销售" width="180" align="center"/>
+      <el-table-column prop="periodNum" label="营期数"  align="center" />
+      <el-table-column prop="periodPersonNum" label="营期人数"  align="center" />
+      <el-table-column prop="sendNum" label="发课数"  align="center" />
+      <el-table-column prop="notRegisteredNum" label="待看课"  align="center" />
+      <el-table-column prop="registeredNum" label="上线数"  align="center" />
+      <el-table-column prop="interruptNum" label="看课中断"  align="center" />
+      <el-table-column prop="completedNum" label="已完课"  align="center" />
+      <el-table-column prop="qwRepeatNum" label="企微重粉"  align="center" >
+        <template slot-scope="scope">
+          <span>{{scope.row.qwRepeatNum || 0}}</span>
+        </template>
+      </el-table-column>
+      <el-table-column prop="userRepeatNum" label="小程序重粉"  align="center" >
+        <template slot-scope="scope">
+          <span>{{scope.row.userRepeatNum || 0}}</span>
+        </template>
+      </el-table-column>
+      <el-table-column prop="blackNum" label="拉黑数"  align="center" />
+      <el-table-column prop="deletedNum" label="删除数"  align="center" />
+      <el-table-column prop="orderNum" label="订单总数"  align="center" />
+      <el-table-column prop="orderMoneyTotal" label="订单总金额(元)"  align="center" />
+      <el-table-column prop="redPackageMoneyTotal" label="红包总金额(元)"  align="center" />
+      <el-table-column prop="callNum" label="总拨打数"  align="center" />
+      <el-table-column prop="receivePassNum" label="接通数"  align="center" />
+      <el-table-column prop="receiveNotNum" label="未接通数"  align="center" />
+      <el-table-column prop="callTimeTotal" label="通话时长(单位s)"  align="center" />
+      <el-table-column prop="remindPendingNum" label="催课未处理数"  align="center" />
+      <el-table-column prop="remindProcessedNum" label="催课已处理数"  align="center" />
+
+      <el-table-column prop="regRate" label="上线率" align="center">
+        <template slot-scope="scope">
+          <span v-if="typeof scope.row.regRate === 'number'">
+            {{ (scope.row.regRate * 100).toFixed(2) }}%
+          </span>
+          <span v-else>
+            0.00%
+          </span>
+        </template>
+      </el-table-column>
+
+      <el-table-column prop="finishedRate" label="完课率" align="center">
+        <template slot-scope="scope">
+          <span v-if="typeof scope.row.finishedRate === 'number'">
+            {{ (scope.row.finishedRate * 100).toFixed(2) }}%
+          </span>
+          <span v-else>
+            0.00%
+          </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 {
+  listEmployeeStats,
+  getEmployeeList,
+  getChannelList,
+  exportEmployeeStats,
+  getSOPTaskData,
+  getDeptData,
+  exportSellerData, listInlineList,exportInlineList
+} from '@/api/system/employeeStats'
+import {getUserList} from "@/api/company/companyUser";
+import SelectTree from "@/components/TreeSelect/index.vue";
+
+export default {
+  name: "EmployeeStats",
+  components: {SelectTree},
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      companys:[],
+      selectedMultipleTasks: [],
+      selectedCompanyList: [],
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 员工统计表格数据
+      statsList: [],
+      // 员工列表
+      employeeList: [],
+      companyUserList: [],
+      // 频道列表
+      channelList: [],
+      // 日期范围
+      dateRange: [],
+      deptList: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        channel: null,
+        startDate: null,
+        endDate: null,
+        companyId: null,
+        periodList: [],
+        // 包含未发课数
+        includeSend0: false
+      },
+      // 总体统计
+      totalStats: {
+        totalEmployees: 0,
+        onlineEmployees: 0,
+        offlineEmployees: 0,
+        completionRate: '0.00'
+      }
+    };
+  },
+  created() {
+    this.getList();
+
+    // getCompanyList().then(response => {
+    //   this.companys = response.data;
+    // });
+
+    // getSOPTaskData().then(response => {
+    //   this.channelList = response.data;
+    // });
+
+    getDeptData().then(response => {
+      this.deptList = response.data;
+    })
+  },
+  methods: {
+    getSummaries(param) {
+      const { columns, data } = param;
+      const sums = [];
+
+      columns.forEach((column, index) => {
+        if (index === 0) {
+          sums[index] = '合计';
+          return;
+        }
+
+        const values = data.map(item => Number(item[column.property]));
+
+        if (['periodPersonNum','periodNum', 'sendNum', 'notRegisteredNum', 'registeredNum',
+        'interruptNum', 'completedNum', 'qwRepeatNum', 'userRepeatNum','blackNum','deletedNum',
+        'orderNum','orderMoneyTotal','redPackageMoneyTotal','callNum','receivePassNum','receiveNotNum',
+      'callTimeTotal','remindPendingNum','remindProcessedNum'].includes(column.property)) {
+          if (!values.every(value => isNaN(value))) {
+            sums[index] = values.reduce((prev, curr) => {
+              const value = Number(curr);
+              if (!isNaN(value)) {
+                return prev + curr;
+              } else {
+                return prev;
+              }
+            }, 0);
+          } else {
+            sums[index] = 'N/A';
+          }
+          if(['orderMoneyTotal','redPackageMoneyTotal'].includes(column.property)){
+            sums[index] = sums[index].toFixed(2);
+          }
+        }
+        else if (column.property === 'regRate') {
+          const totalRegistered = data.reduce((sum, item) => sum + (Number(item.registeredNum) || 0), 0);
+          const totalSendNum = data.reduce((sum, item) => sum + (Number(item.sendNum) || 0), 0);
+
+          if (totalSendNum > 0) {
+            const rate = (totalRegistered / totalSendNum * 100).toFixed(2);
+            sums[index] = `${rate}%`;
+          } else {
+            sums[index] = '0.00%';
+          }
+        }
+        else if (column.property === 'finishedRate') {
+          const totalCompleted = data.reduce((sum, item) => sum + (Number(item.completedNum) || 0), 0);
+          const totalSendNum = data.reduce((sum, item) => sum + (Number(item.sendNum) || 0), 0);
+
+          if (totalSendNum > 0) {
+            const rate = (totalCompleted / totalSendNum * 100).toFixed(2);
+            sums[index] = `${rate}%`;
+          } else {
+            sums[index] = '0.00%';
+          }
+        }
+        else {
+          sums[index] = '';
+        }
+      });
+
+      return sums;
+    },
+    handleCompanyUserId(val){
+      if(val == null || val === '') {
+        this.queryParams.companyUserId = null;
+        this.queryParams.userIds = [];
+      }
+    },
+    handleMultiChange(e){
+
+    },
+    handleSeller(){
+      if(this.queryParams.companyId != null) {
+        getUserList(this.queryParams.companyId).then(res=>{
+          if(res.code === 200) {
+            this.companyUserList = res.data
+          }
+        })
+      } else {
+        this.queryParams.companyUserId = null;
+      }
+    },
+    /** 查询员工统计列表 */
+    getList() {
+      this.loading = true;
+      this.queryParams.periodList = this.selectedMultipleTasks;
+      if (this.dateRange && this.dateRange.length === 2) {
+        this.queryParams.startDate = this.dateRange[0];
+        this.queryParams.endDate = this.dateRange[1];
+      } else {
+        this.queryParams.startDate = null;
+        this.queryParams.endDate = null;
+      }
+      if(this.selectedCompanyList != null && this.selectedCompanyList.length > 0) {
+        this.queryParams.userIds = this.selectedCompanyList;
+      }else {
+        this.queryParams.userIds = [];
+      }
+
+      listInlineList(this.queryParams).then(response => {
+        this.statsList = response.data.list;
+        this.total = response.data.total;
+
+      }).finally(()=>{
+        this.loading = false;
+      })
+    },
+    /** 获取员工列表 */
+    getEmployeeList() {
+      getEmployeeList().then(response => {
+        this.employeeList = response.data || [];
+      });
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.dateRange = [];
+      this.selectedMultipleTasks = [];
+      this.selectedCompanyList = [];
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id);
+      this.single = selection.length !== 1;
+      this.multiple = !selection.length;
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      this.queryParams.periodList = this.selectedMultipleTasks;
+      if (this.dateRange && this.dateRange.length === 2) {
+        this.queryParams.startDate = this.dateRange[0];
+        this.queryParams.endDate = this.dateRange[1];
+      } else {
+        this.queryParams.startDate = null;
+        this.queryParams.endDate = null;
+      }
+      if(this.selectedCompanyList != null && this.selectedCompanyList.length > 0) {
+        this.queryParams.userIds = this.selectedCompanyList;
+      }else {
+        this.queryParams.userIds = [];
+      }
+      const queryParams = this.queryParams;
+
+      this.$confirm('是否确认导出当前查询数据?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function() {
+        return exportInlineList(queryParams);
+      }).then(response => {
+        this.download(response.msg);
+      })
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.stats-card {
+  text-align: center;
+  margin-bottom: 16px;
+
+  .stats-number {
+    font-size: 24px;
+    font-weight: bold;
+    color: #409EFF;
+
+    &.online {
+      color: #67C23A;
+    }
+
+    &.offline {
+      color: #F56C6C;
+    }
+
+    &.rate {
+      color: #E6A23C;
+    }
+  }
+}
+
+.mb8 {
+  margin-bottom: 8px;
+}
+
+::v-deep .el-table .el-table__header th {
+  background-color: #f5f7fa;
+}
+
+::v-deep .el-alert {
+  margin-bottom: 16px;
+}
+</style>