update nickname
This commit is contained in:
		| @ -1,7 +1,7 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const path = require("path"); | ||||
| const { getBaseDir } = require("ee-core/ps"); | ||||
| const { getBaseDir, getElectronDir } = require("ee-core/ps"); | ||||
|  | ||||
| /** | ||||
|  * 默认配置 | ||||
| @ -21,7 +21,7 @@ module.exports = () => { | ||||
|         // webSecurity: false, | ||||
|         contextIsolation: false, // false -> 可在渲染进程中使用electron的api,true->需要bridge.js(contextBridge) | ||||
|         nodeIntegration: true, | ||||
|         // preload: path.join(getElectronDir(),  'bridge.js'), | ||||
|         preload: path.join(getElectronDir(), 'preload', 'bridge.js'), // 添加preload脚本 | ||||
|       }, | ||||
|       titleBarStyle: "hidden", | ||||
|       frame: true, | ||||
|  | ||||
| @ -15,6 +15,10 @@ class SystemController { | ||||
|         return await systemService.getBaseInfo(args,event); | ||||
|     } | ||||
|  | ||||
|     async getUserDataPath(args, event) { | ||||
|         return await systemService.getUserDataPath(args, event); | ||||
|     } | ||||
|  | ||||
|     async login(args,event) { | ||||
|         return await systemService.login(args,event); | ||||
|     } | ||||
|  | ||||
| @ -2,18 +2,132 @@ | ||||
|  * 如果启用了上下文隔离,渲染进程无法使用electron的api, | ||||
|  * 可通过contextBridge 导出api给渲染进程使用 | ||||
|  */ | ||||
| const { contextBridge, ipcRenderer } = require("electron"); | ||||
| try { | ||||
|   const { contextBridge, ipcRenderer } = require("electron"); | ||||
|  | ||||
| contextBridge.exposeInMainWorld("electronAPI", { | ||||
|   // ipcRenderer: ipcRenderer, | ||||
|   onDownloadProgress: (callback) => { | ||||
|     const listener = (_, progress) => callback(progress); | ||||
|     ipcRenderer.on("download-progress", listener); | ||||
|     return () => ipcRenderer.removeListener("download-progress", listener); | ||||
|   }, | ||||
|   onUpdateDownloaded: (callback) => { | ||||
|     const listener = () => callback(); | ||||
|     ipcRenderer.on("update-downloaded", listener); | ||||
|     return () => ipcRenderer.removeListener("update-downloaded", listener); | ||||
|   }, | ||||
| }); | ||||
|   if (contextBridge && contextBridge.exposeInMainWorld) { | ||||
|     contextBridge.exposeInMainWorld("electronAPI", { | ||||
|       // ipcRenderer: ipcRenderer, | ||||
|  | ||||
|       // 添加invoke方法用于昵称管理 | ||||
|       invoke: (channel, data) => { | ||||
|         return ipcRenderer.invoke(channel, data); | ||||
|       }, | ||||
|  | ||||
|       // 添加send方法 | ||||
|       send: (channel, data) => { | ||||
|         return ipcRenderer.send(channel, data); | ||||
|       }, | ||||
|  | ||||
|       // 添加on方法用于监听事件 | ||||
|       on: (channel, listener) => { | ||||
|         return ipcRenderer.on(channel, listener); | ||||
|       }, | ||||
|  | ||||
|       // 添加removeListener方法 | ||||
|       removeListener: (channel, listener) => { | ||||
|         return ipcRenderer.removeListener(channel, listener); | ||||
|       }, | ||||
|  | ||||
|       // 添加removeAllListeners方法 | ||||
|       removeAllListeners: (channel) => { | ||||
|         return ipcRenderer.removeAllListeners(channel); | ||||
|       }, | ||||
|  | ||||
|       // 原有的方法 | ||||
|       onDownloadProgress: (callback) => { | ||||
|         const listener = (_, progress) => callback(progress); | ||||
|         ipcRenderer.on("download-progress", listener); | ||||
|         return () => ipcRenderer.removeListener("download-progress", listener); | ||||
|       }, | ||||
|       onUpdateDownloaded: (callback) => { | ||||
|         const listener = () => callback(); | ||||
|         ipcRenderer.on("update-downloaded", listener); | ||||
|         return () => ipcRenderer.removeListener("update-downloaded", listener); | ||||
|       }, | ||||
|       onUpdateAvailable: (callback) => { | ||||
|         const listener = () => callback(); | ||||
|         ipcRenderer.on("update-available", listener); | ||||
|         return () => ipcRenderer.removeListener("update-available", listener); | ||||
|       }, | ||||
|     }); | ||||
|  | ||||
|     console.log('✅ electronAPI 已通过 contextBridge 暴露'); | ||||
|   } else { | ||||
|     console.log('⚠️ contextBridge 不可用,尝试直接挂载'); | ||||
|  | ||||
|     // 直接挂载到window(不安全但可用) | ||||
|     const { ipcRenderer } = require("electron"); | ||||
|  | ||||
|     window.electronAPI = { | ||||
|       invoke: (channel, data) => { | ||||
|         return ipcRenderer.invoke(channel, data); | ||||
|       }, | ||||
|       send: (channel, data) => { | ||||
|         return ipcRenderer.send(channel, data); | ||||
|       }, | ||||
|       on: (channel, listener) => { | ||||
|         return ipcRenderer.on(channel, listener); | ||||
|       }, | ||||
|       removeListener: (channel, listener) => { | ||||
|         return ipcRenderer.removeListener(channel, listener); | ||||
|       }, | ||||
|       removeAllListeners: (channel) => { | ||||
|         return ipcRenderer.removeAllListeners(channel); | ||||
|       }, | ||||
|       onDownloadProgress: (callback) => { | ||||
|         const listener = (_, progress) => callback(progress); | ||||
|         ipcRenderer.on("download-progress", listener); | ||||
|         return () => ipcRenderer.removeListener("download-progress", listener); | ||||
|       }, | ||||
|       onUpdateDownloaded: (callback) => { | ||||
|         const listener = () => callback(); | ||||
|         ipcRenderer.on("update-downloaded", listener); | ||||
|         return () => ipcRenderer.removeListener("update-downloaded", listener); | ||||
|       }, | ||||
|       onUpdateAvailable: (callback) => { | ||||
|         const listener = () => callback(); | ||||
|         ipcRenderer.on("update-available", listener); | ||||
|         return () => ipcRenderer.removeListener("update-available", listener); | ||||
|       }, | ||||
|     }; | ||||
|  | ||||
|     console.log('✅ electronAPI 已直接挂载到 window'); | ||||
|   } | ||||
| } catch (error) { | ||||
|   console.error('❌ bridge.js 加载失败:', error.message); | ||||
|  | ||||
|   // 最后的fallback - 尝试在渲染进程中创建 | ||||
|   if (typeof window !== 'undefined') { | ||||
|     try { | ||||
|       const { ipcRenderer } = require("electron"); | ||||
|  | ||||
|       window.electronAPI = { | ||||
|         invoke: (channel, data) => { | ||||
|           return ipcRenderer.invoke(channel, data); | ||||
|         }, | ||||
|         send: (channel, data) => { | ||||
|           return ipcRenderer.send(channel, data); | ||||
|         }, | ||||
|         on: (channel, listener) => { | ||||
|           return ipcRenderer.on(channel, listener); | ||||
|         }, | ||||
|         removeListener: (channel, listener) => { | ||||
|           return ipcRenderer.removeListener(channel, listener); | ||||
|         }, | ||||
|         removeAllListeners: (channel) => { | ||||
|           return ipcRenderer.removeAllListeners(channel); | ||||
|         }, | ||||
|         onUpdateAvailable: (callback) => { | ||||
|           const listener = () => callback(); | ||||
|           ipcRenderer.on("update-available", listener); | ||||
|           return () => ipcRenderer.removeListener("update-available", listener); | ||||
|         }, | ||||
|       }; | ||||
|  | ||||
|       console.log('✅ electronAPI fallback 创建成功'); | ||||
|     } catch (fallbackError) { | ||||
|       console.error('❌ electronAPI fallback 也失败了:', fallbackError.message); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -5,6 +5,11 @@ | ||||
| const { contextBridge, ipcRenderer } = require("electron"); | ||||
|  | ||||
| contextBridge.exposeInMainWorld("electronAPI", { | ||||
|   // 通用invoke方法 - 用于昵称管理 | ||||
|   invoke: (channel, data) => { | ||||
|     return ipcRenderer.invoke(channel, data); | ||||
|   }, | ||||
|  | ||||
|   ipcRenderer: ipcRenderer, | ||||
|   loginNotify: (args) => { | ||||
|     return ipcRenderer.invoke("online-notify", args); | ||||
| @ -34,6 +39,26 @@ contextBridge.exposeInMainWorld("electronAPI", { | ||||
|   getLanguage: (args) => { | ||||
|     return ipcRenderer.invoke("get-language", args); | ||||
|   }, | ||||
|  | ||||
|   // 昵称管理专用方法 | ||||
|   saveContactNickname: (args) => { | ||||
|     return ipcRenderer.invoke("save-contact-nickname", args); | ||||
|   }, | ||||
|   getContactNickname: (args) => { | ||||
|     return ipcRenderer.invoke("get-contact-nickname", args); | ||||
|   }, | ||||
|   getAllNicknames: () => { | ||||
|     return ipcRenderer.invoke("get-all-nicknames"); | ||||
|   }, | ||||
|  | ||||
|   // 调试方法 | ||||
|   debugContactList: (args) => { | ||||
|     return ipcRenderer.invoke("debug-contact-list", args); | ||||
|   }, | ||||
|   debugCurrentChat: (args) => { | ||||
|     return ipcRenderer.invoke("debug-current-chat", args); | ||||
|   }, | ||||
|  | ||||
|   // 增强监控功能的IPC方法 | ||||
|   avatarChangeNotify: (args) => { | ||||
|     return ipcRenderer.invoke("avatar-change-notify", args); | ||||
| @ -54,6 +79,5 @@ contextBridge.exposeInMainWorld("electronAPI", { | ||||
|     return ipcRenderer.invoke("detect-language", args); | ||||
|   }, | ||||
|  | ||||
|  | ||||
|   onMessageFromMain: (callback) => ipcRenderer.on('message-from-main', (event, message) => callback(message)), | ||||
| }); | ||||
|  | ||||
| @ -8,8 +8,7 @@ const { translateService } = require("../service/translate"); | ||||
| const { contactInfoService } = require("../service/contactInfo"); | ||||
| const { startDownload, quitAndInstall, checkUpdate } = require("../updater/index"); | ||||
|  | ||||
| // const setupBridge = require("./bridge"); | ||||
| // setupBridge(); // ✅ 主动挂载 electronAPI 到 window | ||||
| // require("./bridge"); // ❌ bridge.js 只能在WebView的preload环境中使用,不能在主窗口中使用 | ||||
|  | ||||
| const preload = async () => { | ||||
|   ipcMainListener(); | ||||
| @ -304,6 +303,19 @@ const ipcMainListener = () => { | ||||
|     const sessionObj = await app.sdb.selectOne("session_list", { | ||||
|       windowId: senderId, | ||||
|     }); | ||||
|  | ||||
|     logger.info("msg-count-change 查找会话结果:", { | ||||
|       platform, | ||||
|       windowId: senderId, | ||||
|       sessionFound: !!sessionObj, | ||||
|       sessionData: sessionObj ? { | ||||
|         partitionId: sessionObj.partitionId, | ||||
|         platform: sessionObj.platform, | ||||
|         windowId: sessionObj.windowId, | ||||
|         currentMsgCount: sessionObj.msgCount | ||||
|       } : null | ||||
|     }); | ||||
|  | ||||
|     if (sessionObj) { | ||||
|       logger.info("msg-count-change condition:", { platform, windowId: senderId }); | ||||
|       let resp= await app.sdb.update( | ||||
| @ -312,10 +324,29 @@ const ipcMainListener = () => { | ||||
|         { platform, windowId: senderId } | ||||
|       ); | ||||
|  | ||||
|       logger.info("数据库更新结果:", { | ||||
|         updateResult: resp, | ||||
|         newMsgCount: msgCount, | ||||
|         platform, | ||||
|         windowId: senderId | ||||
|       }); | ||||
|  | ||||
|        // 通知渲染进程 发送前再次校验窗口状态 | ||||
|       if (!mainWin.isDestroyed()) { | ||||
|         mainWin.webContents.send("msg-count-notify", { msgCount:msgCount,platform,windowId:senderId }); | ||||
|         const notifyData = { msgCount:msgCount,platform,windowId:senderId }; | ||||
|         mainWin.webContents.send("msg-count-notify", notifyData); | ||||
|         logger.info("发送前端通知:", notifyData); | ||||
|       } | ||||
|     } else { | ||||
|       logger.warn("未找到对应的会话记录:", { platform, windowId: senderId }); | ||||
|  | ||||
|       // 查找所有相关会话用于调试 | ||||
|       const allSessions = await app.sdb.select("session_list", { platform }); | ||||
|       logger.info("该平台的所有会话:", allSessions.map(s => ({ | ||||
|         partitionId: s.partitionId, | ||||
|         windowId: s.windowId, | ||||
|         windowStatus: s.windowStatus | ||||
|       }))); | ||||
|     } | ||||
|   }); | ||||
|  | ||||
| @ -358,6 +389,12 @@ const ipcMainListener = () => { | ||||
|     return { status: true, data: app.wsBaseUrl }; | ||||
|   }); | ||||
|  | ||||
|   // 添加getUserDataPath处理器 | ||||
|   ipcMain.handle("getUserDataPath", async (event, args) => { | ||||
|     const { systemService } = require("../service/system"); | ||||
|     return await systemService.getUserDataPath(args, event); | ||||
|   }); | ||||
|  | ||||
|  | ||||
|   // 语言检测(转发到后端) | ||||
|   ipcMain.handle("detect-language", async (event, args) => { | ||||
| @ -605,6 +642,96 @@ const ipcMainListener = () => { | ||||
|       return { status: false, message: `打开失败: ${error.message}` }; | ||||
|     } | ||||
|   }); | ||||
|   // WhatsApp昵称管理 - 保存联系人昵称 | ||||
|   ipcMain.handle("save-contact-nickname", async (event, args) => { | ||||
|     const { contactId, nickname, displayName } = args; | ||||
|     try { | ||||
|       // 保存到数据库 | ||||
|       const existing = await app.sdb.selectOne("contact_nicknames", { contactId }); | ||||
|       if (existing) { | ||||
|         await app.sdb.update("contact_nicknames", | ||||
|           { nickname, displayName, updatedAt: new Date() }, | ||||
|           { contactId } | ||||
|         ); | ||||
|       } else { | ||||
|         await app.sdb.insert("contact_nicknames", { | ||||
|           contactId, | ||||
|           nickname, | ||||
|           displayName, | ||||
|           createdAt: new Date(), | ||||
|           updatedAt: new Date() | ||||
|         }); | ||||
|       } | ||||
|       return { status: true, message: "昵称保存成功" }; | ||||
|     } catch (error) { | ||||
|       logger.error("保存联系人昵称失败:", error); | ||||
|       return { status: false, message: "保存失败" }; | ||||
|     } | ||||
|   }); | ||||
|  | ||||
|   // WhatsApp昵称管理 - 获取联系人昵称 | ||||
|   ipcMain.handle("get-contact-nickname", async (event, args) => { | ||||
|     const { contactId } = args; | ||||
|     try { | ||||
|       const result = await app.sdb.selectOne("contact_nicknames", { contactId }); | ||||
|       return { status: true, data: result }; | ||||
|     } catch (error) { | ||||
|       logger.error("获取联系人昵称失败:", error); | ||||
|       return { status: false, data: null }; | ||||
|     } | ||||
|   }); | ||||
|  | ||||
|   // WhatsApp昵称管理 - 获取所有昵称 | ||||
|   ipcMain.handle("get-all-nicknames", async (event) => { | ||||
|     try { | ||||
|       const results = await app.sdb.select("contact_nicknames"); | ||||
|       return { status: true, data: results }; | ||||
|     } catch (error) { | ||||
|       logger.error("获取所有昵称失败:", error); | ||||
|       return { status: false, data: [] }; | ||||
|     } | ||||
|   }); | ||||
|  | ||||
|   // WhatsApp调试 - 打印联系人列表信息 | ||||
|   ipcMain.handle("debug-contact-list", async (event, args) => { | ||||
|     const { contacts } = args; | ||||
|     try { | ||||
|       console.log("=== 联系人列表调试信息 ==="); | ||||
|       console.log(`总联系人数: ${contacts.length}`); | ||||
|       contacts.forEach((contact, index) => { | ||||
|         console.log(`\n联系人 ${index + 1}:`); | ||||
|         console.log(`  显示名称: "${contact.displayName}"`); | ||||
|         console.log(`  聊天ID: "${contact.chatId}"`); | ||||
|         console.log(`  React Key: "${contact.reactKey}"`); | ||||
|         console.log(`  元素信息: ${contact.elementInfo}`); | ||||
|       }); | ||||
|       console.log("=== 联系人列表调试结束 ===\n"); | ||||
|       return { status: true }; | ||||
|     } catch (error) { | ||||
|       logger.error("打印联系人列表失败:", error); | ||||
|       return { status: false }; | ||||
|     } | ||||
|   }); | ||||
|  | ||||
|   // WhatsApp调试 - 打印当前会话信息 | ||||
|   ipcMain.handle("debug-current-chat", async (event, args) => { | ||||
|     const { chatInfo } = args; | ||||
|     try { | ||||
|       console.log("=== 当前会话调试信息 ==="); | ||||
|       console.log(`会话类型: ${chatInfo.type}`); | ||||
|       console.log(`联系人ID: "${chatInfo.contactId}"`); | ||||
|       console.log(`显示名称: "${chatInfo.displayName}"`); | ||||
|       console.log(`手机号: "${chatInfo.phoneNumber || '未获取到'}"`); | ||||
|       console.log(`URL聊天ID: "${chatInfo.urlChatId || '未获取到'}"`); | ||||
|       console.log(`头部名称: "${chatInfo.headerName || '未获取到'}"`); | ||||
|       console.log(`获取方式: ${chatInfo.source}`); | ||||
|       console.log("=== 当前会话调试结束 ===\n"); | ||||
|       return { status: true }; | ||||
|     } catch (error) { | ||||
|       logger.error("打印当前会话信息失败:", error); | ||||
|       return { status: false }; | ||||
|     } | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  | ||||
| @ -106,6 +106,17 @@ const initializeDatabase = async () => { | ||||
|       }, | ||||
|       constraints: [], | ||||
|     }, | ||||
|     contact_nicknames: { | ||||
|       columns: { | ||||
|         id: "INTEGER PRIMARY KEY AUTOINCREMENT", | ||||
|         contactId: "TEXT NOT NULL UNIQUE", | ||||
|         nickname: "TEXT NOT NULL", | ||||
|         displayName: "TEXT", | ||||
|         createdAt: "DATETIME DEFAULT CURRENT_TIMESTAMP", | ||||
|         updatedAt: "DATETIME DEFAULT CURRENT_TIMESTAMP", | ||||
|       }, | ||||
|       constraints: [], | ||||
|     }, | ||||
|     follow_record: { | ||||
|       columns: { | ||||
|         id: "INTEGER PRIMARY KEY AUTOINCREMENT", | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -24,6 +24,39 @@ class SystemService { | ||||
|     return systemInfo; | ||||
|   } | ||||
|  | ||||
|   async getUserDataPath(args, event) { | ||||
|     const path = require('path'); | ||||
|     const fs = require('fs'); | ||||
|  | ||||
|     const userDataPath = app.getPath('userData'); | ||||
|     const localStoragePath = path.join(userDataPath, 'Local Storage'); | ||||
|  | ||||
|     let localStorageExists = false; | ||||
|     let localStorageFiles = []; | ||||
|  | ||||
|     try { | ||||
|       const stats = fs.statSync(localStoragePath); | ||||
|       localStorageExists = stats.isDirectory(); | ||||
|       if (localStorageExists) { | ||||
|         localStorageFiles = fs.readdirSync(localStoragePath); | ||||
|       } | ||||
|     } catch (error) { | ||||
|       // localStorage目录不存在 | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|       status: true, | ||||
|       data: { | ||||
|         userDataPath, | ||||
|         localStoragePath, | ||||
|         localStorageExists, | ||||
|         localStorageFiles, | ||||
|         cwd: process.cwd(), | ||||
|         appPath: app.getAppPath() | ||||
|       } | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   //   * 错误码说明: | ||||
|   // *  {401} 参数缺失 - 请求缺少必要参数或参数格式错误 | ||||
|   // *  {402} 账户不存在 - 提供的凭证未关联任何注册账户 | ||||
|  | ||||
| @ -163,13 +163,15 @@ class WindowService { | ||||
|           logger.info(`[${sessionPartitionId}] 等待代理设置生效...`); | ||||
|           await new Promise(resolve => setTimeout(resolve, 1000)); | ||||
|  | ||||
|           // 添加网络请求监听以诊断问题 | ||||
|           // 添加网络请求监听以诊断问题(仅记录重要请求,避免日志暴涨) | ||||
|           view.webContents.session.webRequest.onBeforeRequest((details, callback) => { | ||||
|             // 只记录WebSocket和API请求,忽略静态资源 | ||||
|             if (details.url.startsWith('wss://') || details.url.startsWith('ws://')) { | ||||
|               logger.info(`[${sessionPartitionId}] WebSocket请求: ${details.method} ${details.url}`); | ||||
|             } else { | ||||
|               logger.info(`[${sessionPartitionId}] 网络请求: ${details.method} ${details.url}`); | ||||
|             } else if (details.url.includes('/api/') || details.url.includes('ajax') || details.url.includes('graphql')) { | ||||
|               logger.info(`[${sessionPartitionId}] API请求: ${details.method} ${details.url}`); | ||||
|             } | ||||
|             // 不再记录所有网络请求,避免日志暴涨 | ||||
|             callback({}); | ||||
|           }); | ||||
|  | ||||
| @ -276,11 +278,13 @@ class WindowService { | ||||
|           windowStatus: "true", | ||||
|           windowId: view.webContents.id, | ||||
|           userAgent: userAgent, | ||||
|           onlineStatus: "true", // 会话启动时立即设置为在线状态 | ||||
|         }, | ||||
|         { platform: platform, partitionId: inputPartitionId } | ||||
|       ); | ||||
|       sessionObj.windowStatus = "true"; | ||||
|       sessionObj.windowId = view.webContents.id; | ||||
|       sessionObj.onlineStatus = "true"; // 同步更新内存中的对象 | ||||
|  | ||||
|       return { status: true, message: "启动成功", data: sessionObj }; | ||||
|     } catch (err) { | ||||
| @ -436,18 +440,20 @@ class WindowService { | ||||
|         if (sessionObj) { | ||||
|           logger.info(`[${partitionId}] 刷新会话:重新加载代理配置`); | ||||
|  | ||||
|           // 清除会话缓存和旧的代理设置 | ||||
|           // 清除会话缓存和旧的代理设置(保留登录状态) | ||||
|           logger.info(`[${partitionId}] 清除会话缓存和旧代理设置`); | ||||
|           try { | ||||
|             // 清除网络缓存 | ||||
|             // 只清除网络缓存,不清除存储数据以保持登录状态 | ||||
|             await view.webContents.session.clearCache(); | ||||
|             // 清除存储数据(但保留cookies和localStorage) | ||||
|             await view.webContents.session.clearStorageData({ | ||||
|               storages: ['appcache', 'serviceworkers', 'cachestorage', 'websql', 'indexdb'] | ||||
|             }); | ||||
|  | ||||
|             // ❌ 注释掉清除存储数据,避免清除登录状态 | ||||
|             // await view.webContents.session.clearStorageData({ | ||||
|             //   storages: ['appcache', 'serviceworkers', 'cachestorage', 'websql', 'indexdb'] | ||||
|             // }); | ||||
|  | ||||
|             // 重置代理为系统代理,确保清除旧配置 | ||||
|             await view.webContents.session.setProxy({ mode: 'system' }); | ||||
|             logger.info(`[${partitionId}] 会话缓存清除完成`); | ||||
|             logger.info(`[${partitionId}] 会话缓存清除完成(保留登录状态)`); | ||||
|           } catch (clearError) { | ||||
|             logger.warn(`[${partitionId}] 清除缓存时出现警告:`, clearError.message); | ||||
|           } | ||||
| @ -1345,7 +1351,7 @@ class WindowService { | ||||
|         callback(0); // 0 = 验证成功 | ||||
|       }); | ||||
|  | ||||
|       // 2. 优化网络请求超时设置 | ||||
|       // 2. 优化网络请求超时设置(不记录日志,避免暴涨) | ||||
|       session.webRequest.onBeforeRequest((details, callback) => { | ||||
|         // 增加超时时间,减少连接中断 | ||||
|         callback({}); | ||||
| @ -1875,9 +1881,11 @@ class WindowService { | ||||
|             return; | ||||
|           } | ||||
|  | ||||
|           // 阻止所有其他网络请求 | ||||
|           logger.error(`[${partitionId}] 🔥 阻止网络请求: ${details.url}`); | ||||
|           logger.error(`[${partitionId}] 🔥 原因: 全局代理配置无效,严格模式不允许网络访问`); | ||||
|           // 阻止所有其他网络请求(减少日志输出) | ||||
|           if (details.url.includes('/api/') || details.url.startsWith('wss://') || details.url.startsWith('ws://')) { | ||||
|             logger.error(`[${partitionId}] 🔥 阻止重要请求: ${details.url}`); | ||||
|             logger.error(`[${partitionId}] 🔥 原因: 全局代理配置无效,严格模式不允许网络访问`); | ||||
|           } | ||||
|           callback({ cancel: true }); | ||||
|         }); | ||||
|  | ||||
|  | ||||
							
								
								
									
										12
									
								
								electron/sql/contact_nicknames.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								electron/sql/contact_nicknames.sql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| -- 联系人昵称表 | ||||
| CREATE TABLE IF NOT EXISTS contact_nicknames ( | ||||
|   id INTEGER PRIMARY KEY AUTOINCREMENT, | ||||
|   contactId TEXT NOT NULL UNIQUE, | ||||
|   nickname TEXT NOT NULL, | ||||
|   displayName TEXT, | ||||
|   createdAt DATETIME DEFAULT CURRENT_TIMESTAMP, | ||||
|   updatedAt DATETIME DEFAULT CURRENT_TIMESTAMP | ||||
| ); | ||||
|  | ||||
| -- 创建索引 | ||||
| CREATE INDEX IF NOT EXISTS idx_contact_nicknames_contactId ON contact_nicknames(contactId); | ||||
| @ -88,22 +88,31 @@ onMounted(async () => { | ||||
|     menuStore.setTranslationRoute(finalRoutes) | ||||
|   } | ||||
|  | ||||
|   const result = await ipc.invoke(ipcApiRoute.getWsBaseUrl, {}); | ||||
|   const ws = new WebSocket(result.data + '/ws/notice'); | ||||
|   ws.onopen = () => { | ||||
|     console.log('Connected to server'); | ||||
|   }; | ||||
|   ws.onmessage = (event) => { | ||||
|     const data = JSON.parse(event.data); | ||||
|     if (data.type === 'password_changed' && data.user_id === menuStore.userInfo.userId) { | ||||
|       ElMessage.error('密码已被修改,请重新登录') | ||||
|       localStorage.removeItem('session') | ||||
|       router.push('/login') | ||||
|   // 只有在用户已登录时才建立WebSocket连接 | ||||
|   if (menuStore.userInfo && menuStore.userInfo.userId) { | ||||
|     try { | ||||
|       const result = await ipc.invoke(ipcApiRoute.getWsBaseUrl, {}); | ||||
|       const ws = new WebSocket(result.data + '/ws/notice'); | ||||
|       ws.onopen = () => { | ||||
|         console.log('WebSocket连接成功'); | ||||
|       }; | ||||
|       ws.onmessage = (event) => { | ||||
|         const data = JSON.parse(event.data); | ||||
|         if (data.type === 'password_changed' && data.user_id === menuStore.userInfo.userId) { | ||||
|           ElMessage.error('密码已被修改,请重新登录') | ||||
|           localStorage.removeItem('session') | ||||
|           router.push('/login') | ||||
|         } | ||||
|       }; | ||||
|       ws.onerror = (error) => { | ||||
|         console.error('WebSocket连接错误:', error); | ||||
|       }; | ||||
|     } catch (error) { | ||||
|       console.warn('WebSocket连接失败:', error); | ||||
|     } | ||||
|   }; | ||||
|   ws.onerror = (error) => { | ||||
|     console.error('WebSocket error:', error); | ||||
|   }; | ||||
|   } else { | ||||
|     console.log('用户未登录,跳过WebSocket连接,userInfo:', menuStore.userInfo); | ||||
|   } | ||||
| }) | ||||
| import { useDark } from '@vueuse/core' | ||||
| const isDark = useDark() | ||||
|  | ||||
| @ -99,25 +99,87 @@ export const useMenuStore = defineStore("platform", { | ||||
|       } | ||||
|     }, | ||||
|     updateChildrenMenuMsgCount(childMenu) { | ||||
|       console.log('🔄 menuStore收到消息计数更新:', childMenu) | ||||
|       const pMenu = this.menus.find((item) => item.id === childMenu.platform); | ||||
|       if (pMenu) { | ||||
|         const index = pMenu.children.findIndex( | ||||
|         console.log('✅ 找到平台菜单:', pMenu.id, '子菜单数量:', pMenu.children.length) | ||||
|  | ||||
|         // 修复:优先使用windowId匹配,如果没有则使用platform匹配 | ||||
|         let index = pMenu.children.findIndex( | ||||
|           (item) => item.windowId === childMenu.windowId | ||||
|         ); | ||||
|  | ||||
|         // 如果通过windowId没找到,尝试多种备用匹配策略 | ||||
|         if (index === -1 && childMenu.windowId) { | ||||
|           console.log('🔍 windowId匹配失败,尝试备用策略...') | ||||
|  | ||||
|           // 策略1:如果只有一个会话,直接使用 | ||||
|           if (pMenu.children.length === 1) { | ||||
|             index = 0; | ||||
|             console.log('🔄 策略1: 通过platform匹配到唯一会话,索引:', index) | ||||
|           } | ||||
|  | ||||
|           // 策略2:查找windowStatus为true的会话(当前活跃的会话) | ||||
|           if (index === -1) { | ||||
|             index = pMenu.children.findIndex(item => item.windowStatus === 'true'); | ||||
|             if (index !== -1) { | ||||
|               console.log('🔄 策略2: 通过windowStatus匹配到活跃会话,索引:', index) | ||||
|             } | ||||
|           } | ||||
|  | ||||
|           // 策略3:如果是WhatsApp平台,使用第一个可用会话 | ||||
|           if (index === -1 && childMenu.platform === 'WhatsApp') { | ||||
|             index = 0; | ||||
|             console.log('🔄 策略3: WhatsApp平台使用第一个会话,索引:', index) | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         console.log('🔍 查找windowId:', childMenu.windowId, '找到索引:', index) | ||||
|         if (index !== -1) { | ||||
|           const targetChildMenu = pMenu.children[index]; | ||||
|           console.log('🎯 目标子菜单:', targetChildMenu.partitionId, '当前msgCount:', targetChildMenu.msgCount, '新msgCount:', childMenu.msgCount) | ||||
|  | ||||
|           // 确保 msgCount 有效且确实发生了变化 | ||||
|           if ( | ||||
|             childMenu.msgCount !== undefined && | ||||
|             targetChildMenu.msgCount !== childMenu.msgCount | ||||
|           ) { | ||||
|             // 直接修改目标子菜单的 msgCount 属性 | ||||
|             // 这是最常见的做法,Vue 应该能响应 | ||||
|             targetChildMenu.msgCount = childMenu.msgCount; | ||||
|           // 修复:确保数据类型一致性,处理字符串和数字的转换 | ||||
|           const newMsgCount = Number(childMenu.msgCount) || 0; | ||||
|           const currentMsgCount = Number(targetChildMenu.msgCount) || 0; | ||||
|  | ||||
|           console.log('📊 数值比较:', { | ||||
|             原始新值: childMenu.msgCount, | ||||
|             解析新值: newMsgCount, | ||||
|             原始当前值: targetChildMenu.msgCount, | ||||
|             解析当前值: currentMsgCount, | ||||
|             数据类型新值: typeof childMenu.msgCount, | ||||
|             数据类型当前值: typeof targetChildMenu.msgCount, | ||||
|             是否未定义: childMenu.msgCount === undefined, | ||||
|             是否相等: currentMsgCount === newMsgCount | ||||
|           }) | ||||
|  | ||||
|           // 修复:只要msgCount不是undefined就更新,确保数据类型一致 | ||||
|           if (childMenu.msgCount !== undefined) { | ||||
|             // 使用Vue的响应式更新方式 | ||||
|             // 创建一个新的对象来触发响应式更新 | ||||
|             const updatedChild = { ...targetChildMenu, msgCount: newMsgCount }; | ||||
|             pMenu.children.splice(index, 1, updatedChild); | ||||
|             console.log('✅ 消息计数已更新:', updatedChild.partitionId, '新值:', updatedChild.msgCount, '类型:', typeof updatedChild.msgCount) | ||||
|  | ||||
|             // 强制触发响应式更新 | ||||
|             this.menus = [...this.menus]; | ||||
|             console.log('🔄 强制触发响应式更新完成') | ||||
|           } else { | ||||
|             console.log('❌ 消息计数未更新,原因: msgCount未定义') | ||||
|           } | ||||
|         }  | ||||
|       }  | ||||
|         } else { | ||||
|           console.log('❌ 未找到对应的子菜单,windowId:', childMenu.windowId) | ||||
|           console.log('📋 现有子菜单的windowId:', pMenu.children.map(child => ({ | ||||
|             partitionId: child.partitionId, | ||||
|             windowId: child.windowId, | ||||
|             windowStatus: child.windowStatus | ||||
|           }))) | ||||
|         } | ||||
|       } else { | ||||
|         console.log('❌ 未找到平台菜单:', childMenu.platform) | ||||
|         console.log('📋 现有平台菜单:', this.menus.map(menu => menu.id)) | ||||
|       } | ||||
|     }, | ||||
|     deleteChildrenMenu(childMenu) { | ||||
|       const pMenu = this.menus.find((item) => item.id === childMenu.platform); | ||||
|  | ||||
| @ -1,9 +1,11 @@ | ||||
| // 优先使用 electronAPI(通过 contextBridge 暴露),然后回退到直接访问 | ||||
| const electronAPI = window.electronAPI; | ||||
| const Renderer = (window.require && window.require('electron')) || window.electron || {}; | ||||
|  | ||||
| /** | ||||
|  * ipc | ||||
|  * 官方api说明:https://www.electronjs.org/zh/docs/latest/api/ipc-renderer | ||||
|  *  | ||||
|  * | ||||
|  * 属性/方法 | ||||
|  * ipc.invoke(channel, param) - 发送异步消息(invoke/handle 模型) | ||||
|  * ipc.sendSync(channel, param) - 发送同步消息(send/on 模型) | ||||
| @ -18,16 +20,24 @@ const Renderer = (window.require && window.require('electron')) || window.electr | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * ipc | ||||
|  * ipc - 优先使用 electronAPI,回退到直接访问 | ||||
|  */ | ||||
| const ipc = Renderer.ipcRenderer || undefined; | ||||
| const ipc = electronAPI || Renderer.ipcRenderer || undefined; | ||||
|  | ||||
| /** | ||||
|  * 是否为EE环境 | ||||
|  */ | ||||
| const isEE = ipc ? true : false; | ||||
|  | ||||
| // 调试信息 | ||||
| console.log('🔍 IPC 初始化状态:', { | ||||
|   hasElectronAPI: !!electronAPI, | ||||
|   hasRenderer: !!Renderer.ipcRenderer, | ||||
|   finalIpc: !!ipc, | ||||
|   isEE | ||||
| }); | ||||
|  | ||||
| export { | ||||
|   Renderer, ipc, isEE | ||||
|   Renderer, ipc, isEE, electronAPI | ||||
| }; | ||||
|  | ||||
|  | ||||
| @ -110,39 +110,30 @@ | ||||
|                 </div> | ||||
|                 <div class="child-menu-item-line" /> | ||||
|                 <div class="child-menu-item-img" :class="{ 'margin-top-10': !isEmpty(child.avatarUrl) }"> | ||||
|                   <el-badge :offset="[-38, 15]" is-dot :type="child.onlineStatus === 'true' ? 'success' : 'info'"> | ||||
|                     <el-badge v-if="isCollapse && child?.msgCount > 0" :offset="[0, 0]" :value="child.msgCount" | ||||
|                       :max="99" :show-zero="false" type="error" class="msg-badge"> | ||||
|                       <!-- 如果有头像,展示头像 --> | ||||
|                       <el-avatar v-if="child.avatarUrl" :src="child.avatarUrl" style="height: 30px;width: 30px;" /> | ||||
|                       <!-- 如果没有头像,展示默认头像 --> | ||||
|                       <template v-else> | ||||
|                         <el-avatar style="height: 30px;width: 30px;"> | ||||
|                           <el-icon v-if="!isCustomWeb(menu)"> | ||||
|                             <User /> | ||||
|                           </el-icon> | ||||
|                           <el-icon color="#30ace1" v-else> | ||||
|                             <component :is="google" /> | ||||
|                           </el-icon> | ||||
|                         </el-avatar> | ||||
|                       </template> | ||||
|                     </el-badge> | ||||
|                     <el-badge v-else :offset="[0, 0]" :value="0" :max="99" :show-zero="false" type="danger"> | ||||
|                       <!-- 如果有头像,展示头像 --> | ||||
|                       <el-avatar v-if="child.avatarUrl" :src="child.avatarUrl" style="height: 30px;width: 30px;" /> | ||||
|                       <!-- 如果没有头像,展示默认头像 --> | ||||
|                       <template v-else> | ||||
|                         <el-avatar style="height: 30px;width: 30px;"> | ||||
|                           <el-icon v-if="!isCustomWeb(menu)"> | ||||
|                             <User /> | ||||
|                           </el-icon> | ||||
|                           <el-icon size="35" color="#30ace1" v-else> | ||||
|                             <component :is="google" /> | ||||
|                           </el-icon> | ||||
|                         </el-avatar> | ||||
|                       </template> | ||||
|                     </el-badge> | ||||
|  | ||||
|                   <!-- 修复后的状态显示逻辑: | ||||
|                        状态0:未启动/未登录 → 灰色dot | ||||
|                        状态1:已启动/已登录无消息 → 绿色dot | ||||
|                        状态2:有消息 → 红色badge(带数字) --> | ||||
|                   <el-badge :offset="[-38, 15]" | ||||
|                     :is-dot="getStatusValue(child) === ''" | ||||
|                     :type="getStatusType(child)" | ||||
|                     :value="getStatusValue(child)" | ||||
|                     :show-zero="false" | ||||
|                     :max="99" | ||||
|                     class="status-badge"> | ||||
|                     <!-- 如果有头像,展示头像 --> | ||||
|                     <el-avatar v-if="child.avatarUrl" :src="child.avatarUrl" style="height: 30px;width: 30px;" /> | ||||
|                     <!-- 如果没有头像,展示默认头像 --> | ||||
|                     <template v-else> | ||||
|                       <el-avatar style="height: 30px;width: 30px;"> | ||||
|                         <el-icon v-if="!isCustomWeb(menu)"> | ||||
|                           <User /> | ||||
|                         </el-icon> | ||||
|                         <el-icon color="#30ace1" v-else> | ||||
|                           <component :is="google" /> | ||||
|                         </el-icon> | ||||
|                       </el-avatar> | ||||
|                     </template> | ||||
|                   </el-badge> | ||||
|                 </div> | ||||
|                 <!--              折叠隐藏部分--> | ||||
| @ -160,8 +151,17 @@ | ||||
|                   </div> | ||||
|                 </div> | ||||
|                 <div v-if="isCollapse" class="child-menu-item-icon margin-top-10"> | ||||
|                   <el-badge :offset="[-15, 0]" type="error" :show-zero="false" :max="99" | ||||
|                     :value="child?.msgCount"></el-badge> | ||||
|                   <!-- 折叠状态下的修复状态显示: | ||||
|                        状态0:未启动/未登录 → 灰色dot | ||||
|                        状态1:已启动/已登录无消息 → 绿色dot | ||||
|                        状态2:有消息 → 红色badge(带数字) --> | ||||
|                   <el-badge :offset="[-15, 0]" | ||||
|                     :is-dot="getStatusValue(child) === ''" | ||||
|                     :type="getStatusType(child)" | ||||
|                     :value="getStatusValue(child)" | ||||
|                     :show-zero="false" | ||||
|                     :max="99" | ||||
|                     class="status-badge"></el-badge> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
| @ -333,6 +333,49 @@ const isEmpty = (value) => { | ||||
|   return false | ||||
| } | ||||
|  | ||||
| // 修复后的状态计算逻辑 - 使用响应式函数 | ||||
| const getStatusType = (child) => { | ||||
|   // 修复:使用Number()确保数据类型一致性 | ||||
|   const msgCount = Number(child?.msgCount) || 0 | ||||
|   const isStarted = child.windowStatus === 'true' | ||||
|   const isOnline = child.onlineStatus === 'true' | ||||
|  | ||||
|   console.log(`🎨 状态计算 [${child.partitionId}]:`, { | ||||
|     原始msgCount: child?.msgCount, | ||||
|     解析msgCount: msgCount, | ||||
|     数据类型: typeof child?.msgCount, | ||||
|     windowStatus: child.windowStatus, | ||||
|     onlineStatus: child.onlineStatus, | ||||
|     isStarted, | ||||
|     isOnline, | ||||
|     时间戳: new Date().toLocaleTimeString() | ||||
|   }) | ||||
|  | ||||
|   // 状态2:有消息 → 红色badge(不使用dot) | ||||
|   if (msgCount > 0) { | ||||
|     console.log(`🔴 ${child.partitionId} → 红色 (有消息: ${msgCount})`) | ||||
|     return 'error' | ||||
|   } | ||||
|  | ||||
|   // 状态1:已启动/已登录无消息 → 绿色dot | ||||
|   if (isStarted || isOnline) { | ||||
|     console.log(`🟢 ${child.partitionId} → 绿色 (已启动或在线)`) | ||||
|     return 'success' | ||||
|   } | ||||
|  | ||||
|   // 状态0:未启动/未登录 → 灰色dot | ||||
|   console.log(`⚪ ${child.partitionId} → 灰色 (未启动且离线)`) | ||||
|   return 'info' | ||||
| } | ||||
|  | ||||
| const getStatusValue = (child) => { | ||||
|   // 修复:使用Number()确保数据类型一致性 | ||||
|   const msgCount = Number(child?.msgCount) || 0 | ||||
|   console.log(`📊 值计算 [${child.partitionId}]: msgCount=${msgCount}, 类型=${typeof child?.msgCount}, 返回值="${msgCount > 0 ? msgCount : ''}"`) | ||||
|   // 只有在有消息时才显示数字 | ||||
|   return msgCount > 0 ? msgCount : '' | ||||
| } | ||||
|  | ||||
| const menuMessageFilterState = ref({}) | ||||
|  | ||||
| const toggleMessageFilter = (menuId) => { | ||||
| @ -368,7 +411,7 @@ const filteredChildren = computed(() => (children, menuId) => { | ||||
|     nMenus = nMenus.filter(child => !(child.isTop === true || child.isTop === 'true')) | ||||
|   } | ||||
|   if (menuMessageFilterState.value[menuId]) { | ||||
|     nMenus = nMenus.filter(child => child.msgCount > 0) | ||||
|     nMenus = nMenus.filter(child => parseInt(child.msgCount || 0) > 0) | ||||
|   } | ||||
|   // 搜索过滤 | ||||
|   if (menuSearchText.value && menuSearchText.value.trim() !== '') { | ||||
| @ -599,6 +642,7 @@ onMounted(async () => { | ||||
|  | ||||
|   //消息变更通知 | ||||
|   const handleMsgCountNotify = (event, args) => { | ||||
|     console.log('前端收到消息计数更新通知:', args) | ||||
|     menuStore.updateChildrenMenuMsgCount(args) | ||||
|   } | ||||
|  | ||||
|  | ||||
| @ -50,15 +50,46 @@ onMounted(async () => { | ||||
|  | ||||
|   //检测是否有更新 | ||||
|   window.electronAPI?.onUpdateAvailable(() => { | ||||
|     console.log('🔄 检测到更新,禁用自动登录') | ||||
|     canAutoLogin.value = false | ||||
|   }) | ||||
|  | ||||
|   const savedSession = localStorage.getItem('session') | ||||
|   if (savedSession && canAutoLogin) { | ||||
|   console.log('🔍 检查自动登录状态:', { | ||||
|     savedSession: !!savedSession, | ||||
|     canAutoLogin: canAutoLogin.value, | ||||
|     sessionData: savedSession ? JSON.parse(savedSession) : null | ||||
|   }) | ||||
|  | ||||
|   // 调试localStorage状态 | ||||
|   console.log('🔍 localStorage调试信息:', { | ||||
|     localStorage: typeof localStorage, | ||||
|     localStorageLength: localStorage.length, | ||||
|     allKeys: Object.keys(localStorage), | ||||
|     userDataPath: window.electronAPI ? 'Electron环境' : '浏览器环境' | ||||
|   }) | ||||
|  | ||||
|   // 获取Electron用户数据路径信息 | ||||
|   if (window.electronAPI) { | ||||
|     try { | ||||
|       const pathInfo = await ipc.invoke('getUserDataPath', {}) | ||||
|       console.log('🔍 Electron用户数据路径信息:', pathInfo) | ||||
|     } catch (error) { | ||||
|       console.error('❌ 获取用户数据路径失败:', error) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (savedSession && canAutoLogin.value) { | ||||
|     console.log('✅ 执行自动登录') | ||||
|     const session = JSON.parse(savedSession) | ||||
|     menuStore.setUserInfo(session) | ||||
|     keepAuth.value = true | ||||
|     await router.push('/index') | ||||
|   } else { | ||||
|     console.log('❌ 不满足自动登录条件:', { | ||||
|       hasSavedSession: !!savedSession, | ||||
|       canAutoLogin: canAutoLogin.value | ||||
|     }) | ||||
|   } | ||||
| }) | ||||
|  | ||||
| @ -155,10 +186,30 @@ const handleLogin = async () => { | ||||
|         offset: 40, | ||||
|       }) | ||||
|  | ||||
|       console.log('🔍 登录成功,检查keepAuth状态:', { | ||||
|         keepAuth: keepAuth.value, | ||||
|         sessionData: res.data | ||||
|       }) | ||||
|  | ||||
|       if (keepAuth.value) { | ||||
|         localStorage.setItem('session', JSON.stringify(res.data)) | ||||
|         try { | ||||
|           localStorage.setItem('session', JSON.stringify(res.data)) | ||||
|           console.log('✅ 已保存session到localStorage') | ||||
|  | ||||
|           // 立即验证是否保存成功 | ||||
|           const savedSession = localStorage.getItem('session') | ||||
|           console.log('🔍 验证保存结果:', { | ||||
|             saved: !!savedSession, | ||||
|             data: savedSession ? JSON.parse(savedSession) : null, | ||||
|             allKeys: Object.keys(localStorage), | ||||
|             localStorageLength: localStorage.length | ||||
|           }) | ||||
|         } catch (error) { | ||||
|           console.error('❌ 保存session失败:', error) | ||||
|         } | ||||
|       } else { | ||||
|         localStorage.removeItem('session') | ||||
|         console.log('❌ 未勾选记住登录状态,清除session') | ||||
|       } | ||||
|       logger.info("login sssssssssssssssss",res.data) | ||||
|       menuStore.setUserInfo(res.data) | ||||
|  | ||||
| @ -26,11 +26,13 @@ const filteredTableData = computed(() => { | ||||
|   if (!keyword) return list; | ||||
|   return list.filter((row) => { | ||||
|     const nick = String(row?.nickName || '').toLowerCase(); | ||||
|     const displayNick = String(row?._displayNickName || '').toLowerCase(); | ||||
|     const remarks = String(row?.remarks || '').toLowerCase(); | ||||
|     const user = String(row?.userName || '').toLowerCase(); | ||||
|     const webUrl = String(row?.webUrl || '').toLowerCase(); | ||||
|     return ( | ||||
|       nick.includes(keyword) || | ||||
|       displayNick.includes(keyword) || | ||||
|       remarks.includes(keyword) || | ||||
|       user.includes(keyword) || | ||||
|       webUrl.includes(keyword) | ||||
| @ -41,8 +43,100 @@ const filteredTableData = computed(() => { | ||||
| const tableData = ref([]); | ||||
| const getTableData = async () => { | ||||
|   tableData.value = await menuStore.getCurrentChildren(); | ||||
|   // 获取每个会话的代理状态 | ||||
|   // 获取每个会话的代理状态和联系人昵称 | ||||
|   const rows = Array.isArray(tableData.value) ? tableData.value : []; | ||||
|  | ||||
|   // 按平台分组,批量获取联系人昵称 | ||||
|   const platformGroups = {}; | ||||
|   rows.forEach(row => { | ||||
|     if (!platformGroups[row.platform]) { | ||||
|       platformGroups[row.platform] = []; | ||||
|     } | ||||
|     platformGroups[row.platform].push(row); | ||||
|   }); | ||||
|  | ||||
|   // 为每个平台批量获取联系人昵称 | ||||
|   for (const [platform, platformRows] of Object.entries(platformGroups)) { | ||||
|     try { | ||||
|       console.log(`开始处理平台 ${platform},会话数量: ${platformRows.length}`); | ||||
|  | ||||
|       // 获取该平台所有联系人的昵称配置 | ||||
|       const nicknameRes = await ipc.invoke(ipcApiRoute.getBatchContactNicknames, { | ||||
|         platform: platform | ||||
|       }); | ||||
|  | ||||
|       console.log(`平台 ${platform} 昵称配置响应:`, nicknameRes); | ||||
|  | ||||
|       if (nicknameRes.status && nicknameRes.data && nicknameRes.data.nicknameMap) { | ||||
|         const nicknameMap = nicknameRes.data.nicknameMap; | ||||
|         console.log(`平台 ${platform} 昵称映射:`, nicknameMap); | ||||
|  | ||||
|         // 为每个会话获取真实的用户ID并设置对应的联系人昵称 | ||||
|         for (const row of platformRows) { | ||||
|           try { | ||||
|             console.log(`处理会话: ${row.partitionId}, 原始昵称: ${row.nickName}, userName: ${row.userName}`); | ||||
|  | ||||
|             // 通过partitionId获取当前会话的联系人信息 | ||||
|             const contactRes = await ipc.invoke(ipcApiRoute.getContactInfo, { | ||||
|               platform: row.platform, | ||||
|               partitionId: row.partitionId | ||||
|             }); | ||||
|  | ||||
|             console.log(`会话 ${row.partitionId} 联系人信息响应:`, contactRes); | ||||
|  | ||||
|             if (contactRes.status && contactRes.data && contactRes.data.userInfo && contactRes.data.userInfo.userId) { | ||||
|               const realUserId = contactRes.data.userInfo.userId; | ||||
|               console.log(`会话 ${row.partitionId} 的真实用户ID: ${realUserId}`); | ||||
|  | ||||
|               // 检查是否有配置的昵称 | ||||
|               if (nicknameMap[realUserId]) { | ||||
|                 row._displayNickName = nicknameMap[realUserId]; | ||||
|                 console.log(`✅ 找到配置昵称: ${realUserId} -> ${nicknameMap[realUserId]}`); | ||||
|               } else { | ||||
|                 row._displayNickName = row.nickName; | ||||
|                 console.log(`❌ 未找到配置昵称,使用原始昵称: ${row.nickName}`); | ||||
|               } | ||||
|             } else { | ||||
|               console.log(`无法获取真实用户ID,回退到原始逻辑`); | ||||
|               // 如果无法获取真实用户ID,尝试使用userName或nickName | ||||
|               const userId = row.userName || row.nickName; | ||||
|               if (userId && nicknameMap[userId]) { | ||||
|                 row._displayNickName = nicknameMap[userId]; | ||||
|                 console.log(`✅ 使用回退逻辑找到昵称: ${userId} -> ${nicknameMap[userId]}`); | ||||
|               } else { | ||||
|                 row._displayNickName = row.nickName; | ||||
|                 console.log(`❌ 回退逻辑也未找到昵称,使用原始昵称: ${row.nickName}`); | ||||
|               } | ||||
|             } | ||||
|           } catch (contactError) { | ||||
|             console.error(`获取会话 ${row.partitionId} 联系人信息失败:`, contactError); | ||||
|             // 回退到原始逻辑 | ||||
|             const userId = row.userName || row.nickName; | ||||
|             if (userId && nicknameMap[userId]) { | ||||
|               row._displayNickName = nicknameMap[userId]; | ||||
|               console.log(`✅ 异常回退找到昵称: ${userId} -> ${nicknameMap[userId]}`); | ||||
|             } else { | ||||
|               row._displayNickName = row.nickName; | ||||
|               console.log(`❌ 异常回退未找到昵称,使用原始昵称: ${row.nickName}`); | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } else { | ||||
|         // 如果获取昵称失败,使用原来的nickName | ||||
|         platformRows.forEach(row => { | ||||
|           row._displayNickName = row.nickName; | ||||
|         }); | ||||
|       } | ||||
|     } catch (error) { | ||||
|       console.error(`获取平台 ${platform} 联系人昵称失败:`, error); | ||||
|       // 如果获取昵称失败,使用原来的nickName | ||||
|       platformRows.forEach(row => { | ||||
|         row._displayNickName = row.nickName; | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // 获取每个会话的代理状态 | ||||
|   for (const row of rows) { | ||||
|     try { | ||||
|       const res = await ipc.invoke(ipcApiRoute.checkProxyStatus, { partitionId: row.partitionId, platform: row.platform }); | ||||
| @ -653,7 +747,7 @@ const handleBatchProxySave = async () => { | ||||
|                 </el-badge> | ||||
|               </div> | ||||
|               <div class="session-record-nickname"> | ||||
|                 <el-text>{{ row.nickName ? row.nickName : '-' }}</el-text> | ||||
|                 <el-text>{{ row._displayNickName ? row._displayNickName : (row.nickName ? row.nickName : '-') }}</el-text> | ||||
|               </div> | ||||
|             </div> | ||||
|           </template> | ||||
| @ -666,7 +760,7 @@ const handleBatchProxySave = async () => { | ||||
|               </div> | ||||
|               <div class="nickname"> | ||||
|                 <el-text :truncated="true"> | ||||
|                   {{ row.nickName ? row.nickName : '-' }} | ||||
|                   {{ row._displayNickName ? row._displayNickName : (row.nickName ? row.nickName : '-') }} | ||||
|                 </el-text> | ||||
|               </div> | ||||
|             </div> | ||||
|  | ||||
							
								
								
									
										2
									
								
								public/dist/index.html
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								public/dist/index.html
									
									
									
									
										vendored
									
									
								
							| @ -85,7 +85,7 @@ | ||||
|       } | ||||
|  | ||||
|     </style> | ||||
|     <script type="module" crossorigin src="./assets/index-DUbG0YLV.js"></script> | ||||
|     <script type="module" crossorigin src="./assets/index-B1hQJHCb.js"></script> | ||||
|     <link rel="stylesheet" crossorigin href="./assets/index-BOEy93f0.css"> | ||||
|   </head> | ||||
|   <body style="padding: 0; margin: 0;"> | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 unknown
					unknown