Quellcode durchsuchen

优化会话记录页面

cgp vor 6 Tagen
Ursprung
Commit
23883cc080

+ 16 - 43
src/api/qw/companySession.js

@@ -3,65 +3,38 @@
 import request from '@/utils/request';
 
 
-/**
- * 企微扫码登录(用 code 换取用户信息)
- * @param {object} param { code: string }
- * @returns {Promise}
- */
-export function qwLogin(param) {
+// 登录
+export function qwLogin({ code, corpid }) {
   return request({
     url: '/weChatSpace/login',
     method: 'post',
-    data: param
+    data: { code, corpid }
   });
 }
 
-/**
- * 获取 agentConfig 签名
- * @param {object} query { url: string }
- * @returns {Promise}
- */
-export function qwSignature(query) {
+// 签名
+export function qwSignature({ url, corpid }) {
   return request({
     url: '/weChatSpace/getAgentConfigSignature',
     method: 'get',
-    params: query
+    params: { url, corpid }
   });
 }
 
-// ----------获取企微专区会话配置 ----------
-export function getQwSessionConfig() {
+// 获取会话记录
+export function qwConversations({ customerId, staffUserId, limit, cursor, corpid }) {
   return request({
-    url: '/weChatSpace/getQwSessionConfig',
-    method: 'get'
+    url: '/weChatSpace/conversations',
+    method: 'get',
+    params: { customerId, staffUserId, limit, cursor, corpid }
   });
 }
 
-
-// ---------- 会话记录 ----------
-
-/**
- * 获取会话记录列表 调用中转接口
- * @param {object} params
- * @returns {Promise}
- */
-
-export async function qwConversations({ customerId, staffUserId, limit = 50, cursor = '', timeout = 30 }) {
-  const params = {
-    limit,
-    timeout,
-    customerId,
-    staffUserId
-  };
-  // 只有 cursor 非空时才传递(后端接口判断空字符串时不传)
-  if (cursor) {
-    params.cursor = cursor;
-  }
-  const res = await request({
-    url: '/weChatSpace/conversations',
-    method: 'get',
-    params
+// 获取配置
+export function getQwSessionConfig(corpid) {
+  return request({
+    url: `/weChatSpace/getQwSessionConfig/${corpid}`,
+    method: 'get'
   });
-  return res;
 }
 

+ 61 - 18
src/views/qw/companySession/ConversationPanel.vue

@@ -51,6 +51,7 @@ const LOGIN_STORAGE_KEY = 'wecom_session_expire';
 export default {
   name: 'ConversationPanel',
   props: {
+    corpId: { type: String, default: null },
     customerId: { type: String, default: null },
     customerAvatar: { type: String, default: '' },
     staffUserId: { type: String, default: null }
@@ -72,19 +73,32 @@ export default {
         domain: ''
       },
       configReady: false,
+      tokenCheckTimer: null  // 声明定时器变量
     };
   },
   watch: {
     customerId(newId, oldId) {
-      if (newId && newId !== oldId && this.isLoggedIn && this.staffUserId && this.configReady) {
+      if (newId && newId !== oldId && this.isLoggedIn && this.staffUserId && this.configReady && this.corpId) {
         this.resetAndReload();
       }
     },
     staffUserId(newStaffId, oldStaffId) {
-      if (newStaffId && newStaffId !== oldStaffId && this.customerId && this.isLoggedIn && this.configReady) {
+      if (newStaffId && newStaffId !== oldStaffId && this.customerId && this.isLoggedIn && this.configReady && this.corpId) {
         this.resetAndReload();
       }
     },
+    corpId(newId, oldId) {
+      if (newId && newId !== oldId) {
+        this.destroyChat();
+        this.clearLoginState();
+        this.isLoggedIn = false;
+        this.isReady = false;
+        this._sdkInited = false;
+        this.configReady = false;
+        this.initConfig();
+        this.handleAuthAndLoad();
+      }
+    },
     customerAvatar: {
       handler(newAvatar) {
         if (this.chatInstance && newAvatar) {
@@ -97,15 +111,34 @@ 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;
+        }
+      }
+    }, 90 * 60 * 1000);
   },
   beforeDestroy() {
+    if (this.tokenCheckTimer) {
+      clearInterval(this.tokenCheckTimer);
+      this.tokenCheckTimer = null;
+    }
     this.destroyChat();
   },
   methods: {
     // ========== 配置初始化 ==========
     async initConfig() {
+      if (!this.corpId) return;
       try {
-        const configRes = await getQwSessionConfig();
+        const configRes = await getQwSessionConfig(this.corpId);
         const configData = this._extractResponse(configRes);
         if (!configData.corpid || !configData.agentid) {
           throw new Error('配置数据不完整');
@@ -132,13 +165,13 @@ export default {
         window.history.replaceState({}, '', newUrl);
         await this.handleLogin(code);
         this.storeLoginState();
-        if (this.customerId && this.staffUserId) {
+        if (this.customerId && this.staffUserId && this.corpId) {
           await this.loadFirstPage();
         }
       } else {
         if (this.checkLoginState()) {
           this.isLoggedIn = true;
-          if (this.customerId && this.staffUserId) {
+          if (this.customerId && this.staffUserId && this.corpId) {
             await this.loadFirstPage();
           }
         } else {
@@ -148,7 +181,7 @@ export default {
     },
 
     async handleLogin(code) {
-      const res = await qwLogin({ code });
+      const res = await qwLogin({ code, corpid: this.corpId });
       const resp = this._extractResponse(res);
       if (resp.errcode !== 0) {
         throw new Error(resp.errmsg || '登录失败');
@@ -157,9 +190,10 @@ export default {
     },
 
     createLoginPanel() {
-      //const redirectUri = this.config.domain + '/companySale/companySession';
+      // const redirectUri = 'http://sestest.ylrzcloud.com' + '/companySale/companySession';
+      // 若需动态获取,可使用:const redirectUri = window.location.href.split('?')[0].split('#')[0];
       const redirectUri = window.location.href.split('?')[0].split('#')[0];
-      console.log("重定向链接:"+redirectUri)
+      console.log("重定向链接:" + redirectUri);
       ww.createWWLoginPanel({
         el: document.getElementById('login-container'),
         params: {
@@ -173,7 +207,7 @@ export default {
         onLoginSuccess: async ({ code }) => {
           await this.handleLogin(code);
           this.storeLoginState();
-          if (this.customerId && this.staffUserId) {
+          if (this.customerId && this.staffUserId && this.corpId) {
             await this.loadFirstPage();
           }
         },
@@ -201,8 +235,21 @@ export default {
     // ========== SDK 与签名 ==========
     async getAgentConfigSignature() {
       const currentUrl = window.location.href.split('#')[0];
-      const res = await qwSignature({ url: currentUrl });
+      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();
+        this.isLoggedIn = false;
+        this.isReady = false;
+        this.destroyChat();
+        this.$nextTick(() => this.createLoginPanel());
+        throw new Error(`签名失败: ${data.errmsg}`);
+      }
+      // 否则认为成功
       return {
         timestamp: data.timestamp,
         nonceStr: data.nonceStr,
@@ -235,7 +282,8 @@ export default {
           customerId: this.customerId,
           staffUserId: this.staffUserId,
           limit: 100,
-          cursor: this.cursor
+          cursor: this.cursor,
+          corpid: this.corpId
         });
         const data = this._extractResponse(res);
         if (data.errcode !== 0) {
@@ -265,7 +313,8 @@ export default {
           customerId: this.customerId,
           staffUserId: this.staffUserId,
           limit: 50,
-          cursor: this.cursor
+          cursor: this.cursor,
+          corpid: this.corpId
         });
         const data = this._extractResponse(res);
         if (data.errcode !== 0) {
@@ -277,7 +326,6 @@ export default {
           this.cursor = data.next_cursor || '';
           this.hasMore = data.has_more === 1;
           if (this.chatInstance) {
-            // 直接更新整个 msgList,模板中使用原始字段 send_time_str
             this.chatInstance.setData({ msgList: this.msgList });
           } else {
             await this.renderOrUpdateChat();
@@ -300,7 +348,6 @@ export default {
       const container = document.getElementById('chat-container');
       if (!container) return;
 
-      // 如果已存在实例,更新数据
       if (this.chatInstance) {
         this.chatInstance.setData({ msgList: this.msgList });
         return;
@@ -366,7 +413,6 @@ export default {
           }
         },
         handleModal: ({ modalUrl, modalSize }) => {
-          // 外部浏览器预览多媒体
           const iframe = document.createElement('iframe');
           iframe.src = modalUrl;
           iframe.style.position = 'fixed';
@@ -395,7 +441,6 @@ export default {
       });
     },
 
-    // ========== 重置与销毁 ==========
     async resetAndReload() {
       this.destroyChat();
       this.msgList = [];
@@ -416,7 +461,6 @@ export default {
       }
     },
 
-    // ========== 工具方法 ==========
     _extractResponse(res) {
       if (!res) return {};
       if (res.errcode !== undefined || res.timestamp || res.corpid) return res;
@@ -427,7 +471,6 @@ export default {
   }
 };
 </script>
-
 <style>
 #chat-container,
 #chat-container > div,

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

@@ -74,7 +74,9 @@
     <!-- 右侧:会话详情面板 -->
     <div class="right-panel">
       <ConversationPanel
+        v-if="queryParams.corpId"
         :key="conversationPanelKey"
+        :corpId="queryParams.corpId"
         :customerId="selectedCustomerId"
         :customerAvatar="selectedCustomerAvatar"
         :staffUserId="selectedStaffQwUserId"
@@ -114,7 +116,6 @@ export default {
       },
       qwCompanyList: [],
       staffList: [],
-      // 用于强制重建子组件
       conversationPanelKey: 0
     };
   },
@@ -141,7 +142,6 @@ export default {
       }
     });
   },
-  // 当从其他页面切回时,强制重建右侧会话面板
   activated() {
     this.conversationPanelKey++;
   },

+ 117 - 73
src/views/system/config/config.vue

@@ -3087,66 +3087,57 @@
       </el-tab-pane>
 
       <el-tab-pane label="企微专区会话配置" name="qw.sessionConfig">
-        <el-form ref="form41" :model="form41" label-width="160px">
-          <!-- 原有的固定配置保持不变 -->
-          <el-form-item label="企业ID" prop="corpid">
-            <el-input v-model="form41.corpid" placeholder="请输入企业ID" style="width:400px"></el-input>
-          </el-form-item>
-          <el-form-item label="自建应用ID" prop="agentid">
-            <el-input v-model="form41.agentid" placeholder="请输入自建应用ID" style="width:400px"></el-input>
-          </el-form-item>
-          <el-form-item label="自建应用可信域名" prop="domain">
-            <el-input v-model="form41.domain" placeholder="请输入自建应用可信域名" style="width:400px"></el-input>
-          </el-form-item>
-          <el-form-item label="自建应用密钥" prop="agentSecret">
-            <el-input v-model="form41.agentSecret" placeholder="请输入自建应用密钥" style="width:400px"></el-input>
-          </el-form-item>
-          <el-form-item label="专区程序id" prop="programId">
-            <el-input v-model="form41.programId" placeholder="请输入专区程序id" style="width:400px"></el-input>
-          </el-form-item>
-          <el-form-item label="会话密钥(私钥)" prop="privateKey" required>
-            <el-input v-model="form41.privateKey" type="textarea" :rows="6" placeholder="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----" style="width:600px"></el-input>
-          </el-form-item>
+        <div v-for="(enterprise, idx) in form41" :key="idx" style="border:1px solid #DCDFE6; margin-bottom:20px; padding:15px; border-radius:8px; position:relative;">
+          <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:10px;">
+            <h4 style="margin:0;">企业配置 {{ idx+1 }}</h4>
+            <el-button type="danger" size="small" icon="el-icon-delete" @click="removeEnterprise(idx)">删除</el-button>
+          </div>
 
-          <!-- 新增:动态能力ID配置区 -->
-          <el-divider content-position="left">能力ID配置</el-divider>
-          <div v-for="(item, index) in form41.abilityIds" :key="index" style="margin-bottom: 10px;">
-            <el-form-item
-              :label="'能力标识 (Key)'"
-              :prop="'abilityIds.' + index + '.key'"
-              :rules="{ required: true, message: '能力标识不能为空', trigger: 'blur' }"
-              style="display: inline-block; width: 35%;"
-            >
-              <el-input v-model="item.key" placeholder="例如:invokeSyncMsg"></el-input>
+          <el-form :model="enterprise" label-width="160px">
+            <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>
-
-            <el-form-item
-              :label="'能力值 (Value)'"
-              :prop="'abilityIds.' + index + '.value'"
-              :rules="{ required: true, message: '能力值不能为空', trigger: 'blur' }"
-              style="display: inline-block; width: 35%; margin-left: 2%;"
-            >
-              <el-input v-model="item.value" placeholder="例如:invoke_sync_msg"></el-input>
+            <el-form-item label="自建应用ID" prop="agentid">
+              <el-input v-model="enterprise.agentid" placeholder="请输入自建应用ID" style="width:400px"></el-input>
+            </el-form-item>
+            <el-form-item label="自建应用密钥" prop="agentSecret">
+              <el-input v-model="enterprise.agentSecret" placeholder="请输入自建应用密钥" style="width:400px"></el-input>
+            </el-form-item>
+            <el-form-item label="专区程序id" prop="programId">
+              <el-input v-model="enterprise.programId" placeholder="请输入专区程序id" style="width:400px"></el-input>
+            </el-form-item>
+            <el-form-item label="会话密钥(私钥)" prop="privateKey" required>
+              <el-input v-model="enterprise.privateKey" type="textarea" :rows="6" placeholder="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----" style="width:600px"></el-input>
             </el-form-item>
 
-            <el-button
-              type="danger"
-              icon="el-icon-delete"
-              circle
-              @click="removeAbilityId(index)"
-              style="margin-left: 10px;"
-            ></el-button>
-          </div>
+            <!-- 能力ID配置 -->
+            <el-divider content-position="left">能力ID配置</el-divider>
+            <div v-for="(ability, aIdx) in enterprise.abilityIds" :key="aIdx" style="margin-bottom:10px;">
+              <el-form-item
+                :label="'能力标识 (Key)'"
+                style="display: inline-block; width: 35%;"
+              >
+                <el-input v-model="ability.key" placeholder="例如:invokeSyncMsg"></el-input>
+              </el-form-item>
+              <el-form-item
+                :label="'能力值 (Value)'"
+                style="display: inline-block; width: 35%; margin-left: 2%;"
+              >
+                <el-input v-model="ability.value" placeholder="例如:invoke_sync_msg"></el-input>
+              </el-form-item>
+              <el-button type="danger" icon="el-icon-delete" circle @click="removeAbilityId(idx, aIdx)"></el-button>
+            </div>
+            <el-button type="primary" size="small" icon="el-icon-plus" @click="addAbilityId(idx)">添加能力ID</el-button>
+          </el-form>
+        </div>
 
-          <el-form-item>
-            <el-button type="primary" icon="el-icon-plus" @click="addAbilityId">添加能力ID</el-button>
-          </el-form-item>
-          <!-- 动态能力ID配置区结束 -->
+        <div style="margin:15px 0;">
+          <el-button type="primary" icon="el-icon-plus" @click="addEnterprise">添加企业配置</el-button>
+        </div>
 
-          <div class="footer">
-            <el-button type="primary" @click="submitQwSessionConfig41">提交</el-button>
-          </div>
-        </el-form>
+        <div class="footer">
+          <el-button type="primary" @click="submitQwSessionConfig41">提交全部配置</el-button>
+        </div>
       </el-tab-pane>
 
     </el-tabs>
@@ -3371,15 +3362,7 @@ export default {
         endIndex: 11,
         numberCalls:1,
       },
-      form41: {
-        corpid: '',
-        agentid: '',
-        domain: '',
-        agentSecret: '',
-        programId: '',
-        privateKey: '',
-        abilityIds: [] //用于存放动态的 key-value 数组
-      },
+      form41: [],   //数组,每一项是一个企业配置对象
       storeProductScrmColumns:[],
       storeScrmColumns: [],
       photoArr: [],
@@ -3874,7 +3857,26 @@ export default {
         }
         if (key == 'qw.sessionConfig') {
           if (response.data && response.data.configValue) {
-            this.form41 = JSON.parse(response.data.configValue);
+            let parsed = JSON.parse(response.data.configValue);
+            // 确保是数组,如果不是,则重置为空数组(清空旧数据)
+            if (!Array.isArray(parsed)) {
+              parsed = [];
+            }
+            // 确保每个元素都有 abilityIds
+            parsed.forEach(ent => {
+              if (!ent.abilityIds) ent.abilityIds = [];
+            });
+            this.form41 = parsed;
+          } else {
+            // 无配置时,默认一个空企业模板
+            this.form41 = [{
+              corpid: '',
+              agentid: '',
+              agentSecret: '',
+              programId: '',
+              privateKey: '',
+              abilityIds: []
+            }];
           }
         }
         if (key == 'his.brand') {
@@ -4426,25 +4428,67 @@ export default {
 
       this.saveConfig40();
     },
-    // 新增一行能力配置
-    addAbilityId() {
-      this.form41.abilityIds.push({ key: '', value: '' });
+    // 添加一个新企业
+    addEnterprise() {
+      this.form41.push({
+        corpid: '',
+        agentid: '',
+        agentSecret: '',
+        programId: '',
+        privateKey: '',
+        abilityIds: []
+      });
     },
-    // 删除指定行
-    removeAbilityId(index) {
-      this.form41.abilityIds.splice(index, 1);
+
+    // 删除指定企业
+    removeEnterprise(index) {
+      this.$confirm('确定删除该企业配置吗?', '提示', { type: 'warning' }).then(() => {
+        this.form41.splice(index, 1);
+        if (this.form41.length === 0) {
+          // 如果全部删光,再给一个空的,避免界面空白
+          this.addEnterprise();
+        }
+      }).catch(() => {});
     },
+
+    // 为某个企业添加能力ID
+    addAbilityId(entIndex) {
+      this.form41[entIndex].abilityIds.push({ key: '', value: '' });
+    },
+
+    // 删除某个企业的某条能力ID
+    removeAbilityId(entIndex, abilityIndex) {
+      this.form41[entIndex].abilityIds.splice(abilityIndex, 1);
+    },
+
+    // 提交多企业配置
     submitQwSessionConfig41() {
+      // 简单校验:至少有一个企业且企业ID和私钥不能为空
+      if (this.form41.length === 0) {
+        this.$message.error('请至少添加一个企业配置');
+        return;
+      }
+      for (let i = 0; i < this.form41.length; i++) {
+        const ent = this.form41[i];
+        if (!ent.corpid) {
+          this.$message.error(`第${i+1}个企业的企业ID不能为空`);
+          return;
+        }
+        if (!ent.privateKey) {
+          this.$message.error(`第${i+1}个企业的会话密钥不能为空`);
+          return;
+        }
+      }
       const param = {
         configId: this.configId,
         configKey: this.configKey,
         configValue: JSON.stringify(this.form41)
-      }
+      };
       updateConfigByKey(param).then(response => {
         if (response.code === 200) {
-          this.msgSuccess('修改成功')
+          this.msgSuccess('保存成功');
         }
-      })
+      });
     },
 
     saveConfig40() {