吴树波 3 dienas atpakaļ
vecāks
revīzija
4c6a1d09eb
1 mainītis faili ar 354 papildinājumiem un 4 dzēšanām
  1. 354 4
      src/views/course/courseWatchLog/index.vue

+ 354 - 4
src/views/course/courseWatchLog/index.vue

@@ -123,6 +123,60 @@
           </el-option>
         </el-select>
       </el-form-item>
+      <el-form-item label="主体" prop="corpId" v-if="queryParams.sendType == 2">
+        <el-select v-model="queryParams.corpId" placeholder="请选择主体" clearable size="small" @change="updateCorpId">
+          <el-option
+            v-for="dict in myQwCompanyList"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="标签" prop="tagIds" v-if="queryParams.sendType == 2">
+        <div class="filter-tag-wrap">
+          <el-select
+            v-model="queryParams.tagFilterType"
+            size="small"
+            style="width: 110px;margin-right: 6px;"
+            :disabled="!queryParams.corpId"
+            placeholder="匹配规则">
+            <el-option :value="2" label="含任意标签" />
+            <el-option :value="1" label="含全部标签" />
+          </el-select>
+          <div
+            class="filter-tag-select"
+            :class="{ 'is-disabled': !queryParams.corpId }"
+            @click="handleOpenFilterTags">
+            <div class="filter-tag-select__inner">
+              <template v-if="selectFilterTags && selectFilterTags.length">
+                <el-tag
+                  type="success"
+                  size="mini"
+                  closable
+                  :disable-transitions="false"
+                  v-for="list in selectFilterTags"
+                  :key="list.tagId"
+                  @close.stop="handleCloseFilterTag(list)"
+                  style="margin: 2px;">{{ list.name }}</el-tag>
+              </template>
+              <span v-else class="filter-tag-select__placeholder">
+                {{ queryParams.corpId ? '请选择标签(可多选)' : '请先选择主体' }}
+              </span>
+            </div>
+          </div>
+          <el-tooltip
+            effect="dark"
+            placement="top"
+            popper-class="filter-tag-tip-popper">
+            <div slot="content" class="filter-tag-tip-content">
+              <p>含任意标签:客户具备所选标签中的任意一个即可命中</p>
+              <p>含全部标签:客户需同时具备所选的全部标签(与标签顺序无关,可有其他多余标签)</p>
+            </div>
+            <i class="el-icon-question filter-tag-tip-icon"></i>
+          </el-tooltip>
+        </div>
+      </el-form-item>
       <el-form-item label="所属企微" prop="qwUserName" v-if="queryParams.sendType == 2">
         <el-select v-model="queryParams.qwUserName"
                    clearable filterable
@@ -671,6 +725,44 @@
       </div>
     </el-dialog>
 
+    <!-- 筛选:选择标签(交互对齐创建SOP,样式适配本页筛选项) -->
+    <el-dialog :title="filterTagDialog.title" :visible.sync="filterTagDialog.open" width="800px" append-to-body>
+      <div>搜索标签:
+        <el-input v-model="queryTagParams.name" placeholder="请输入标签名称" clearable size="small" style="width: 200px;margin-right: 10px" />
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleSearchFilterTags">搜索</el-button>
+        <el-button type="primary" icon="el-icon-plus" size="mini" @click="resetFilterTagSearch">重置</el-button>
+      </div>
+      <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="scroll-wrapper">
+          <div class="tag-container">
+            <a
+              v-for="tagItem in item.tag"
+              :key="tagItem.tagId"
+              class="tag-box"
+              @click="tagSelection(tagItem)"
+              :class="{ 'tag-selected': tagItem.isSelected }"
+            >
+              {{ tagItem.name }}
+            </a>
+          </div>
+        </div>
+      </div>
+      <pagination
+        v-show="tagTotal>0"
+        :total="tagTotal"
+        :page.sync="queryTagParams.pageNum"
+        :limit.sync="queryTagParams.pageSize"
+        @pagination="loadFilterTagGroupAndMark"
+      />
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitFilterTags">确 定</el-button>
+        <el-button @click="cancelFilterTags">取 消</el-button>
+      </div>
+    </el-dialog>
+
 
     <el-dialog
       :title="resultTitle"
