xw пре 3 дана
родитељ
комит
55a47b2901

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

@@ -162,6 +162,13 @@ export function resignedTransfer(data) {
     data: data
   })
 }
+export function transferPreCheck(data) {
+  return request({
+    url: '/qw/externalContact/transferPreCheck',
+    method: 'post',
+    data: data
+  })
+}
 export function transfer(data) {
   return request({
     url: '/qw/externalContact/transfer',

+ 2 - 0
src/layout/components/Navbar.vue

@@ -119,10 +119,12 @@ export default {
     this.getMsgCount();
     // 启动消息轮询,每30秒检查一次新消息
     this.startMsgPolling();
+    this.$bus.$on('transfer-submitted', this.getMsgCount);
   },
   beforeDestroy() {
     // 组件销毁前清除定时器
     this.stopMsgPolling();
+    this.$bus.$off('transfer-submitted', this.getMsgCount);
   },
   methods: {
     startMsgPolling() {

+ 12 - 0
src/main.js

@@ -68,6 +68,18 @@ Vue.prototype.msgInfo = function (msg) {
   this.$message.info(msg);
 }
 
+/** 企微转接任务已提交:展示 res.msg,引导用户在「转接通知」查看最终结果 */
+Vue.prototype.msgTransferSubmitted = function (msg) {
+  const text = msg || '转接任务已提交,完成后请留意转接通知';
+  this.$message({
+    showClose: true,
+    message: text,
+    type: 'info',
+    duration: 5000
+  });
+  this.$bus.$emit('transfer-submitted');
+}
+
 // 全局事件总线:项目里大量组件使用 this.$bus.$emit / $on,但之前没有挂载,未挂载导致 undefined.$emit 报错
 Vue.prototype.$bus = new Vue()
 

+ 36 - 0
src/utils/transferMsg.js

@@ -0,0 +1,36 @@
+/**
+ * 解析转接通知 content(按【中文原因】分组,不含数字错误码)
+ * 示例:转接成功:2,转接失败:3。失败原因:【当前客户正在转接中】Admin、大碗面;【没有好友关系】张三;
+ */
+export function parseTransferContent(content) {
+  if (!content || typeof content !== 'string') {
+    return null;
+  }
+  if (!/(?:转接|接替)成功:/.test(content)) {
+    return null;
+  }
+  // 旧格式「客户名(错误码)」走纯文本展示
+  if (/(\d+)/.test(content) && !content.includes('【')) {
+    return null;
+  }
+
+  const groups = [];
+  const groupRegex = /【([^】]+)】([^【]*)/g;
+  let match;
+  while ((match = groupRegex.exec(content)) !== null) {
+    const reason = match[1].trim();
+    const names = match[2].replace(/^[::\s]+/, '').replace(/[;;\s]+$/, '').trim();
+    if (reason) {
+      groups.push({ reason, names });
+    }
+  }
+
+  const summaryPart = content.split(/失败原因[::]/)[0] || content;
+  const summary = summaryPart.replace(/[。\s]+$/, '').trim();
+
+  return {
+    summary,
+    groups,
+    hasGroups: groups.length > 0
+  };
+}

+ 104 - 10
src/views/crm/components/msg.vue

@@ -14,10 +14,24 @@
       </div>
       <div class="mst-list">
         <el-timeline   >
-          <el-timeline-item  :timestamp="item.createTime" placement="top" v-for="(item, index) in list">
+          <el-timeline-item  :timestamp="item.createTime" placement="top" v-for="(item, index) in list" :key="item.id || index">
             <el-card>
-              <h4>{{item.title}}</h4>
-              <p>{{item.content}}</p>
+              <h4>{{ item.title }}</h4>
+              <div v-if="item._transferDisplay" class="transfer-msg">
+                <p class="msg-summary">{{ item._transferDisplay.summary }}</p>
+                <div v-if="item._transferDisplay.hasGroups" class="transfer-fail-block">
+                  <div class="fail-reason-title">失败原因:</div>
+                  <div
+                    v-for="(group, gi) in item._transferDisplay.groups"
+                    :key="gi"
+                    class="fail-group-item"
+                  >
+                    <div class="fail-group-reason">{{ group.reason }}</div>
+                    <div class="fail-group-names">{{ group.names }}</div>
+                  </div>
+                </div>
+              </div>
+              <p v-else class="msg-content">{{ item.content }}</p>
               <div><el-button type="text" v-if="item.isRead==0" @click="setRead(item)">标记为已读</el-button></div>
             </el-card>
           </el-timeline-item>
@@ -32,6 +46,7 @@
 <script>
 
 import { setAllRead,getMsg,getMsgList,getMsgCount,setRead } from "@/api/crm/msg";
+import { parseTransferContent } from "@/utils/transferMsg";
 
 export default {
   name: "msg",
@@ -55,18 +70,21 @@ export default {
       total: 0,
       // 产品表格数据
       list: [],
-       queryParams: {
+      queryParams: {
         pageNum: 1,
         pageSize: 10,
       },
+      transferPollTimer: null,
+      transferPollCount: 0,
     };
   },
   created() {
-      
       this.getMsg();
+      this.$bus.$on('transfer-submitted', this.onTransferSubmitted);
   },
-  mounted() {
-   
+  beforeDestroy() {
+      this.$bus.$off('transfer-submitted', this.onTransferSubmitted);
+      this.stopTransferPoll();
   },
   methods: {
     loadMore () {
@@ -111,7 +129,11 @@ export default {
       this.loading = true;
       this.queryParams.type=this.currentTypeId;
       getMsgList(this.queryParams).then(response => {
-        this.list=this.list.concat( response.data.list);
+        const items = (response.data.list || []).map(item => ({
+          ...item,
+          _transferDisplay: parseTransferContent(item.content)
+        }));
+        this.list = this.list.concat(items);
         this.total = response.data.total;
         this.loading = false;
         this.isMore=response.data.hasNextPage;
@@ -122,8 +144,40 @@ export default {
         this.msgType = response.counts;
       });
     },
-    
-    
+    /** 转接任务提交后刷新未读数,并轮询「转接通知」(type=4) 直至结果写入 */
+    onTransferSubmitted() {
+      getMsg().then(response => {
+        this.msgType = response.counts;
+        this.$emit('update-count');
+      });
+      this.startTransferPoll();
+    },
+    startTransferPoll() {
+      this.stopTransferPoll();
+      this.transferPollCount = 0;
+      this.transferPollTimer = setInterval(() => {
+        this.transferPollCount += 1;
+        getMsg().then(response => {
+          this.msgType = response.counts;
+          if (this.currentTypeId === 4) {
+            this.queryParams.pageNum = 1;
+            this.list = [];
+            this.getList();
+          }
+          this.$emit('update-count');
+        });
+        if (this.transferPollCount >= 40) {
+          this.stopTransferPoll();
+        }
+      }, 30000);
+    },
+    stopTransferPoll() {
+      if (this.transferPollTimer) {
+        clearInterval(this.transferPollTimer);
+        this.transferPollTimer = null;
+      }
+      this.transferPollCount = 0;
+    },
   }
 };
 </script>
@@ -147,6 +201,46 @@ export default {
 .mst-list{
   margin: 10px 0px;
 }
+.msg-content {
+  word-break: break-word;
+  white-space: pre-wrap;
+  line-height: 1.6;
+  margin: 0;
+}
+.transfer-msg {
+  line-height: 1.6;
+}
+.msg-summary {
+  margin: 0 0 8px;
+  font-weight: 500;
+  color: #303133;
+}
+.transfer-fail-block {
+  margin-top: 4px;
+}
+.fail-reason-title {
+  color: #606266;
+  margin-bottom: 6px;
+}
+.fail-group-item {
+  margin-bottom: 8px;
+  padding: 8px 10px;
+  background: #fef0f0;
+  border-radius: 4px;
+  border-left: 3px solid #f56c6c;
+}
+.fail-group-item:last-child {
+  margin-bottom: 0;
+}
+.fail-group-reason {
+  font-weight: 500;
+  color: #f56c6c;
+  margin-bottom: 4px;
+}
+.fail-group-names {
+  color: #606266;
+  word-break: break-word;
+}
 
 </style>
 <style >

+ 34 - 26
src/views/qw/externalContactTransfer/deptTransferIndex.vue

@@ -270,10 +270,21 @@
 
       </el-form>
       <div slot="footer" class="dialog-footer">
-        <el-button type="primary" @click="submitForm">确 定</el-button>
-        <el-button @click="cancel">取 消</el-button>
+        <el-button type="primary" @click="submitForm" :loading="submitLoading || preCheckLoading">确 定</el-button>
+        <el-button @click="cancel" :disabled="submitLoading || preCheckLoading">取 消</el-button>
       </div>
     </el-dialog>
+
+    <transfer-pre-check-result
+      :visible.sync="preCheckOpen"
+      :transferable="preCheckData.transferable"
+      :untransferable="preCheckData.untransferable"
+      :transferable-count="preCheckData.transferableCount"
+      :untransferable-count="preCheckData.untransferableCount"
+      :confirm-loading="submitLoading"
+      @confirm="confirmTransfer"
+      @back="backFromPreCheck"
+    />
   </div>
 </template>
 
@@ -283,9 +294,11 @@ import { listTag, getTag, delTag, addTag, updateTag, exportTag } from "@/api/qw/
 import { qwUserList } from "@/api/qw/user";
 import qwUserSelectOne from '@/views/qw/user/qwUserSelectOne.vue'
 import { getMyQwUserList,getMyQwCompanyList } from "@/api/qw/user";
+import transferPreCheckMixin from '@/views/qw/externalContactTransfer/transferPreCheckMixin';
 
 export default {
   name: "deptTransferIndex",
+  mixins: [transferPreCheckMixin],
   components: { qwUserSelectOne },
   data() {
     return {
@@ -293,6 +306,7 @@ export default {
       loading: true,
       // 导出遮罩层
       exportLoading: false,
+      submitLoading: false,
       // 选中数组
       ids: [],
       myQwCompanyList:[],
@@ -503,30 +517,24 @@ export default {
       }
       return obj;
     },
-    /** 提交按钮 */
-    submitForm() {
-      this.$refs["form"].validate(valid => {
-        if (valid) {
-          const form = {
-            filter: this.filter,
-            addType: 2,
-            userId: this.form.userId,
-            corpId: this.queryParams.corpId,
-            content: this.form.content,
-            needClearTag: 0
-          };
-          if (this.filter) {
-            form.param = this.buildTransferParam();
-          } else {
-            form.ids = this.ids;
-          }
-          transfer(form).then(response => {
-            this.msgSuccess(response.msg);
-            this.open = false;
-            this.getList();
-          });
-        }
-      });
+    buildTransferForm() {
+      const form = {
+        filter: this.filter,
+        addType: 2,
+        userId: this.form.userId,
+        corpId: this.queryParams.corpId,
+        content: this.form.content,
+        needClearTag: 0
+      };
+      if (this.filter) {
+        form.param = this.buildTransferParam();
+      } else {
+        form.ids = this.ids;
+      }
+      return form;
+    },
+    doTransfer(form) {
+      return transfer(form);
     },
     /** 删除按钮操作 */
     handleDelete(row) {

+ 35 - 25
src/views/qw/externalContactTransfer/index.vue

@@ -313,10 +313,21 @@
 
       </el-form>
       <div slot="footer" class="dialog-footer">
-        <el-button type="primary" @click="submitForm">确 定</el-button>
-        <el-button @click="cancel">取 消</el-button>
+        <el-button type="primary" @click="submitForm" :loading="submitLoading || preCheckLoading">确 定</el-button>
+        <el-button @click="cancel" :disabled="submitLoading || preCheckLoading">取 消</el-button>
       </div>
     </el-dialog>
+
+    <transfer-pre-check-result
+      :visible.sync="preCheckOpen"
+      :transferable="preCheckData.transferable"
+      :untransferable="preCheckData.untransferable"
+      :transferable-count="preCheckData.transferableCount"
+      :untransferable-count="preCheckData.untransferableCount"
+      :confirm-loading="submitLoading"
+      @confirm="confirmTransfer"
+      @back="backFromPreCheck"
+    />
   </div>
 </template>
 
@@ -326,9 +337,11 @@ import { listTag, tagGroupList, getTag, delTag, addTag, updateTag, exportTag } f
 import { qwUserList } from "@/api/qw/user";
 import qwUserSelectOne from '@/views/qw/user/qwUserSelectOne.vue'
 import { getMyQwUserList,getMyQwCompanyList } from "@/api/qw/user";
+import transferPreCheckMixin from '@/views/qw/externalContactTransfer/transferPreCheckMixin';
 
 export default {
   name: "ExternalContact",
+  mixins: [transferPreCheckMixin],
   components: { qwUserSelectOne },
   data() {
     return {
@@ -336,6 +349,7 @@ export default {
       loading: true,
       // 导出遮罩层
       exportLoading: false,
+      submitLoading: false,
       // 选中数组
       ids: [],
       myQwCompanyList:[],
@@ -665,29 +679,25 @@ export default {
       return obj;
     },
 
-    /** 提交按钮 */
-    submitForm() {
-      this.$refs["form"].validate(valid => {
-        if (valid) {
-          const form = {
-            filter: this.filter,
-            addType: 0,
-            userId: this.form.userId,
-            corpId: this.queryParams.corpId,
-            content: this.form.content,
-            needClearTag: this.form.needClearTag
-          };
-          if (this.filter) {
-            form.param = this.buildTransferParam();
-          } else {
-            form.ids = this.ids;
-          }
-          transfer(form).then(response => {
-            this.msgSuccess(response.msg);
-            this.open = false;
-            this.getList();
-          });        }
-      });
+    /** 构建转接/前置校验请求体 */
+    buildTransferForm() {
+      const form = {
+        filter: this.filter,
+        addType: 0,
+        userId: this.form.userId,
+        corpId: this.queryParams.corpId,
+        content: this.form.content,
+        needClearTag: this.form.needClearTag
+      };
+      if (this.filter) {
+        form.param = this.buildTransferParam();
+      } else {
+        form.ids = this.ids;
+      }
+      return form;
+    },
+    doTransfer(form) {
+      return transfer(form);
     },
     /** 删除按钮操作 */
     handleDelete(row) {

+ 93 - 0
src/views/qw/externalContactTransfer/transferPreCheckMixin.js

@@ -0,0 +1,93 @@
+import { transferPreCheck } from '@/api/qw/externalContact';
+import TransferPreCheckResult from '@/views/qw/externalContactTransfer/transferPreCheckResult.vue';
+
+export default {
+  components: { TransferPreCheckResult },
+  data() {
+    return {
+      preCheckOpen: false,
+      preCheckLoading: false,
+      preCheckData: {
+        transferable: [],
+        untransferable: [],
+        transferableCount: 0,
+        untransferableCount: 0
+      },
+      pendingTransferForm: null
+    };
+  },
+  methods: {
+    /** 子页面实现:构建前置校验/转接请求体 */
+    buildTransferForm() {
+      throw new Error('buildTransferForm must be implemented');
+    },
+    /** 子页面实现:调用 transfer 或 resignedTransfer */
+    doTransfer(form) {
+      throw new Error('doTransfer must be implemented');
+    },
+    /** 分配弹窗点「确定」:先前置校验 */
+    submitForm() {
+      if (this.beforeTransferSubmit) {
+        this.beforeTransferSubmit();
+      }
+      this.$refs['form'].validate(valid => {
+        if (!valid) {
+          return;
+        }
+        if (this.submitLoading || this.preCheckLoading) {
+          return;
+        }
+        const form = this.buildTransferForm();
+        this.pendingTransferForm = form;
+        this.preCheckLoading = true;
+        this.submitLoading = true;
+        transferPreCheck(form).then(response => {
+          const data = response.data || {};
+          this.preCheckData = {
+            transferable: data.transferable || [],
+            untransferable: data.untransferable || [],
+            transferableCount: data.transferableCount != null ? data.transferableCount : (data.transferable || []).length,
+            untransferableCount: data.untransferableCount != null
+              ? data.untransferableCount
+              : (data.untransferable || []).reduce((sum, group) => sum + (group.customers || []).length, 0)
+          };
+          this.open = false;
+          this.preCheckOpen = true;
+        }).finally(() => {
+          this.preCheckLoading = false;
+          this.submitLoading = false;
+        });
+      });
+    },
+    /** 校验结果弹窗点「确认转接」:仅提交可转接客户 id */
+    confirmTransfer() {
+      const transferable = this.preCheckData.transferable || [];
+      if (!transferable.length) {
+        return this.$message.warning('暂无可转接客户');
+      }
+      if (this.submitLoading) {
+        return;
+      }
+      const ids = transferable.map(item => item.id);
+      const form = Object.assign({}, this.pendingTransferForm, {
+        filter: false,
+        ids
+      });
+      delete form.param;
+      this.submitLoading = true;
+      this.doTransfer(form).then(response => {
+        this.msgTransferSubmitted(response.msg);
+        this.preCheckOpen = false;
+        this.pendingTransferForm = null;
+        this.onTransferSubmitDone && this.onTransferSubmitDone();
+      }).finally(() => {
+        this.submitLoading = false;
+        this.getList();
+      });
+    },
+    backFromPreCheck() {
+      this.preCheckOpen = false;
+      this.open = true;
+    }
+  }
+};

+ 179 - 0
src/views/qw/externalContactTransfer/transferPreCheckResult.vue

@@ -0,0 +1,179 @@
+<template>
+  <el-dialog
+    title="转接校验结果"
+    :visible.sync="dialogVisible"
+    width="920px"
+    append-to-body
+    @close="handleClose"
+  >
+    <el-row :gutter="20" class="precheck-result">
+      <el-col :span="12">
+        <div class="precheck-panel precheck-panel--ok">
+          <div class="precheck-panel-title">可转接({{ transferableCount }}人)</div>
+          <div class="precheck-list">
+            <div
+              v-for="item in transferable"
+              :key="item.id"
+              class="precheck-customer"
+            >{{ customerName(item) }}</div>
+            <div v-if="!transferable.length" class="precheck-empty">暂无可转接客户</div>
+          </div>
+        </div>
+      </el-col>
+      <el-col :span="12">
+        <div class="precheck-panel precheck-panel--fail">
+          <div class="precheck-panel-title">不可转接({{ untransferableCount }}人)</div>
+          <div class="precheck-list">
+            <div
+              v-for="(group, index) in untransferable"
+              :key="index"
+              class="precheck-group"
+            >
+              <div class="precheck-group-reason">{{ group.reason }}</div>
+              <div class="precheck-group-names">{{ formatCustomers(group.customers) }}</div>
+            </div>
+            <div v-if="!untransferable.length" class="precheck-empty">无不可转接客户</div>
+          </div>
+        </div>
+      </el-col>
+    </el-row>
+    <div v-if="!transferableCount" class="precheck-tip">
+      暂无可转接客户,请返回修改接替员工或重新选择客户
+    </div>
+    <div slot="footer" class="dialog-footer">
+      <el-button @click="handleClose" :disabled="confirmLoading">返 回</el-button>
+      <el-button
+        type="primary"
+        :loading="confirmLoading"
+        :disabled="!transferableCount"
+        @click="$emit('confirm')"
+      >确认转接</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+export default {
+  name: 'TransferPreCheckResult',
+  props: {
+    visible: {
+      type: Boolean,
+      default: false
+    },
+    transferable: {
+      type: Array,
+      default: () => []
+    },
+    untransferable: {
+      type: Array,
+      default: () => []
+    },
+    transferableCount: {
+      type: Number,
+      default: 0
+    },
+    untransferableCount: {
+      type: Number,
+      default: 0
+    },
+    confirmLoading: {
+      type: Boolean,
+      default: false
+    }
+  },
+  computed: {
+    dialogVisible: {
+      get() {
+        return this.visible;
+      },
+      set(val) {
+        this.$emit('update:visible', val);
+      }
+    }
+  },
+  methods: {
+    customerName(item) {
+      return item.name || item.customerName || item.externalUserId || '-';
+    },
+    formatCustomers(customers) {
+      if (!customers || !customers.length) {
+        return '-';
+      }
+      return customers.map(c => this.customerName(c)).join('、');
+    },
+    handleClose() {
+      this.$emit('update:visible', false);
+      this.$emit('back');
+    }
+  }
+};
+</script>
+
+<style scoped>
+.precheck-result {
+  min-height: 320px;
+}
+.precheck-panel {
+  border: 1px solid #ebeef5;
+  border-radius: 4px;
+  height: 360px;
+  display: flex;
+  flex-direction: column;
+}
+.precheck-panel--ok {
+  border-top: 3px solid #67c23a;
+}
+.precheck-panel--fail {
+  border-top: 3px solid #f56c6c;
+}
+.precheck-panel-title {
+  padding: 12px 16px;
+  font-weight: 600;
+  font-size: 15px;
+  border-bottom: 1px solid #ebeef5;
+  background: #fafafa;
+}
+.precheck-list {
+  flex: 1;
+  overflow-y: auto;
+  padding: 12px 16px;
+}
+.precheck-customer {
+  padding: 6px 0;
+  color: #303133;
+  border-bottom: 1px dashed #ebeef5;
+}
+.precheck-customer:last-child {
+  border-bottom: none;
+}
+.precheck-group {
+  margin-bottom: 12px;
+  padding: 10px;
+  background: #fef0f0;
+  border-radius: 4px;
+}
+.precheck-group:last-child {
+  margin-bottom: 0;
+}
+.precheck-group-reason {
+  font-weight: 500;
+  color: #f56c6c;
+  margin-bottom: 6px;
+}
+.precheck-group-names {
+  color: #606266;
+  line-height: 1.6;
+  word-break: break-word;
+}
+.precheck-empty {
+  color: #909399;
+  text-align: center;
+  padding: 40px 0;
+}
+.precheck-tip {
+  margin-top: 12px;
+  color: #e6a23c;
+  font-size: 13px;
+  text-align: center;
+}
+</style>

+ 36 - 26
src/views/qw/externalContactUnassigned/deptUnassignedIndex.vue

@@ -223,10 +223,21 @@
 
       </el-form>
       <div slot="footer" class="dialog-footer">
-        <el-button type="primary" @click="submitForm">确 定</el-button>
-        <el-button @click="cancel">取 消</el-button>
+        <el-button type="primary" @click="submitForm" :loading="submitLoading || preCheckLoading">确 定</el-button>
+        <el-button @click="cancel" :disabled="submitLoading || preCheckLoading">取 消</el-button>
       </div>
     </el-dialog>
+
+    <transfer-pre-check-result
+      :visible.sync="preCheckOpen"
+      :transferable="preCheckData.transferable"
+      :untransferable="preCheckData.untransferable"
+      :transferable-count="preCheckData.transferableCount"
+      :untransferable-count="preCheckData.untransferableCount"
+      :confirm-loading="submitLoading"
+      @confirm="confirmTransfer"
+      @back="backFromPreCheck"
+    />
   </div>
 </template>
 
@@ -245,10 +256,12 @@ import {
 import { listTag, getTag, delTag, addTag, updateTag, exportTag } from "@/api/qw/tag";
 // import { qwUserList } from "@/api/qw/user";
 import qwUserSelectOne from "@/views/qw/user/qwUserSelectOne.vue";
+import transferPreCheckMixin from '@/views/qw/externalContactTransfer/transferPreCheckMixin';
 import { getMyQwUserList,getMyQwCompanyList } from "@/api/qw/user";
 
 export default {
   name: "deptUnassignedIndex",
+  mixins: [transferPreCheckMixin],
   components:{qwUserSelectOne},
   data() {
     return {
@@ -256,6 +269,7 @@ export default {
       loading: true,
       // 导出遮罩层
       exportLoading: false,
+      submitLoading: false,
       // 选中数组
       ids: [],
       // 非单个禁用
@@ -452,30 +466,26 @@ export default {
       }
       return obj;
     },
-    /** 提交按钮 */
-    submitForm() {
-      this.nickName=null;
-      this.$refs["form"].validate(valid => {
-        if (valid) {
-          const form = {
-            filter: this.filter,
-            addType: 2,
-            userId: this.form.userId,
-            corpId: this.queryParams.corpId,
-            needClearTag: this.form.needClearTag
-          };
-          if (this.filter) {
-            form.param = this.buildTransferParam();
-          } else {
-            form.ids = this.ids;
-          }
-          resignedTransfer(form).then(response => {
-            this.msgSuccess(response.msg);
-            this.open = false;
-            this.getList();
-          });
-        }
-      });
+    beforeTransferSubmit() {
+      this.nickName = null;
+    },
+    buildTransferForm() {
+      const form = {
+        filter: this.filter,
+        addType: 2,
+        userId: this.form.userId,
+        corpId: this.queryParams.corpId,
+        needClearTag: this.form.needClearTag
+      };
+      if (this.filter) {
+        form.param = this.buildTransferParam();
+      } else {
+        form.ids = this.ids;
+      }
+      return form;
+    },
+    doTransfer(form) {
+      return resignedTransfer(form);
     },
     /** 删除按钮操作 */
     handleDelete(row) {

+ 41 - 29
src/views/qw/externalContactUnassigned/index.vue

@@ -237,10 +237,21 @@
 
       </el-form>
       <div slot="footer" class="dialog-footer">
-        <el-button type="primary" @click="submitForm">确 定</el-button>
-        <el-button @click="cancel">取 消</el-button>
+        <el-button type="primary" @click="submitForm" :loading="submitLoading || preCheckLoading">确 定</el-button>
+        <el-button @click="cancel" :disabled="submitLoading || preCheckLoading">取 消</el-button>
       </div>
     </el-dialog>
+
+    <transfer-pre-check-result
+      :visible.sync="preCheckOpen"
+      :transferable="preCheckData.transferable"
+      :untransferable="preCheckData.untransferable"
+      :transferable-count="preCheckData.transferableCount"
+      :untransferable-count="preCheckData.untransferableCount"
+      :confirm-loading="submitLoading"
+      @confirm="confirmTransfer"
+      @back="backFromPreCheck"
+    />
   </div>
 </template>
 
@@ -250,9 +261,11 @@ import { listTag, getTag, delTag, addTag, updateTag, exportTag } from "@/api/qw/
 import { qwUserList,userList } from "@/api/qw/user";
 import qwUserSelectOne from "@/views/qw/user/qwUserSelectOne.vue";
 import { getMyQwUserList,getMyQwCompanyList } from "@/api/qw/user";
+import transferPreCheckMixin from '@/views/qw/externalContactTransfer/transferPreCheckMixin';
 
 export default {
   name: "ExternalContact",
+  mixins: [transferPreCheckMixin],
   components:{qwUserSelectOne},
   data() {
     return {
@@ -260,6 +273,7 @@ export default {
       loading: true,
       // 导出遮罩层
       exportLoading: false,
+      submitLoading: false,
       // 选中数组
       ids: [],
       // 非单个禁用
@@ -497,33 +511,31 @@ export default {
       this.title = "分配该员工所有客户";
 
     },
-    /** 提交按钮 */
-    submitForm() {
-      this.nickName=null;
-      this.$refs["form"].validate(valid => {
-        if (valid) {
-          const form = {
-            qwUserName: this.qwUserName,
-            type: this.type,
-            filter: this.filter,
-            addType: 0,
-            userId: this.form.userId,
-            corpId: this.queryParams.corpId,
-            needClearTag: this.form.needClearTag
-          };
-          if (this.filter) {
-            form.param = this.buildTransferParam();
-          } else {
-            form.ids = this.ids;
-          }
-          resignedTransfer(form).then(response => {
-            this.msgSuccess(response.msg);
-            this.open = false;
-            this.getList();
-          });
-        }
-      });
-      this.qwUserName=null;
+    beforeTransferSubmit() {
+      this.nickName = null;
+    },
+    onTransferSubmitDone() {
+      this.qwUserName = null;
+    },
+    buildTransferForm() {
+      const form = {
+        qwUserName: this.qwUserName,
+        type: this.type,
+        filter: this.filter,
+        addType: 0,
+        userId: this.form.userId,
+        corpId: this.queryParams.corpId,
+        needClearTag: this.form.needClearTag
+      };
+      if (this.filter) {
+        form.param = this.buildTransferParam();
+      } else {
+        form.ids = this.ids;
+      }
+      return form;
+    },
+    doTransfer(form) {
+      return resignedTransfer(form);
     },
     /** 删除按钮操作 */
     handleDelete(row) {