Ver Fonte

qw 模块同步使用master的

xgb há 3 semanas atrás
pai
commit
57b9d33e67

+ 189 - 32
src/views/qw/contactWay/index.vue

@@ -110,6 +110,7 @@
         </el-row>
         <div style=" height: calc(100% - 40px); overflow-y: auto;">
           <el-table v-loading="loading" :data="contactWayList" @selection-change="handleSelectionChange" border>
+            <el-table-column label="id" align="center" prop="id" />
             <el-table-column label="名称" align="center" prop="name" />
             <el-table-column label="二维码" align="center" prop="qrCode" width="150px" >
               <template slot-scope="scope">
@@ -203,15 +204,27 @@
               <el-col :span="22">
                 <div style="background-color: #fbfbfb;padding: 15px;  border: 1px solid #e6e6e6; margin-bottom: 20px;">
                   <el-form :model="item"  label-width="80px">
-                    <el-form-item label="员工选择"  style="height: 50px;">
-                       <el-select v-model="item.userIds" remote multiple placeholder="请选择" filterable  style="width: 100%;">
-                         <el-option
-                           v-for="dict in companyUserList"
-                           :key="dict.qwUserId"
-                           :label="dict.qwUserName"
-                           :value="dict.qwUserId">
-                         </el-option>
-                       </el-select>
+                    <el-form-item label="员工选择">
+                      <div>
+                        <el-button
+                          size="medium"
+                          icon="el-icon-circle-plus-outline"
+                          plain
+                          @click="handleListUserForTimeSlot(index)">请选择使用员工
+                        </el-button>
+                      </div>
+                      <div style="margin-top: 10px;">
+                        <el-tag
+                          style="margin-left: 5px; margin-top: 5px;"
+                          size="medium"
+                          :key="user.id"
+                          v-for="user in item.userList || []"
+                          closable
+                          :disable-transitions="false"
+                          @close="handleCloseTimeSlotUser(index, user)">
+                          {{ user.qwUserName }}
+                        </el-tag>
+                      </div>
                     </el-form-item>
                     <el-form-item label="工作周期" prop="week" style="height: 50px;" >
                        <el-select v-model="item.week" remote multiple placeholder="请选择" filterable  style="width: 100%;">
@@ -255,14 +268,27 @@
            <el-link type="primary" class="el-icon-plus" :underline="false" @click='addUserTime()'>添加其他工作周期</el-link>
         </el-form-item>
         <el-form-item label=""  v-if="form.userType==1">
-         <el-select v-model="userIds" remote multiple placeholder="选择全天在线员工" filterable  style="width: 800px;" :change="userChange()">
-           <el-option
-             v-for="dict in companyUserList"
-             :label="dict.qwUserName"
-             :value="dict.qwUserId">
-           </el-option>
+          <div>
+            <el-button
+              size="medium"
+              icon="el-icon-circle-plus-outline"
+              plain
+              @click="handleListUser(form.type,form.sendType, true)">请选择使用员工
+            </el-button>
+          </div>
+          <div>
+            <el-tag
+              style="margin-left: 5px"
+              size="medium"
+              :key="user.id"
+              v-for="user in userSelectList"
+              closable
+              :disable-transitions="false"
+              @close="handleCloseGroupUser(user)">
+              {{ user.qwUserName }}
+            </el-tag>
+          </div>
 
-         </el-select>
         </el-form-item>
         <el-form-item label="员工添加上限"v-if="form.userType==1" prop="isUserLimit">
           <el-switch
@@ -594,7 +620,7 @@
           </div>
           <div v-if="fileFrom.videoId!=null">
             <el-form-item label="图文链接:"  label-width="100px" >
-              <el-tag type="warning" v-model="fileFrom.linkUrl='待生成'">选择的课程小节 即为卡片链接地址</el-tag>
+              <el-tag type="warning">选择的课程小节 即为卡片链接地址</el-tag>
             </el-form-item>
           </div>
           <div v-if="fileFrom.videoId!=null">
@@ -645,6 +671,9 @@
       </div>
     </el-dialog>
 
+    <el-dialog :title="listUser.title" :visible.sync="listUser.open" width="700px" append-to-body>
+      <qwUserList ref="QwUserList" @selectUserList="selectUserList"></qwUserList>
+    </el-dialog>
 
     </div>
         <el-drawer
@@ -666,9 +695,10 @@ import ImageUpload from '@/views/qw/material/ImageUpload.vue'
 import statisDetails from '@/views/qw/contactWay/statisDetails';
 import { getMyQwUserList,getMyQwCompanyList } from "@/api/qw/user";
 import {courseList, videoList} from "@/api/qw/sop";
