فهرست منبع

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	src/api/company/company.js
吴树波 2 ماه پیش
والد
کامیت
21eb2e23fd

+ 7 - 1
src/api/company/company.js

@@ -1,6 +1,12 @@
 import request from '@/utils/request'
 
-
+export function listCompany(query) {
+  return request({
+    url: '/company/company/list',
+    method: 'get',
+    params: query
+  })
+}
 
 export function getCompanyInfo() {
   return request({

+ 19 - 0
src/api/company/companyUser.js

@@ -209,3 +209,22 @@ export function generateSubDomain() {
     method: 'get'
   })
 }
+
+export function getCitysAreaList(query) {
+  return request({
+    url: '/company/user/getCitysAreaList',
+    method: 'get',
+    params: query
+  })
+}
+
+/**
+ * 批量修改 销售的所属区域(临时的)
+ */
+export function updateCompanyUserAreaList(data) {
+  return request({
+    url: '/company/user/updateCompanyUserAreaList',
+    method: 'post',
+    data: data
+  })
+}

+ 53 - 0
src/api/users/approval.js

@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 查询客户转移审批列表
+export function listApproval(query) {
+  return request({
+    url: '/system/approval/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询客户转移审批详细
+export function getApproval(id) {
+  return request({
+    url: '/system/approval/' + id,
+    method: 'get'
+  })
+}
+
+// 新增客户转移审批
+export function addApproval(data) {
+  return request({
+    url: '/system/approval',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改客户转移审批
+export function updateApproval(data) {
+  return request({
+    url: '/system/approval',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除客户转移审批
+export function delApproval(id) {
+  return request({
+    url: '/system/approval/' + id,
+    method: 'delete'
+  })
+}
+
+// 导出客户转移审批
+export function exportApproval(query) {
+  return request({
+    url: '/system/approval/export',
+    method: 'get',
+    params: query
+  })
+}

+ 19 - 0
src/api/users/user.js

@@ -52,3 +52,22 @@ export function updateUser(data) {
     data: data
   })
 }
+
+// 列出当前公司的客户
+export function listUser(data) {
+  return request({
+    url: '/fsuser/user/list',
+    method: 'get',
+    params: data
+  })
+}
+
+// 转移客户
+export function transferUser(data) {
+  return request({
+    url: '/fsuser/user/transfer',
+    method: 'post',
+    data: data
+  })
+}
+

+ 66 - 0
src/views/company/companyConfig/index.vue

@@ -82,6 +82,53 @@
               <el-button type="primary" @click="onSubmit2">提交</el-button>
             </div>
         </el-tab-pane>
+        <el-tab-pane label="红包商户配置" name="redPacketConfig" >
+          <el-form ref="redPacketConfig" :model="redPacketConfig"  label-width="150px">
+            <el-form-item   label="红包接口类型" prop="isNew">
+              <el-radio-group v-model="redPacketConfig.isNew">
+                <el-radio label="0">商家转账到零钱(旧)</el-radio>
+                <el-radio label="1">商家转账(新)</el-radio>
+              </el-radio-group>
+            </el-form-item>
+            <el-form-item   label="公众号appid" prop="appId">
+              <el-input   v-model="redPacketConfig.appId"  label="请输入appId"></el-input>
+            </el-form-item>
+            <el-form-item   label="小程序appid" prop="appId">
+              <el-input   v-model="redPacketConfig.miniappId"  label="请输入appId"></el-input>
+            </el-form-item>
+            <el-form-item   label="商户号" prop="mchId">
+              <el-input   v-model="redPacketConfig.mchId"  label="请输入mchId"></el-input>
+            </el-form-item>
+            <el-form-item   label="商户密钥" prop="mchKey">
+              <el-input   v-model="redPacketConfig.mchKey"  label="mchKey"></el-input>
+            </el-form-item>
+            <el-form-item   label="p12证书路径" prop="keyPath">
+              <el-input   v-model="redPacketConfig.keyPath"  label="请输入keyPath"></el-input>
+            </el-form-item>
+            <el-form-item   label="apiV3密钥" prop="apiV3Key">
+              <el-input   v-model="redPacketConfig.apiV3Key"  label="请输入apiV3Key"></el-input>
+            </el-form-item>
+            <el-form-item   label="公钥ID" prop="publicKeyId">
+              <el-input   v-model="redPacketConfig.publicKeyId"  label="请输入公钥ID"></el-input>
+            </el-form-item>
+            <el-form-item   label="公钥证书" prop="publicKeyPath">
+              <el-input   v-model="redPacketConfig.publicKeyPath"  label="请输入publicKeyPath"></el-input>
+            </el-form-item>
+            <el-form-item   label="key路径" prop="privateKeyPath">
+              <el-input   v-model="redPacketConfig.privateKeyPath"  label="请输入"></el-input>
+            </el-form-item>
+            <el-form-item   label="cert路径" prop="privateCertPath">
+              <el-input   v-model="redPacketConfig.privateCertPath"  label="请输入"></el-input>
+            </el-form-item>
+            <el-form-item   label="回调地址" prop="notifyUrl">
+              <el-input   v-model="redPacketConfig.notifyUrl"  label="请输入"></el-input>
+            </el-form-item>
+
+            <div style="float:right;margin-right:20px">
+              <el-button type="primary" @click="onSubmit4">提交</el-button>
+            </div>
+          </el-form>
+        </el-tab-pane>
       </el-tabs>
 
       <el-dialog :title="customerExt.title" :visible.sync="customerExt.open" width="500px" append-to-body>
@@ -151,6 +198,10 @@ export default {
       qwConfig:{
 
       },
+      redPacketConfig:{},
+
+      redPacketConfigForm:{},
+
       qwConfigForm:{},
       qwkfConfigForm:{},
     };
@@ -160,6 +211,7 @@ export default {
     this.getConfigKey("sys:config");
     this.getConfigKey("qw:config");
     this.getConfigKey("customer:config");
+    this.getConfigKey("redPacket:config");
     this.getDicts("sys_company_status").then((response) => {
       this.statusOptions = response.data;
     });
@@ -258,6 +310,11 @@ export default {
                 if(response.data.configValue!=null){
                     this.customerConfig=JSON.parse(response.data.configValue);
                 }
+            }else if(key=="redPacket:config"){
+              this.redPacketConfigForm=response.data;
+              if(response.data.configValue!=null){
+                this.redPacketConfig=JSON.parse(response.data.configValue);
+              }
             }
 
         });
@@ -289,6 +346,15 @@ export default {
         }
       });
     },
+    onSubmit4() {
+      this.redPacketConfigForm.configValue=JSON.stringify(this.redPacketConfig);
+      updateConfig(this.redPacketConfigForm).then(response => {
+        if (response.code === 200) {
+          this.msgSuccess("修改成功");
+          this.getConfigKey("redPacket:config");
+        }
+      });
+    },
   }
 };
 </script>

+ 82 - 6
src/views/company/companyUser/index.vue

@@ -63,15 +63,15 @@
              v-hasPermi="['qw:user:sync']"
            >同步企微员工和部门</el-button>
           </el-col>
-          <!-- <el-col :span="1.5">
+          <el-col :span="1.5">
             <el-button
               type="primary"
               plain
               size="mini"
-              @click="qwSyncDept"
-              v-hasPermi="['qw:qwDept:list']"
-            >同步企微部门</el-button>
-          </el-col> -->
+              :disabled="multiple"
+              @click="handerCompanyUserAreaList"
+            >批量设置销售所属区域(原有的暂用)</el-button>
+          </el-col>
           <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
         </el-row>
 
@@ -305,6 +305,21 @@
               <el-button type="primary" @click="generateSubDomain" style="margin-left: 10px;">生成</el-button>
             </el-form-item>
           </el-col>
+
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="销售区域" prop="addressId">
+              <el-select v-model="form.addressId"  filterable placeholder="请选择所属销售的区域" style="width: 200px;">
+                <el-option
+                  v-for="item in citysAreaList"
+                  :key="item.cityId"
+                  :label="item.cityName"
+                  :value="item.cityId"
+                ></el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
         </el-row>
       </el-form>
       <div slot="footer" class="dialog-footer">
@@ -316,11 +331,37 @@
       <selectUser ref="selectUser" @bindQwUser="bindQwUser"></selectUser>
     </el-dialog>
     <gw-query-list-dialog :visible="gwSearchOpen"/>
+    <el-dialog :title="companyUserArea.title" :visible.sync="companyUserArea.open" width="300px" append-to-body>
+      <el-select v-model="addressId"  filterable placeholder="请选择所属销售的区域" style="width: 200px;">
+        <el-option
+          v-for="item in citysAreaList"
+          :key="item.cityId"
+          :label="item.cityName"
+          :value="item.cityId"
+        ></el-option>
+      </el-select>
+      <div slot="footer" style="text-align: center;">
+        <el-button type="primary" @click="submitFormArea(addressId)">确 定</el-button>
+        <el-button @click="cancelArea">取 消</el-button>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
 <script>
-import {qwList, delUser,addUser,getUser,updateUser,exportUser, resetUserPwd,changeUserStatus,importTemplate,generateSubDomain} from "@/api/company/companyUser";
+import {
+  qwList,
+  delUser,
+  addUser,
+  getUser,
+  updateUser,
+  exportUser,
+  resetUserPwd,
+  changeUserStatus,
+  importTemplate,
+  generateSubDomain,
+  getCitysAreaList, updateCompanyUserAreaList
+} from '@/api/company/companyUser'
 import { getToken } from "@/utils/auth";
 import { treeselect } from "@/api/company/companyDept";
 import Treeselect from "@riophae/vue-treeselect";
@@ -336,6 +377,13 @@ export default {
   components: {GwQueryListDialog, Treeselect ,selectUser},
   data() {
     return {
+      companyUserArea:{
+        open:false,
+        title:"分配区域",
+      },
+      //选择的区域
+      addressId:null,
+      citysAreaList:[],
       // 遮罩层
       loading: false,
       qwUserList:[],
@@ -440,6 +488,9 @@ export default {
         password: [
           { required: true, message: "员工密码不能为空", trigger: "blur" },
         ],
+        addressId: [
+          { required: true, message: "销售所属区域不能为空", trigger: "blur" },
+        ],
         idCard: [
           { required: true, message: "身份证号不能为空", trigger: "blur" },
         ],
@@ -476,6 +527,9 @@ export default {
     },
   },
   created() {
+    getCitysAreaList().then(res=>{
+      this.citysAreaList=res.data;
+    })
     this.getList();
     this.getTreeselect();
     this.getDicts("sys_normal_disable").then((response) => {
@@ -496,6 +550,28 @@ export default {
     });
   },
   methods: {
+    submitFormArea(address){
+      const uIds = this.ids;
+
+      if (address == null){
+        this.$message.error("请选择地区");
+        return;
+      }
+      updateCompanyUserAreaList({userIds:uIds,addressId:address}).then(res=>{
+        this.companyUserArea.open=false;
+        this.getList();
+        this.msgSuccess("操作成功");
+
+      })
+
+    },
+    cancelArea(){
+      this.companyUserArea.open=false;
+      this.addressId=null;
+    },
+    handerCompanyUserAreaList(){
+      this.companyUserArea.open=true;
+    },
     // 绑定个微
     gwBind(row){
       this.gwOpen = true

+ 149 - 78
src/views/components/course/userCourseVideoDetails.vue

@@ -16,9 +16,9 @@
         </el-descriptions-item>
         <el-descriptions-item label="封面图" >
           <el-popover v-if="item!=null"
-            placement="right"
-            title=""
-            trigger="hover">
+                      placement="right"
+                      title=""
+                      trigger="hover">
             <img slot="reference" :src="item.thumbnail" width="50px">
             <img :src="item.thumbnail" style="max-width: 150px;">
           </el-popover>
@@ -27,31 +27,47 @@
           <span v-if="item!=null">{{item.description}}</span>
         </el-descriptions-item>
         <el-descriptions-item label="时长" >
-            <span v-if="item!=null">{{formatDuration(item.duration)}}</span>
+          <span v-if="item!=null">{{formatDuration(item.duration)}}</span>
         </el-descriptions-item>
         <el-descriptions-item label="课程排序" >
-            <span v-if="item!=null">{{item.courseSort}}</span>
+          <span v-if="item!=null">{{item.courseSort}}</span>
         </el-descriptions-item>
 
         <el-descriptions-item label="创建时间" >
-            <span v-if="item!=null">{{item.createTime}}</span>
+          <span v-if="item!=null">{{item.createTime}}</span>
         </el-descriptions-item>
         <el-descriptions-item label="审核时间" >
-            <span v-if="item!=null">{{item.auditTime}}</span>
+          <span v-if="item!=null">{{item.auditTime}}</span>
         </el-descriptions-item>
+
+        <el-descriptions-item label="默认红包" >
+          <span v-if="item!=null">{{item.redPacketMoney}}</span>
+        </el-descriptions-item>
+
         <el-descriptions-item label="备注" >
-            <span v-if="item!=null">{{item.remark}}</span>
+          <span v-if="item!=null">{{item.remark}}</span>
         </el-descriptions-item>
       </el-descriptions>
     </div>
-    <div class="contentx" v-if="item">
+    <div class="contentx" v-if="item" @contextmenu.prevent>
       <div class="desct">
         视频内容
       </div>
-      <div style="display: flex; justify-content: center; align-items: center;">
-          <div style="display: grid; place-items: center;" v-if="item.videoUrl!=null">
-             <video  :src="item.videoUrl" controls style="max-width: 400px; max-height: 400px;"></video>
+      <div style="display: flex; justify-content: center; align-items: center; position: relative;">
+        <!-- 父容器设置为 relative,水印根据此容器定位 -->
+        <div style="display: grid; place-items: center; position: relative;" v-if="item.videoUrl != null">
+          <video ref="video" :src="item.videoUrl" controls disablepictureinpicture controlsList="nodownload nofullscreen nopictureinpicture"
+                 style="max-width: 800px; max-height: 600px;" @loadedmetadata="onVideoLoaded">
+          </video>
+
+          <!-- 动态生成的20个水印,按照4排每排5个 -->
+          <div class="watermarks" :style="watermarkGridStyle">
+            <div class="watermark" v-for="n in 20" :key="n"
+                 :style="getWatermarkPosition(n)">
+              {{ user.nickName }} : {{ user.userId }}
+            </div>
           </div>
+        </div>
       </div>
     </div>
     <div class="contentx" v-if="item">
@@ -85,82 +101,137 @@
 
 <script>
 import {getUserCourseVideo} from "@/api/course/userCourseVideo";
-  export default {
-    name: "userCourseVideoDetails",
-    data() {
+export default {
+  name: "userCourseVideoDetails",
+  data() {
+    return {
+      videoWidth: 0,  // 视频宽度
+      videoHeight: 0, // 视频高度
+      watermarkCount: 20, // 水印数量,固定为20个
+      watermarkSize: 20, // 单个水印的大小,固定为40px
+      user: this.$store.state.user.user,  // 从 Vuex 获取当前用户
+      typeOptions:[],
+      packageTypeOptions: [],
+      diseaseTypeOptions: [],
+      packageSubTypeOptions:[],
+      item:null,
+      packageItem:null,
+      productJson:[],
+      courseIdOptions: [],
+      // 视频状态 1:草稿,2:待审核,3:发布字典
+      statusOptions: [],
+      // 是否展示字典
+      orOptions: [],
+      // 来源 1 用户 2 后台字典
+      sourceOptions: [],
+      // 删除标志字典
+      isDelOptions: [],
+      isAuditOptions:[],
+      specShowOptions:[]
+    }
+  },
+  created() {
+    this.getDicts("sys_course_temp_type").then(response => {
+      this.typeOptions = response.data;
+    });
+  },
+  computed: {
+    // 动态计算水印容器的样式
+    watermarkGridStyle() {
       return {
-        typeOptions:[],
-        packageTypeOptions: [],
-        diseaseTypeOptions: [],
-        packageSubTypeOptions:[],
-        item:null,
-        packageItem:null,
-        productJson:[],
-        courseIdOptions: [],
-        // 视频状态 1:草稿,2:待审核,3:发布字典
-        statusOptions: [],
-        // 是否展示字典
-        orOptions: [],
-        // 来源 1 用户 2 后台字典
-        sourceOptions: [],
-        // 删除标志字典
-        isDelOptions: [],
-        isAuditOptions:[],
-        specShowOptions:[]
-      }
+        position: 'absolute',
+        top: '0',
+        left: '0',
+        width: '100%',
+        height: '100%',
+        zIndex: 1,
+        pointerEvents: 'none', // 让水印不会干扰视频控制
+        display: 'grid',
+        gridTemplateColumns: `repeat(5, 1fr)`,  // 每排5个水印
+        gridTemplateRows: `repeat(4, auto)`,   // 四排水印
+        gap: `${this.gap}px`,  // 水印之间的间隙
+      };
+    }
+  },
+  methods: {
+    // 视频加载完成后获取其尺寸
+    onVideoLoaded() {
+      const video = this.$refs.video;
+      this.videoWidth = video.videoWidth;
+      this.videoHeight = video.videoHeight;
+
+      // // 根据视频尺寸计算水印的数量和大小
+      // this.watermarkSize = Math.floor(this.videoWidth / 30); // 水印大小为视频宽度的 1/10
     },
-    created() {
-      this.getDicts("sys_course_temp_type").then(response => {
-        this.typeOptions = response.data;
-      });
+    // 计算每个水印的位置,按照四排5列排列
+    getWatermarkPosition(n) {
+      return {
+        position: 'relative',
+        color: 'rgba(255, 255, 255, 0.3)', // 提高透明度,确保不干扰视频内容
+        fontSize: `${this.watermarkSize}px`, // 固定水印的字体大小
+        textAlign: 'center',
+        lineHeight: `${this.watermarkSize}px`, // 水平方向和垂直方向居中
+        transform: 'rotate(-30deg)',
+      };
     },
-    methods: {
-      formatDuration(seconds) {
-        if (seconds === null || seconds === undefined) {
-          return '未上传视频'; // 或者您可以根据具体需求返回其他默认值
-        }
-        const hours = Math.floor(seconds / 3600);
-        const minutes = Math.floor((seconds % 3600) / 60);
-        const remainingSeconds = seconds % 60;
+    formatDuration(seconds) {
+      if (seconds === null || seconds === undefined) {
+        return '未上传视频'; // 或者您可以根据具体需求返回其他默认值
+      }
+      const hours = Math.floor(seconds / 3600);
+      const minutes = Math.floor((seconds % 3600) / 60);
+      const remainingSeconds = seconds % 60;
 
-        const formattedHours = hours > 0 ? hours.toString() + ':' : '';
-        const formattedMinutes = minutes.toString().padStart(2, '0');
-        const formattedSeconds = remainingSeconds.toString().padStart(2, '0');
+      const formattedHours = hours > 0 ? hours.toString() + ':' : '';
+      const formattedMinutes = minutes.toString().padStart(2, '0');
+      const formattedSeconds = remainingSeconds.toString().padStart(2, '0');
 
-        return `${formattedHours}${formattedMinutes}:${formattedSeconds}`;
-      },
-      getDetails(videoId) {
-        console.log(videoId);
-        this.item=null;
-        getUserCourseVideo(videoId).then(response => {
-          this.item = response.data;
-        });
-      },
-    }
+      return `${formattedHours}${formattedMinutes}:${formattedSeconds}`;
+    },
+    getDetails(videoId) {
+      console.log(videoId);
+      this.item=null;
+      getUserCourseVideo(videoId).then(response => {
+        this.item = response.data;
+      });
+    },
   }
+}
 </script>
 
 <style>
-  .contentx{
-      height: 100%;
-      background-color: #fff;
-      padding: 0px 20px 20px;
+/* 水印样式 */
+.watermark, .watermark-bottom-right {
+  position: absolute;
+  color: rgba(255, 255, 255, 0.3);  /* 提高透明度,确保水印不干扰视频内容 */
+  font-size: 10vw;  /* 动态字体大小,确保水印不会过大 */
+  transform: rotate(-30deg);
+  z-index: 1;
+  pointer-events: none;  /* 不干扰视频的交互 */
+}
 
+/* 设置视频和水印层的绝对定位,使水印显示在视频上方 */
+.watermark-full {
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  text-align: center;
+  line-height: 100%;  /* 水平和垂直居中 */
+}
+.contentx{
+  height: 100%;
+  background-color: #fff;
+  padding: 0px 20px 20px;
 
-      margin: 20px;
-  }
-  .el-descriptions-item__label.is-bordered-label{
-    font-weight: normal;
-  }
-  .el-descriptions-item__content {
-    max-width: 150px;
-    min-width: 100px;
-  }
-  .desct{
-      padding-top: 20px;
-      padding-bottom: 20px;
-      color: #524b4a;
-      font-weight: bold;
-    }
+
+  margin: 20px;
+}
+.desct{
+  padding-top: 20px;
+  padding-bottom: 20px;
+  color: #524b4a;
+  font-weight: bold;
+}
 
 </style>

+ 9 - 0
src/views/qw/sopUserLogsInfo/sopUserLogsInfoDetails.vue

@@ -179,6 +179,12 @@
               />
             </el-select>
           </el-form-item>
+          <el-form-item label="是否只发送注册用户" prop="isRegister" >
+                  <el-radio-group v-model="msgForm.isRegister">
+                     <el-radio v-model="msgForm.isRegister" :label="1">是</el-radio>
+                     <el-radio v-model="msgForm.isRegister" :label="2">否</el-radio>
+                  </el-radio-group>
+          </el-form-item>
           <el-form-item label="规则" prop="setting"  >
             <div v-for="(item, index) in setting" :key="index" style="background-color: #fdfdfd; border: 1px solid #e6e6e6; margin-bottom: 20px;">
               <el-row>
@@ -416,6 +422,7 @@ export default {
         ids:null,
         sopId: null,
         startTime: null,
+        isRegister:2
       },
       // 表单校验
       rules: {},
@@ -758,6 +765,8 @@ export default {
               courseId:null,
               courseType:null,
               setting:null,
+              isRegister:2,
+
             }
             this.getList();
           }).finally(()=>{

+ 178 - 66
src/views/qw/user/index.vue

@@ -66,8 +66,8 @@
       <el-table-column label="企微成员ID" align="center" prop="id" />
       <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="configId" />
+      <el-table-column label="员工称呼" align="center" prop="welcomeText" />
+      <!--      <el-table-column label="所属部门" align="center" prop="departmentName" />-->
       <el-table-column label="联系我二维码" align="center" prop="contactWay" >
         <template slot-scope="scope">
           <el-image
@@ -80,19 +80,19 @@
       </el-table-column>
       <el-table-column label="绑定的AI客服" align="center" prop="fastGptRoleName" />
       <el-table-column label="授权码" align="center" prop="appKey" />
-      <el-table-column label="企微状态" align="center" prop="loginStatus">
-        <template slot-scope="scope">
-          <el-tag v-if="scope.row.loginStatus == 1 && scope.row.toolStatus==1" type="success">在线</el-tag>
-          <el-tag v-else type="danger">离线</el-tag>
-        </template>
-      </el-table-column>
-      <el-table-column label="插件状态" align="center" prop="toolStatus">
-        <template slot-scope="scope">
-          <el-tag v-if="scope.row.toolStatus == 1" type="success">在线</el-tag>
-          <el-tag v-else type="danger">离线</el-tag>
-        </template>
-      </el-table-column>
-      <el-table-column label="插件版本" align="center" prop="version"/>
+      <!--      <el-table-column label="企微状态" align="center" prop="loginStatus">-->
+      <!--        <template slot-scope="scope">-->
+      <!--          <el-tag v-if="scope.row.loginStatus == 1 && scope.row.toolStatus==1" type="success">在线</el-tag>-->
+      <!--          <el-tag v-else type="danger">离线</el-tag>-->
+      <!--        </template>-->
+      <!--      </el-table-column>-->
+      <!--      <el-table-column label="插件状态" align="center" prop="toolStatus">-->
+      <!--        <template slot-scope="scope">-->
+      <!--          <el-tag v-if="scope.row.toolStatus == 1" type="success">在线</el-tag>-->
+      <!--          <el-tag v-else type="danger">离线</el-tag>-->
+      <!--        </template>-->
+      <!--      </el-table-column>-->
+      <!--      <el-table-column label="插件版本" align="center" prop="version"/>-->
       <el-table-column label="服务器地址" align="center" prop="loginCodeUrl">
         <template slot-scope="scope">
           <el-tooltip class="item" effect="dark" :content="scope.row.loginCodeUrl" placement="top">
@@ -102,33 +102,44 @@
           </el-tooltip>
         </template>
       </el-table-column>
-      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="100px" fixed="right">
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="120px" fixed="right">
         <template slot-scope="scope">
           <el-button
             size="mini"
             type="text"
-            icon="el-icon-sunny"
+            icon="el-icon-user-solid"
             plain
-            @click="handleLoginQwCode(scope.row)"
-            v-hasPermi="['qw:user:login']"
+            @click="handleAppellation(scope.row)"
           >
-            登录企微
-          </el-button>
-          <el-button
-            v-if="scope.row.appKey!=null && scope.row.toolStatus === 1 && scope.row.loginStatus === 1"
-            size="mini"
-            type="text"
-            icon="el-icon-moon"
-            plain
-            @click="handleLoginOutQwStatus(scope.row)"
-            v-hasPermi="['qw:user:login']"
-          >
-            退出企微
+            修改员工称呼
           </el-button>
 
+          <!--          <el-button-->
+          <!--            v-if="scope.row.toolStatus==1"-->
+          <!--            size="mini"-->
+          <!--            type="text"-->
+          <!--            icon="el-icon-sunny"-->
+          <!--            plain-->
+          <!--            @click="handleLoginQwCode(scope.row)"-->
+          <!--            v-hasPermi="['qw:user:login']"-->
+          <!--          >-->
+          <!--            登录企微-->
+          <!--          </el-button>-->
+          <!--          <el-button-->
+          <!--            v-if="scope.row.appKey!=null && scope.row.toolStatus === 1 && scope.row.loginStatus === 1"-->
+          <!--            size="mini"-->
+          <!--            type="text"-->
+          <!--            icon="el-icon-moon"-->
+          <!--            plain-->
+          <!--            @click="handleLoginOutQwStatus(scope.row)"-->
+          <!--            v-hasPermi="['qw:user:login']"-->
+          <!--          >-->
+          <!--            退出企微-->
+          <!--          </el-button>-->
+
         </template>
       </el-table-column>
-      <el-table-column label="主机" align="center" class-name="small-padding fixed-width" width="170px" fixed="right">
+      <el-table-column label="主机" align="center" class-name="small-padding fixed-width" width="110px" fixed="right">
         <template slot-scope="scope">
 
           <el-button
@@ -138,11 +149,11 @@
             icon="el-icon-s-check"
             plain
             v-hasPermi="['qw:user:authAppKey']"
-            @click="handleAuthorizeKey(scope.row)"
+            @click="uploadAuthorizeKey2(scope.row)"
           >授权key
           </el-button>
           <el-button
-            v-if="scope.row.loginCodeUrl==null"
+            v-if="scope.row.loginCodeUrl==null && scope.row.appKey !=null"
             size="mini"
             type="text"
             icon="el-icon-sunny"
@@ -156,7 +167,7 @@
             v-if="scope.row.loginCodeUrl!=null"
             size="mini"
             type="text"
-            icon="el-icon-moon"
+            icon="el-icon-video-camera-solid"
             plain
             @click="handleCloudAP(scope.row.loginCodeUrl)"
             v-hasPermi="['qw:user:cloudAP']"
@@ -176,7 +187,7 @@
           </el-button>
         </template>
       </el-table-column>
-      <el-table-column label="AI客服" align="center" class-name="small-padding fixed-width" width="180px" fixed="right">
+      <el-table-column label="AI客服" align="center" class-name="small-padding fixed-width" width="100px" fixed="right">
         <template slot-scope="scope">
           <el-button
             size="mini"
@@ -219,16 +230,16 @@
       <fast-gpt-role ref="fastGptRole" @refreshFastGptList="refreshFastGptList" ></fast-gpt-role>
     </el-dialog>
 
-<!--    <el-dialog :visible.sync="updateIp.open" width="600px" append-to-body>-->
-<!--      <el-form ref="updateIpForm" :model="updateIpForm" :rules="updateIpRule" label-width="100px">-->
-<!--        <el-form-item label="新云主机IP" prop="Ip">-->
-<!--          <el-input v-model="updateIpForm.newIp" placeholder="请输入新IP" />-->
-<!--        </el-form-item>-->
-<!--      </el-form>-->
-<!--      <div slot="footer" class="dialog-footer" >-->
-<!--        <el-button type="primary" @click="submitUpdateIpForm">确 定</el-button>-->
-<!--      </div>-->
-<!--    </el-dialog>-->
+    <!--    <el-dialog :visible.sync="updateIp.open" width="600px" append-to-body>-->
+    <!--      <el-form ref="updateIpForm" :model="updateIpForm" :rules="updateIpRule" label-width="100px">-->
+    <!--        <el-form-item label="新云主机IP" prop="Ip">-->
+    <!--          <el-input v-model="updateIpForm.newIp" placeholder="请输入新IP" />-->
+    <!--        </el-form-item>-->
+    <!--      </el-form>-->
+    <!--      <div slot="footer" class="dialog-footer" >-->
+    <!--        <el-button type="primary" @click="submitUpdateIpForm">确 定</el-button>-->
+    <!--      </div>-->
+    <!--    </el-dialog>-->
 
     <el-dialog title="云主机信息" :visible.sync="cloudAPOpen.open" append-to-body>
       <el-card class="box-card">
@@ -241,15 +252,26 @@
       </el-card>
     </el-dialog>
 
+    <el-dialog :title="callOpen.title" :visible.sync="callOpen.open" width="500px" append-to-body>
+      <el-form ref="callOpenFrom" :model="callOpenFrom" :rules="callOpenRule" label-width="110px">
+        <el-form-item label="员工称呼" prop="welcomeText">
+          <el-input v-model="callOpenFrom.welcomeText" placeholder="请输入员工称呼" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer" >
+        <el-button type="primary" @click="submitCallOpenFrom">确 定</el-button>
+      </div>
+    </el-dialog>
+
     <el-dialog title="授权key" :visible.sync="authorizeKeyOpen" width="500px" append-to-body>
-        <el-form ref="authorizeKeyFrom" :model="authorizeKeyFrom" :rules="authorizeKeyRule" label-width="110px">
-            <el-form-item label="授权的key值" prop="appKey">
-              <el-input v-model="authorizeKeyFrom.appKey" placeholder="请输入授权key" />
-            </el-form-item>
-        </el-form>
-            <div slot="footer" class="dialog-footer" >
-              <el-button type="primary" @click="submitAuthorizeKeyForm">确 定</el-button>
-            </div>
+      <el-form ref="authorizeKeyFrom" :model="authorizeKeyFrom" :rules="authorizeKeyRule" label-width="110px">
+        <el-form-item label="授权的key值" prop="appKey">
+          <el-input v-model="authorizeKeyFrom.appKey" placeholder="请输入授权key" type="Number"/>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer" >
+        <el-button type="primary" @click="submitAuthorizeKeyForm">确 定</el-button>
+      </div>
     </el-dialog>
     <!--二维码   -->
     <el-dialog
@@ -383,6 +405,15 @@ export default {
         admin:null,
         passWord:null,
       },
+      callOpen:{
+        open:false,
+        title: '修改员工称呼',
+
+      },
+      callOpenFrom:{
+        id:null,
+        welcomeText:null,
+      },
       qrCodeInterval:null,
       loginQwInterval:null,
       loginOutQwInterval:null,
@@ -403,6 +434,9 @@ export default {
       authorizeKeyRule:{
         appKey:[{required:true,message:"授权码不能为空",trigger:"blur"}]
       },
+      callOpenRule:{
+        welcomeText:[{required:true,message:"员工称呼不能为空",trigger:"blur"}]
+      },
       // 表单校验
       rules: {
       },
@@ -482,13 +516,17 @@ export default {
 
     },
 
+    handleAppellation(val){
+      this.callOpen.open=true;
+      this.callOpenFrom.welcomeText=val.welcomeText;
+      this.callOpenFrom.id=val.id;
+    },
     //登录
-
     handleLoginQwCode(val){
 
       // v-if="scope.row.appKey!=null && scope.row.toolStatus === 1 && scope.row.loginStatus === 0"
       if (val.appKey==null || val.appKey===''){
-        return this.$message.warning("没有授权码,无法登录企业微信,请联系管理员开通");
+        return this.$message.warning("没有授权码,无法登录企业微信,请授权");
       }
 
       if (val.toolStatus===0 || val.toolStatus==null ){
@@ -674,14 +712,31 @@ export default {
     },
 
     handleUnbindCloudHost(val){
-      qwUnbindCloudHost(val.appKey).then(res => {
-        this.$message.success('解绑成功');
 
-      }).finally(() => {
+      const appKey=val.appKey;
+
+      this.$confirm(
+        '确定要给企微账号:<span style="color: green;">' +val.qwUserId + '' +
+        '</span><br>企微昵称:<span style="color: red;">【' + val.qwUserName + '】</span>' +
+        '</span><br><span style="color: orange;">解绑【Ps:解绑后此云主机可能会分配给他人】</span></span>',
+        "警告",
+        {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+          dangerouslyUseHTMLString: true // 允许使用 HTML 字符串
+        }
+      ).then(() => {
+        return  qwUnbindCloudHost(appKey);
+      }).then(response => {
+        this.$message.success('解绑成功');
+      }).finally(res=>{
         this.getList();
       })
+
     },
 
+
     handleAuthorizeKey(val){
       this.authorizeKeyFrom.id=val.id;
       this.authorizeKeyFrom.qwUserId=val.qwUserId;
@@ -699,6 +754,22 @@ export default {
         }
       });
     },
+    submitCallOpenFrom(){
+
+      this.$refs["callOpenFrom"].validate(valid => {
+        if (valid) {
+
+          if (this.callOpenFrom.id != null && this.callOpenFrom.welcomeText != null) {
+            updateUser(this.callOpenFrom).then(res=>{
+              this.$message.success('修改成功');
+              this.callOpen.open=false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+
 
     uploadAuthorizeKey(){
       this.$confirm(
@@ -723,17 +794,58 @@ export default {
       })
 
     },
+
+    uploadAuthorizeKey2(val){
+      const id=val.id;
+      this.$confirm(
+        '确定要给企微账号:<span style="color: green;">' +val.qwUserId + '' +
+        '</span><br>企微昵称:<span style="color: red;">【' + val.qwUserName + '】</span>' +
+        '</span><br>授权key</span>?',
+        "警告",
+        {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+          dangerouslyUseHTMLString: true // 允许使用 HTML 字符串
+        }
+      ).then(() => {
+        return handleAuthAppKey({id:id});
+      }).then(response => {
+        this.msgSuccess("授权key完成");
+      }).finally(res=>{
+        this.resetAuthorizeKeyFrom();
+        this.getList();
+      })
+    },
+
     handleBindCloudHost(val){
 
       if (val.appKey == null || val.appKey == '') {
         return this.$message.warning('没有授权码,无法绑定主机,请联系管理员');
       }
 
-      qwBindCloudHost(val.appKey).then(res => {
-        this.$message.success('绑定成功');
-      }).finally(() => {
+      const appKey=val.appKey;
+
+      this.$confirm(
+        '确定要给企微账号:<span style="color: green;">' +val.qwUserId + '' +
+        '</span><br>企微昵称:<span style="color: red;">【' + val.qwUserName + '】</span>' +
+        '</span><br><span style="color: dodgerblue;">绑定云主机?</span></span>',
+        "警告",
+        {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+          dangerouslyUseHTMLString: true // 允许使用 HTML 字符串
+        }
+      ).then(() => {
+        return qwBindCloudHost(appKey);
+      }).then(response => {
+        this.$message.success('绑定成功,请登录云主机进行配置~~');
+      }).finally(res=>{
         this.getList();
       })
+
+
     },
 
     openImageViewer(url) {
@@ -766,10 +878,10 @@ export default {
     //重置授权
     resetAuthorizeKeyFrom(){
       this.authorizeKeyFrom={
-          id:null,
-          appKey:null,
-          qwUserId:null,
-          qwUserName:null
+        id:null,
+        appKey:null,
+        qwUserId:null,
+        qwUserName:null
       };
     },
     //重置登录

+ 254 - 0
src/views/users/user/transfer.vue

@@ -0,0 +1,254 @@
+<template>
+  <div class="app-container">
+    <!-- 搜索区域 -->
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="销售" prop="salesName">
+        <el-select v-model="queryParams.companyUserId" remote placeholder="请选择" filterable clearable  style="width: 100%;" @keyup.enter.native="handleQuery">
+          <el-option
+            v-for="dict in companyUserList"
+            :key="`${dict.nickName} - ${dict.userName}`"
+            :label="`${dict.nickName} - ${dict.userName}`"
+            :value="dict.userId">
+          </el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="手机号码" prop="phone">
+        <el-input
+          style="width: 220px"
+          v-model="queryParams.phone"
+          placeholder="请输入手机号码"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </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-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          icon="el-icon-d-arrow-right"
+          size="mini"
+          :disabled="multiple"
+          @click="handleTransfer"
+          v-hasPermi="['company:user:transfer']"
+        >转移</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <!-- 表格数据 -->
+    <el-table height="500" border v-loading="loading" :data="customerList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="客户ID" align="center" prop="userId" />
+      <el-table-column label="昵称" align="center" prop="nickname" />
+      <el-table-column label="所属销售" align="center" prop="companyUserName" />
+      <el-table-column label="手机号码" align="center" prop="phone" />
+      <el-table-column label="状态" align="center" prop="statusText" >
+        <template slot-scope="scope">
+          <el-tag :type="scope.row.statusText === '正常' ? 'success' : 'danger'">{{ scope.row.statusText }}</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ scope.row.createTime }}</span>
+        </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="客户转移" :visible.sync="openTransferDialog" width="500px" append-to-body>
+      <el-form ref="transferForm" :model="transferForm" label-width="100px" :rules="cusTransfer">
+        <el-alert
+          title="会经过总后台审核后才进行转移"
+          type="warning">
+        </el-alert>
+        <el-form-item label="转移至销售" prop="targetUserId">
+          <el-select v-model="transferForm.targetUserId" remote placeholder="请选择" filterable clearable>
+            <el-option
+              v-for="dict in companyUserList"
+              :key="`${dict.nickName} - ${dict.userName}`"
+              :label="`${dict.nickName} - ${dict.userName}`"
+              :value="dict.userId">
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="转移原因" prop="content">
+          <el-input type="textarea" v-model="transferForm.content" placeholder="转移原因"></el-input>
+        </el-form-item>
+        <p>确定要转移选中的 <strong>{{ ids.length }}</strong> 个客户吗?</p>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="danger" @click="submitTransfer">提交申请</el-button>
+        <el-button @click="cancelTransfer">取 消</el-button>
+      </div>
+    </el-dialog>
+
+  </div>
+</template>
+
+<script>
+import { listUser, transferUser} from "@/api/users/user";
+import {parseTime} from "../../../utils/common";
+import {getUserList} from "@/api/company/companyUser"; // 假设API文件路径
+
+export default {
+  name: "CustomerTransfer",
+  data() {
+    return {
+      loading: true,
+      ids: [],
+      single: true,
+      multiple: true,
+      showSearch: true,
+      openTransferDialog: false,
+      total: 0,
+      customerList: [],
+      companyUserList: [],
+      transferForm: {
+        targetUserId: null,
+        content: null
+      },
+      cusTransfer: {
+        targetUserId: [{required: true, message: '请选择转移至销售', trigger: 'change'}],
+        content: [{required: true, message: '请选择转移至销售', trigger: 'change'}]
+      },
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        salesName: null,
+        phoneNumber: null,
+      },
+    };
+  },
+  created() {
+    getUserList().then(res=>{
+      if(res.code === 200) {
+        this.companyUserList = res.data
+      }
+    });
+
+    this.getList();
+  },
+  methods: {
+    parseTime,
+    /** 查询客户列表 */
+    getList() {
+      this.loading = true;
+      listUser(this.queryParams).then(response => {
+        this.customerList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      }).catch(() => {
+        this.loading = false;
+      });
+    },
+    cancelTransfer() {
+      this.openTransferDialog = false;
+      this.resetTransferForm();
+    },
+    resetTransferForm() {
+      this.transferForm = {
+        targetUserId: null,
+        content: null
+      };
+      this.resetForm("transferForm"); // 假设 transferForm 是 el-form 的 ref
+    },
+
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.userId)
+      this.single = selection.length !== 1;
+      this.multiple = !selection.length;
+    },
+
+    /** 转移按钮操作 */
+    handleTransfer(row) {
+      const userIds = row.userId ? [row.userId] : this.ids;
+      if (userIds.length === 0) {
+        this.$message.warning("请至少选择一个客户进行转移");
+        return;
+      }
+
+      this.resetTransferForm();
+      this.openTransferDialog = true;
+    },
+
+    /** 提交转移按钮 (如果使用对话框) */
+
+    submitTransfer() {
+      this.$refs["transferForm"].validate(valid => {
+        if (valid) {
+          transferUser({
+            userIds: this.ids,
+            targetCompanyUserId: this.transferForm.targetUserId,
+            content: this.transferForm.content
+          }).then(response => {
+            if (response.code === 200) {
+              this.msgSuccess(response.msg);
+              this.openTransferDialog = false;
+              this.getList();
+            } else {
+              this.msgError(response.msg || "转移失败");
+            }
+          }).catch(() => {
+             this.msgError("转移请求失败");
+          });
+        }
+      });
+    },
+
+
+    /** 导出按钮操作 */
+
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有符合条件的客户数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          // 调用 exportCustomer API
+          return exportCustomer(queryParams);
+        }).then(response => {
+          // 处理下载
+          this.download(response.msg); // RuoYi 提供的下载方法
+        }).catch(function() {});
+    }
+
+  }
+};
+</script>
+
+<style scoped>
+/* 可以添加一些自定义样式 */
+.mb8 {
+  margin-bottom: 8px;
+}
+</style>

+ 399 - 0
src/views/users/user/transferLog.vue

@@ -0,0 +1,399 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="转移类型" prop="transferType">
+        <el-select v-model="queryParams.transferType" placeholder="请选择转移类型" clearable size="small">
+          <el-option
+            v-for="item in transferTypeList"
+            :key="item.dictValue"
+            :label="item.dictLabel"
+            :value="item.dictValue"/>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="目标接收销售" prop="targetUserId">
+        <el-select v-model="queryParams.targetUserId" remote
+                   placeholder="请选择" filterable clearable
+                   style="width: 100%;" @keyup.enter.native="handleQuery">
+          <el-option
+            v-for="dict in companyUserList"
+            :key="`${dict.nickName} - ${dict.userName}`"
+            :label="`${dict.nickName} - ${dict.userName}`"
+            :value="dict.userId">
+          </el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="发起人" prop="initiatorUserId">
+        <el-select v-model="queryParams.initiatorUserId" remote placeholder="请选择" filterable clearable
+                   style="width: 100%;" @keyup.enter.native="handleQuery"
+        >
+          <el-option
+            v-for="dict in companyUserList"
+            :key="`${dict.nickName} - ${dict.userName}`"
+            :label="`${dict.nickName} - ${dict.userName}`"
+            :value="dict.userId">
+          </el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="审批状态" prop="approvalStatus">
+        <el-select v-model="queryParams.approvalStatus" placeholder="请选择审批状态" clearable size="small">
+          <el-option
+            v-for="item in approvalStatusList"
+            :key="item.dictValue"
+            :label="item.dictLabel"
+            :value="item.dictValue"/>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="审批处理时间" prop="processedAt">
+        <el-date-picker clearable size="small" style="width: 200px"
+          v-model="queryParams.processedAt"
+          type="date"
+          value-format="yyyy-MM-dd"
+          placeholder="选择审批处理时间">
+        </el-date-picker>
+      </el-form-item>
+      <el-form-item label="记录创建时间" prop="createdAt">
+        <el-date-picker clearable size="small" style="width: 200px"
+          v-model="queryParams.createdAt"
+          type="date"
+          value-format="yyyy-MM-dd"
+          placeholder="选择记录创建时间">
+        </el-date-picker>
+      </el-form-item>
+      <el-form-item label="记录最后更新时间" prop="updatedAt">
+        <el-date-picker clearable size="small" style="width: 200px"
+          v-model="queryParams.updatedAt"
+          type="date"
+          value-format="yyyy-MM-dd"
+          placeholder="选择记录最后更新时间">
+        </el-date-picker>
+      </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-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          icon="el-icon-download"
+          size="mini"
+          @click="handleExport"
+          v-hasPermi="['system:approval:export']"
+        >导出</el-button>
+      </el-col>
+	  <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="approvalList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="ID" align="center" prop="id" />
+      <el-table-column label="所属公司" align="center" prop="companyName" />
+      <el-table-column label="转移类型" align="center" prop="transferTypeText" />
+      <el-table-column label="原负责人用户" align="center" prop="originalUserName" />
+      <el-table-column label="目标接收销售用户" align="center" prop="targetUserName" />
+      <el-table-column label="发起此转移请求的用户" align="center" prop="initiatorUserName" />
+      <el-table-column label="转移提示内容/原因" align="center" prop="content" />
+      <el-table-column label="审批状态" align="center" prop="approvalStatus" >
+        <template slot-scope="scope">
+          <el-tag :type="scope.row.approvalStatus == 1 ? 'success' : scope.row.approvalStatus == 2 ? 'danger' : ''">{{ scope.row.approvalStatusText }}</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="审批意见/备注" align="center" prop="approvalRemark" />
+      <el-table-column label="记录创建时间" align="center" prop="createdAt" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createdAt, '{y}-{m}-{d}') }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="记录最后更新时间" align="center" prop="updatedAt" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.updatedAt, '{y}-{m}-{d}') }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            v-if="scope.row.approvalStatus == 0"
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="viewMode=false;handleUpdate(scope.row)"
+            v-hasPermi="['system:approval:edit']"
+          >撤回</el-button>
+          <el-button
+            v-if="scope.row.approvalStatus != 0"
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="viewMode=true;handleUpdate(scope.row)"
+            v-hasPermi="['system:approval:edit']"
+          >查看</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-dialog :title="title" :visible.sync="open" width="900px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="企业" prop="corpId">
+          <el-input v-model="form.companyName" placeholder="企业" disabled="disabled"/>
+        </el-form-item>
+        <el-form-item label="转移类型" prop="transferType">
+          <el-input v-model="form.transferTypeText" placeholder="转移类型" disabled="disabled"/>
+        </el-form-item>
+        <el-form-item label="转移矩阵" prop="customerIds">
+          <el-table :data="form.customerList" border>
+            <el-table-column label="客户" align="center" prop="userName" />
+            <el-table-column label="转移前销售" align="center" prop="beforeCompanyUserName" />
+            <el-table-column label="转移后销售" align="center" prop="afterCompanyUserName" />
+          </el-table>
+        </el-form-item>
+        <el-form-item label="目标接收销售用户" prop="targetUserId">
+          <el-input v-model="form.targetUserName" placeholder="请输入目标接收销售用户 ID" disabled="disabled"/>
+        </el-form-item>
+        <el-form-item label="发起此转移请求的用户" prop="initiatorUserId">
+          <el-input v-model="form.initiatorUserName" placeholder="请输入发起此转移请求的用户 ID" disabled="disabled"/>
+        </el-form-item>
+        <el-form-item label="转移提示内容/原因">
+          <el-input v-model="form.content" :min-height="192" disabled="disabled"/>
+        </el-form-item>
+        <el-form-item label="审批状态">
+          <el-radio-group v-model="form.approvalStatus">
+            <el-tag>{{form.approvalStatusText}}</el-tag>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="审批意见/备注" prop="approvalRemark">
+          <el-input v-model="form.approvalRemark" type="textarea" placeholder="请输入内容" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="success" @click="submitForm(3)" v-if="!viewMode">撤回</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listApproval, getApproval, delApproval, addApproval, updateApproval, exportApproval } from "@/api/users/approval";
+import Editor from '@/components/Editor';
+import {getDicts} from "@/api/system/dict/data";
+import {getCompanyList} from "@/api/company/company";
+import {getAllUserListLimit} from "@/api/store/user";
+import {getUserList} from "@/api/company/companyUser";
+
+export default {
+  name: "Approval",
+  components: { Editor },
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 客户转移审批表格数据
+      approvalList: [],
+      companys:[],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 转移类型
+      transferTypeList: [],
+      companyUserList: [],
+      companyUserList3: [],
+      // 审批状态
+      approvalStatusList: [],
+      viewMode: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        corpId: null,
+        transferType: null,
+        customerIds: null,
+        originalUserId: null,
+        targetUserId: null,
+        initiatorUserId: null,
+        content: null,
+        approvalStatus: null,
+        approverUserId: null,
+        approvalRemark: null,
+        processedAt: null,
+        createdAt: null,
+        updatedAt: null
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        corpId: [
+          { required: true, message: "企业不能为空", trigger: "blur" }
+        ],
+        transferType: [
+          { required: true, message: "转移类型不能为空", trigger: "change" }
+        ],
+        customerIds: [
+          { required: true, message: "客户 ID 列表不能为空", trigger: "blur" }
+        ],
+        targetUserId: [
+          { required: true, message: "目标接收销售用户不能为空", trigger: "blur" }
+        ],
+        initiatorUserId: [
+          { required: true, message: "发起此转移请求的用户不能为空", trigger: "blur" }
+        ],
+        approvalStatus: [
+          { required: true, message: "审批状态不能为空", trigger: "blur" }
+        ],
+        createdAt: [
+          { required: true, message: "记录创建时间不能为空", trigger: "blur" }
+        ],
+        updatedAt: [
+          { required: true, message: "记录最后更新时间不能为空", trigger: "blur" }
+        ]
+      }
+    };
+  },
+  created() {
+    getDicts('transfer_type').then(response => {
+      this.transferTypeList = response.data;
+    })
+    getDicts('transfer_approval_status').then(response => {
+      this.approvalStatusList = response.data;
+    })
+    getUserList().then(res=>{
+      if(res.code === 200) {
+        this.companyUserList = res.data
+      }
+    });
+    this.getList();
+  },
+  methods: {
+    /** 查询客户转移审批列表 */
+    getList() {
+      this.loading = true;
+      listApproval(this.queryParams).then(response => {
+        this.approvalList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        corpId: null,
+        transferType: null,
+        customerIds: null,
+        originalUserId: null,
+        targetUserId: null,
+        initiatorUserId: null,
+        content: null,
+        approvalStatus: 0,
+        approverUserId: null,
+        approvalRemark: null,
+        processedAt: null,
+        createdAt: null,
+        updatedAt: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加客户转移审批";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id || this.ids
+      getApproval(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改客户转移审批";
+      });
+    },
+    /** 提交按钮 */
+    submitForm(type) {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != null) {
+            updateApproval({...this.form,approvalStatus: type}).then(response => {
+              if (response.code === 200) {
+                this.msgSuccess(response.msg);
+                this.open = false;
+                this.getList();
+              }
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除客户转移审批编号为"' + ids + '"的数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return delApproval(ids);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(function() {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有客户转移审批数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return exportApproval(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+        }).catch(function() {});
+    }
+  }
+};
+</script>