@@ -774,6 +866,7 @@ import Calendar from 'vue-mobile-calendar'
 import {infoSop} from "@/api/qw/sop";
 import {getCompanyUserListLikeName} from "../../../api/company/companyUser";
 import {getQwList} from "@/api/qw/qwUser";
+import {getMyQwCompanyList} from "@/api/qw/user";
 import {treeselect} from "@/api/company/companyDept";
 import Treeselect from "@riophae/vue-treeselect";
 import "@riophae/vue-treeselect/dist/vue-treeselect.css";
@@ -927,6 +1020,15 @@ export default {
       sopSearchText: '', // SOP搜索框显示的文本
       selectedSopId: null, // 选中的SOP ID
       sysCompanyOr: [],
+      // 企微主体列表
+      myQwCompanyList: [],
+      // 筛选用已选标签(展示名,交互对齐创建SOP)
+      selectFilterTags: [],
+      // 筛选标签弹窗
+      filterTagDialog: {
+        open: false,
+        title: '选择标签',
+      },
       // 查询参数
       queryParams: {
         pageNum: 1,
@@ -961,6 +1063,11 @@ export default {
         isVip: null,
         sopId: null, // sopId
         watchType:null,
+        corpId: null, // 企微主体
+        tagIds: [], // 客户标签(多选,请求时转 tagIdList)
+        tagIdList: null,
+        // 标签匹配规则:1含全部 2含任意(对齐创建SOP)
+        tagFilterType: 2,
       },
       // 表单参数
       form: {},
@@ -1023,6 +1130,11 @@ export default {
       this.sysCompanyOr = response.data;
     });
 
+    // 加载企微主体列表(筛选用)
+    getMyQwCompanyList().then(response => {
+      this.myQwCompanyList = response.data || [];
+    });
+
     // 设置默认当天时间 xgb 防止频繁查询大量数据
     this.setToday();
 
@@ -1033,6 +1145,176 @@ export default {
   },
   methods: {
 
+    /**
+     * 组装列表/导出查询参数(标签传参对齐企微客户:tagIds 逗号字符串)
+     */
+    buildWatchLogQueryParams() {
+      const params = Object.assign({}, this.queryParams);
+      const selectedIds = (this.selectFilterTags || []).map(item => item.tagId).filter(Boolean);
+      // 页面内仍用数组,请求参数与企微客户一致传逗号串
+      this.queryParams.tagIds = selectedIds;
+      if (selectedIds.length > 0) {
+        params.tagIds = selectedIds.join(',');
+        params.tagIdList = selectedIds.join(',');
+      } else {
+        params.tagIds = null;
+        params.tagIdList = null;
+      }
+      return params;
+    },
+
+    /**
+     * 同步筛选项上的标签 ID(与企微客户 handleCloseTags 一致,最终以逗号串参与查询)
+     */
+    syncFilterTagIds() {
+      const selectedIds = (this.selectFilterTags || []).map(item => item.tagId).filter(Boolean);
+      this.queryParams.tagIds = selectedIds;
+      this.queryParams.tagIdList = selectedIds.length ? selectedIds.join(',') : null;
+    },
+
+    /**
+     * 切换主体:清空标签与所属企微,重载所属企微下拉
+     */
+    updateCorpId() {
+      this.selectFilterTags = [];
+      this.queryParams.tagIds = [];
+      this.queryParams.tagIdList = null;
+      this.queryParams.qwUserName = null;
+      this.qwUserOptions = [];
+      this.qwUserOptionsParams.pageNum = 1;
+      this.qwUserOptionsParams.name = undefined;
+      this.qwUserFirstLoad = true;
+
+      if (this.queryParams.corpId) {
+        this.getQwList();
+      }
+    },
+
+    /** 清空主体、标签及相关下拉缓存 */
+    clearCorpAndTagFilter() {
+      this.queryParams.corpId = null;
+      this.selectFilterTags = [];
+      this.queryParams.tagIds = [];
+      this.queryParams.tagIdList = null;
+      this.queryParams.tagFilterType = 2;
+      this.queryParams.qwUserName = null;
+      this.qwUserOptions = [];
+      this.qwUserOptionsParams.pageNum = 1;
+      this.qwUserOptionsParams.name = undefined;
+      this.qwUserFirstLoad = true;
+      this.filterTagDialog.open = false;
+    },
+
+    /**
+     * 打开筛选标签弹窗(对齐创建SOP的标签选择)
+     */
+    handleOpenFilterTags() {
+      if (!this.queryParams.corpId) {
+        this.$message.warning('请先选择主体');
+        return;
+      }
+      this.filterTagDialog.title = '选择标签';
+      this.filterTagDialog.open = true;
+      this.queryTagParams.pageNum = 1;
+      this.queryTagParams.name = null;
+      this.loadFilterTagGroupAndMark();
+    },
+
+    /** 加载标签组并回显已选 */
+    loadFilterTagGroupAndMark() {
+      this.queryTagParams.corpId = this.queryParams.corpId;
+      allListTagGroup(this.queryTagParams).then(response => {
+        this.tagGroupList = response.rows || [];
+        this.tagTotal = response.total || 0;
+        this.markFilterTagSelected();
+      });
+    },
+
+    /** 根据已选标签标记弹窗内选中态 */
+    markFilterTagSelected() {
+      const selectedTagIds = new Set(
+        (this.selectFilterTags || []).map(tagItem => tagItem && tagItem.tagId)
+      );
+      (this.tagGroupList || []).forEach(group => {
+        (group.tag || []).forEach(tagItem => {
+          tagItem.isSelected = selectedTagIds.has(tagItem.tagId);
+        });
+      });
+      this.$forceUpdate();
+    },
+
+    /** 筛选弹窗内搜索标签 */
+    handleSearchFilterTags() {
+      if (!this.queryTagParams.name) {
+        return this.$message.error('请输入要搜索的标签');
+      }
+      this.queryTagParams.corpId = this.queryParams.corpId;
+      searchTags(this.queryTagParams).then(response => {
+        this.tagGroupList = response.rows || [];
+        this.tagTotal = response.total || 0;
+        this.markFilterTagSelected();
+      });
+    },
+
+    /** 重置筛选弹窗标签搜索 */
+    resetFilterTagSearch() {
+      this.queryTagParams.name = null;
+      this.queryTagParams.pageNum = 1;
+      this.loadFilterTagGroupAndMark();
+    },
+
+    /** 确认筛选标签(多选) */
+    submitFilterTags() {
+      const selected = [];
+      (this.tagGroupList || []).forEach(group => {
+        (group.tag || []).forEach(tagItem => {
+          if (tagItem.isSelected) {
+            const exists = selected.some(t => t.tagId === tagItem.tagId);
+            if (!exists) {
+              selected.push({
+                tagId: tagItem.tagId,
+                name: tagItem.name,
+                id: tagItem.id,
+              });
+            }
+          }
+        });
+      });
+      // 合并跨页已选:保留不在当前页但仍已选中的
+      const currentPageIds = new Set();
+      (this.tagGroupList || []).forEach(group => {
+        (group.tag || []).forEach(tagItem => {
+          currentPageIds.add(tagItem.tagId);
+        });
+      });
+      (this.selectFilterTags || []).forEach(oldTag => {
+        if (!currentPageIds.has(oldTag.tagId)) {
+          const exists = selected.some(t => t.tagId === oldTag.tagId);
+          if (!exists) {
+            selected.push(oldTag);
+          }
+        }
+      });
+      this.selectFilterTags = selected;
+      this.syncFilterTagIds();
+      this.filterTagDialog.open = false;
+    },
+
+    /** 取消筛选标签弹窗 */
+    cancelFilterTags() {
+      this.filterTagDialog.open = false;
+    },
+
+    /** 移除单个已选筛选标签 */
+    handleCloseFilterTag(list) {
+      const idx = (this.selectFilterTags || []).findIndex(t => t.tagId === list.tagId);
+      if (idx > -1) {
+        this.selectFilterTags.splice(idx, 1);
+        this.selectFilterTags = [...this.selectFilterTags];
+      }
+      this.syncFilterTagIds();
+    },
+
     showContentDialog(questionJson){
       // 解析 JSON 字符串为 JavaScript 对象
       // 替换非法换行符等控制字符
@@ -1186,6 +1468,8 @@ export default {
         pageSize: this.qwUserOptionsParams.pageSize,
         // 添加销售ID作为过滤条件
         companyUserId: this.queryParams.companyUserId || null,
+        // 按主体过滤所属企微
+        corpId: this.queryParams.corpId || null,
         disableCompanyId: 1
       };
       console.log("企微参数", this.queryParams);
@@ -1510,7 +1794,7 @@ export default {
         this.queryParams.logType = null;
       }
 
-      listCourseWatchLog(this.queryParams).then(response => {
+      listCourseWatchLog(this.buildWatchLogQueryParams()).then(response => {
         this.courseWatchLogList = response.rows;
         this.total = response.total;
         this.loading = false;
@@ -1579,6 +1863,9 @@ export default {
       this.periodList = [];
       this.videoList = [];
 
+      // 重置主体、标签与所属企微下拉
+      this.clearCorpAndTagFilter();
+
       // 重置SOP搜索
       this.handleSopClear();
 
@@ -1648,7 +1935,7 @@ export default {
     },
     /** 导出按钮操作 */
     handleExport() {
-      const queryParams = this.queryParams;
+      const queryParams = this.buildWatchLogQueryParams();
       this.$confirm('是否确认导出所有短链课程看课记录数据项?', "警告", {
         confirmButtonText: "确定",
         cancelButtonText: "取消",
@@ -1700,6 +1987,11 @@ export default {
       this.videoList = [];
       this.courseLists = [];
 
+      // 离开企微方式时清空主体、标签
+      if (this.queryParams.sendType != 2) {
+        this.clearCorpAndTagFilter();
+      }
+
       // 重新加载课程列表
       this.loadCourseList();
 
@@ -1986,8 +2278,14 @@ export default {
 
       let obj = JSON.parse(JSON.stringify(this.queryParams))
       console.log(obj);
-      if(obj.tagIds !== null && obj.tagIds !== undefined && obj.tagIds !== ''){
-        obj.tagIds = obj.tagIds.split(",");
+      // 与企微客户一致:标签多选以逗号串/数组均可,后端 prepare 会解析
+      const selectedIds = (this.selectFilterTags || []).map(item => item.tagId).filter(Boolean);
+      if (selectedIds.length > 0) {
+        obj.tagIds = selectedIds;
+        obj.tagIdList = selectedIds.join(',');
+      } else {
+        obj.tagIds = null;
+        obj.tagIdList = null;
       }
       batchUpdateExternalContactNotesByWatchLog({
         // addType: 0,
@@ -2098,6 +2396,49 @@ export default {
   color: #606266;
 }
 
+.filter-tag-wrap {
+  display: inline-flex;
+  align-items: flex-start;
+}
+.filter-tag-tip-icon {
+  margin-left: 6px;
+  margin-top: 8px;
+  color: #909399;
+  font-size: 14px;
+  cursor: help;
+  line-height: 1;
+}
+/* 筛选标签选择框:对齐创建SOP交互,尺寸适配本页筛选项 */
+.filter-tag-select {
+  cursor: pointer;
+  border: 1px solid #DCDFE6;
+  background-color: #fff;
+  border-radius: 4px;
+  width: 220px;
+  min-height: 32px;
+  box-sizing: border-box;
+  overflow: hidden;
+}
+.filter-tag-select.is-disabled {
+  cursor: not-allowed;
+  background-color: #F5F7FA;
+  border-color: #E4E7ED;
+}
+.filter-tag-select__inner {
+  min-height: 30px;
+  max-height: 72px;
+  overflow-y: auto;
+  padding: 2px 6px;
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+}
+.filter-tag-select__placeholder {
+  color: #C0C4CC;
+  font-size: 13px;
+  line-height: 28px;
+}
+
 
 
 .button-new-tag {
@@ -2128,3 +2469,12 @@ export default {
   border-radius: 3px;
 }
 </style>
+<style>
+.filter-tag-tip-content p {
+  margin: 0;
+  line-height: 1.6;
+}
+.filter-tag-tip-content p + p {
+  margin-top: 4px;
+}
+</style>