+import qwUserList from "@/views/qw/user/qwUserList.vue";
 export default {
   name: "ContactWay",
-   components: { contactWayGroup,ImageUpload,statisDetails},
+   components: {qwUserList, contactWayGroup,ImageUpload,statisDetails},
   data() {
     return {
       // 遮罩层
@@ -678,7 +708,8 @@ export default {
       exportLoading: false,
       // 选中数组
       ids: [],
-      userTimeJson:[{userIds:null,week:[1,2,3,4,5,6,7],startTime:null,endTime:null}],
+      userTimeJson:[{userIds:null,userList:[],week:[1,2,3,4,5,6,7],startTime:null,endTime:null}],
+      currentEditingTimeSlotIndex: -1, // 当前正在编辑的时间段索引
       userIds:[],
       show:{
            title:"医院详情",
@@ -828,6 +859,13 @@ export default {
       tagGroupList:[],
       tagList:[],
       tagListForm:[],
+      // 选择使用员工相关参数
+      userSelectList: [],
+      //企业微信员工
+      listUser: {
+        title: "",
+        open: false
+      },
     };
   },
   created() {
@@ -1171,7 +1209,7 @@ export default {
       });
     },
     addUserTime(){
-      this.userTimeJson.push({userIds:null,week:[1,2,3,4,5,6,7],startTime:null,endTime:null})
+      this.userTimeJson.push({userIds:null,userList:[],week:[1,2,3,4,5,6,7],startTime:null,endTime:null})
     },
     delUserTime(index){
       this.userTimeJson.splice(index,1)
@@ -1269,9 +1307,11 @@ export default {
               userTimeJson: null,
               userType: 2
             };
-              this.userTimeJson=[{userIds:null,week:[1,2,3,4,5,6,7],startTime:null,endTime:null}];
+              this.userTimeJson=[{userIds:null,userList:[],week:[1,2,3,4,5,6,7],startTime:null,endTime:null}];
+              this.currentEditingTimeSlotIndex = -1;
               this.userLimitJson=[];
               this.userIds=[];
+              this.userSelectList=[];
               this.spareUserIds=[];
               this.tagListForm=[];
               this.closeWelcomeWord=[];
@@ -1331,15 +1371,42 @@ export default {
       getContactWay(id).then(response => {
         this.form = response.data;
         this.open = true;
-        if(this.form.userTimeJson!=null){
-          this.userTimeJson=JSON.parse(this.form.userTimeJson)
-        }else{ this.userTimeJson=[{userIds:null,week:[1,2,3,4,5,6,7],startTime:null,endTime:null}]}
+
+        // 根据 userType 区分处理
+        if(this.form.userType === 2) {
+          // 自动上下线模式 - 只回显 userTimeJson
+          if(this.form.userTimeJson!=null){
+            this.userTimeJson=JSON.parse(this.form.userTimeJson)
+            // 根据 userIds回显userList
+            this.userTimeJson.forEach(timeSlot => {
+              if (!timeSlot.userList) {
+                timeSlot.userList = [];
+              }
+              if (timeSlot.userIds && Array.isArray(timeSlot.userIds)) {
+                timeSlot.userList = timeSlot.userIds.map(qwUserId => {
+                  return this.companyUserList.find(u => u.qwUserId === qwUserId);
+                }).filter(u => u); // 过滤掉 undefined
+              }
+            });
+          }else{
+            this.userTimeJson=[{userIds:null,userList:[],week:[1,2,3,4,5,6,7],startTime:null,endTime:null}]
+          }
+        } else if(this.form.userType === 1) {
+          // 全天在线模式 - 只回显 userSelectList
+          if(this.form.userIds!=null){
+            this.userIds=JSON.parse(this.form.userIds)
+            this.userSelectList = this.userIds.map(qwUserId => {
+              return this.companyUserList.find(u => u.qwUserId === qwUserId);
+            }).filter(u => u); // 过滤掉 undefined
+          }else{
+            this.userIds=[]
+            this.userSelectList=[]
+          }
+        }
+
         if(this.form.userLimitJson!=null){
           this.userLimitJson=JSON.parse(this.form.userLimitJson)
         }else{ this.userLimitJson=[]}
-        if(this.form.userIds!=null){
-          this.userIds=JSON.parse(this.form.userIds)
-        }else{ this.userIds=[]}
         if(this.form.spareUserIds!=null){
           this.spareUserIds=JSON.parse(this.form.spareUserIds)
         }else{ this.spareUserIds=[]}
@@ -1361,10 +1428,23 @@ export default {
       this.$refs["form"].validate(valid => {
         if (valid) {
           this.form.corpId=this.queryParams.corpId
+          if(this.form.userType === 1){
+            if(Object.keys(this.userIds).length === 0){
+              return this.$message({
+                message: '请选择客服类型-使用员工',
+                type: 'warning'
+              });
+            }
+          }
           if(this.form.userType==2){
             var jsonUserIds=[];
             for (let i = 0; i < this.userTimeJson.length; i++) {
-                if(this.userTimeJson[i].userIds==null||this.userTimeJson[i].userIds==""){
+                // 确保 userIds 与 userList 同步
+                if(this.userTimeJson[i].userList && this.userTimeJson[i].userList.length > 0){
+                  this.userTimeJson[i].userIds = this.userTimeJson[i].userList.map(u => u.qwUserId);
+                }
+
+                if(this.userTimeJson[i].userIds==null||this.userTimeJson[i].userIds=="" || this.userTimeJson[i].userIds.length==0){
                   return this.$message('人员不能为空');
                 }
                 if(this.userTimeJson[i].week==null||this.userTimeJson[i].week==""){
@@ -1378,9 +1458,7 @@ export default {
                 }
 
                 for (let j = 0; j < this.userTimeJson[i].userIds.length; j++) {
-                  console.log("!jsonUserIds.find(item=>item==this.userTimeJson[i].userIds[j])")
                   if(!jsonUserIds.find(item=>item==this.userTimeJson[i].userIds[j])){
-
                     jsonUserIds.push(this.userTimeJson[i].userIds[j]);
                   }
                 }
@@ -1391,7 +1469,12 @@ export default {
           }
           this.form.closeWelcomeWord=JSON.stringify(this.closeWelcomeWord)
           this.form.userIds=JSON.stringify(this.userIds)
-          this.form.userTimeJson=JSON.stringify(this.userTimeJson)
+          // 提交前移除 userList 字段(因为后端只需要 userIds)
+          const userTimeJsonForSubmit = this.userTimeJson.map(item => {
+            const {userList, ...rest} = item;
+            return rest;
+          });
+          this.form.userTimeJson=JSON.stringify(userTimeJsonForSubmit)
           this.form.userLimitJson=JSON.stringify(this.userLimitJson)
           this.form.spareUserIds=JSON.stringify(this.spareUserIds)
           this.form.tags=JSON.stringify(this.tagListForm)
@@ -1440,7 +1523,81 @@ export default {
           this.download(response.msg);
           this.exportLoading = false;
         }).catch(() => {});
-    }
+    },
+    //选择企微员工时
+    handleListUser(type, sendType, selectOne) {
+      setTimeout(() => {
+        this.$refs.QwUserList.getDetails(this.form.corpId, type, sendType, selectOne);
+      }, 1);
+
+      this.listUser.title = "选择企微员工"
+      this.listUser.open = true;
+
+    },
+    //企业微信员工信息子组件返回
+    selectUserList(list) {
+      this.listUser.open = false;
+      console.log("选择的员工",list)
+
+      // 判断是全天在线还是自动上下线
+      if (this.currentEditingTimeSlotIndex === -1) {
+        // 全天在线模式
+        list.forEach(obj => {
+          if (!this.userSelectList.some(item => item.id === obj.id)) {
+            this.userSelectList.push(obj);  // 存储完整对象
+            this.userIds.push(obj.qwUserId);
+          }
+        });
+      } else {
+        // 自动上下线模式 - 为特定时间段添加员工
+        const timeSlot = this.userTimeJson[this.currentEditingTimeSlotIndex];
+        if (!timeSlot.userList) {
+          this.$set(timeSlot, 'userList', []);
+        }
+        if (!timeSlot.userIds) {
+          this.$set(timeSlot, 'userIds', []);
+        }
+
+        list.forEach(obj => {
+          if (!timeSlot.userList.some(item => item.id === obj.id)) {
+            timeSlot.userList.push(obj);
+            timeSlot.userIds.push(obj.qwUserId);
+          }
+        });
+
+        // 重置索引
+        this.currentEditingTimeSlotIndex = -1;
+      }
+    },
+    // 全天在线模式 - 删除员工标签
+    handleCloseGroupUser(user) {
+      const index = this.userSelectList.findIndex(t => t.id === user.id);
+      if (index !== -1) {
+        this.userSelectList.splice(index, 1);
+        this.userIds.splice(index, 1);
+      }
+    },
+    // 自动上下线模式 - 打开员工选择弹窗
+    handleListUserForTimeSlot(timeSlotIndex) {
+      this.currentEditingTimeSlotIndex = timeSlotIndex;
+      setTimeout(() => {
+        this.$refs.QwUserList.getDetails(this.form.corpId, this.form.type, this.form.sendType, true);
+      }, 1);
+      this.listUser.title = "选择企微员工"
+      this.listUser.open = true;
+    },
+    // 自动上下线模式 - 删除时间段员工标签
+    handleCloseTimeSlotUser(timeSlotIndex, user) {
+      const timeSlot = this.userTimeJson[timeSlotIndex];
+      const userListIndex = timeSlot.userList.findIndex(t => t.id === user.id);
+      if (userListIndex !== -1) {
+        // 使用 splice 删除并强制更新
+        timeSlot.userList.splice(userListIndex, 1);
+        timeSlot.userIds.splice(userListIndex, 1);
+        // 强制更新整个 userTimeJson 数组以触发响应式更新
+        this.$set(this.userTimeJson, timeSlotIndex, {...timeSlot});
+      }
+    },
   }
 };
 </script>

+ 1 - 0
src/views/qw/externalContact/deptIndex.vue

@@ -359,6 +359,7 @@
           <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"/>

+ 21 - 0
src/views/qw/externalContact/index.vue

@@ -20,6 +20,15 @@
           @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
@@ -382,6 +391,9 @@
           <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"/>
@@ -960,6 +972,7 @@ export default {
         eTime:null,
         createTime:null,
         level:null,
+        wayId:null,
         levelType:null,
         companyUser:null
       },
@@ -1861,6 +1874,14 @@ export default {
       this.notesOpen.filter = false;
 
     },
+    notesCancel(){
+      this.notesOpen={
+        open: false,
+        notes: null,
+        type: 1,
+        nameType:3,
+      }
+    },
     notesSubmitForm() {
 
       if (this.notesOpen.notes == null || this.notesOpen.notes == "") {

+ 16 - 2
src/views/qw/externalContact/myExternalContact.vue

@@ -258,7 +258,7 @@
           plain
           size="mini"
           @click="addUserTag"
-
+          v-hasPermi="['qw:externalContact:myAddTag']"
         >批量添加标签</el-button>
       </el-col>
       <el-col :span="1.5">
@@ -267,7 +267,7 @@
           plain
           size="mini"
           @click="delUserTag"
-
+          v-hasPermi="['qw:externalContact:myDelTag']"
         >批量移除标签</el-button>
       </el-col>
 	  <el-col :span="1.5">
@@ -369,6 +369,12 @@
           <dict-tag :options="ratingType" :value="scope.row.level"/>
         </template>
       </el-table-column>
+      <el-table-column label="下单次数" align="center" width="100px">
+        <template #default="scope">
+          {{ scope.row.orderCount && scope.row.orderCount !== 0 ? scope.row.orderCount : '' }}
+        </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"/>
@@ -1979,6 +1985,14 @@ export default {
       this.notesOpen.filter = false;
 
     },
+    notesCancel(){
+      this.notesOpen={
+        open: false,
+        notes: null,
+        type: 1,
+        nameType:3,
+      }
+    },
     notesSubmitForm() {
 
       if (this.notesOpen.notes == null || this.notesOpen.notes == "") {

+ 445 - 0
src/views/qw/externalContactTransfer/companyTransfer.vue

@@ -0,0 +1,445 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="110px">
+      <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="所属员工" prop="qwUserName">
+        <el-input
+          v-model="queryParams.qwUserName"
+          placeholder="请输入所属员工名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="用户类别" prop="type">
+        <el-select v-model="queryParams.type" placeholder="请选择用户类别" clearable size="small">
+          <el-option
+            v-for="dict in typeOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="客户等级" prop="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="gender">
+        <el-input
+          v-model="queryParams.gender"
+          placeholder="请输入性别"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </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="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>
+      </el-form-item>
+      <el-form-item label="添加时间" prop="createTime">
+        <el-date-picker clearable size="small"
+                        v-model="queryParams.createTime"
+                        type="date"
+                        value-format="yyyy-MM-dd"
+                        placeholder="选择添加时间">
+        </el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          :loading="exportLoading"
+          @click="handleExport"
+          v-hasPermi="['qw:externalContact:companyExport']"
+        >导出</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          size="mini"
+          @click="handleTransfer"
+          :disabled="multiple"
+          v-hasPermi="['qw:externalContact:companyTransfer']"
+        >分配客户</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="externalContactList" @selection-change="handleSelectionChange" border>
+      <el-table-column type="selection" width="55" align="center" />
+      <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="name" />
+      <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="用户类别" 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="tagIds" width="300px">
+        <template slot-scope="scope">
+          <div class="tag-container">
+            <div class="tag-list">
+              <div v-for="i in JSON.parse(scope.row.tagIds)" :key="i" style="display: inline;">
+                <el-tag type="success" v-for="ii in tagList" :key="ii.id" style="margin: 3px;" v-if="ii.tagId==i">{{ii.name}}</el-tag>
+              </div>
+            </div>
+          </div>
+        </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="添加时间" align="center" prop="createTime" width="100px"/>
+      <el-table-column label="状态" align="center" prop="status" width="100px">
+        <template slot-scope="scope">
+          <dict-tag :options="statusOptions" :value="scope.row.status"/>
+        </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="备注电话号码" align="center" prop="remarkMobiles" width="150px">
+        <template slot-scope="scope">
+          <span v-for="i in JSON.parse(scope.row.remarkMobiles)" :key="i">{{i}}</span>
+        </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>
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <el-dialog :title="title" :visible.sync="open" width="1000px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <div style="background-color: rgb(239, 250, 255); margin: 10px;padding: 15px;">
+          <div>可将选中的客户转接给其他员工,进行后续服务</div>
+          <div>注意:90天内客户只能被转接一次,一个客户最多只能被转接两次</div>
+          <div>需等待总后台审核才会进行转接</div>
+        </div>
+
+        <el-form-item label="接替员工" prop="nickName">
+          <el-input style="width: 150px" disabled>
+            <template slot="prefix">
+              <el-button
+                plain
+                size="small"
+                type="success"
+                v-if="form.nickName">
+                {{ form.nickName }}
+              </el-button>
+            </template>
+          </el-input>
+        </el-form-item>
+        <el-form-item label="消息内容" prop="content">
+          <el-input v-model="form.content" placeholder="请输入内容" />
+          <div style="color: #999;font-size: 14px;display: flex;align-items: center;">
+            <i class="el-icon-info"></i>
+            自定义转接的时候发给客户的消息内容(选填)ps:不填则是官方默认话术
+          </div>
+        </el-form-item>
+        <el-card>
+          <companyTransferQwUserSelect :corpId="queryParams.corpId" :companyId="companyId" @selectUser="selectUser"/>
+        </el-card>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary"
+                   @click="submitForm"
+                   :disabled="submitLoading"
+                   :loading="submitLoading">提交审核</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listTag } from '@/api/qw/tag'
+import { getMyQwCompanyList } from '@/api/qw/user'
+import { companyExtList, companyTransfer, exportExternalContact } from '@/api/qw/externalContact'
+import companyTransferQwUserSelect from '@/views/qw/externalContactTransfer/companyTransferQwUserSelect.vue'
+
+export default {
+  name: "companyTransfer",
+  components: { companyTransferQwUserSelect },
+  data() {
+    return {
+      loading: false,
+      showSearch: true,
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        corpId: null,
+        name: null,
+        qwUserName: null,
+        type: null,
+        level: null,
+        gender: null,
+        transferStatus: null,
+        tagIds: null,
+        createTime: null,
+        status: 0
+      },
+      myQwCompanyList:[],
+      tagList:[],
+      selectTags:[],
+      typeOptions: [],
+      ratingType: [],
+      transferStatusOptions:[],
+      exportLoading: false,
+      statusOptions: [],
+      genderOptions: [],
+      addWayOptions:[],
+      externalContactList: [],
+      total: 0,
+      ids: [],
+      multiple: true,
+      form: {
+        qwUserId: null,
+        nickName: null,
+        content: null
+      },
+      rules: {
+        nickName: [
+          { required: true, message: "接替员工不能为空", trigger: "blur" }
+        ]
+      },
+      open: false,
+      title: "",
+      companyId: null,
+      submitLoading: false,
+    };
+  },
+  created() {
+    // 从store中获取当前用户的companyId
+    this.companyId = this.$store.state.user.user?.companyId || null;
+
+    this.getQwCompanyList();
+    this.getDicts("sys_qw_externalContact_type").then(response => {
+      this.typeOptions = response.data;
+    });
+    this.getDicts("sys_user_sex").then(response => {
+      this.genderOptions = response.data;
+    });
+    this.getDicts("sys_qw_externalContact_addWay").then(response => {
+      this.addWayOptions = response.data;
+    });
+    this.getDicts("sys_qw_sop_rating_type").then(response => {
+      this.ratingType = 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: {
+    handleQuery() {
+      this.queryParams.tagIds=this.selectTags.join(',')
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    resetQuery() {
+      this.queryParams = {
+        pageNum: 1,
+        pageSize: 10,
+        corpId: null,
+        name: null,
+        qwUserName: null,
+        type: null,
+        level: null,
+        gender: null,
+        transferStatus: null,
+        tagIds: null,
+        createTime: null,
+        status: 0
+      };
+      this.selectTags=[];
+      this.resetForm("queryForm");
+      this.queryParams.corpId = this.myQwCompanyList?.[0]?.dictValue || null
+      this.getList();
+    },
+    getList() {
+      this.loading = true
+      companyExtList(this.queryParams).then(response => {
+        this.externalContactList = response.rows;
+        this.total = response.total;
+        this.loading = false
+      })
+    },
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.multiple = !selection.length
+    },
+    updateCorpId(){
+      listTag({corpId:this.queryParams.corpId}).then(response => {
+        this.tagList = response.rows;
+      });
+      this.getList();
+    },
+    getQwCompanyList() {
+      getMyQwCompanyList().then(response => {
+        this.myQwCompanyList = response.data;
+        if(this.myQwCompanyList!=null){
+          this.queryParams.corpId=this.myQwCompanyList[0].dictValue;
+          listTag({corpId:this.queryParams.corpId}).then(response => {
+            this.tagList = response.rows;
+          });
+          this.getList();
+        }
+      });
+    },
+    handleExport() {
+      const 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(() => {});
+    },
+    reset() {
+      this.form = {
+        qwUserId: null,
+        nickName: null,
+        content: null
+      };
+      this.resetForm("form");
+    },
+    handleTransfer() {
+      this.reset()
+      if(!this.ids){
+        this.$message('请选择需要分配的客户')
+        return
+      }
+
+      this.submitLoading = false
+      this.title = "分配客户"
+      this.open = true
+    },
+    selectUser(selection) {
+      this.form.qwUserId = selection.id
+      this.form.nickName = selection.qwUserName
+    },
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+
+          if (this.submitLoading) {
+            return;
+          }
+          this.submitLoading = true
+
+          const params = {
+            ids: this.ids,
+            qwUserId: this.form.qwUserId,
+            content: this.form.content,
+            transferType: 1
+          }
+
+          companyTransfer(params).then(() => {
+            this.$message.success("审核已提交,等待管理员审核");
+            this.open = false;
+          })
+        }
+      });
+    }
+  },
+};
+</script>
+
+<style scoped>
+
+</style>

+ 144 - 0
src/views/qw/externalContactTransfer/companyTransferQwUserSelect.vue

@@ -0,0 +1,144 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
+      <el-form-item label="销售公司" prop="companyId">
+        <el-select v-model="queryParams.companyId" placeholder="销售公司"  size="small">
+          <el-option
+            v-for="dict in companyList"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="员工昵称" prop="nickName">
+        <el-input
+          v-model="queryParams.nickName"
+          placeholder="请输入员工昵称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">刷新/重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-table v-loading="loading" :data="userList" ref="userList" border>
+      <el-table-column label="归属销售公司" align="center" prop="companyName" />
+      <el-table-column label="企微员工账号" align="center" prop="qwUserId" />
+      <el-table-column label="员工昵称" align="center" prop="qwUserName" />
+      <el-table-column label="员工部门" align="center" prop="departmentName" />
+      <el-table-column label="员工昵称" align="center" prop="nickName"/>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="120px" >
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleSelectionChange(scope.row)"
+          >选择此人</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="handlePaginationChange"
+    />
+  </div>
+</template>
+
+<script>
+import { companyQwUserlist, listUser } from '@/api/qw/user'
+import { getCompanyListByCorId } from '@/api/company/company'
+
+
+export default {
+  name: "companyTransferQwUserSelect",
+  props: {
+    corpId: {
+      type: String,
+      default: null
+    },
+    companyId: {
+      type: Number,
+      default: null
+    }
+  },
+  data() {
+    return {
+      // 遮罩层
+      loading: false,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 企微用户表格数据
+      userList: [],
+      companyList: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        companyId: null,
+        corpId: null,
+        nickName: null
+      },
+    };
+  },
+  created() {
+    getCompanyListByCorId(this.corpId).then(response => {
+      this.companyList = response.data.filter(item => item.dictValue !== this.companyId)
+      if (!Array.isArray(this.companyList) || this.companyList.length === 0) {
+        return
+      }
+      this.queryParams.companyId = this.companyList[0].dictValue;
+      this.handleQuery();
+    })
+  },
+  methods: {
+    /** 查询企微用户列表 */
+    getList() {
+      this.loading = true;
+      companyQwUserlist(this.queryParams).then(response => {
+        this.userList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    handlePaginationChange(row) {
+      this.queryParams.pageNum = row.page;
+      this.queryParams.pageSize = row.limit;
+      this.getList();
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.queryParams.corpId = this.corpId;
+      if (!Array.isArray(this.companyList) || this.companyList.length === 0) {
+        return
+      }
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      if (!Array.isArray(this.companyList) || this.companyList.length === 0) {
+        return
+      }
+      this.queryParams.companyId = this.companyList[0].dictValue;
+      this.handleQuery();
+    },
+    // 选中数据
+    handleSelectionChange(selection) {
+      this.$emit("selectUser",selection);
+    },
+  }
+};
+</script>

+ 68 - 82
src/views/qw/externalContactTransfer/index.vue

@@ -30,20 +30,15 @@
           @keyup.enter.native="handleQuery"
         />
       </el-form-item>
-<el-form-item label="所属员工" prop="qwUserName">
-              <el-select @change="handleSelectChange" v-model="queryParams.qwUserName" remote filterable clearable reserve-keyword
-                         placeholder="请输入所属员工名称" :remote-method="qwUserMethod">
-                <el-option
-                  v-for="item in qwUserNameList"
-                  :key="item.id"
-                  :label="item.qwUserName"
-                  :value="item.qwUserName">
-                  <span style="float: left">{{ item.qwUserName }}</span>
-                </el-option>
-              </el-select>
-            </el-form-item>
-
-
+      <el-form-item label="所属员工" prop="qwUserName">
+        <el-input
+          v-model="queryParams.qwUserName"
+          placeholder="请输入所属员工名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
       <el-form-item label="用户类别" prop="type">
         <el-select v-model="queryParams.type" placeholder="请选择用户类别" clearable size="small">
           <el-option
@@ -54,6 +49,16 @@
           />
         </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="gender">
         <el-input
           v-model="queryParams.gender"
@@ -82,6 +87,15 @@
           </el-option>
         </el-select>
       </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 clearable size="small"
                         v-model="queryParams.createTime"
@@ -126,15 +140,6 @@
           v-hasPermi="['qw:externalContact:transfer']"
         >分配客户</el-button>
       </el-col>
-      <el-col :span="1.5">
-        <el-button v-if="isQwUserISNull"
-          type="primary"
-          plain
-          size="mini"
-          @click="handleTransferAll"
-          v-hasPermi="['qw:externalContact:transfer']"
-        >分配此员工所有客户</el-button>
-      </el-col>
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
@@ -168,7 +173,7 @@
       </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="tagIds" width="150px">
+      <el-table-column label="标签" align="center" prop="tagIds" width="300px">
         <template slot-scope="scope">
           <div class="tag-container">
             <div class="tag-list">
@@ -179,15 +184,9 @@
           </div>
         </template>
       </el-table-column>
-      <el-table-column label="备注电话号码" align="center" prop="remarkMobiles" width="150px">
+      <el-table-column label="客户等级" align="center" prop="level" width="120px" >
         <template slot-scope="scope">
-          <span v-for="i in JSON.parse(scope.row.remarkMobiles)" :key="i">{{i}}</span>
-        </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"/>
+          <dict-tag :options="ratingType" :value="scope.row.level"/>
         </template>
       </el-table-column>
       <el-table-column label="添加时间" align="center" prop="createTime" width="100px"/>
@@ -202,7 +201,17 @@
         </template>
       </el-table-column>
       <el-table-column label="企业id" align="center" prop="corpId" />
-
+      <el-table-column label="备注电话号码" align="center" prop="remarkMobiles" width="150px">
+        <template slot-scope="scope">
+          <span v-for="i in JSON.parse(scope.row.remarkMobiles)" :key="i">{{i}}</span>
+        </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" class-name="small-padding fixed-width">
         <template slot-scope="scope">
           <el-button
@@ -221,7 +230,6 @@
       :total="total"
       :page.sync="queryParams.pageNum"
       :limit.sync="queryParams.pageSize"
-      :page-sizes="[100, 200, 300, 500]"
       @pagination="getList"
     />
 
@@ -249,6 +257,10 @@
         </el-form-item>
 		<el-form-item label="消息内容" prop="content">
 		  <el-input v-model="form.content" placeholder="请输入内容" />
+      <div style="color: #999;font-size: 14px;display: flex;align-items: center;">
+        <i class="el-icon-info"></i>
+        自定义转接的时候发给客户的消息内容(选填)ps:不填则是官方默认话术
+      </div>
 		</el-form-item>
 
         <el-card>
@@ -266,9 +278,9 @@
 </template>
 
 <script>
-import { transfer,listExternalContact, getExternalContact, delExternalContact, addExternalContact, updateExternalContact, exportExternalContact } from "../../../api/qw/externalContact";
+import { transfer,listExternalContact, getExternalContact, delExternalContact, addExternalContact, updateExternalContact, exportExternalContact } from "@/api/qw/externalContact";
 import { listTag, getTag, delTag, addTag, updateTag, exportTag } from "@/api/qw/tag";
-import { qwUserList,userList } from "@/api/qw/user";
+import { qwUserList } from "@/api/qw/user";
 import qwUserSelectOne from '@/views/qw/user/qwUserSelectOne.vue'
 import { getMyQwUserList,getMyQwCompanyList } from "@/api/qw/user";
 
@@ -298,8 +310,10 @@ export default {
       title: "",
       // 是否显示弹出层
       open: false,
+      filter: false,
       // 用户类别字典
       typeOptions: [],
+      ratingType: [],
       selectTags:[],
       // 性别字典
       genderOptions: [],
@@ -307,22 +321,16 @@ export default {
       addWayOptions: [],
       nickName:null,
       qwUserList:[],
-      isQwUserISNull:false,
-      qwUserNameList:[],
-      qwUserName:null,
-      type:'0',
-      qwUserNameParam:{
-        qwUserName:null
-      },
       // 查询参数
       queryParams: {
         pageNum: 1,
-        pageSize: 300,
+        pageSize: 10,
         userId: null,
         externalUserId: null,
         name: null,
         avatar: null,
         type: null,
+        remark:null,
         gender: null,
         description: null,
         tagIds: null,
@@ -371,6 +379,10 @@ export default {
     this.getDicts("sys_qw_externalContact_addWay").then(response => {
       this.addWayOptions = response.data;
     });
+    this.getDicts("sys_qw_sop_rating_type").then(response => {
+      this.ratingType = response.data;
+    });
+
 
     this.getDicts("sys_qw_external_contact_status").then(response => {
       this.statusOptions = response.data;
@@ -381,24 +393,6 @@ export default {
 
   },
   methods: {
-
-     qwUserMethod(query) {
-      if (query !== '') {
-        this.qwUserNameParam.qwUserName = query;
-        userList(this.qwUserNameParam).then(response => {
-          this.qwUserNameList = response.rows;
-        });
-      }
-    },
-     handleSelectChange(value) {
-    console.log('选中的值:', value);
-    if(value == ''){
-      this.qwUserNameList=null
-    this.isQwUserISNull = false;
-    }else{
-    this.isQwUserISNull = true;
-    }
-  },
     updateCorpId(){
       listTag({corpId:this.queryParams.corpId}).then(response => {
         this.tagList = response.rows;
@@ -458,8 +452,6 @@ export default {
     },
     /** 重置按钮操作 */
     resetQuery() {
-      this.qwUserNameList=null
-       this.isQwUserISNull = false;
       this.selectTags=[];
       this.resetForm("queryForm");
       this.handleQuery();
@@ -481,43 +473,38 @@ export default {
 
     handleTransfer(row) {
       this.reset();
-      this.type="0";
       if(this.ids==null||this.ids==""){
         return  this.$message('请选择需要分配的客户');
       }
-
       setTimeout(() => {
                     this.$refs.qwUserSelectOne.getDetails(this.queryParams.corpId);
        }, 1);
       this.open = true;
+      this.filter = false;
       this.title = "分配客户";
-
     },
 
-     handleTransferAll(row) {
-      this.reset();
-      this.qwUserName=this.queryParams.qwUserName;
-      this.type="1";
-      setTimeout(() => {
-                    this.$refs.qwUserSelectOne.getDetails(this.queryParams.corpId);
-       }, 1);
-      this.open = true;
-      this.title = "分配该员工所有客户";
-
-    },
     /** 提交按钮 */
     submitForm() {
       this.$refs["form"].validate(valid => {
 
 
+        let obj = JSON.parse(JSON.stringify(this.queryParams))
+        if(obj.tagIds == "" && obj.tagIds.length == 0){
+          obj.tagIds = null;
+        }
+        if(obj.tagIds !== null && obj.tagIds !== undefined){
+          obj.tagIds = obj.tagIds.split(",");
+        }
         if (valid) {
             var form={
               ids:this.ids,
-              qwUserName:this.qwUserName,
-              type:this.type,
+              addType: 0,
+              filter: this.filter,
+              param: obj,
               userId:this.form.userId,
               corpId:this.queryParams.corpId,
-              content:this.form.content,
+			        content:this.form.content,
             }
             transfer(form).then(response => {
               this.msgSuccess(response.msg);
@@ -527,7 +514,6 @@ export default {
 
         }
       });
-      this.qwUserName=null;
     },
     /** 删除按钮操作 */
     handleDelete(row) {

+ 604 - 0
src/views/qw/externalContactTransfer/index旧.vue

@@ -0,0 +1,604 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="110px">
+
+<!--      <el-form-item label="外部联系人账号" prop="externalUserId">-->
+<!--        <el-input-->
+<!--          v-model="queryParams.externalUserId"-->
+<!--          placeholder="外部联系人账号"-->
+<!--          clearable-->
+<!--          size="small"-->
+<!--          @keyup.enter.native="handleQuery"-->
+<!--        />-->
+<!--      </el-form-item>-->
+      <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="所属员工" prop="qwUserName">
+              <el-select @change="handleSelectChange" v-model="queryParams.qwUserName" remote filterable clearable reserve-keyword
+                         placeholder="请输入所属员工名称" :remote-method="qwUserMethod">
+                <el-option
+                  v-for="item in qwUserNameList"
+                  :key="item.id"
+                  :label="item.qwUserName"
+                  :value="item.qwUserName">
+                  <span style="float: left">{{ item.qwUserName }}</span>
+                </el-option>
+              </el-select>
+            </el-form-item>
+
+
+      <el-form-item label="用户类别" prop="type">
+        <el-select v-model="queryParams.type" placeholder="请选择用户类别" clearable size="small">
+          <el-option
+            v-for="dict in typeOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="性别" prop="gender">
+        <el-input
+          v-model="queryParams.gender"
+          placeholder="请输入性别"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </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="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>
+      </el-form-item>
+      <el-form-item label="添加时间" prop="createTime">
+        <el-date-picker clearable size="small"
+                        v-model="queryParams.createTime"
+                        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
+          size="mini"
+          @click="handleAdd"
+          v-hasPermi="['qw:externalContact:add']"
+        >同步</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="handleTransfer"
+          v-hasPermi="['qw:externalContact:transfer']"
+        >分配客户</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button v-if="isQwUserISNull"
+          type="primary"
+          plain
+          size="mini"
+          @click="handleTransferAll"
+          v-hasPermi="['qw:externalContact:transfer']"
+        >分配此员工所有客户</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="externalContactList" @selection-change="handleSelectionChange" border>
+      <el-table-column type="selection" width="55" align="center" />
+
+      <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="externalUserId" width="120px"/>-->
+      <el-table-column label="客户名称" align="center" prop="name" />
+      <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="用户类别" 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="tagIds" width="150px">
+        <template slot-scope="scope">
+          <div class="tag-container">
+            <div class="tag-list">
+              <div v-for="i in JSON.parse(scope.row.tagIds)" :key="i" style="display: inline;">
+              <el-tag type="success" v-for="ii in tagList" :key="ii.id" style="margin: 3px;" v-if="ii.tagId==i">{{ii.name}}</el-tag>
+              </div>
+            </div>
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column label="备注电话号码" align="center" prop="remarkMobiles" width="150px">
+        <template slot-scope="scope">
+          <span v-for="i in JSON.parse(scope.row.remarkMobiles)" :key="i">{{i}}</span>
+        </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="createTime" width="100px"/>
+      <el-table-column label="状态" align="center" prop="status" width="100px">
+        <template slot-scope="scope">
+          <dict-tag :options="statusOptions" :value="scope.row.status"/>
+        </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="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['qw:externalContact:edit']"
+          >修改</el-button>
+
+        </template>
+      </el-table-column> -->
+    </el-table>
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      :page-sizes="[100, 200, 300, 500]"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改企业微信客户对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <div style="background-color: rgb(239, 250, 255); margin: 10px;padding: 15px;">
+          <div>可将选中的客户转接给其他员工,进行后续服务</div>
+          <div>注意:90天内客户只能被转接一次,一个客户最多只能被转接两次</div>
+        </div>
+
+        <el-form-item label="接替员工" prop="userId">
+<!--          <el-button type="success" v-if="this.nickName">{{ nickName }}</el-button>-->
+          <el-input style="width: 150px" disabled>
+            <template slot="prefix">
+              <el-button
+                plain
+                size="small"
+                type="success"
+                v-if="this.nickName">
+                {{ nickName }}
+              </el-button>
+            </template>
+          </el-input>
+        </el-form-item>
+		<el-form-item label="消息内容" prop="content">
+		  <el-input v-model="form.content" placeholder="请输入内容" />
+		</el-form-item>
+
+        <el-card>
+          <qwUserSelectOne ref="qwUserSelectOne" @selectUser="selectUser"></qwUserSelectOne>
+        </el-card>
+
+
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { transfer,listExternalContact, getExternalContact, delExternalContact, addExternalContact, updateExternalContact, exportExternalContact } from "../../../api/qw/externalContact";
+import { listTag, getTag, delTag, addTag, updateTag, exportTag } from "@/api/qw/tag";
+import { qwUserList,userList } from "@/api/qw/user";
+import qwUserSelectOne from '@/views/qw/user/qwUserSelectOne.vue'
+import { getMyQwUserList,getMyQwCompanyList } from "@/api/qw/user";
+
+export default {
+  name: "ExternalContact",
+  components: { qwUserSelectOne },
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      myQwCompanyList:[],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 企业微信客户表格数据
+      externalContactList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 用户类别字典
+      typeOptions: [],
+      selectTags:[],
+      // 性别字典
+      genderOptions: [],
+      // 来源字典
+      addWayOptions: [],
+      nickName:null,
+      qwUserList:[],
+      isQwUserISNull:false,
+      qwUserNameList:[],
+      qwUserName:null,
+      type:'0',
+      qwUserNameParam:{
+        qwUserName:null
+      },
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 300,
+        userId: null,
+        externalUserId: null,
+        name: null,
+        avatar: null,
+        type: null,
+        gender: null,
+        description: null,
+        tagIds: null,
+        remarkMobiles: null,
+        remarkCorpName: null,
+        addWay: null,
+        operUserid: null,
+        corpId: null,
+        companyId: null,
+        status:0,
+        transferStatus:null
+      },
+      // 表单参数
+      form: {},
+      tagList:[],
+      transferStatusOptions:[],
+      statusOptions:[],
+      // 表单校验
+      rules: {
+        userId: [{ required: true, message: '请选择接替员工', trigger: 'blur' }],
+      }
+    };
+  },
+  created() {
+    getMyQwCompanyList().then(response => {
+            this.myQwCompanyList = response.data;
+            if(this.myQwCompanyList!=null){
+              this.queryParams.corpId=this.myQwCompanyList[0].dictValue;
+              listTag({corpId:this.queryParams.corpId}).then(response => {
+                this.tagList = response.rows;
+              });
+              qwUserList(this.queryParams.corpId).then(response => {
+                this.qwUserList = response.rows;
+              });
+
+              this.getList();
+            }
+    });
+
+    this.getDicts("sys_qw_externalContact_type").then(response => {
+      this.typeOptions = response.data;
+    });
+    this.getDicts("sys_user_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: {
+
+     qwUserMethod(query) {
+      if (query !== '') {
+        this.qwUserNameParam.qwUserName = query;
+        userList(this.qwUserNameParam).then(response => {
+          this.qwUserNameList = response.rows;
+        });
+      }
+    },
+     handleSelectChange(value) {
+    console.log('选中的值:', value);
+    if(value == ''){
+      this.qwUserNameList=null
+    this.isQwUserISNull = false;
+    }else{
+    this.isQwUserISNull = true;
+    }
+  },
+    updateCorpId(){
+      listTag({corpId:this.queryParams.corpId}).then(response => {
+        this.tagList = response.rows;
+      });
+      qwUserList(this.queryParams.corpId).then(response => {
+        this.qwUserList = response.rows;
+      });
+      this.getList();
+     },
+    /** 查询企业微信客户列表 */
+    getList() {
+      this.loading = true;
+      listExternalContact(this.queryParams).then(response => {
+        this.externalContactList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        userId: null,
+        externalUserId: null,
+        name: 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
+      };
+      this.resetForm("form");
+    },
+
+    selectUser(row){
+      this.form.userId=row.id
+	  console.log(row)
+      this.nickName=row.qwUserName
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.tagIds=this.selectTags.join(',')
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.qwUserNameList=null
+       this.isQwUserISNull = false;
+      this.selectTags=[];
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+     addExternalContact(this.form).then(response => {
+       this.msgSuccess("同步成功");
+       this.getList();
+     });
+
+    },
+
+    handleTransfer(row) {
+      this.reset();
+      this.type="0";
+      if(this.ids==null||this.ids==""){
+        return  this.$message('请选择需要分配的客户');
+      }
+
+      setTimeout(() => {
+                    this.$refs.qwUserSelectOne.getDetails(this.queryParams.corpId);
+       }, 1);
+      this.open = true;
+      this.title = "分配客户";
+
+    },
+
+     handleTransferAll(row) {
+      this.reset();
+      this.qwUserName=this.queryParams.qwUserName;
+      this.type="1";
+      setTimeout(() => {
+                    this.$refs.qwUserSelectOne.getDetails(this.queryParams.corpId);
+       }, 1);
+      this.open = true;
+      this.title = "分配该员工所有客户";
+
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+
+
+        if (valid) {
+            var form={
+              ids:this.ids,
+              qwUserName:this.qwUserName,
+              type:this.type,
+              userId:this.form.userId,
+              corpId:this.queryParams.corpId,
+              content:this.form.content,
+            }
+            transfer(form).then(response => {
+              this.msgSuccess(response.msg);
+              this.open = false;
+              this.getList();
+            });
+
+        }
+      });
+      this.qwUserName=null;
+    },
+    /** 删除按钮操作 */
+    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(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const 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(() => {});
+    }
+  }
+};
+</script>
+<style  scoped>
+.tag-container {
+  max-height: 300px;
+  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>

+ 213 - 0
src/views/qw/externalContactTransferLog/companyTransferDetail.vue

@@ -0,0 +1,213 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="公司名称" prop="companyName">
+        <el-input
+          v-model="queryParams.companyName"
+          placeholder="请输入销售公司名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="主体名称" prop="corpName">
+        <el-input
+          v-model="queryParams.corpName"
+          placeholder="请输入企微主体名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="销售名称" prop="companyUserName">
+        <el-input
+          v-model="queryParams.companyUserName"
+          placeholder="请输入销售名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="审核状态" prop="status">
+        <el-select v-model="queryParams.status" placeholder="请选择" clearable >
+          <el-option :value="0" label="全部"/>
+          <el-option
+            v-for="item in auditStatusOptions"
+            :key="item.dictValue"
+            :label="item.dictLabel"
+            :value="item.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-table v-loading="loading" :data="list" border>
+      <el-table-column label="主体名称" align="center" prop="corpName" />
+      <el-table-column label="接替公司名称" align="center" prop="companyName" />
+      <el-table-column label="接替销售名称" align="center" prop="companyUserName" />
+      <el-table-column label="接替企微用户名称" align="center" prop="qwUserName" />
+      <el-table-column label="审核状态" align="center" prop="status">
+        <template slot-scope="scope">
+          <dict-tag :options="auditStatusOptions" :value="scope.row.status"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="提交时间" align="center" prop="createTime" />
+      <el-table-column label="审核时间" align="center" prop="auditTime" />
+      <el-table-column label="被拒原因" align="center" prop="reason" />
+      <el-table-column label="提交人" align="center" prop="createBy" />
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            @click="handleView(scope.row)"
+            v-hasPermi="['qw:externalContactTransferCompanyAudit:detail']"
+          >详情</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <el-drawer title="详情" size="75%" :visible.sync="dialogViewVisible" append-to-body>
+      <el-row :gutter="10" class="mb8">
+        <el-col :span="1.5">
+          <el-button
+            style="margin-left: 10px"
+            type="primary"
+            plain
+            size="mini"
+            v-if="currentAudit.status === 2"
+            :loading="syncLoading"
+            @click="sync"
+            v-hasPermi="['qw:externalContactTransferCompanyAudit:sync']"
+          >同步</el-button>
+        </el-col>
+      </el-row>
+      <el-table v-loading="detailLoading" :data="userList" border>
+        <el-table-column label="客户名称" align="center" prop="externalUserName" />
+        <el-table-column label="原公司名称" align="center" prop="companyName" />
+        <el-table-column label="原销售名称" align="center" prop="companyUserName" />
+        <el-table-column label="原企微用户名称" align="center" prop="qwUserName" />
+        <el-table-column label="备注" align="center" prop="remark" />
+        <el-table-column label="接替状态" align="center" prop="status">
+          <template slot-scope="scope">
+            <dict-tag :options="replaceStatusOptions" :value="scope.row.status"/>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-drawer>
+  </div>
+</template>
+
+<script>
+import { detail, listExternalContactTransferAudit, sync } from '@/api/qw/externalContactTransferAudit'
+
+export default {
+  name: "companyTransferDetail",
+  data() {
+    return {
+      loading: false,
+      showSearch: true,
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        companyName: null,
+        corpName: null,
+        companyUserName: null,
+        status: 0,
+      },
+      total: 0,
+      list: [],
+      auditStatusOptions: [],
+      replaceStatusOptions: [],
+      dialogAuditVisible: false,
+      dialogViewVisible: false,
+      detailLoading: false,
+      userList: [],
+      currentAudit: {},
+      syncLoading: false,
+    }
+  },
+  created() {
+    this.getDicts("sys_qw_transfer_audit_status").then((response) => {
+      this.auditStatusOptions = response.data;
+    });
+    this.getDicts("sys_qw_transfer_status").then((response) => {
+      this.replaceStatusOptions = response.data;
+    });
+    this.handleQuery()
+  },
+  methods: {
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    resetQuery() {
+      this.queryParams = {
+        pageNum: 1,
+        pageSize: 10,
+        companyName: null,
+        corpName: null,
+        companyUserName: null,
+        status: 0,
+      };
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    getList() {
+      this.loading = true
+      const params = {
+        ...this.queryParams,
+        status: this.queryParams.status === 0 ? null : this.queryParams.status
+      }
+      listExternalContactTransferAudit(params).then(response => {
+        this.list = response.rows.map(item => {
+          return {
+            ...item,
+            createTime: item.createTime ? item.createTime.replace("T", " ") : null,
+            auditTime: item.auditTime ? item.auditTime.replace("T", " ") : null,
+          }
+        })
+        this.total = response.total
+        this.loading = false
+      })
+    },
+    handleView(row) {
+      this.currentAudit = row
+      this.detailLoading = true
+      detail(row.id).then(response => {
+        this.userList = response.data
+        this.detailLoading = false
+      })
+      this.dialogViewVisible = true
+    },
+    sync() {
+      const id = this.currentAudit.id
+      this.syncLoading = true
+      sync(id).then(() => {
+        this.detailLoading = true
+        detail(id).then(response => {
+          this.userList = response.data
+          this.detailLoading = false
+        })
+        this.syncLoading = false
+      })
+    },
+  }
+};
+</script>
+
+<style scoped>
+
+</style>

+ 464 - 0
src/views/qw/externalContactUnassigned/companyUnassigned.vue

@@ -0,0 +1,464 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="110px">
+      <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="qwUserName">
+        <el-select @change="handleSelectChange" v-model="queryParams.qwUserName" remote filterable clearable reserve-keyword
+                   placeholder="请输入原所属员工名称" :remote-method="qwUserMethod">
+          <el-option
+            v-for="item in qwUserNameList"
+            :key="item.id"
+            :label="item.qwUserName"
+            :value="item.qwUserName">
+            <span style="float: left">{{ item.qwUserName }}</span>
+          </el-option>
+        </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="用户类别" 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="gender">
+        <el-input
+          v-model="queryParams.gender"
+          placeholder="请输入性别"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </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>
+        <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
+          size="mini"
+          @click="handleSync"
+          v-hasPermi="['qw:externalContact:addUnassigned']"
+        >同步</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="handleTransfer"
+          :disabled="multiple"
+          v-hasPermi="['qw:externalContact:companyTransfer']"
+        >分配客户</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          v-if="isQwUserISNull"
+          type="primary"
+          plain
+          size="mini"
+          @click="handleTransferAll"
+          v-hasPermi="['qw:externalContact:companyTransfer']"
+        >分配此员工所有客户</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="externalContactList" @selection-change="handleSelectionChange" border>
+      <el-table-column type="selection" width="55" align="center" />
+
+      <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="externalUserId" width="120px"/>
+      <el-table-column label="客户名称" align="center" prop="name" />
+      <el-table-column label="头像" align="center" prop="avatar" width="120px">
+        <template slot-scope="scope">
+          <el-popover
+            placement="right"
+            title=""
+            trigger="hover">
+            <img slot="reference" :src="scope.row.avatar" width="100px">
+            <img :src="scope.row.avatar" style="max-width: 150px;">
+          </el-popover>
+        </template>
+      </el-table-column>
+      <el-table-column label="用户类别" align="center" prop="type">
+        <template slot-scope="scope">
+          <dict-tag :options="typeOptions" :value="scope.row.type"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="性别" align="center" prop="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="tagIds" width="150px">
+        <template slot-scope="scope">
+          <div class="tag-container">
+            <div class="tag-list">
+              <div v-for="i in JSON.parse(scope.row.tagIds)" :key="i" style="display: inline;">
+                <el-tag type="success" v-for="ii in tagList" :key="ii.id" style="margin: 3px;" v-if="ii.tagId==i">{{ii.name}}</el-tag>
+              </div>
+            </div>
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column label="备注电话号码" align="center" prop="remarkMobiles" width="150px">
+        <template slot-scope="scope">
+          <span v-for="i in JSON.parse(scope.row.remarkMobiles)" :key="i">{{i}}</span>
+        </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="status" width="100px">
+        <template slot-scope="scope">
+          <dict-tag :options="statusOptions" :value="scope.row.status"/>
+        </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>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      :page-sizes="[100, 200, 300, 500]"
+      @pagination="getList"
+    />
+
+    <el-dialog :title="title" :visible.sync="open" width="1000px"  append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="100px" style="height: 100%">
+        <div style="background-color: rgb(239, 250, 255); margin: 10px;padding: 15px;">
+          <div>原跟进成员离职时间不能超过1年且离职前一年内至少登录过一次企业微信</div>
+          <div>接替成员最近一年内至少登陆过一次企业微信。</div>
+          <div>需等待总后台审核才会进行转接</div>
+        </div>
+        <el-form-item label="接替员工:" prop="nickName">
+
+          <el-input style="width: 150px" disabled>
+            <template slot="prefix">
+              <el-button
+                plain
+                size="small"
+                type="success"
+                v-if="form.nickName">
+                {{ form.nickName }}
+              </el-button>
+            </template>
+          </el-input>
+        </el-form-item>
+        <el-card>
+          <companyTransferQwUserSelect :corpId="queryParams.corpId" :companyId="companyId" @selectUser="selectUser"/>
+        </el-card>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary"
+                   @click="submitForm"
+                   :disabled="submitLoading"
+                   :loading="submitLoading">提交审核</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listTag } from '@/api/qw/tag'
+import { getMyQwCompanyList, userList } from '@/api/qw/user'
+import { addUnassigned, companyExtList, companyTransfer, exportExternalContact } from '@/api/qw/externalContact'
+import companyTransferQwUserSelect from '@/views/qw/externalContactTransfer/companyTransferQwUserSelect.vue'
+
+export default {
+  name: "companyUnassigned",
+  components: { companyTransferQwUserSelect },
+  data() {
+    return {
+      showSearch: true,
+      loading: false,
+      ids: [],
+      multiple: true,
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        corpId: null,
+        qwUserName: null,
+        name: null,
+        type: null,
+        gender: null,
+        transferStatus: null,
+        status: 1
+      },
+      total: 0,
+      externalContactList: [],
+      myQwCompanyList: [],
+      typeOptions: [],
+      genderOptions: [],
+      addWayOptions: [],
+      statusOptions: [],
+      transferStatusOptions: [],
+      qwUserNameParam: {
+        qwUserName: null,
+      },
+      qwUserNameList: [],
+      isQwUserISNull: false,
+      form: {
+        corpId: null,
+        qwUserId: null,
+        nickName: null,
+        content: null,
+        type: 0,
+        oldQwUserId: null
+      },
+      rules: {
+        nickName: [
+          { required: true, message: "接替员工不能为空", trigger: "blur" }
+        ]
+      },
+      exportLoading: false,
+      companyId: null,
+      submitLoading: false,
+      tagList: [],
+      open: false,
+      title: "",
+    };
+  },
+  created() {
+    // 从store中获取当前用户的companyId
+    this.companyId = this.$store.state.user.user?.companyId || null;
+
+    this.getQwCompanyList();
+    this.getDicts("sys_qw_externalContact_type").then(response => {
+      this.typeOptions = response.data;
+    });
+    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: {
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    resetQuery() {
+      this.queryParams = {
+        pageNum: 1,
+        pageSize: 10,
+        corpId: null,
+        qwUserName: null,
+        name: null,
+        type: null,
+        gender: null,
+        transferStatus: null,
+        status: 1
+      };
+      this.qwUserNameList = null
+      this.isQwUserISNull = false
+      this.resetForm("queryForm")
+      this.queryParams.corpId = this.myQwCompanyList?.[0]?.dictValue || null
+      this.getList()
+    },
+    getList() {
+      this.loading = true
+      companyExtList(this.queryParams).then(response => {
+        this.externalContactList = response.rows;
+        this.total = response.total;
+        this.loading = false
+      })
+    },
+    getQwCompanyList() {
+      getMyQwCompanyList().then(response => {
+        this.myQwCompanyList = response.data;
+        if(this.myQwCompanyList!=null){
+          this.queryParams.corpId=this.myQwCompanyList[0].dictValue;
+          listTag({corpId:this.queryParams.corpId}).then(response => {
+            this.tagList = response.rows;
+          });
+          this.getList();
+        }
+      });
+    },
+    updateCorpId() {
+      listTag({corpId:this.queryParams.corpId}).then(response => {
+        this.tagList = response.rows;
+      });
+      this.getList();
+    },
+    handleSelectChange(value) {
+      if(!value){
+        this.qwUserNameList=null
+        this.isQwUserISNull = false;
+      }else{
+        this.isQwUserISNull = true;
+      }
+    },
+    qwUserMethod(query) {
+      if (query) {
+        this.qwUserNameParam.qwUserName = query;
+        userList(this.qwUserNameParam).then(response => {
+          this.qwUserNameList = response.rows;
+        });
+      }
+    },
+    handleSync() {
+      this.form.corpId=this.queryParams.corpId;
+      addUnassigned(this.form).then(() => {
+        this.msgSuccess("同步成功");
+        this.getList();
+      });
+    },
+    handleExport() {
+      const 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(() => {});
+    },
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.multiple = !selection.length
+    },
+    reset() {
+      this.form = {
+        corpId: null,
+        qwUserId: null,
+        nickName: null,
+        content: null,
+        type: 0,
+        oldQwUserId: null
+      };
+      this.resetForm("form");
+    },
+    handleTransfer() {
+      this.reset()
+      if(!this.ids){
+        this.$message('请选择需要分配的客户')
+        return
+      }
+
+      this.submitLoading = false
+      this.title = "分配客户"
+      this.open = true
+    },
+    handleTransferAll() {
+      this.reset()
+      this.form.type = 1
+      this.form.oldQwUserId = this.qwUserNameList.find(item => item.qwUserName === this.queryParams.qwUserName)?.id || null
+      this.submitLoading = false
+      this.title = "分配该员工所有客户"
+      this.open = true
+    },
+    selectUser(selection) {
+      this.form.qwUserId = selection.id
+      this.form.nickName = selection.qwUserName
+    },
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+
+          if (this.submitLoading) {
+            return;
+          }
+          this.submitLoading = true
+
+          const params = {
+            ids: this.ids,
+            qwUserId: this.form.qwUserId,
+            content: this.form.content,
+            type: this.form.type,
+            transferType: 2,
+            oldQwUserId: this.form.oldQwUserId
+          }
+
+          companyTransfer(params).then(() => {
+            this.$message.success("审核已提交,等待管理员审核");
+            this.open = false;
+          })
+        }
+      });
+    }
+  },
+};
+</script>
+
+<style scoped>
+
+</style>

+ 13 - 1
src/views/qw/friendWelcome/deptFriendWelcome.vue

@@ -66,6 +66,8 @@
 
 
     <el-table v-loading="loading" :data="friendWelcomeList" @selection-change="handleSelectionChange" border>
+      <el-table-column label="id" align="center" prop="id" width="180"/>
+      <el-table-column label="标题" align="center" prop="welcomeTitle"/>
       <el-table-column label="消息内容" align="left" prop="welcomeText"  width="400px" >
         <template slot-scope="scope">
           <span style="color:rgb(19, 154, 50);" v-if="scope.row.isDayparting==='1'">[共 {{JSON.parse(scope.row.daypartingItemlist).length+1}} 时段]</span>
@@ -160,6 +162,16 @@
               </el-tag>
             </div>
           </el-form-item>
+          <!-- 新增整体标题 -->
+          <el-form-item label="标题:" prop="welcomeTitle">
+            <el-input v-model="form.welcomeTitle" placeholder="请输入欢迎语标题"/>
+            <el-alert style="margin-top: 8px;height: 30px"
+                      title="此标题仅用于欢迎语的用途区分"
+                      type="info"
+                      :closable="false"
+                      show-icon>
+            </el-alert>
+          </el-form-item>
           <el-form-item label="是否发送欢迎语">
             <el-switch
               v-model="form.isSendMsg"
@@ -731,7 +743,7 @@ export default {
     //选择群发的企业成员账号
     handlelistUser(){
       setTimeout(() => {
-        this.$refs.QwUserList.getDetails(this.queryParams.corpId);
+        this.$refs.QwUserList.getDetails(this.queryParams.corpId,null,null,"2");
       }, 1);
       this.listUser.title="选择企业成员"
       this.listUser.open=true;

+ 12 - 0
src/views/qw/friendWelcome/indexNew.vue

@@ -66,6 +66,8 @@
 
 
     <el-table v-loading="loading" :data="friendWelcomeList" @selection-change="handleSelectionChange" border>
+      <el-table-column label="id" align="center" prop="id" width="180"/>
+      <el-table-column label="标题" align="center" prop="welcomeTitle"/>
       <el-table-column label="消息内容" align="left" prop="welcomeText"  width="400px" >
         <template slot-scope="scope">
           <span style="color:rgb(19, 154, 50);" v-if="scope.row.isDayparting==='1'">[共 {{JSON.parse(scope.row.daypartingItemlist).length+1}} 时段]</span>
@@ -161,6 +163,16 @@
               </el-tag>
             </div>
           </el-form-item>
+          <!-- 新增整体标题 -->
+          <el-form-item label="标题:" prop="welcomeTitle">
+            <el-input v-model="form.welcomeTitle" placeholder="请输入欢迎语标题"/>
+            <el-alert style="margin-top: 8px;height: 30px"
+                      title="此标题仅用于欢迎语的用途区分"
+                      type="info"
+                      :closable="false"
+                      show-icon>
+            </el-alert>
+          </el-form-item>
           <el-form-item label="是否发送欢迎语">
             <el-switch
               v-model="form.isSendMsg"

+ 13 - 2
src/views/qw/friendWelcome/myWelcome.vue

@@ -66,6 +66,8 @@
 
 
     <el-table v-loading="loading" :data="friendWelcomeList" @selection-change="handleSelectionChange" border>
+      <el-table-column label="id" align="center" prop="id" width="180"/>
+      <el-table-column label="标题" align="center" prop="welcomeTitle"/>
       <el-table-column label="消息内容" align="left" prop="welcomeText"  width="400px" >
         <template slot-scope="scope">
           <span style="color:rgb(19, 154, 50);" v-if="scope.row.isDayparting==='1'">[共 {{JSON.parse(scope.row.daypartingItemlist).length+1}} 时段]</span>
@@ -138,7 +140,6 @@
           </el-alert>
         </div>
         <el-form ref="form" :model="form" :rules="rules" label-width="120px">
-
           <el-form-item label="选择使用成员:" prop="qwUserIds" style="margin-top: 2%">
             <div>
               <el-button
@@ -160,6 +161,16 @@
               </el-tag>
             </div>
           </el-form-item>
+          <!-- 新增整体标题 -->
+          <el-form-item label="标题:" prop="welcomeTitle">
+            <el-input v-model="form.welcomeTitle" placeholder="请输入欢迎语标题"/>
+            <el-alert style="margin-top: 8px;height: 30px"
+                      title="此标题仅用于欢迎语的用途区分"
+                      type="info"
+                      :closable="false"
+                      show-icon>
+            </el-alert>
+          </el-form-item>
           <el-form-item label="是否发送欢迎语">
             <el-switch
               v-model="form.isSendMsg"
@@ -731,7 +742,7 @@ export default {
     //选择群发的企业成员账号
     handlelistUser(){
       setTimeout(() => {
-        this.$refs.QwUserList.getDetails(this.queryParams.corpId);
+        this.$refs.QwUserList.getDetails(this.queryParams.corpId,null,null,"1");
       }, 1);
       this.listUser.title="选择企业成员"
       this.listUser.open=true;

+ 8 - 8
src/views/qw/sop/addSop.vue

@@ -29,10 +29,10 @@
               :label="1"
             >标签
             </el-radio>
-<!--            <el-radio-->
-<!--              :label="2"-->
-<!--            >群聊-->
-<!--            </el-radio>-->
+            <el-radio
+              :label="2"
+            >群聊
+            </el-radio>
           </el-radio-group>
           <Tip :title="'标签:根据企微客户的标签筛选客户进入SOP'" />
         </el-form-item>
@@ -76,7 +76,7 @@
           </el-select>
           <Tip title="选择的企业微信员工下面的群聊" />
         </el-form-item>
-        <el-form-item label="客户评级" prop="isRating" style="margin-top: 2%">
+        <el-form-item label="客户评级" prop="isRating" style="margin-top: 2%" v-if="form.filterMode == 1">
           <el-switch
             v-model="form.isRating"
             active-color="#13ce66"
@@ -235,7 +235,7 @@
 
           </el-form-item>
         </div>
-        <el-form-item label="是否固定营期" prop="isFixed" v-if="form.type != 3">
+        <el-form-item label="是否固定营期" prop="isFixed" v-if="form.type != 3 && form.filterMode == 1">
           <el-radio-group v-model="form.isFixed">
             <el-radio
               :label="1"
@@ -265,7 +265,7 @@
           </el-row>
           <Tip title="任务生成的代发送消息,如果超过此设置的时间还未发送,自动作废" />
         </el-form-item>
-        <el-form-item v-if="(form.sendType==2 || form.sendType==4 || form.sendType==11) && form.type != 3 && form.isFixed == 0" label="自动添加SOP"
+        <el-form-item v-if="(form.sendType==2 || form.sendType==4 || form.sendType==11) && form.type != 3 && form.isFixed == 0 && form.filterMode == 1" label="自动添加SOP"
                       prop="autoSopTime.autoSopType">
           <el-radio-group v-model="form.autoSopTime.autoSopType">
             <el-radio
@@ -332,7 +332,7 @@
 <!--        </el-form-item>-->
 
 
-        <el-form-item label="是否只发送注册用户" prop="isRegister" v-if="form.type != 3">
+        <el-form-item label="是否只发送注册用户" prop="isRegister" v-if="form.type != 3 && form.filterMode == 1">
           <el-radio-group v-model="form.isRegister">
             <el-radio
               :label="1"

+ 44 - 10
src/views/qw/sop/sop.vue

@@ -288,7 +288,7 @@
             type="text"
             style="color: green;"
             @click="handleQueryDetails(scope.row)"
-            v-hasPermi="['qw:sopTemp:info']"
+            v-hasPermi="['qw:sop:list']"
           >查看模板
           </el-button>
         </template>
@@ -760,7 +760,20 @@
                       </el-card>
                     </div>
                     <div v-if="item.contentType == 4">
-
+                      <el-card class="box-card">
+                        <el-form-item label="标题" prop="miniprogramTitle">
+                          <el-input v-model="item.miniprogramTitle" placeholder="请输入小程序消息标题,最长为64字"  />
+                        </el-form-item>
+                        <el-form-item label="封面" prop="miniprogramPicUrl">
+                          <ImageUpload v-model="item.miniprogramPicUrl"  type="image" :num="10" :width="150" :height="150" />
+                        </el-form-item>
+                        <el-form-item label="appid" prop="miniprogramAppid" v-show="false" >
+                          <el-input v-model="item.miniprogramAppid" disabled />
+                        </el-form-item>
+                        <el-form-item label="page路径" prop="miniprogramPage" v-show="false" label-width="100px" style="margin-left: -30px" >
+                          <el-input v-model="item.miniprogramPage" placeholder="小程序消息打开后的路径"  disabled />
+                        </el-form-item>
+                      </el-card>
                     </div>
                     <div v-if="item.contentType == 5 ">
 
@@ -1401,18 +1414,28 @@ export default {
             this.$set(this.setting[i], 'linkTitle', selectedCourse.dictLabel);
             this.$set(this.setting[i], 'linkImageUrl', selectedCourse.dictImgUrl);
           }
+          if (selectedCourse && this.setting[i].contentType == 4 && this.msgForm.courseId != null) {
+            this.$set(this.setting[i], 'miniprogramPicUrl', selectedCourse.dictImgUrl);
+          }
 
         }
 
       }
-      if (this.msgForm.videoId != null ) {
+      if (this.msgForm.videoId != null) {
         // 查找选中的课节对应的 label
         const selectedVideo = this.videoList.find(course => parseInt(course.dictValue) === this.msgForm.videoId);
 
         for (let i = 0; i < this.setting.length; i++) {
           //响应式直接给链接的描述上值
-          if (selectedVideo && this.setting[i].contentType == 3  && this.msgForm.videoId != null) {
-            this.$set(this.setting[i], 'linkDescribe', selectedVideo.dictLabel);
+          if (selectedVideo && this.msgForm.videoId != null) {
+            console.log(2, this.setting[i].contentType)
+            if (this.setting[i].contentType == 3 || this.setting[i].contentType == 9) {
+              this.$set(this.setting[i], 'linkDescribe', selectedVideo.dictLabel);
+            }
+            if (this.setting[i].contentType == 4) {
+              this.$set(this.setting[i], 'miniprogramTitle', selectedVideo.dictLabel);
+            }
+
           }
         }
       }
@@ -1420,14 +1443,22 @@ export default {
 
     },
     videoIdChange() {
-      if (this.msgForm.videoId != null ) {
+      if (this.msgForm.videoId != null) {
         // 查找选中的课节对应的 label
         const selectedVideo = this.videoList.find(course => parseInt(course.dictValue) === this.msgForm.videoId);
 
         for (let i = 0; i < this.setting.length; i++) {
           //响应式直接给链接的描述上值
-          if (selectedVideo && this.setting[i].contentType == 3  && this.msgForm.videoId != null) {
-            this.$set(this.setting[i], 'linkDescribe', selectedVideo.dictLabel);
+          if (selectedVideo && this.msgForm.videoId != null) {
+            if (this.setting[i].contentType == 3 || this.setting[i].contentType == 9) {
+              this.$set(this.setting[i], 'linkDescribe', selectedVideo.dictLabel);
+            }
+
+            if (this.setting[i].contentType == 4) {
+              this.$set(this.setting[i], 'miniprogramTitle', selectedVideo.dictLabel);
+            }
+
+
           }
         }
       }
@@ -1851,7 +1882,7 @@ export default {
       });
     },
     courseChange() {
-      if (this.msgForm.courseId != null ) {
+      if (this.msgForm.courseId != null) {
         const selectedCourse = this.courseList.find(course => parseInt(course.dictValue) === this.msgForm.courseId);
         for (let i = 0; i < this.setting.length; i++) {
           //响应式直接给链接的标题/封面上值
@@ -1859,12 +1890,15 @@ export default {
             this.$set(this.setting[i], 'linkTitle', selectedCourse.dictLabel);
             this.$set(this.setting[i], 'linkImageUrl', selectedCourse.dictImgUrl);
           }
+          if (selectedCourse && this.setting[i].contentType == 4 && this.msgForm.courseId != null) {
+            this.$set(this.setting[i], 'miniprogramPicUrl', selectedCourse.dictImgUrl);
+          }
 
         }
 
       }
       videoList(this.msgForm.courseId).then(response => {
-        this.videoList=response.list;
+        this.videoList = response.list;
       });
     },
     cancelMsgForm(){

+ 29 - 1
src/views/qw/sopTemp/index.vue

@@ -228,6 +228,18 @@
             />
           </el-select>
         </el-form-item>
+
+        <el-form-item label="归属部门" prop="createByDept">
+          <treeselect
+            style="width: 220px"
+            :clearable="false"
+            v-model="form.createByDept"
+            :options="deptOptions"
+            clearable
+            :show-count="true"
+            placeholder="请选择归属部门"
+          />
+        </el-form-item>
         <el-form-item label="课程" prop="courseId" v-if="form.sendType == 11 && !form.id">
           <el-select v-model="form.courseId"placeholder="请选择课程" style=" margin-right: 10px;" size="mini" filterable>
             <el-option
@@ -391,9 +403,12 @@ import {
 } from "@/api/qw/sopTemp";
 import { getCompanyList, listCompany } from '@/api/company/company'
 import {courseList, getRoles} from "@/api/qw/sop";
-
+import {treeselect} from "../../../api/company/companyDept";
+import Treeselect from "@riophae/vue-treeselect";
+import "@riophae/vue-treeselect/dist/vue-treeselect.css";
 export default {
   name: "SopTemp",
+  components: {Treeselect},
   data() {
     return {
       // 遮罩层
@@ -407,6 +422,7 @@ export default {
       roles: [],
       //选中的公司
       companys: [],
+      deptOptions: [],
       // 非单个禁用
       single: true,
       // 非多个禁用
@@ -486,6 +502,9 @@ export default {
         project:[{
           required: true, message: '所属项目不能为空', trigger: 'blur'
         }],
+        createByDept:[
+          { required: true, message: '归属部门不能为空', trigger: 'blur' }
+        ]
       },
       contentRules: {
         time: [{required: true, message: '时间不能为空', trigger: 'blur'}],
@@ -494,6 +513,9 @@ export default {
   },
   created() {
     this.getList();
+
+    this.getDeptTreeSelect();
+
     getRoles().then(res => {
       this.roles = res.data;
     })
@@ -536,6 +558,12 @@ export default {
       this.handleCompanyQuery();
     },
 
+    getDeptTreeSelect() {
+      treeselect().then((response) => {
+        this.deptOptions = response.data;
+      });
+    },
+
     /** 查询企业列表 */
     getCompanyList() {
       this.companysloading = true;

+ 161 - 51
src/views/qw/sopTemp/updateSopTemp.vue

@@ -109,7 +109,7 @@
                                     </el-time-picker>
                                   </el-form-item>
                                 </div>
-                                <el-form-item label="官方群发" v-if="contentIndex==0 && content.type==2">
+                                <el-form-item label="官方群发" v-if="contentIndex==0 && content.type==2  && form.sendType != 4">
                                   <el-switch
                                     v-model="content.isOfficial"
                                     active-color="#13ce66"
@@ -250,7 +250,7 @@
                                      v-if="content.type != 4 && content.type != 5">
                                   <el-row style="padding-bottom: 20px">
                                     <el-col :span="22">
-                                      <el-form v-if="form.sendType != 4" :model="setList" label-width="70px">
+                                      <el-form  :model="setList" label-width="70px">
                                         <el-form-item label="内容类别" style="margin: 2%">
                                           <div v-if="form.sendType == 1 ">
                                             <el-radio-group v-model="setList.contentType" :disabled="formType == 3 || !roles.includes('edit_sop_temp_content')">
@@ -276,6 +276,18 @@
                                               </el-radio>
                                             </el-radio-group>
                                           </div>
+                                          <div v-if=" form.sendType == 4">
+                                            <el-radio-group v-model="setList.contentType"
+                                                            :disabled="formType == 3"
+                                                            @change="handleContentTypeChange(content,index,contentIndex,setIndex)">
+                                              <el-radio
+                                                :key="item.dictValue"
+                                                :label="item.dictValue"
+                                                :disabled="((content.type!=2 || form.sendType == 4) && (item.dictValue === '8' || item.dictValue === '9') || (content.isOfficial==1 && ['5','6','7','8','9'].includes(item.dictValue)))"
+                                                v-for="item in sysQwSopAiContentType">{{ item.dictLabel }}
+                                              </el-radio>
+                                            </el-radio-group>
+                                          </div>
                                           <div v-if="form.sendType == 11">
                                             <el-radio-group v-model="setList.contentType"
                                                             :disabled="formType == 3 || (form.sendType == 11 && contentIndex != 0 && setIndex == 0)"
@@ -285,7 +297,7 @@
                                                 :label="item.dictValue"
                                                 :disabled="(content.type!=2 && item.dictValue === '9') || (content.isOfficial==1 && ['5','6','7','8','9'].includes(item.dictValue))"
                                                 v-for="item in sysQwSopAiContentType"
-                                                v-if="setIndex == 0 ? courseTypeList.includes(item.dictValue) : !courseTypeList.includes(item.dictValue)">{{ item.dictLabel }}
+                                                v-if="courseTypeList.includes(item.dictValue)">{{ item.dictLabel }}
                                               </el-radio>
                                             </el-radio-group>
                                           </div>
@@ -308,6 +320,14 @@
                                           >
                                             {{ setList.isSalesCallAdded ? '移除#销售称呼#' : '添加#销售称呼#' }}
                                           </el-link>
+                                          <el-link
+                                            v-if="setList.contentType == 1 && roles.includes('edit_sop_temp_content')"
+                                            type="primary"
+                                            @click="toggleUserNameCall(index, contentIndex, setIndex)"
+                                            style="margin-top: 10px; margin-left: 20px"
+                                          >
+                                            {{ setList.isUserNameCallAdded ? '移除#客户称呼#' : '添加#客户称呼#' }}
+                                          </el-link>
 
                                           <ImageUpload :disabled="formType == 3 || !roles.includes('edit_sop_temp_content')" v-if="setList.contentType == 2 "
                                                        v-model="setList.imgUrl"
@@ -476,26 +496,26 @@
                                             </el-card>
                                           </div>
                                         </el-form-item>
-                                        <el-form-item label="添加短链"
-                                                      v-if="content.type == 2 && setList.contentType == 1  ">
-                                          <el-tooltip content="请先根据课程选定课程小节之后再添加" effect="dark"
-                                                      :disabled="!content.videoId">
-                                            <el-switch
-                                              @change="updateHtml"
-                                              v-model="setList.isBindUrl"
-                                              :disabled="!content.videoId && formType == 3 && !roles.includes('edit_sop_temp_content')"
-                                              active-color="#13ce66"
-                                              inactive-color="#DCDFE6"
-                                              active-value="1"
-                                              inactive-value="2">
-                                            </el-switch>
-                                          </el-tooltip>
-
-                                          <span v-if="setList.isBindUrl == '1'"
-                                                style="margin-left: 10px; color: #13ce66">添加URL</span>
-                                          <span v-if="setList.isBindUrl == '2'"
-                                                style="margin-left: 10px; color: #b1b4ba">不加URL</span>
-                                        </el-form-item>
+<!--                                        <el-form-item label="添加短链"-->
+<!--                                                      v-if="content.type == 2 && setList.contentType == 1  ">-->
+<!--                                          <el-tooltip content="请先根据课程选定课程小节之后再添加" effect="dark"-->
+<!--                                                      :disabled="!content.videoId">-->
+<!--                                            <el-switch-->
+<!--                                              @change="updateHtml"-->
+<!--                                              v-model="setList.isBindUrl"-->
+<!--                                              :disabled="!content.videoId && formType == 3 && !roles.includes('edit_sop_temp_content')"-->
+<!--                                              active-color="#13ce66"-->
+<!--                                              inactive-color="#DCDFE6"-->
+<!--                                              active-value="1"-->
+<!--                                              inactive-value="2">-->
+<!--                                            </el-switch>-->
+<!--                                          </el-tooltip>-->
+
+<!--                                          <span v-if="setList.isBindUrl == '1'"-->
+<!--                                                style="margin-left: 10px; color: #13ce66">添加URL</span>-->
+<!--                                          <span v-if="setList.isBindUrl == '2'"-->
+<!--                                                style="margin-left: 10px; color: #b1b4ba">不加URL</span>-->
+<!--                                        </el-form-item>-->
                                         <el-form-item label="课节过期时间"
                                                       v-if="content.type == 2 && (setList.isBindUrl == '1' || setList.contentType==4) && setList.contentType != 2  && setList.contentType != 5  && setList.contentType != 6 && setList.contentType != 8 && setList.contentType != 9 && setList.contentType != 10  ">
                                           <el-row>
@@ -518,30 +538,30 @@
                                             v-model="setList.intervalTime"
                                             :min="1"
                                             :max="1440"
-                                            style="width:100px;margin-top: 10px;"
+                                            style="width:150px;margin-top: 10px;"
                                           >
                                           </el-input-number>
                                           <span class="tip">单位:分钟,最大1440分钟(24小时)</span>
                                         </el-form-item>
-                                        <el-form-item label="内容" style="margin: 2%">
-                                          <el-input
-                                            v-model="setList.value"
-                                            type="textarea"
-                                            :rows="3"
-                                            placeholder="内容"
-                                            style="width: 90%;margin-top: 10px;"/>
-                                        </el-form-item>
-                                        <el-form-item label="交流状态" style="margin: 2%">
-                                          <el-select v-model="setList.talkType" placeholder="更改交流状态" size="mini"
-                                                     style=" margin-right: 10px;" clearable>
-                                            <el-option label="非首次交流" value="非首次交流"></el-option>
-                                            <el-option label="首次交流1" value="首次交流1"></el-option>
-                                            <el-option label="首次交流2" value="首次交流2"></el-option>
-                                            <el-option label="交流状态1" value="交流状态1"></el-option>
-                                            <el-option label="交流状态2" value="交流状态2"></el-option>
-                                            <el-option label="交流状态3" value="交流状态3"></el-option>
-                                          </el-select>
-                                        </el-form-item>
+<!--                                        <el-form-item label="内容" style="margin: 2%">-->
+<!--                                          <el-input-->
+<!--                                            v-model="setList.value"-->
+<!--                                            type="textarea"-->
+<!--                                            :rows="3"-->
+<!--                                            placeholder="内容"-->
+<!--                                            style="width: 90%;margin-top: 10px;"/>-->
+<!--                                        </el-form-item>-->
+<!--                                        <el-form-item label="交流状态" style="margin: 2%">-->
+<!--                                          <el-select v-model="setList.talkType" placeholder="更改交流状态" size="mini"-->
+<!--                                                     style=" margin-right: 10px;" clearable>-->
+<!--                                            <el-option label="非首次交流" value="非首次交流"></el-option>-->
+<!--                                            <el-option label="首次交流1" value="首次交流1"></el-option>-->
+<!--                                            <el-option label="首次交流2" value="首次交流2"></el-option>-->
+<!--                                            <el-option label="交流状态1" value="交流状态1"></el-option>-->
+<!--                                            <el-option label="交流状态2" value="交流状态2"></el-option>-->
+<!--                                            <el-option label="交流状态3" value="交流状态3"></el-option>-->
+<!--                                          </el-select>-->
+<!--                                        </el-form-item>-->
                                       </el-form>
                                     </el-col>
                                     <el-col :span="1" :offset="1">
@@ -1159,11 +1179,57 @@ export default {
           for (let content of day.content) {
             for (let set of content.setting) {
               if (!set.intervalTime) {
-                this.$message.error("客服数量不能为空");
+                this.$message.error("客服时间不能为空");
+                return false;
+              }
+              if (set.contentType == 1 && (set.value == null || set.value == "")) {
+                this.$message.error("内容不能为空")
+                return false;
+              }
+              if (set.contentType == 2 && (set.imgUrl == null || set.imgUrl == "")) {
+                this.$message.error("图片不能为空")
+                return false;
+              }
+              if ((set.contentType == 3 || set.contentType == 9) && (set.linkTitle == null || set.linkTitle == "")) {
+                this.$message.error("链接标题不能为空")
                 return false;
               }
-              if (!set.value) {
-                this.$message.error("内容不能为空");
+              if ((set.contentType == 3 || set.contentType == 9) && (set.linkDescribe == null || set.linkDescribe == "")) {
+                this.$message.error("链接描述不能为空")
+                return false;
+              }
+              if ((set.contentType == 3 || set.contentType == 9) && (set.linkImageUrl == null || set.linkImageUrl == "")) {
+                this.$message.error("链接图片不能为空")
+                return false;
+              }
+              if (set.contentType == 3 && set.type == 1 && (set.linkUrl == null || set.linkUrl == "")) {
+                this.$message.error("链接地址不能为空")
+                return false;
+              }
+
+              if (set.contentType == 4 && (set.miniprogramTitle == null || set.miniprogramTitle == "")) {
+                this.$message.error("小程序消息标题不能为空")
+                return false;
+              }
+              if (set.contentType == 4 && (set.miniprogramPicUrl == null || set.miniprogramPicUrl == "")) {
+                this.$message.error("小程序封面地址不能为空")
+                return false;
+              }
+
+              if (set.contentType == 5 && (set.fileUrl == null || set.fileUrl == "")) {
+                this.$message.error("文件不能为空")
+                return false;
+              }
+              if (set.contentType == 6 && (set.videoUrl == null || set.videoUrl == "")) {
+                this.$message.error("视频不能为空")
+                return false;
+              }
+              if (set.contentType == 7 && (set.value == null || set.value == "")) {
+                this.$message.error("语音文本不能为空")
+                return false;
+              }
+              if (set.contentType == 8 && (set.url == null || set.url == "")) {
+                this.$message.error("视频号信息不能为空")
                 return false;
               }
             }
@@ -1206,11 +1272,19 @@ export default {
           contentType: '1',
           value: '',
         };
-        if (this.form.sendType == 4) {
-          newSetting.intervalTime = 5;
+        if (this.form.sendType === 4) {
+          for (let i = 0; i < content.length; i++) {
+            if (content[i].setting.length < 5) {
+              // 将新设置项添加到 content.setting 数组中
+              content[contentIndex].setting.push(newSetting);
+            } else {
+              return this.$message.error("因为要求限制,新课对话模板一条消息只能设置最多~5个内容!!")
+            }
+          }
+        } else {
+          // 将新设置项添加到 content.setting 数组中
+          content[contentIndex].setting.push(newSetting);
         }
-        // 将新设置项添加到 content.setting 数组中
-        content[contentIndex].setting.push(newSetting);
       }
 
 
@@ -1258,6 +1332,7 @@ export default {
     addSetting() {
       let content = [{type: this.defaultContentType, contentType: '1', setting: [{contentType: '1', value: "",}]}];
       if (this.form.sendType == 4) {
+        content = [{type: 2, contentType: '1', setting: [{contentType: '1', value: "",}]}];
         content[0].setting[0].intervalTime = 5;
       }
       this.setting.push({
@@ -1416,6 +1491,38 @@ export default {
         textarea.setSelectionRange(newCursorPos, newCursorPos);
       });
     },
+    toggleUserNameCall(itemIndex, contentIndex, setIndex) {
+      // 获取目标对象
+      const setItem = this.setting[itemIndex].content[contentIndex].setting[setIndex];
+      const salesCall = '#客户称呼#';
+      const refKey = `textarea-${itemIndex}-${contentIndex}-${setIndex}`;
+      const textarea = this.$refs[refKey][0]?.$refs?.textarea;
+
+      if (!textarea) return;
+
+      // 获取光标位置
+      const cursorPosition = textarea.selectionStart;
+
+      if (setItem.isUserNameCallAdded) {
+        // 移除所有标签
+        setItem.value = setItem.value.replace(new RegExp(salesCall, 'g'), '');
+      } else {
+        // 插入到光标位置
+        setItem.value =
+          setItem.value.slice(0, cursorPosition) +
+          salesCall +
+          setItem.value.slice(cursorPosition);
+      }
+
+      // 切换状态
+      setItem.isUserNameCallAdded = !setItem.isUserNameCallAdded;
+
+      // 保持光标位置
+      this.$nextTick(() => {
+        const newCursorPos = cursorPosition + (setItem.isUserNameCallAdded ? salesCall.length : 0);
+        textarea.setSelectionRange(newCursorPos, newCursorPos);
+      });
+    },
     handleKeydown(event, itemIndex, contentIndex, setIndex) {
 
       const setItem = this.setting[itemIndex].content[contentIndex].setting[setIndex];
@@ -1513,7 +1620,7 @@ export default {
       for (let i = 0; i < content.setting.length; i++) {
         //课程消息-文本内容
         if (content.setting[i].contentType == 1 && content.type == 2) {
-          this.$set(content.setting[i], 'isBindUrl', '1');
+          this.$set(content.setting[i], 'isBindUrl', '2');
         }
         //如果是链接的才上
         if (selectedVideo && content.type == 2 && content.videoId != null) {
@@ -1689,6 +1796,9 @@ export default {
                     if (setList && !Object.hasOwn(setList, 'isSalesCallAdded')) {
                       this.$set(setList, 'isSalesCallAdded', false);
                     }
+                    if (setList && !Object.hasOwn(setList, 'isUserNameCallAdded')) {
+                      this.$set(setList, 'isUserNameCallAdded', false);
+                    }
                   });
                 }
               });

+ 2 - 1
src/views/qw/user/qwUserList.vue

@@ -102,7 +102,7 @@ export default {
   },
   methods: {
 
-    getDetails(corpId,type,sendType){
+    getDetails(corpId,type,sendType,isRemark){
       this.type=type;
       this.sendType=sendType;
       if (type!=null&&sendType!=null){
@@ -110,6 +110,7 @@ export default {
         this.queryParams.sendType=sendType;
       }
       this.queryParams.corpId=corpId;
+      this.queryParams.isRemark=isRemark;
       this.getList();
     },
     /** 查询企微用户列表 */