cgp 1 день назад
Родитель
Сommit
3f66a65737

+ 302 - 107
src/views/qw/companySession/ConversationPanel.vue

@@ -1,11 +1,17 @@
 <template>
   <div class="conversation-panel">
     <!-- 配置未加载 -->
-    <div v-if="!configReady" class="loading-tip">
+    <div v-if="!configReady && !configError && !configLoading" class="loading-tip">
       <i class="el-icon-loading"></i>
       <p>正在加载配置...</p>
     </div>
 
+    <!-- 配置加载失败 -->
+    <div v-else-if="configError" class="empty-tip">
+      <i class="el-icon-warning"></i>
+      <p>企微未配置,请先配置企微应用</p>
+    </div>
+
     <!-- 未登录 -->
     <div v-else-if="!isLoggedIn" class="login-area">
       <div class="login-tip">请先扫码登录企微</div>
@@ -46,7 +52,24 @@ import * as ww from '@wecom/jssdk';
 import defaultStaffAvatar from '@/assets/images/user.png';
 import { qwLogin, qwSignature, qwConversations, getQwSessionConfig } from '@/api/qw/companySession';
 
-const LOGIN_STORAGE_KEY = 'wecom_session_expire';
+// ==================== 全局缓存(挂载到 window,避免 HMR 或快速重建导致缓存丢失) ====================
+window._QW_CONFIG_CACHE = window._QW_CONFIG_CACHE || new Map();     // corpId -> { config, timestamp }
+window._QW_CONFIG_PENDING = window._QW_CONFIG_PENDING || new Map(); // corpId -> Promise
+const CACHE_TTL = 5 * 60 * 1000; // 5分钟
+
+function getCachedConfig(corpId) {
+  if (!corpId) return null;
+  const cached = window._QW_CONFIG_CACHE.get(corpId);
+  if (cached && (Date.now() - cached.timestamp) < CACHE_TTL) {
+    return cached.config;
+  }
+  return null;
+}
+
+function setCachedConfig(corpId, config) {
+  if (!corpId) return;
+  window._QW_CONFIG_CACHE.set(corpId, { config, timestamp: Date.now() });
+}
 
 export default {
   name: 'ConversationPanel',
@@ -73,30 +96,51 @@ export default {
         domain: ''
       },
       configReady: false,
-      tokenCheckTimer: null  // 声明定时器变量
+      configError: false,
+      configLoading: false,
+      tokenCheckTimer: null,
+      // 防止短时间内重复触发 logout 导致父组件频繁重建
+      lastLogoutTime: 0
     };
   },
   watch: {
     customerId(newId, oldId) {
-      if (newId && newId !== oldId && this.isLoggedIn && this.staffUserId && this.configReady && this.corpId) {
+      if (newId && newId !== oldId && this.isLoggedIn && this.staffUserId && this.configReady && this.corpId && !this.configError) {
         this.resetAndReload();
       }
     },
     staffUserId(newStaffId, oldStaffId) {
-      if (newStaffId && newStaffId !== oldStaffId && this.customerId && this.isLoggedIn && this.configReady && this.corpId) {
+      if (newStaffId && newStaffId !== oldStaffId && this.customerId && this.isLoggedIn && this.configReady && this.corpId && !this.configError) {
         this.resetAndReload();
       }
     },
     corpId(newId, oldId) {
-      if (newId && newId !== oldId) {
+      if (newId !== oldId) {
         this.destroyChat();
-        this.clearLoginState();
-        this.isLoggedIn = false;
         this.isReady = false;
         this._sdkInited = false;
-        this.configReady = false;
-        this.initConfig();
-        this.handleAuthAndLoad();
+        this.msgList = [];
+        this.cursor = '';
+        this.hasMore = true;
+        this.loadingMore = false;
+
+        const cachedConfig = getCachedConfig(newId);
+        if (cachedConfig) {
+          this.config = cachedConfig;
+          this.configReady = true;
+          this.configError = false;
+          this.configLoading = false;
+          this.handleAuthAndLoad();
+        } else {
+          this.configReady = false;
+          this.configError = false;
+          this.configLoading = false;
+          this.initConfig().then(() => {
+            if (this.configReady && !this.configError) {
+              this.handleAuthAndLoad();
+            }
+          });
+        }
       }
     },
     customerAvatar: {
@@ -109,20 +153,23 @@ export default {
     }
   },
   async mounted() {
-    await this.initConfig();
-    await this.handleAuthAndLoad();
-    console.log('configReady:', this.configReady);
-    console.log('corpId:', this.corpId);
-    // 每 1.5 小时检查一次登录态
-    this.tokenCheckTimer = setInterval(async () => {
-      if (this.isLoggedIn && this.configReady) {
-        try {
-          await this.getAgentConfigSignature();
-        } catch (e) {
-          // 签名失败会触发重新登录,清除定时器
-          clearInterval(this.tokenCheckTimer);
-          this.tokenCheckTimer = null;
-        }
+    const cachedConfig = getCachedConfig(this.corpId);
+    if (cachedConfig) {
+      this.config = cachedConfig;
+      this.configReady = true;
+      this.configError = false;
+      this.configLoading = false;
+      await this.handleAuthAndLoad();
+    } else {
+      await this.initConfig();
+      if (this.configReady && !this.configError) {
+        await this.handleAuthAndLoad();
+      }
+    }
+
+    this.tokenCheckTimer = setInterval(() => {
+      if (this.isLoggedIn && this.configReady && !this.configError && this._sdkInited) {
+        this.getAgentConfigSignature().catch(e => console.warn('签名刷新失败', e));
       }
     }, 90 * 60 * 1000);
   },
@@ -134,68 +181,166 @@ export default {
     this.destroyChat();
   },
   methods: {
-    // ========== 配置初始化 ==========
+    getStorageKey(corpId, suffix = 'expire') {
+      if (!corpId) return null;
+      return `wecom_session_${corpId}_${suffix}`;
+    },
+
+    checkLoginState(corpId) {
+      if (!corpId) return false;
+      const expireKey = this.getStorageKey(corpId, 'expire');
+      const expire = localStorage.getItem(expireKey);
+      if (!expire) return false;
+      return Date.now() < parseInt(expire, 10);
+    },
+
+    storeLoginState(corpId) {
+      if (!corpId) return;
+      const expireKey = this.getStorageKey(corpId, 'expire');
+      localStorage.setItem(expireKey, (Date.now() + 115 * 60 * 1000).toString());
+    },
+
+    clearLoginState(corpId) {
+      if (!corpId) return;
+      const expireKey = this.getStorageKey(corpId, 'expire');
+      localStorage.removeItem(expireKey);
+    },
+
     async initConfig() {
       if (!this.corpId) return;
-      try {
-        const configRes = await getQwSessionConfig(this.corpId);
-        const configData = this._extractResponse(configRes);
-        if (!configData.corpid || !configData.agentid) {
-          throw new Error('配置数据不完整');
-        }
-        this.config = {
-          corpid: configData.corpid,
-          agentid: String(configData.agentid),
-          domain: configData.domain || ''
-        };
+      if (this.configReady) return;
+      if (this.configError) return;
+
+      const cached = getCachedConfig(this.corpId);
+      if (cached) {
+        this.config = cached;
         this.configReady = true;
-      } catch (e) {
-        console.error('获取企微配置失败:', e);
-        this.$message.error('获取企微配置失败,请刷新重试');
+        this.configError = false;
+        this.configLoading = false;
+        return;
       }
+
+      // 全局并发锁
+      if (window._QW_CONFIG_PENDING.has(this.corpId)) {
+        return window._QW_CONFIG_PENDING.get(this.corpId);
+      }
+
+      this.configLoading = true;
+      const requestPromise = (async () => {
+        try {
+          const configRes = await getQwSessionConfig(this.corpId);
+          const configData = this._extractResponse(configRes);
+          if (!configData.corpid || !configData.agentid) {
+            throw new Error('配置数据不完整');
+          }
+          const cfg = {
+            corpid: configData.corpid,
+            agentid: String(configData.agentid),
+            domain: configData.domain || ''
+          };
+          this.config = cfg;
+          this.configReady = true;
+          this.configError = false;
+          setCachedConfig(this.corpId, cfg);
+        } catch (e) {
+          console.error('获取企微配置失败:', e);
+          this.configReady = false;
+          this.configError = true;
+        } finally {
+          this.configLoading = false;
+          window._QW_CONFIG_PENDING.delete(this.corpId);
+        }
+      })();
+      window._QW_CONFIG_PENDING.set(this.corpId, requestPromise);
+      return requestPromise;
     },
 
-    // ========== 登录与授权 ==========
     async handleAuthAndLoad() {
+      if (this.configError) return;
+      if (!this.configReady) {
+        await this.initConfig();
+        if (this.configError) return;
+      }
+
       const urlParams = new URLSearchParams(window.location.search);
       const code = urlParams.get('code');
 
       if (code) {
         const newUrl = window.location.origin + window.location.pathname;
         window.history.replaceState({}, '', newUrl);
-        await this.handleLogin(code);
-        this.storeLoginState();
-        if (this.customerId && this.staffUserId && this.corpId) {
-          await this.loadFirstPage();
+        try {
+          await this.handleLogin(code);
+          this.storeLoginState(this.corpId);
+          this.isLoggedIn = true;
+          if (this.customerId && this.staffUserId) {
+            await this.loadFirstPage();
+          }
+        } catch (loginErr) {
+          console.error('登录失败', loginErr);
+          this.clearLoginState(this.corpId);
+          this.isLoggedIn = false;
+          this.emitLogoutWithThrottle();
         }
       } else {
-        if (this.checkLoginState()) {
+        if (this.checkLoginState(this.corpId)) {
           this.isLoggedIn = true;
-          if (this.customerId && this.staffUserId && this.corpId) {
+          if (this.customerId && this.staffUserId) {
             await this.loadFirstPage();
           }
         } else {
-          this.$nextTick(() => this.createLoginPanel());
+          this.isLoggedIn = false;
+          this.emitLogoutWithThrottle();
+          this.$nextTick(() => {
+            this.createLoginPanelWithRetry();
+          });
         }
       }
     },
 
+    // 防止短时间内多次触发 logout 导致父组件疯狂重建
+    emitLogoutWithThrottle() {
+      const now = Date.now();
+      if (now - this.lastLogoutTime > 1000) {
+        this.lastLogoutTime = now;
+        this.$emit('logout');
+      }
+    },
+
     async handleLogin(code) {
       const res = await qwLogin({ code, corpid: this.corpId });
       const resp = this._extractResponse(res);
       if (resp.errcode !== 0) {
         throw new Error(resp.errmsg || '登录失败');
       }
+      this.storeLoginState(this.corpId);
       this.isLoggedIn = true;
+      this.$emit('login-success');
+    },
+
+    createLoginPanelWithRetry(maxWait = 3000, interval = 200) {
+      const startTime = Date.now();
+      const tryCreate = () => {
+        if (!this.configReady || this.configError || !this.config.corpid || !this.config.agentid) {
+          console.warn('配置未就绪,无法创建登录面板');
+          return;
+        }
+        const container = document.getElementById('login-container');
+        if (container) {
+          this._doCreateLoginPanel(container);
+        } else if (Date.now() - startTime < maxWait) {
+          setTimeout(tryCreate, interval);
+        } else {
+          console.error('登录容器未找到,超时放弃');
+          this.$message.error('无法加载登录面板,请刷新页面重试');
+        }
+      };
+      tryCreate();
     },
 
-    createLoginPanel() {
-      // const redirectUri = 'http://sestest.ylrzcloud.com' + '/companySale/companySession';
-      // 若需动态获取,可使用:const redirectUri = window.location.href.split('?')[0].split('#')[0];
+    _doCreateLoginPanel(container) {
       const redirectUri = window.location.href.split('?')[0].split('#')[0];
-      console.log("重定向链接:" + redirectUri);
       ww.createWWLoginPanel({
-        el: document.getElementById('login-container'),
+        el: container,
         params: {
           login_type: 'CorpApp',
           appid: this.config.corpid,
@@ -206,50 +351,37 @@ export default {
         },
         onLoginSuccess: async ({ code }) => {
           await this.handleLogin(code);
-          this.storeLoginState();
           if (this.customerId && this.staffUserId && this.corpId) {
             await this.loadFirstPage();
           }
         },
         onLoginError: (err) => {
           console.error('登录面板错误:', err);
-          this.clearLoginState();
+          this.clearLoginState(this.corpId);
+          this.isLoggedIn = false;
+          this.emitLogoutWithThrottle();
         }
       });
     },
 
-    checkLoginState() {
-      const stored = localStorage.getItem(LOGIN_STORAGE_KEY);
-      if (!stored) return false;
-      return Date.now() < parseInt(stored, 10);
-    },
-
-    storeLoginState() {
-      localStorage.setItem(LOGIN_STORAGE_KEY, (Date.now() + 30 * 60 * 1000).toString());
-    },
-
-    clearLoginState() {
-      localStorage.removeItem(LOGIN_STORAGE_KEY);
-    },
-
-    // ========== SDK 与签名 ==========
     async getAgentConfigSignature() {
+      if (!this.configReady || this.configError) {
+        throw new Error('配置未就绪,无法获取签名');
+      }
       const currentUrl = window.location.href.split('#')[0];
-      console.log('请求签名的 URL:', currentUrl);
       const res = await qwSignature({ url: currentUrl, corpid: this.corpId });
       const data = this._extractResponse(res);
-      console.log('签名响应:', data);
-      // 如果有 errcode 且不为 0,则认为失败
       if (data.errcode && data.errcode !== 0) {
-        console.error('获取签名失败,将重新登录', data);
-        this.clearLoginState();
+        console.error('获取签名失败,将清除登录状态', data);
+        this.clearLoginState(this.corpId);
         this.isLoggedIn = false;
         this.isReady = false;
         this.destroyChat();
-        this.$nextTick(() => this.createLoginPanel());
+        if (!this.configError) {
+          this.emitLogoutWithThrottle();
+        }
         throw new Error(`签名失败: ${data.errmsg}`);
       }
-      // 否则认为成功
       return {
         timestamp: data.timestamp,
         nonceStr: data.nonceStr,
@@ -259,6 +391,10 @@ export default {
 
     async initSDKOnce() {
       if (this._sdkInited) return true;
+      if (!this.configReady || this.configError) {
+        console.warn('SDK初始化失败:配置未就绪');
+        return false;
+      }
       try {
         await ww.register({
           corpId: this.config.corpid,
@@ -275,8 +411,11 @@ export default {
       }
     },
 
-    // ========== 数据拉取 ==========
     async loadFirstPage() {
+      if (this.configError) {
+        this.$message.error('企微配置错误,无法加载会话');
+        return;
+      }
       try {
         const res = await qwConversations({
           customerId: this.customerId,
@@ -306,7 +445,7 @@ export default {
     },
 
     async loadMore() {
-      if (!this.cursor || this.loadingMore || !this.hasMore) return;
+      if (!this.cursor || this.loadingMore || !this.hasMore || this.configError) return;
       this.loadingMore = true;
       try {
         const res = await qwConversations({
@@ -326,7 +465,7 @@ export default {
           this.cursor = data.next_cursor || '';
           this.hasMore = data.has_more === 1;
           if (this.chatInstance) {
-            this.chatInstance.setData({ msgList: this.msgList });
+            this.chatInstance.setData({msgList: this.msgList});
           } else {
             await this.renderOrUpdateChat();
           }
@@ -341,15 +480,15 @@ export default {
       }
     },
 
-    // ========== 组件渲染与更新 ==========
     async renderOrUpdateChat() {
       if (this.msgList.length === 0) return;
+      if (this.configError) return;
 
       const container = document.getElementById('chat-container');
       if (!container) return;
 
       if (this.chatInstance) {
-        this.chatInstance.setData({ msgList: this.msgList });
+        this.chatInstance.setData({msgList: this.msgList});
         return;
       }
 
@@ -373,7 +512,8 @@ export default {
           <scroll-view scroll-y="{{true}}" bindscrolltolower="onScrollToLower" style="height: 100%;">
             <view wx:for="{{data.msgList}}" wx:for-item="msg" wx:key="msgid" style="margin-bottom: 15px;">
               <view style="text-align: center; margin-bottom: 8px;">
-                <text style="background:#e9e9e9; color:#333; padding:4px 12px; border-radius:16px; font-size:12px; font-weight:500;">
+                <text
+                  style="background:#e9e9e9; color:#333; padding:4px 12px; border-radius:16px; font-size:12px; font-weight:500;">
                   {{ msg.send_time_str }}
                 </text>
               </view>
@@ -397,7 +537,7 @@ export default {
         data: templateData,
         methods: {
           onScrollToLower: () => {
-            if (this.hasMore && !this.loadingMore) {
+            if (this.hasMore && !this.loadingMore && !this.configError) {
               this.loadMore();
             }
           }
@@ -405,37 +545,91 @@ export default {
         error: (e) => {
           console.error('[企微组件] 错误', e);
           if (e && (e.errCode === 42006 || e.errCode === 42003 || e.errCode === 40029)) {
-            this.clearLoginState();
-            this.isLoggedIn = false;
-            this.isReady = false;
-            this.destroyChat();
-            this.$nextTick(() => this.createLoginPanel());
+            if (!this.configError) {
+              this.clearLoginState(this.corpId);
+              this.isLoggedIn = false;
+              this.isReady = false;
+              this.destroyChat();
+              this.emitLogoutWithThrottle();
+              this.$nextTick(() => this.createLoginPanelWithRetry());
+            } else {
+              console.warn('登录态失效,但由于配置错误,不自动重新登录');
+            }
           }
         },
-        handleModal: ({ modalUrl, modalSize }) => {
+        handleModal: ({modalUrl, modalSize}) => {
+          const mask = document.createElement('div');
+          mask.style.position = 'fixed';
+          mask.style.top = '0';
+          mask.style.left = '0';
+          mask.style.width = '100%';
+          mask.style.height = '100%';
+          mask.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
+          mask.style.zIndex = 9999;
+          mask.style.display = 'flex';
+          mask.style.alignItems = 'center';
+          mask.style.justifyContent = 'center';
+
+          const content = document.createElement('div');
+          content.style.position = 'relative';
+          content.style.maxWidth = '90vw';
+          content.style.maxHeight = '90vh';
+          content.style.width = modalSize?.width ? `${modalSize.width}px` : '80%';
+          content.style.height = modalSize?.height ? `${modalSize.height}px` : '80%';
+          content.style.backgroundColor = '#fff';
+          content.style.borderRadius = '8px';
+          content.style.overflow = 'hidden';
+          content.style.boxShadow = '0 0 20px rgba(0,0,0,0.3)';
+
+          const closeBtn = document.createElement('button');
+          closeBtn.innerText = '✕';
+          closeBtn.style.position = 'absolute';
+          closeBtn.style.top = '10px';
+          closeBtn.style.right = '10px';
+          closeBtn.style.zIndex = 10001;
+          closeBtn.style.width = '32px';
+          closeBtn.style.height = '32px';
+          closeBtn.style.borderRadius = '50%';
+          closeBtn.style.border = 'none';
+          closeBtn.style.backgroundColor = 'rgba(0,0,0,0.5)';
+          closeBtn.style.color = '#fff';
+          closeBtn.style.fontSize = '20px';
+          closeBtn.style.cursor = 'pointer';
+          closeBtn.style.display = 'flex';
+          closeBtn.style.alignItems = 'center';
+          closeBtn.style.justifyContent = 'center';
+          closeBtn.onclick = (e) => {
+            e.stopPropagation();
+            mask.remove();
+            document.removeEventListener('keydown', escHandler);
+          };
+
           const iframe = document.createElement('iframe');
           iframe.src = modalUrl;
-          iframe.style.position = 'fixed';
-          iframe.style.top = '50%';
-          iframe.style.left = '50%';
-          iframe.style.transform = 'translate(-50%, -50%)';
-          iframe.style.width = modalSize?.width ? `${modalSize.width}px` : '80%';
-          iframe.style.height = modalSize?.height ? `${modalSize.height}px` : '80%';
-          iframe.style.maxWidth = '90vw';
-          iframe.style.maxHeight = '90vh';
-          iframe.style.zIndex = 10000;
+          iframe.style.width = '100%';
+          iframe.style.height = '100%';
           iframe.style.border = 'none';
-          iframe.style.boxShadow = '0 0 20px rgba(0,0,0,0.3)';
-          iframe.style.backgroundColor = '#fff';
-          document.body.appendChild(iframe);
-          iframe.onclick = () => iframe.remove();
-          const onKeyDown = (e) => {
+
+          content.appendChild(iframe);
+          content.appendChild(closeBtn);
+          mask.appendChild(content);
+          document.body.appendChild(mask);
+
+          mask.onclick = (e) => {
+            if (e.target === mask) {
+              mask.remove();
+              document.removeEventListener('keydown', escHandler);
+            }
+          };
+
+          const escHandler = (e) => {
             if (e.key === 'Escape') {
-              iframe.remove();
-              document.removeEventListener('keydown', onKeyDown);
+              mask.remove();
+              document.removeEventListener('keydown', escHandler);
             }
           };
-          document.addEventListener('keydown', onKeyDown);
+          document.addEventListener('keydown', escHandler);
+
           return true;
         }
       });
@@ -471,6 +665,7 @@ export default {
   }
 };
 </script>
+
 <style>
 #chat-container,
 #chat-container > div,

+ 44 - 2
src/views/qw/companySession/index.vue

@@ -80,6 +80,8 @@
         :customerId="selectedCustomerId"
         :customerAvatar="selectedCustomerAvatar"
         :staffUserId="selectedStaffQwUserId"
+        @logout="handleConversationLogout"
+        @login-success="handleConversationLoginSuccess"
       />
     </div>
   </div>
@@ -146,14 +148,46 @@ export default {
     this.conversationPanelKey++;
   },
   methods: {
+    // ========== 子组件事件处理 ==========
+    // 当会话组件检测到登录态失效时触发,清空选中的客户和员工,并清空客户列表
+    handleConversationLogout() {
+      this.selectedCustomerId = null;
+      this.selectedCustomerAvatar = null;
+      this.queryParams.qwUserId = null;
+      this.externalContactList = [];
+      this.total = 0;
+      // 可选:强制刷新右侧面板,避免状态残留
+      this.conversationPanelKey++;
+    },
+
+    // 当会话组件登录成功时触发,可重新加载员工列表(如果依赖授权)
+    handleConversationLoginSuccess() {
+      if (this.queryParams.corpId) {
+        this.fetchQwUserList(this.queryParams.corpId);
+      }
+      // 如果之前有选中的员工,自动重新查询客户列表
+      if (this.queryParams.qwUserId) {
+        this.handleQuery();
+      }
+    },
+
+    // ========== 原有方法,增强切换公司时的清理逻辑 ==========
     handleCorpChange(corpId) {
+      // 清空员工选择、客户列表和选中状态
       this.queryParams.qwUserId = null;
+      this.selectedCustomerId = null;
+      this.selectedCustomerAvatar = null;
+      this.externalContactList = [];
+      this.total = 0;
       if (corpId) {
         this.fetchQwUserList(corpId);
       } else {
         this.staffList = [];
       }
+      // 强制刷新右侧会话面板(通过改变 key 值)
+      this.conversationPanelKey++;
     },
+
     async fetchQwUserList(corpId) {
       try {
         const res = await listAllQwUserList({corpId});
@@ -170,6 +204,7 @@ export default {
         this.staffList = [];
       }
     },
+
     getAllUserlist(companyId) {
       if (companyId) {
         getAllUserlist({companyId}).then(response => {
@@ -177,7 +212,12 @@ export default {
         });
       }
     },
+
     getList() {
+      if (!this.queryParams.corpId || !this.queryParams.qwUserId) {
+        this.$message.warning('请完整选择企微主体和员工');
+        return;
+      }
       this.loading = true;
       listExternalContact(this.queryParams).then(response => {
         this.externalContactList = response.rows;
@@ -187,6 +227,7 @@ export default {
         this.loading = false;
       });
     },
+
     handleQuery() {
       if (!this.queryParams.qwUserId) {
         this.$message.warning('请先选择企微员工');
@@ -195,6 +236,7 @@ export default {
       this.queryParams.pageNum = 1;
       this.getList();
     },
+
     handleRowClick(row) {
       this.selectedCustomerId = row.externalUserId;
       this.selectedCustomerAvatar = row.avatar;
@@ -211,8 +253,8 @@ export default {
 }
 
 .left-panel {
-  width: 420px;
-  min-width: 380px;
+  width: 460px;
+  min-width: 400px;
   flex-shrink: 0;
   border-right: 1px solid #e8e8e8;
   background: #fff;

+ 5 - 0
src/views/system/config/config.vue

@@ -3094,6 +3094,9 @@
           </div>
 
           <el-form :model="enterprise" label-width="160px">
+            <el-form-item label="企业名称" prop="corpName" :rules="{ required: true, message: '企业名称不能为空', trigger: 'blur' }">
+              <el-input v-model="enterprise.corpName" placeholder="请输入企业名称" style="width:400px"></el-input>
+            </el-form-item>
             <el-form-item label="企业ID" prop="corpid" :rules="{ required: true, message: '企业ID不能为空', trigger: 'blur' }">
               <el-input v-model="enterprise.corpid" placeholder="请输入企业ID" style="width:400px"></el-input>
             </el-form-item>
@@ -3871,6 +3874,7 @@ export default {
             // 无配置时,默认一个空企业模板
             this.form41 = [{
               corpid: '',
+              corpName: '',
               agentid: '',
               agentSecret: '',
               programId: '',
@@ -4432,6 +4436,7 @@ export default {
     addEnterprise() {
       this.form41.push({
         corpid: '',
+        corpName: '',
         agentid: '',
         agentSecret: '',
         programId: '',