Sfoglia il codice sorgente

1.提交总后台展示企微客户
2.展示sop的token消耗

jzp 1 giorno fa
parent
commit
27ba7f0646

+ 9 - 0
src/api/fastGpt/fastgptPushTokenTotal.js

@@ -0,0 +1,9 @@
+import request from '@/utils/request'
+
+export function getFastGptPushTokenTotal(query) {
+  return request({
+    url: '/qw/qwPushCount/tokenList',
+    method: 'get',
+    params: query
+  })
+}

+ 240 - 3
src/api/qw/externalContact.js

@@ -9,6 +9,37 @@ export function listExternalContact(query) {
   })
 }
 
+// 查询企业微信客户列表
+export function getRepeat(query) {
+  return request({
+    url: '/qw/externalContact/getRepeat',
+    method: 'get',
+    params: query
+  })
+}
+
+export function myDeptExtList(query) {
+  return request({
+    url: '/qw/externalContact/myDeptExtList',
+    method: 'get',
+    params: query
+  })
+}
+export function myList(query) {
+  return request({
+    url: '/qw/externalContact/myList',
+    method: 'get',
+    params: query
+  })
+}
+/** 查询 预计发送客户的总数 */
+export function selectCountCustomer(param) {
+  return request({
+    url: '/qw/externalContact/expectQwGroupMsgCountCustomer',
+    method: 'post',
+    data:param,
+  })
+}
 // 查询企业微信客户详细
 export function getExternalContact(id) {
   return request({
@@ -26,6 +57,81 @@ export function addExternalContact(data) {
   })
 }
 
+//同步我的企微客户
+export function syncMyExternalContact(id) {
+  return request({
+    url: '/qw/externalContact/syncMyExternalContact/'+id,
+    method: 'get',
+  })
+}
+
+export function syncMyAddExternalContact(id) {
+  return request({
+    url: '/qw/externalContact/syncAddMyExternalContact/'+id,
+    method: 'get',
+  })
+}
+export function getUserInfo(id) {
+  return request({
+    url: '/qw/externalContact/getUserInfo/'+id,
+    method: 'get',
+  })
+}
+
+export function addUnassigned(data) {
+  return request({
+    url: '/qw/externalContact/addUnassigned',
+    method: 'post',
+    data: data
+  })
+}
+export function addTag(data) {
+  return request({
+    url: '/qw/externalContact/addTag',
+    method: 'post',
+    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',
+    method: 'post',
+    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',
+    method: 'put',
+    data: data
+  })
+}
+export function transfer(data) {
+  return request({
+    url: '/qw/externalContact/transfer',
+    method: 'put',
+    data: data
+  })
+}
 // 修改企业微信客户
 export function updateExternalContact(data) {
   return request({
@@ -35,6 +141,44 @@ export function updateExternalContact(data) {
   })
 }
 
+// 修改企业微信客户称呼
+export function updateExternalContactCall(data) {
+  return request({
+    url: '/qw/externalContact/call',
+    method: 'put',
+    data: data
+  })
+}
+// 修改企业微信客户
+export function editStatus(data) {
+  return request({
+    url: '/qw/externalContact/editStatus',
+    method: 'put',
+    data: data
+  })
+}
+
+export function editbindCustomer(data) {
+  return request({
+    url: '/qw/externalContact/editbindCustomer',
+    method: 'put',
+    data: data
+  })
+}
+export function bindUserId(data) {
+  return request({
+    url: '/qw/externalContact/bindUserId',
+    method: 'put',
+    data: data
+  })
+}
+
+export function unBindUserId(id) {
+  return request({
+    url: '/qw/externalContact/unBindUserId/'+id,
+    method: 'get',
+  })
+}
 // 删除企业微信客户
 export function delExternalContact(id) {
   return request({
@@ -51,11 +195,104 @@ export function exportExternalContact(query) {
     params: query
   })
 }
+export function exportMyExternalContact(query) {
+  return request({
+    url: '/qw/externalContact/myExport',
+    method: 'get',
+    params: query
+  })
+}
+
+/**
+ * 获取CRM客户列表
+ */
+export function getMyCustomerList(query) {
+  return request({
+    url: '/qw/externalContact/getMyCustomerList',
+    method: 'get',
+    params: query
+  })
+}
+
+/**
+ * 获取小程序的客户
+ */
+export function getMiniProgramCustomer(query) {
+  return request({
+    url: '/qw/externalContact/getMiniCustomer',
+    method: 'get',
+    params: query
+  })
+}
 
-// 导出企业微信客户unionId
-export function exportUnionId(query) {
+/**
+ * 获取课程列表
+ */
+export function getCourseStudyList(query) {
+  return request({
+    url: '/qw/externalContact/getCourseStudyList',
+    method: 'get',
+    params: query
+  })
+}
+
+/**
+ * 设置客户-课节SOP
+ */
+export function setCustomerCourseSop(data) {
+  return request({
+    url: '/qw/externalContact/setCustomerCourseSop',
+    method: 'post',
+    data: data
+  })
+}
+
+
+/**
+ * 批量设置客户-课节SOP
+ */
+export function setCustomerCourseSopList(data) {
+  return request({
+    url: '/qw/externalContact/setCustomerCourseSopList',
+    method: 'post',
+    data: data
+  })
+}
+
+/**
+ * 查询是否已经设置过客户-某个课节的SOP
+ */
+export function getCustomerCourseSop(query) {
+  return request({
+    url: '/qw/externalContact/getCustomerCourseSop',
+    method: 'get',
+    params: query
+  })
+}
+
+
+export function batchUpdateExternalContactNotes(data) {
+  return request({
+    url: '/qw/externalContact/batchUpdateExternalContactNotes',
+    method: 'post',
+    data: data
+  })
+}
+
+// 查询企业微信客户流失删除统计列表
+export function delLossStatistics(query) {
+  return request({
+    url: '/qw/externalContact/delLossStatistics',
+    method: 'get',
+    params: query
+  })
+}
+
+
+// 导出企业微信客户
+export function delLossStatisticsExport(query) {
   return request({
-    url: '/qw/externalContact/exportUnionId',
+    url: '/qw/externalContact/delLossStatisticsExport',
     method: 'get',
     params: query
   })

+ 2055 - 0
src/views/crm/externalContact/index.vue

@@ -0,0 +1,2055 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
+      <el-form-item label="企微公司" prop="corpId">
+            <el-select v-model="queryParams.corpId" placeholder="企微公司"  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="name">
+        <el-input
+          v-model="queryParams.name"
+          placeholder="请输入客户名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="活码id" prop="wayId">
+        <el-input
+          v-model="queryParams.wayId"
+          placeholder="请输入活码id"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+
+      <el-form-item label="销售企微昵称" prop="qwUserName">
+        <el-input
+          v-model="queryParams.qwUserName"
+          placeholder="请输入销售企微昵称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+          @input="searchQwUser"
+          @focus="showQwUserDropdown = true"
+          @blur="hideDropdownWithDelay"
+          @clear="onQwUserNameClear"
+        />
+        <!-- 下拉建议 -->
+        <div v-if="showQwUserDropdown && qwUserSuggestions.length > 0" class="suggestion-box"  @scroll="handleScroll">
+          <div
+            v-for="item in qwUserSuggestions"
+            :key="item.dictValue"
+            class="suggestion-item"
+            @click="selectQwUser(item.dictValue,item.dictLabel)"
+          >
+            {{ item.dictLabel }}
+          </div>
+        </div>
+      </el-form-item>
+      <el-form-item label="用户类别" prop="type">
+        <el-select v-model="queryParams.type" placeholder="请选择用户类别" clearable size="small">
+          <el-option
+            v-for="dict in typeOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="所属销售" prop="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
+            v-for="dict in genderOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="客户等级" prop="level">
+        <el-select v-model="queryParams.level" placeholder="客户等级" clearable size="small">
+          <el-option
+            v-for="dict in ratingType"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="等级升降" prop="levelType">
+        <el-select v-model="queryParams.levelType" placeholder="等级升降" clearable size="small">
+          <el-option
+            v-for="dict in ratingUpFall"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+
+
+      <el-form-item label="电话号码" prop="remarkMobiles">
+        <el-input
+          v-model="queryParams.remarkMobiles"
+          placeholder="请输入备注电话号码"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+
+      <el-form-item label="来源" prop="addWay">
+
+        <el-select v-model="queryParams.addWay" placeholder="来源" clearable size="small">
+          <el-option
+            v-for="dict in addWayOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+
+        <el-select v-model="queryParams.status" placeholder="状态" clearable size="small">
+          <el-option
+            v-for="dict in statusOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="转接状态" prop="addWay">
+
+        <el-select v-model="queryParams.transferStatus" placeholder="转接状态" clearable size="small">
+          <el-option
+            v-for="dict in transferStatusOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="是否绑定会员" prop="isBindMini">
+        <el-select v-model="queryParams.isBindMini" placeholder="是否绑定会员" clearable size="small" @change="handleQuery" >
+          <el-option
+            v-for="dict in isBindMiniOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="标签" prop="tagIds">
+<!--        <el-select v-model="selectTags" remote multiple placeholder="请选择" filterable  style="width: 100%;">-->
+<!--          <el-option-->
+<!--            v-for="dict in tagList"-->
+<!--            :label="dict.name"-->
+<!--            :value="dict.tagId">-->
+<!--          </el-option>-->
+<!--        </el-select>-->
+
+        <div @click="hangleChangeTags()" style="cursor: pointer; border: 1px solid #e6e6e6; background-color: white; overflow: hidden; flex-grow: 1;width: 250px">
+          <div style="min-height: 35px; max-height: 200px; overflow-y: auto;">
+            <el-tag type="success"
+                    closable
+                    :disable-transitions="false"
+                    v-for="list in this.selectTags"
+                    :key="list.tagId"
+                    @close="handleCloseTags(list)"
+                    style="margin: 3px;"
+            >{{list.name}}
+            </el-tag>
+          </div>
+        </div>
+
+
+      </el-form-item>
+      <el-form-item label="备注" prop="remark">
+        <el-input
+          v-model="queryParams.remark"
+          placeholder="请输入备注"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+
+       <el-form-item label="添加时间" prop="createTime">
+          <el-date-picker v-model="createTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="change"></el-date-picker>
+      </el-form-item>
+
+
+
+
+      <el-form-item label="流失时间" prop="lossTime">
+        <el-date-picker clearable size="small"
+                        v-model="queryParams.lossTime"
+                        type="date"
+                        value-format="yyyy-MM-dd"
+                        placeholder="选择流失时间">
+        </el-date-picker>
+      </el-form-item>
+      <el-form-item label="删除时间" prop="delTime">
+        <el-date-picker clearable size="small"
+                        v-model="queryParams.delTime"
+                        type="date"
+                        value-format="yyyy-MM-dd"
+                        placeholder="选择删除时间">
+        </el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+<!--      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          v-hasPermi="['qw:externalContact:add']"
+        >同步</el-button>
+      </el-col> -->
+<!--      <el-col :span="1.5">
+        <el-button
+          type="success"
+          plain
+          icon="el-icon-edit"
+          size="mini"
+          @click="handleBatchUpdateNotes"
+          v-hasPermi="['qw:externalContact:edit']"
+        >批量修改备注
+        </el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="success"
+          plain
+          icon="el-icon-edit"
+          size="mini"
+          @click="handleBatchUpdateNotesFilter"
+          v-hasPermi="['qw:externalContact:edit']"
+        >批量修改备注(筛选条件)
+        </el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="success"
+          plain
+          icon="el-icon-edit"
+          size="mini"
+          :disabled="single"
+          @click="handleUpdate"
+          v-hasPermi="['qw:externalContact:edit']"
+        >修改备注</el-button>
+      </el-col>-->
+
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          :loading="exportLoading"
+          @click="handleExport"
+          v-hasPermi="['qw:externalContact:export']"
+        >导出</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>
+	  <el-col :span="1.5">
+	    <el-button
+	      type="primary"
+	      plain
+	      size="mini"
+	      @click="updateTalk"
+        v-hasPermi="['qw:externalContactInfo:updateTalk']"
+	    >批量更改交流状态</el-button>
+	  </el-col>-->
+<!--       <el-col :span="1.5">-->
+<!--        <el-button-->
+<!--          type="primary"-->
+<!--          plain-->
+<!--          size="mini"-->
+<!--          @click="setUserCourseSop"-->
+<!--          v-hasPermi="['qw:externalContact:setCourseSop']"-->
+<!--        >批量设置课程SOP</el-button>-->
+<!--      </el-col>-->
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+<!--    <el-tabs type="card" v-model="isBindActiveName" @tab-click="handleClickX">-->
+<!--      <el-tab-pane label="全部" name="all"></el-tab-pane>-->
+<!--      <el-tab-pane label="已绑定CRM" name="isBind"></el-tab-pane>-->
+<!--      <el-tab-pane label="未绑定CRM" name="noBind"></el-tab-pane>-->
+<!--    </el-tabs>-->
+
+    <el-table v-loading="loading" :data="externalContactList" @selection-change="handleSelectionChange" border>
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="企微客户ID" align="center" prop="id" />
+      <el-table-column label="企微客户头像" align="center" prop="avatar" width="100px">
+        <template slot-scope="scope">
+          <el-popover
+            placement="right"
+            title=""
+            trigger="hover">
+            <img slot="reference" :src="scope.row.avatar" width="60px">
+            <img :src="scope.row.avatar" style="max-width: 200px;">
+          </el-popover>
+        </template>
+      </el-table-column>
+      <el-table-column label="企微客户名称"  prop="name" width="110px"/>
+      <el-table-column label="客户称呼"  prop="stageStatus" width="110px"/>
+      <el-table-column label="销售企微昵称" align="center" prop="qwUserName" width="120px"/>
+      <el-table-column label="企微部门" align="center" prop="departmentName" width="120px"/>
+      <el-table-column label="用户类别" align="center" prop="type">
+        <template slot-scope="scope">
+          <dict-tag :options="typeOptions" :value="scope.row.type"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="性别" align="center" prop="gender">
+        <template slot-scope="scope">
+          <dict-tag :options="genderOptions" :value="scope.row.gender"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="备注" align="center" prop="remark" />
+      <el-table-column label="描述信息" align="center" prop="description" />
+      <el-table-column label="标签" align="center" prop="tagIdsName" width="300px">
+<!--        <template slot-scope="scope">-->
+<!--          <div v-for="name in scope.row.tagIdsName"  style="display: inline;">-->
+<!--          <el-tag type="success">{{ name }}</el-tag>-->
+<!--          </div>-->
+<!--        </template>-->
+        <template slot-scope="scope">
+          <div class="tag-container">
+            <div class="tag-list">
+              <el-tag
+                v-for="name in scope.row.tagIdsName"
+                :key="name"
+                type="success"
+                size="small"
+              >
+                {{ name }}
+              </el-tag>
+            </div>
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column label="是否回复" align="center" prop="isReply" width="120px" >
+        <template slot-scope="scope">
+          <span v-if="scope.row.isReply === 1"><el-tag type="success">已回复</el-tag></span>
+          <span v-else-if="scope.row.isReply === 0"><el-tag type="info">未回复</el-tag></span>
+          <span v-else>{{ scope.row.isReply }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="状态" align="center" prop="status" width="120px" >
+        <template slot-scope="scope">
+          <dict-tag :options="statusOptions" :value="scope.row.status"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="客户等级" align="center" prop="level" width="120px" >
+        <template slot-scope="scope">
+          <dict-tag :options="ratingType" :value="scope.row.level"/>
+        </template>
+      </el-table-column>
+
+      <el-table-column label="state参数" align="center" prop="state" width="100px" />
+
+      <el-table-column label="等级状态" align="center" prop="levelType" width="120px" >
+        <template slot-scope="scope">
+          <dict-tag :options="ratingUpFall" :value="scope.row.levelType"/>
+        </template>
+      </el-table-column>
+
+      <el-table-column label="添加时间" align="center" prop="createTime" width="100px" />
+      <el-table-column label="流失时间" align="center" prop="lossTime" width="100px" />
+      <el-table-column label="删除时间" align="center" prop="delTime" width="100px" />
+      <el-table-column label="注册时间" align="center" prop="registerTime" width="100px" />
+      <el-table-column label="备注电话号码" align="center" prop="remarkMobiles" width="150px">
+        <template slot-scope="scope">
+          <div v-for="i in JSON.parse(scope.row.remarkMobiles)" :key="i">{{i}}</div>
+        </template>
+      </el-table-column>
+      <el-table-column label="备注企业名称" align="center" prop="remarkCorpName" />
+      <el-table-column label="来源" align="center" prop="addWay" width="100px">
+        <template slot-scope="scope">
+          <dict-tag :options="addWayOptions" :value="scope.row.addWay"/>
+        </template>
+      </el-table-column>
+
+      <el-table-column label="转接状态" align="center" prop="transferStatus" width="100px" >
+        <template slot-scope="scope">
+          <dict-tag :options="transferStatusOptions" :value="scope.row.transferStatus"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="企业id" align="center" prop="corpId" />
+      <el-table-column label="是否绑定会员" width="100px" align="center" fixed="right">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.fsUserId" >已绑定</el-tag>
+          <el-tag v-else type="info"> 未绑定</el-tag>
+        </template>
+      </el-table-column>
+<!--      <el-table-column label="修改" align="center" class-name="small-padding fixed-width" width="120px" fixed="right">
+        <template slot-scope="scope">
+          <el-button
+            v-if="scope.row.status==0||scope.row.status==2"
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['qw:externalContact:edit']"
+          >修改备注</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-user-solid"
+            @click="handleAppellation(scope.row)"
+          >修改客户称呼</el-button>
+        </template>
+      </el-table-column>-->
+<!--      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="120px" fixed="right">
+        <template slot-scope="scope">
+&lt;!&ndash;          <el-button&ndash;&gt;
+&lt;!&ndash;            size="mini"&ndash;&gt;
+&lt;!&ndash;            type="text"&ndash;&gt;
+&lt;!&ndash;            icon="el-icon-edit-outline"&ndash;&gt;
+&lt;!&ndash;            @click="handleUpdateCustomer(scope.row)"&ndash;&gt;
+&lt;!&ndash;            >&ndash;&gt;
+&lt;!&ndash;            <span v-if="scope.row.customerId">换绑CRM</span>&ndash;&gt;
+&lt;!&ndash;            <span v-else>绑定CRM</span>&ndash;&gt;
+&lt;!&ndash;          </el-button>&ndash;&gt;
+
+&lt;!&ndash;          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit-outline"
+            @click="handleUpdateUser(scope.row)"
+            >
+            <span v-if="scope.row.fsUserId">换绑会员</span>
+            <span v-else>绑定会员</span>
+          </el-button>
+
+          <el-button
+            v-if="scope.row.fsUserId"
+            size="mini"
+            type="text"
+            icon="el-icon-thumb"
+            @click="handleUnBindUserId(scope.row)"
+            v-hasPermi="['qw:externalContact:unBindUserId']"
+          >
+            <span>解除会员绑定</span>
+          </el-button>&ndash;&gt;
+
+
+&lt;!&ndash;          <el-button v-if="scope.row.customerId"&ndash;&gt;
+&lt;!&ndash;            size="mini"&ndash;&gt;
+&lt;!&ndash;            type="text"&ndash;&gt;
+&lt;!&ndash;            icon="el-icon-paperclip"&ndash;&gt;
+&lt;!&ndash;            @click="handleShow(scope.row)"&ndash;&gt;
+&lt;!&ndash;          >CRM客户详情</el-button>&ndash;&gt;
+          <el-button
+             size="mini"
+             type="text"
+             @click="handledetails(scope.row)"
+             >AI获取用户信息
+          </el-button>
+          <el-button
+             size="mini"
+             type="text"
+             @click="handleMemberdetails(scope.row)"
+             v-if="scope.row.fsUserId"
+             >
+             <span>会员详细</span>
+          </el-button>
+        </template>
+      </el-table-column>-->
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <el-drawer size="75%" :title="show.title" :visible.sync="show.open">
+      <customer-details  ref="customerDetails" @refreshList="refreshList"/>
+    </el-drawer>
+
+    <el-drawer
+        :with-header="false"
+        size="75%"
+          :title="show.title" :visible.sync="show.open">
+      <userDetails  ref="userDetails" />
+    </el-drawer>
+
+
+    <!--  搜索标签   -->
+    <el-dialog :title="changeTagDialog.title" :visible.sync="changeTagDialog.open" style="width:100%;height: 100%" 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="handleSearchTags(queryTagParams.name)">搜索</el-button>
+        <el-button type="primary" icon="el-icon-plus" size="mini" @click="cancelSearchTags">重置</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"
+              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="getPageListTagGroup"
+      />
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="tagSubmitForm()">确 定</el-button>
+        <el-button @click="tagCancel()">取消</el-button>
+      </div>
+    </el-dialog>
+
+    <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="addTagForm"  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="scroll-wrapper">
+            <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>
+        </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="notesOpen.open" width="800px" append-to-body>
+      <el-card>
+        <el-row>
+          <el-col>
+            <el-radio-group v-model="notesOpen.nameType" style="margin-bottom: 2%">
+              <el-radio :label="1">
+                客户名称添加在【新备注】【前】
+              </el-radio>
+              <el-radio :label="2">
+                客户名称添加在【新备注】【后】
+              </el-radio>
+              <el-radio :label="3">
+                不添加客户名称
+              </el-radio>
+            </el-radio-group>
+          </el-col>
+          <el-col>
+            <el-radio-group v-model="notesOpen.type">
+              <el-radio
+                :label="1"
+              >添加【新备注】在最【前】面
+              </el-radio>
+              <el-radio
+                :label="2"
+              >添加【新备注】在最【后】面
+              </el-radio>
+              <el-radio
+                :label="3"
+              >替换所有备注
+              </el-radio>
+            </el-radio-group>
+          </el-col>
+          <el-col>
+            <el-input v-model="notesOpen.notes" placeholder="请输入客户备注(最多20个字,含已有的)" clearable size="small"
+                      maxlength="20" show-word-limit style="width: 500px;margin-top: 3%"/>
+            <div style="color: #999;font-size: 14px;display: flex;align-items: center;">
+              <i class="el-icon-info"></i>
+              由于企业微信官方限制,备注最多20个字,且自动会去除末尾超出的字
+            </div>
+          </el-col>
+        </el-row>
+      </el-card>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="notesSubmitForm()">确 定</el-button>
+        <el-button @click="notesCancel()">取消</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="addTagForm"  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="scroll-wrapper">
+            <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>
+        </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="title" :visible.sync="open" width="700px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="100px">
+
+
+        <el-form-item label="备注" prop="remark">
+          <el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
+        </el-form-item>
+        <el-form-item label="描述信息" prop="description">
+          <el-input v-model="form.description" type="textarea" :rows="3" placeholder="请输入描述信息" />
+        </el-form-item>
+
+        <el-form-item label="备注电话号码" prop="remarkMobiles">
+
+          <el-tag
+            :key="tag"
+            v-for="tag in remarkMobiles"
+            closable
+            :disable-transitions="false"
+            @close="handleClose(tag)">
+            {{tag}}
+          </el-tag>
+          <el-input
+            style="width:110px"
+            class="input-new-tag"
+            v-if="inputVisible"
+            v-model="inputValue"
+            ref="saveTagInput"
+            size="small"
+            @keyup.enter.native="handleInputConfirm"
+            @blur="handleInputConfirm"
+          >
+          </el-input>
+          <el-button v-else class="button-new-tag" size="small" style="width: 110px" @click="showInput">新增电话</el-button>
+
+        </el-form-item>
+
+
+        <el-form-item label="备注企业名称" prop="remarkCorpName">
+          <el-input v-model="form.remarkCorpName" placeholder="请输入备注企业名称" />
+        </el-form-item>
+
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+
+    <el-dialog :title="callOpen.title" :visible.sync="callOpen.open" width="500px" append-to-body>
+      <el-form ref="callOpenFrom" :model="callOpenFrom" :rules="callOpenRule" label-width="110px">
+        <el-form-item label="客户称呼" prop="stageStatus">
+          <el-input v-model="callOpenFrom.stageStatus" placeholder="请输入客户称呼" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer" >
+        <el-button type="primary" @click="submitCallOpenFrom">确 定</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 绑定客户   -->
+    <el-dialog :title="bindCustomer.title" :visible.sync="bindCustomer.open"  width="1200px" append-to-body>
+      <mycustomer ref="mycustomer"  @bindCustomerId="bindCustomerId"></mycustomer>
+    </el-dialog>
+
+<!--    设置一个课程sop-->
+    <el-dialog :title="setSop.title" :visible.sync="setSop.open"  width="1200px" append-to-body>
+      <SopDialog ref="SopDialog"  @bindCourseSop="bindCourseSop"></SopDialog>
+    </el-dialog>
+
+    <el-dialog :title="user.title" :visible.sync="user.open" width="800px" append-to-body>
+      <selectUser ref="selectUser" @bindMiniCustomerId="bindMiniCustomerId"></selectUser>
+    </el-dialog>
+
+	<el-dialog :title="info.title" :visible.sync="info.open"   width="1100px" append-to-body>
+	  <info  ref="Details" />
+	</el-dialog>
+  </div>
+</template>
+
+<script>
+import {
+  bindUserId,
+  addTag,
+  delTag,
+  batchUpdateExternalContactNotes,
+  listExternalContact,
+  getExternalContact,
+  delExternalContact,
+  addExternalContact,
+  updateExternalContact,
+  exportExternalContact,
+  editbindCustomer,
+  setCustomerCourseSop,
+  getCustomerCourseSop,
+  setCustomerCourseSopList,
+  unBindUserId, updateExternalContactCall
+} from '@/api/qw/externalContact'
+/*import {getMyQwUserList, getMyQwCompanyList, updateUser,getQwUserListLikeName} from "@/api/qw/user";*/
+import {getMyQwUserList, getMyQwCompanyList, updateUser} from "@/api/qw/user";
+import {listTag, getTag, searchTags,} from "@/api/qw/tag";
+import { allListTagGroup} from "@/api/qw/tagGroup";
+/*import mycustomer from '@/views/qw/externalContact/mycustomer'
+import customerDetails from '@/views/qw/externalContact/customerDetails'
+import SopDialog from '@/views/course/sop/SopDialog.vue'
+import  selectUser  from "@/views/qw/externalContact/selectUser.vue";
+import info from "@/views/qw/externalContact/info.vue";
+import { editTalk } from "@/api/qw/externalContactInfo";*/
+import PaginationMore from "../../../components/PaginationMore/index.vue";
+/*import userDetails from '@/views/store/components/userDetails.vue';*/
+export default {
+  name: "ExternalContact",
+  /*components:{PaginationMore, mycustomer,customerDetails,SopDialog,selectUser,info,userDetails},*/
+  components:{PaginationMore},
+  data() {
+    return {
+      notesOpen: {
+        type: 1,
+        nameType: 3,
+        addType: 0,
+        filter: false,
+        open: false,
+        notes: null,
+      },
+      user:{
+        open:false,
+        title:"修改客户"
+      },
+      userForm:{
+        id:null,
+        fsUserId:null,
+      },
+      info:{
+        title:"用户信息",
+        open:false,
+      },
+      // ...其他已有数据
+      qwUserSuggestions: [],       // 已展示的数据
+      showQwUserDropdown: false,   // 控制下拉框显示
+      qwUserLoading: false,        // 加载状态
+      qwUserNoMore: false,         // 是否还有更多数据
+      qwUserPageNum: 1,            // 当前页码
+      qwUserPageSize: 10,          // 每页数量
+      qwUserTotal: 0,               // 总数
+      isBindMiniOptions:[
+        {dictLabel:"已绑定",dictValue:'isBindMini'},
+        {dictLabel:"未绑定",dictValue:'noBindMini'},
+      ],
+      //标签弹窗选择
+      tagChange:{
+        open:false,
+        index:null,
+      },
+	  sTime:null,
+	  eTime:null,
+	  createTime:null,
+      // 遮罩层
+      loading: false,
+      // 导出遮罩层
+      exportLoading: false,
+      tagOpen:false,
+      tagDelOpen:false,
+      // 选中数组
+      ids: [],
+      isBindActiveName:"all",
+      remarkMobiles: [],
+      inputVisible: false,
+      inputValue: '',
+      // 非单个禁用
+      single: true,
+      tagGroupList: [],
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 企业微信客户表格数据
+      externalContactList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 用户类别字典
+      typeOptions: [],
+      ratingType: [],
+      ratingUpFall: [],
+      // 性别字典
+      genderOptions: [],
+
+      addTagForm:{
+        userIds:[],
+        tagIds:[]
+      },
+
+      myQwCompanyList:[],
+      show:{
+        title:"客户详情",
+        open:false,
+      },
+
+      //存储选择的客户
+      chooseCustomerSOP:null,
+
+      setSop:{
+        title:"选择课节SOP",
+        open:false,
+        //区分单选1还是多选2
+        type:null,
+      },
+      //合成的客户-课节SOP参数
+      customerCourseForm:{},
+
+      //查询是否已经设置过客户-某个课节的SOP
+      customerCourseFormLogs:{},
+      //绑定客户
+      bindCustomer:{
+        title:null,
+        open:false,
+      },
+      callOpen:{
+        open:false,
+        title: '修改客户称呼',
+      },
+      callOpenFrom:{
+        id:null,
+        stageStatus:null,
+      },
+      callOpenRule:{
+        stageStatus:[{required:true,message:"员工称呼不能为空",trigger:"blur"}]
+      },
+      //绑定的参数表
+      qwFormCustomer:{
+        externalContactId:null,
+        customerId:null,
+      },
+      // 来源字典
+      addWayOptions: [],
+
+      //标签
+      changeTagDialog:{
+        title:"",
+        open:false,
+      },
+
+      queryTagParams:{
+        pageNum: 1,
+        pageSize: 5,
+        total:0,
+        name:null,
+        corpId:null,
+      },
+
+      tagTotal:0,
+
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        userId: null,
+        qwUserName:null,
+        externalUserId: null,
+        name: null,
+        avatar: null,
+        type: null,
+        qwUserId:null,
+        gender: null,
+        description: null,
+        tagIds: null,
+        remark:null,
+        remarkMobiles: null,
+        remarkCorpName: null,
+        addWay: null,
+        operUserid: null,
+        corpId: null,
+        companyId: null,
+        status:null,
+        transferStatus:null,
+        isBind:null,
+        isBindMini:null,
+        lossTime:null,
+        sTime:null,
+        eTime:null,
+        createTime:null,
+        level:null,
+        wayId:null,
+        levelType:null,
+        companyUser:null
+      },
+      selectTags:[],
+      // 表单参数
+      form: {},
+      tagList:[],
+      transferStatusOptions:[],
+      statusOptions:[],
+      // 表单校验
+      rules: {
+      }
+    };
+  },
+  created() {
+
+    this.getDicts("sys_qw_externalContact_type").then(response => {
+      this.typeOptions = response.data;
+    });
+
+    this.getDicts("sys_qw_sop_rating_type").then(response => {
+      this.ratingType = response.data;
+    });
+
+    this.getDicts("sys_qw_sop_rating_upFall").then(response => {
+      this.ratingUpFall = response.data;
+    });
+
+
+    getMyQwCompanyList().then(response => {
+            this.myQwCompanyList = response.data;
+            if(this.myQwCompanyList!=null){
+              // this.queryParams.corpId=this.myQwCompanyList[0].dictValue
+              //
+              // var listTagFrom={corpId:this.queryParams.corpId}
+              // listTag(listTagFrom).then(response => {
+              //   this.tagList = response.rows;
+              // });
+              this.getList();
+
+            }
+    });
+
+
+    this.getDicts("sys_sex").then(response => {
+      this.genderOptions = response.data;
+    });
+    this.getDicts("sys_qw_externalContact_addWay").then(response => {
+      this.addWayOptions = response.data;
+    });
+
+
+
+    this.getDicts("sys_qw_external_contact_status").then(response => {
+      this.statusOptions = response.data;
+    });
+    this.getDicts("sys_qw_transfer_status").then(response => {
+      this.transferStatusOptions = response.data;
+    });
+
+  },
+  methods: {
+    handleMemberdetails(row){
+            this.show.open=true;
+            setTimeout(() => {
+                 this.$refs.userDetails.getDetails(row.fsUserId);
+            }, 1);
+    },
+    onQwUserNameClear() {
+      this.queryParams.qwUserId = null;  // 同时清空 qwUserId
+    },
+    // 搜索企微用户
+    searchQwUser(query) {
+      if (!query.trim()) {
+        this.qwUserSuggestions = [];
+        this.showQwUserDropdown = false;
+        this.qwUserNoMore = false;
+        this.qwUserPageNum = 1;
+        return;
+      }
+
+      this.queryParams.qwUserName = query;
+      this.qwUserPageNum = 1;
+      this.qwUserNoMore = false;
+      this.qwUserSuggestions = [];
+      this.fetchQwUsers(query);
+    },
+
+    fetchQwUsers(query) {
+      const params = {
+        qwUserName: query,
+        pageNum: this.qwUserPageNum,
+        pageSize: this.qwUserPageSize
+      };
+
+      this.qwUserLoading = true;
+
+      getQwUserListLikeName(params).then(res => {
+        const list = res.data.list || [];
+        const total = res.data.total || 0;
+
+        this.qwUserSuggestions = [...this.qwUserSuggestions, ...list];
+        this.qwUserTotal = total;
+
+        // 判断是否还有更多数据
+        if (this.qwUserSuggestions.length >= total) {
+          this.qwUserNoMore = true;
+        }
+
+        this.showQwUserDropdown = true;
+      }).finally(() => {
+        this.qwUserLoading = false;
+      });
+    },
+
+    // 选择企微用户
+    selectQwUser(key,value) {
+      this.queryParams.qwUserName = value;
+      this.queryParams.qwUserId = key;
+      this.showQwUserDropdown = false;
+      this.handleQuery(); // 可选:自动触发查询
+    },
+
+    // 延迟隐藏下拉框,防止点击失效
+    hideDropdownWithDelay() {
+      setTimeout(() => {
+        this.showQwUserDropdown = false;
+      }, 200);
+    },
+    handleScroll(e) {
+      const container = e.target;
+      const scrollTop = container.scrollTop;
+      const scrollHeight = container.scrollHeight;
+      const clientHeight = container.clientHeight;
+
+      // 距离底部小于 20px 触发加载
+      if (scrollHeight - scrollTop - clientHeight < 20 && !this.qwUserLoading && !this.qwUserNoMore) {
+        this.qwUserPageNum += 1;
+        this.fetchQwUsers(this.queryParams.qwUserName);
+      }
+    },
+
+
+	  change(){
+			if(this.createTime!=null){
+			  this.queryParams.sTime=this.createTime[0];
+			  this.queryParams.eTime=this.createTime[1];
+			}else{
+			  this.queryParams.sTime=null;
+			  this.queryParams.eTime=null;
+			}
+
+		  },
+    updateCorpId(){
+      var listTagFrom={corpId:this.queryParams.corpId}
+        listTag(listTagFrom).then(response => {
+          this.tagList = response.rows;
+        });
+        this.getList();
+     },
+    /** 查询企业微信客户列表 */
+    getList() {
+      this.loading = true;
+      const { qwUserName, ...queryParams } = this.queryParams;
+      listExternalContact(queryParams).then(response => {
+        this.externalContactList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    bindMiniCustomerId(row){
+      console.log(row)
+      this.userForm.fsUserId=row;
+      bindUserId(this.userForm).then(res=>{
+         if (res.code==200){
+           this.$message.success('绑定成功')
+         }else {
+           this.$message.error('绑定失败:',res.msg)
+         }
+         this.getList()
+         this.user.open=false;
+      })
+    },
+    /** 查看客户详情 */
+    handleShow(row){
+      this.show.open=true;
+      var that=this;
+      const tab = "visit";
+      setTimeout(() => {
+        that.$refs.customerDetails.getDetails(row.customerId);
+        that.$refs.customerDetails.handleClick(tab);
+
+      }, 200);
+    },
+
+	handledetails(row){
+		this.info.open=true;
+		setTimeout(() => {
+			 this.$refs.Details.getDetails(row.id);
+		}, 1);
+	},
+
+	closeInfo(){
+		this.info.open=false
+	},
+    handleClickX(tab, event) {
+
+      this.queryParams.isBind=tab.name;
+      this.handleQuery();
+    },
+
+    handleClose(tag) {
+      this.remarkMobiles.splice(this.remarkMobiles.indexOf(tag), 1);
+    },
+    showInput() {
+      this.inputVisible = true;
+      this.$nextTick(_ => {
+        this.$refs.saveTagInput.$refs.input.focus();
+      });
+    },
+    handleInputConfirm() {
+      let inputValue = this.inputValue;
+      if (inputValue) {
+        this.remarkMobiles.push(inputValue);
+      }
+      this.inputVisible = false;
+      this.inputValue = '';
+    },
+
+    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;
+
+    },
+
+
+    getPageListTagGroup(){
+      this.queryTagParams.corpId=this.queryParams.corpId
+      allListTagGroup(this.queryTagParams).then(response => {
+        this.tagGroupList = response.rows;
+        this.tagTotal = response.total;
+      });
+    },
+
+    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;
+
+    },
+
+
+    //搜索的标签
+    hangleChangeTags(){
+
+      this.changeTagDialog.title="搜索的标签"
+      this.changeTagDialog.open=true;
+
+      // 获取 tagListFormIndex 中的所有 tagId,用于快速查找
+      const selectedTagIds = new Set(
+        (this.selectTags || []).map(tagItem => tagItem?.tagId)
+      );
+
+      this.queryTagParams.name=null;
+
+      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 = selectedTagIds.has(this.tagGroupList[i].tag[x].tagId);
+          }
+        }
+      }, 200);
+
+
+    },
+
+    //删除一些选择的标签
+    handleCloseTags(list){
+      const ls = this.selectTags.findIndex(t => t.tagId === list.tagId);
+      if (ls !== -1) {
+        this.selectTags.splice(ls, 1);
+        this.selectTags = [...this.selectTags];
+      }
+
+      if (this.selectTags!=null && this.selectTags.length>0){
+        // 确保 this.form.tags 是数组
+        if (!this.queryParams.tagIds) {
+          this.queryParams.tagIds = []; // 如果未定义,初始化
+        } else {
+          this.queryParams.tagIds = []; // 清空已有数据
+        }
+
+        // 遍历并添加 tagId
+        this.selectTags.forEach(tag => {
+          if (tag.tagId) { // 确保 tagId 存在
+            this.queryParams.tagIds.push(tag.tagId);
+          }
+        });
+        this.queryParams.tagIds=this.queryParams.tagIds.join(",");
+      }else {
+        this.queryParams.tagIds=null;
+      }
+
+    },
+
+    //重新获取页面数据
+    refreshList(){
+      this.getList();
+    },
+
+    //批量设置课程sop
+    setUserCourseSop(){
+
+      if(this.ids==null||this.ids==""){
+        return  this.$message('请选择需要设置课程SOP的客户');
+      }
+
+      this.$confirm('批量设置客户课节SOP可能会存在重复,确定要批量设置吗?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+          this.setSop.open = true;
+          this.setSop.type = 2;
+        })
+        .catch(() => {
+          // 可以处理用户点击“取消”的逻辑
+        });
+
+    },
+    tagSelection(row){
+
+      row.isSelected= !row.isSelected;
+      this.$forceUpdate();
+    },
+
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    addTagCancel() {
+
+      this.tagOpen = false;
+
+      this.addTagForm={
+        userIds:[],
+        tagIds:[]
+      };
+    },
+
+    DelTagCancel() {
+      this.tagDelOpen = false;
+
+      this.addTagForm={
+        userIds:[],
+        tagIds:[]
+      };
+    },
+    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.addTagForm.tagIds.push(this.tagGroupList[i].tag[x].tagId)
+          }
+
+        }
+      }
+      if(this.addTagForm.tagIds==[]||this.addTagForm.tagIds==null||this.addTagForm.tagIds==""){
+        return  this.$message('请选择标签');
+      }
+
+      this.addTagForm.corpId=this.queryParams.corpId
+      this.addTagForm.userIds=this.ids;
+
+
+      let loadingRock = this.$loading({
+        lock: true,
+        text: '正在执行中请稍后~~请不要刷新页面!!',
+        spinner: 'el-icon-loading',
+        background: 'rgba(0, 0, 0, 0.7)'
+      });
+
+
+      addTag(this.addTagForm).then(response => {
+       this.msgSuccess(response.msg);
+       this.tagOpen = false;
+        loadingRock.close();
+       this.addTagForm={
+         userIds:[],
+         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.addTagForm.tagIds.push(this.tagGroupList[i].tag[x].tagId)
+          }
+
+        }
+      }
+      if(this.addTagForm.tagIds==[]||this.addTagForm.tagIds==null||this.addTagForm.tagIds==""){
+        return  this.$message('请选择标签');
+      }
+       this.addTagForm.corpId=this.queryParams.corpId
+      this.addTagForm.userIds=this.ids;
+
+      let loadingRock = this.$loading({
+        lock: true,
+        text: '正在执行中请稍后~~请不要刷新页面!!',
+        spinner: 'el-icon-loading',
+        background: 'rgba(0, 0, 0, 0.7)'
+      });
+
+
+      delTag(this.addTagForm).then(response => {
+       this.msgSuccess(response.msg);
+       this.tagDelOpen = false;
+        loadingRock.close();
+       this.addTagForm={
+         userIds:[],
+         tagIds:[]
+       };
+       this.getList()
+     }).finally(res=>{
+        loadingRock.close();
+      });
+    },
+
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        userId: null,
+        externalUserId: null,
+        name: null,
+        companyUserId:null,
+        customerId:null,
+        avatar: null,
+        type: null,
+        gender: null,
+        remark: null,
+        description: null,
+        tagIds: null,
+        remarkMobiles: null,
+        remarkCorpName: null,
+        addWay: null,
+        operUserid: null,
+        corpId: null,
+        companyId: null,
+        transferStatus:null,
+        status:null,
+        sTime:null,
+        eTime:null,
+        createTime:null,
+        transferTime:null,
+        transferNum:null,
+        lossTime:null,
+        delTime:null,
+        state:null,
+        wayId:null,
+        stageStatus:null,
+        customerName:null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+
+      if (this.selectTags!=null && this.selectTags.length>0){
+        // 确保 this.form.tags 是数组
+        if (!this.queryParams.tagIds) {
+          this.queryParams.tagIds = []; // 如果未定义,初始化
+        } else {
+          this.queryParams.tagIds = []; // 清空已有数据
+        }
+
+        // 遍历并添加 tagId
+        this.selectTags.forEach(tag => {
+          if (tag.tagId) { // 确保 tagId 存在
+            this.queryParams.tagIds.push(tag.tagId);
+          }
+        });
+        this.queryParams.tagIds=this.queryParams.tagIds.join(",");
+      }else {
+        this.queryParams.tagIds=null;
+      }
+
+
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    handleSearchTags(name){
+
+      if (!name){
+        return this.$message.error("请输入要搜索的标签")
+      }
+
+      this.queryTagParams.name=name;
+      this.queryTagParams.corpId=this.queryParams.corpId;
+
+      searchTags(this.queryTagParams).then(response => {
+        this.tagGroupList = response.rows;
+      });
+
+    },
+
+    cancelSearchTags(){
+      this.resetSearchQueryTag()
+
+      this.getPageListTagGroup();
+    },
+    //确定选择标签
+    tagSubmitForm(){
+
+      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) {
+
+            if (!this.selectTags) {
+              this.selectTags = [];
+            }
+
+            // 检查当前 tag 是否已经存在于 tagListFormIndex[index] 中
+            let tagExists = this.selectTags.some(
+              tag => tag.id === this.tagGroupList[i].tag[x].id
+            );
+
+            // 如果 tag 不存在于 tagListFormIndex[index] 中,则新增
+            if (!tagExists) {
+              this.selectTags.push(this.tagGroupList[i].tag[x]);
+            }
+          }
+        }
+      }
+      if (!this.selectTags || this.selectTags.length === 0) {
+        return this.$message('请选择标签');
+      }
+
+      this.changeTagDialog.open = false;
+    },
+
+    //取消选择标签
+    tagCancel(){
+      this.changeTagDialog.open = false;
+    },
+
+
+    resetSearchQueryTag(){
+
+      this.queryTagParams= {
+        pageNum: 1,
+        pageSize: 10,
+        total:0,
+        name:null,
+      };
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.queryParams.corpId= this.myQwCompanyList[0].dictValue;
+      this.selectTags=[];
+	   this.createTime=null;
+	  this.queryParams.sTime=null;
+	  this.queryParams.eTime=null;
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.loading=true;
+      this.form.corpId=this.queryParams.corpId
+     addExternalContact(this.form).then(response => {
+       this.msgSuccess("同步成功");
+       this.getList();
+     }).finally(()=>{
+       this.loading=false;
+     });
+
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id || this.ids
+      getExternalContact(id).then(response => {
+        this.form = response.data;
+        if(this.form.remarkMobiles!=null){
+          this.remarkMobiles=JSON.parse(this.form.remarkMobiles)
+        }else{
+          this.remarkMobiles=[]
+        }
+
+        this.open = true;
+        this.title = "修改企业微信客户";
+      });
+    },
+
+    handleAppellation(val){
+      this.callOpen.open=true;
+      this.callOpenFrom.stageStatus=val.stageStatus;
+      this.callOpenFrom.id=val.id;
+    },
+
+    /** 绑定客户操作 */
+    handleUpdateCustomer(row){
+       this.bindCustomer.title="绑定客户"
+        this.bindCustomer.open=true;
+        this.form.id=row.id
+        this.form.externalUserId=row.externalUserId
+        this.form.name=row.name
+    },
+
+    handleUpdateUser(row){
+        this.user.title="绑定客户"
+        this.user.open=true;
+        this.userForm.id=row.id;
+    },
+
+    handleUnBindUserId(val){
+
+      this.$confirm(
+        '确认解绑客户:<span style="color: green;">' + val.name + '' +
+        '</span> 的小程序用户?<br><span style="color: red;">【ps:可能会导致客户无法看课】</span>',
+        {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+          dangerouslyUseHTMLString: true // 允许使用 HTML 字符串
+        }
+      ).then(() => {
+        return unBindUserId(val.id);
+      }).then(response => {
+        this.getList();
+        this.msgSuccess("解绑成功");
+      }).finally(res=>{
+        this.getList();
+      })
+    },
+
+    bindCustomerId(row){
+
+      console.log("row",row)
+      // this.qwFormCustomer.customerId=row;
+      this.form.customerId=row;
+      this.form.corpId=this.queryParams.corpId;
+      this.msgWarning("绑定中.....同步信息中.....");
+
+      editbindCustomer(this.form).then(res=>{
+        //清空表单
+        this.reset();
+        this.bindCustomer.open = false;
+        this.msgSuccess("绑定成功");
+        this.getList();
+
+      })
+
+    },
+    //设置一个SOP
+    setCourseSOP(row) {
+
+      // 检查 row.miniUserId 是否为 null
+      if (row.miniUserId === null || row.miniUserId === undefined) {
+        return this.$confirm('当前客户【CRM客户详情】中 未绑定小程序客户,请先绑定', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).catch(error => {
+          this.msgWarning("操作取消:", error);
+        });
+      } else {
+        this.chooseCustomerSOP = row;
+        this.setSop.open = true;
+        this.setSop.type=1;
+      }
+    },
+
+    //选择课程SOP
+    // 用于设置 customerCourseForm 和 customerCourseFormLogs 的共同属性
+    setCommonProperties(form, row) {
+      form.qwUserid = this.chooseCustomerSOP.userId;
+      form.companyUserId = this.chooseCustomerSOP.companyUserId;
+      form.externalUserId = this.chooseCustomerSOP.externalUserId;
+      form.customerId = this.chooseCustomerSOP.customerId;
+      form.miniUserId = this.chooseCustomerSOP.miniUserId;
+      form.businessId = row.businessId;
+    },
+
+    bindCourseSop(row,days) {
+
+      if (this.setSop.type==2){
+        this.setSop.open = false;
+        this.loading=true;
+        this.msgWarning("设定中.....同步信息中.....");
+
+        setCustomerCourseSopList({ids:this.ids,fsCourseSopId:row.id,days:days}).then(res=>{
+
+          let msg=" 批量设置成功数【" + res.successNum + "】,<br>"
+
+          if (res.failCRM.length>0){
+            msg+="失败的客户【" + res.failCRM + "】,原因是未绑定CRM客户。<br>"
+          }
+          if (res.failMiNi.length>0){
+            msg+="失败的客户【" + res.failMiNi + "】,原因是CRM中未绑定小程序客户。<br>"
+          }
+          if (res.failCompany.length>0){
+            msg+="失败的客户【" + res.failCompany + "】,原因是客户没有所属成员。<br>"
+          }
+
+
+          return this.$confirm(msg, "提示", {
+            confirmButtonText: "确定",
+            cancelButtonText: "取消",
+            type: "warning",
+            dangerouslyUseHTMLString: true // 允许使用HTML标签
+          }).catch(error => {
+            this.msgSuccess("操作完成~");
+          });
+
+        }).finally(()=>{
+          this.loading = false;
+          this.getList();
+        })
+      }else if (this.setSop.type==1){
+
+      // 设置 customerCourseFormLogs 的属性
+      this.setCommonProperties(this.customerCourseFormLogs, row);
+
+      // 设置 customerCourseForm 的属性
+      this.setCommonProperties(this.customerCourseForm, row);
+      this.customerCourseForm.sopId = row.id;
+      this.customerCourseForm.sopType = row.sopType;
+      this.customerCourseForm.setting = row.setting;
+      this.customerCourseForm.days = days;
+
+      // 执行异步操作
+      getCustomerCourseSop(this.customerCourseFormLogs)
+        .then(res => {
+          if (res) {
+            return this.$confirm('当前客户已设置过相同课程课节SOP,确定还要再次设置吗?', "警告", {
+              confirmButtonText: "确定",
+              cancelButtonText: "取消",
+              type: "warning"
+            });
+          } else {
+            return Promise.resolve(); // 如果没有设置过,直接执行后续操作
+          }
+        })
+        .then(() => {
+          this.loading = true;
+          this.setSop.open = false;
+          this.msgSuccess("设定中.....同步信息中.....");
+
+          return setCustomerCourseSop(this.customerCourseForm);
+        })
+        .then(() => {
+          this.msgSuccess("设定成功");
+        })
+        .catch(error => {
+          this.msgWarning("操作取消:", error);
+        })
+        .finally(() => {
+          this.loading = false;
+          this.getList();
+        });
+      }
+    },
+    submitCallOpenFrom(){
+
+      this.$refs["callOpenFrom"].validate(valid => {
+        if (valid) {
+
+          if (this.callOpenFrom.id != null && this.callOpenFrom.stageStatus != null) {
+            updateExternalContactCall(this.callOpenFrom).then(res=>{
+              this.$message.success('修改成功');
+              this.callOpen.open=false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != null) {
+            this.form.remarkMobiles=JSON.stringify(this.remarkMobiles)
+            updateExternalContact(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addExternalContact(this.form).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除企业微信客户编号为"' + ids + '"的数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return delExternalContact(ids);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+
+	updateTalk(row){
+		const ids = row.id || this.ids;
+		this.$confirm('是否确认批量更改用户信息为非首次交流', "警告", {
+		    confirmButtonText: "确定",
+		    cancelButtonText: "取消",
+		    type: "warning"
+		  }).then(function() {
+		    return editTalk(ids);
+		  }).then(() => {
+		    this.getList();
+		    this.msgSuccess("成功");
+		  }).catch(() => {});
+	},
+    /** 导出按钮操作 */
+    handleExport() {
+      const { qwUserName, ...queryParams } = this.queryParams;
+      this.$confirm('是否确认导出所有企业微信客户数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportExternalContact(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    },
+    handleBatchUpdateNotesFilter() {
+      this.notesOpen.open = true;
+      this.notesOpen.filter = true;
+    },
+    handleBatchUpdateNotes() {
+
+      if (this.ids == null || this.ids == "") {
+        return this.$message('请选择需要添加备注的客户');
+      }
+
+      this.notesOpen.open = true;
+      this.notesOpen.filter = false;
+
+    },
+    notesSubmitForm() {
+
+      if (this.notesOpen.notes == null || this.notesOpen.notes == "") {
+        return this.$message.error("请输入备注内容");
+      }
+
+      // let loadingRock = this.$loading({
+      //   lock: true,
+      //   text: '正在执行中请稍后~~请不要刷新页面!!',
+      //   spinner: 'el-icon-loading',
+      //   background: 'rgba(0, 0, 0, 0.7)'
+      // });
+
+      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(",");
+      }
+      batchUpdateExternalContactNotes({
+        addType: 0,
+        userIds: this.ids,
+        notes: this.notesOpen.notes,
+        type: this.notesOpen.type,
+        nameType: this.notesOpen.nameType,
+        filter: this.notesOpen.filter,
+        param: obj
+      }).then(res => {
+
+        this.resultMessage = res.msg;
+        this.$message.success("正在执行中...");
+        // this.resultDialogVisible = true; // 显示弹窗
+        // this.resultTitle = '批量修改备注结果';
+
+      }).finally(res => {
+        this.getList();
+        // loadingRock.close();
+        this.notesCancel();
+      })
+
+    },
+  }
+};
+
+</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;
+    }
+
+
+.suggestion-box {
+  position: absolute;
+  z-index: 999;
+  background: #fff;
+  border: 1px solid #ddd;
+  max-height: 200px;
+  overflow-y: auto;
+  width: 100%;
+}
+
+.suggestion-item {
+  padding: 10px;
+  cursor: pointer;
+}
+.suggestion-item:hover {
+  background-color: #f5f7fa;
+}
+/* 新增的滚动容器样式(不影响原有样式) */
+.scroll-wrapper {
+  max-height: 130px; /* 大约三行的高度 */
+  overflow-y: auto;  /* 垂直滚动 */
+  padding-right: 5px; /* 为滚动条留出空间 */
+}
+
+/* 美化滚动条(可选) */
+.scroll-wrapper::-webkit-scrollbar {
+  width: 6px;
+}
+.scroll-wrapper::-webkit-scrollbar-thumb {
+  background: rgba(0, 0, 0, 0.2);
+  border-radius: 3px;
+}
+
+.tag-container {
+  max-height: 200px;
+  overflow-y: auto;
+  padding: 1px;
+  border: 1px solid #ebeef5;
+  border-radius: 1px;
+  background-color: #fafafa;
+}
+.tag-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+}
+.scroll-hint {
+  text-align: center;
+  color: #909399;
+  font-size: 12px;
+  padding: 1px 0;
+}
+.container {
+  max-width: 800px;
+  margin: 0 auto;
+  padding: 10px;
+}
+.title {
+  text-align: center;
+  color: #303133;
+  margin-bottom: 30px;
+}
+.demo-table {
+  width: 100%;
+  margin-bottom: 30px;
+}
+.instructions {
+  background-color: #f5f7fa;
+  padding: 15px;
+  border-radius: 1px;
+  margin-bottom: 20px;
+}
+</style>

+ 259 - 0
src/views/fastGpt/fastGptPushTokenTotal/index.vue

@@ -0,0 +1,259 @@
+<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 v-model="queryParams.companyId" placeholder="请选择所属公司" filterable size="small">
+          <el-option v-for="(option, index) in companyList" :key="index" :value="option.dictValue" :label="option.dictLabel"></el-option>
+        </el-select>
+      </el-form-item>
+
+      <el-form-item label="创建时间" prop="createTime">
+        <el-date-picker v-model="createTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"  @change="changeTime"></el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table border v-loading="loading" :data="fastGptPushTokenTotalList" :row-class-name="() => 'fixed-bottom-row'"
+              @selection-change="handleSelectionChange" :max-height="600">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="公司名称" align="center" prop="companyName" />
+      <el-table-column label="token消耗" align="center" prop="count" />
+      <el-table-column label="时间" align="center" prop="statTime" />
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </div>
+</template>
+
+<script>
+import { listFastgptEventLogTotal, exportFastgptEventLogTotal, getFastGptRoleAppKeyList} from "@/api/fastGpt/fastgptEventLogTotal";
+import { getFastGptPushTokenTotal} from "@/api/fastGpt/fastgptPushTokenTotal";
+import SelectTree from "@/components/TreeSelect/index.vue";
+import {getDeptData} from "@/api/system/employeeStats";
+import TreeSelect from '@riophae/vue-treeselect'
+import '@riophae/vue-treeselect/dist/vue-treeselect.css'
+import {allList} from "@/api/company/company";
+
+
+
+export default {
+  name: "FastgptEventLogTotal",
+  components: {SelectTree,TreeSelect},
+  data() {
+    return {
+      createTime:null,
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      selectedCompanyList: [],
+      companyList:[],
+      deptList: [],
+      companyUserList: [],
+      selectedAppKey: null,
+      selectedAppKeyLabel: '',
+      appKeyOptions: [],
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // token统计表格数据
+      fastGptPushTokenTotalList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      typeCountMap: null,
+      // 日志类型字典
+      typeOptions: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        roleId: null,
+        count: null,
+        type: null,
+        companyId: null,
+        companyUserId: null,
+        qwUserId: null,
+        typeCountMap: null,
+        beginTime:null,
+        endTime:null,
+        appKey:null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      }
+    };
+  },
+  created() {
+    this.getList();
+    this.getAllCompany();
+
+  },
+
+  methods: {
+    getAllCompany() {
+      allList().then(response => {
+        this.companyList = response.rows;
+        this.companyList.push({dictLabel: "无",
+          dictValue: "0"})
+      });
+    },
+    /** 查询ai事件埋点统计列表 */
+    getList() {
+      this.loading = true;
+
+      getFastGptPushTokenTotal(this.queryParams).then(response => {
+        console.log(response)
+        this.fastGptPushTokenTotalList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    normalizer(node) {
+      return {
+        id: node.id,
+        label: node.label,
+        children: node.children,
+        disabled: node.disabled
+      }
+    },
+    handleAppKeyChange(value) {
+      const node = this.findNodeById(this.appKeyOptions, value);
+      if (!node) {
+        this.selectedAppKeyLabel = '';
+        return;
+      }
+
+      // 如果是子节点,则找父节点 label
+      if (node.parentLabel) {
+        this.queryParams.appKey = node.parentLabel;
+        this.selectedAppKeyLabel = node.parentLabel;
+        this.selectedAppKey = this.selectedAppKeyLabel;
+      } else {
+        this.queryParams.appKey = node.label;
+        this.selectedAppKeyLabel = node.label;
+        this.selectedAppKey = this.selectedAppKeyLabel;
+      }
+    },
+    findNodeById(nodes, id) {
+      for (const node of nodes) {
+        if (node.id === id) return node;
+        if (node.children) {
+          const found = this.findNodeById(node.children, id);
+          if (found) return found;
+        }
+      }
+      return null;
+    },
+    changeTime(){
+      console.log(this.createTime);
+      if(this.createTime!=null){
+        this.queryParams.beginTime=this.createTime[0];
+        this.queryParams.endTime=this.createTime[1];
+      }else{
+        this.queryParams.beginTime=null;
+        this.queryParams.endTime=null;
+      }
+      console.log(this.queryParams.beginTime);
+      console.log(this.queryParams.endTime);
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        roleId: null,
+        count: null,
+        type: null,
+        companyId: null,
+        companyUserId: null,
+        qwUserId: null,
+        statTime: null
+      };
+      this.resetForm("form");
+    },
+    getPercentageColor(percentage) {
+      // HSL模式从黄色(60度)渐变到红色(0度)
+      const percent = Math.min(100, Math.max(0, parseFloat(percentage)));
+
+      // 调整色相范围:从深黄色(40°)渐变到红色(0°)
+      const hue = 40 - 40 * (percent / 100); // 初始 hue=40(深黄),终点 hue=0(红)
+
+      // 提高饱和度(100%),亮度保持 50%(鲜艳但不刺眼)
+      return `hsl(${hue}, 100%, 50%)`;
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      console.log(this.selectedAppKey)
+      if(this.selectedAppKey === null || typeof this.selectedAppKey === 'undefined'){
+        this.queryParams.appKey = null;
+      }
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.selectedAppKey = null;
+      this.selectedAppKeyLabel = '';
+      this.selectedCompanyList = [];
+      this.queryParams.appKey = null;
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+
+      if(this.selectedCompanyList != null && this.selectedCompanyList.length > 0) {
+        this.queryParams.userIds = this.selectedCompanyList;
+      }else {
+        this.queryParams.userIds = [];
+      }
+      this.$confirm('是否确认导出所有ai事件埋点统计数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportFastgptEventLogTotal(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    }
+  }
+};
+</script>

+ 20 - 4
src/views/fastGpt/fastgptEventLogTotal/index.vue

@@ -64,7 +64,7 @@
       <el-table-column label="角色名称" align="center" prop="roleName" />
       <el-table-column label="时间" align="center" prop="statTime" />
       <el-table-column
-        v-for="dict in typeOptions"
+        v-for="dict in sortedTypeOptions"
         :key="dict.dictValue"
         :label="dict.dictLabel"
         align="center">
@@ -72,9 +72,9 @@
           <span>{{ scope.row.typeCountMap ? scope.row.typeCountMap[dict.dictValue] : '' }}</span>
           <span v-if="dict.dictValue !== '11'"
                 :style="{ fontSize: '12px', marginLeft: '5px',
-                color: getPercentageColor((scope.row.typeCountMap[dict.dictValue] / scope.row.typeCountMap[2]) * 100) }">
-            ({{ ((scope.row.typeCountMap[dict.dictValue] / scope.row.typeCountMap[2]) * 100).toFixed(2) }}%)
-          </span>
+          color: getPercentageColor((scope.row.typeCountMap[dict.dictValue] / scope.row.typeCountMap[2]) * 100) }">
+      ({{ ((scope.row.typeCountMap[dict.dictValue] / scope.row.typeCountMap[2]) * 100).toFixed(2) }}%)
+    </span>
         </template>
       </el-table-column>
     </el-table>
@@ -179,6 +179,22 @@ export default {
     });
 
   },
+  // 在 data() 中添加一个新的计算属性或方法来处理排序后的 typeOptions
+  computed: {
+    sortedTypeOptions() {
+      if (!this.typeOptions || this.typeOptions.length === 0) return [];
+
+      // 将 dictValue 为 '11' 的选项过滤出来并放到最后
+      const normalOptions = this.typeOptions.filter(option => option.dictValue !== '11');
+      const lastOption = this.typeOptions.find(option => option.dictValue === '11');
+
+      if (lastOption) {
+        return [...normalOptions, lastOption];
+      }
+      return normalOptions;
+    }
+  },
+
   methods: {
     /** 查询ai事件埋点统计列表 */
     getList() {

+ 13 - 0
src/views/index.vue

@@ -85,6 +85,15 @@
                 <count-to :start-val="0" :end-val="balance" :duration="3600" class="card-panel-num" />
               </div>
             </div>
+            <div class="property-card propertyline">
+              <div class="property-title">
+                <i class="el-icon-money"></i>
+                润天余额(元)
+              </div>
+              <div class="card-value highlight">
+                <count-to :start-val="0" :end-val="runTianBalance" :duration="3600" class="card-panel-num" />
+              </div>
+            </div>
             <div class="property-card">
               <div class="property-title">
                 <span>今日消耗 (元)</span>
@@ -956,6 +965,7 @@ export default {
       todayComsumption: 0,
       yesterdayComsumption: 0,
       balance: 0,
+      runTianBalance: 0,
       autoRefreshInterval: null,
       // 今日新增用户数
       todayIncreaseUserNum: 0,
@@ -1072,6 +1082,7 @@ export default {
     /**
      * 计算余额预计可持续的天数
      * @param {number} balance - 当前账户余额
+     * @param {number} runTianBalance - 润天账户余额
      * @param {number} todayConsumption - 今日消耗金额
      * @param {number} yesterdayConsumption - 昨日消耗金额
      * @return {Object} 包含天数和进度百分比的对象
@@ -1178,8 +1189,10 @@ export default {
     },
     refresh() {
       rechargeComsumption(this.staticParam).then(res => {
+        console.log(res);
         if (res.code === 200) {
           this.balance = res.data.balance;
+          this.runTianBalance = res.data.runTianBalance;
           this.todayComsumption = res.data.todayComsumption;
           this.yesterdayComsumption = res.data.yesterdayComsumption;
           let calculateRemainingDays1 = this.calculateRemainingDays(this.balance, this.todayComsumption, this.yesterdayComsumption);