update nickname

This commit is contained in:
unknown
2025-09-25 23:52:39 +08:00
parent 40e0b37a9a
commit 1b621e1359
17 changed files with 1958 additions and 222 deletions

View File

@ -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的apitrue->需要bridge.js(contextBridge)
nodeIntegration: true,
// preload: path.join(getElectronDir(), 'bridge.js'),
preload: path.join(getElectronDir(), 'preload', 'bridge.js'), // 添加preload脚本
},
titleBarStyle: "hidden",
frame: true,

View File

@ -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);
}

View File

@ -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);
}
}
}

View File

@ -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)),
});

View File

@ -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 };
}
});
};
/**

View File

@ -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

View File

@ -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} 账户不存在 - 提供的凭证未关联任何注册账户

View File

@ -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 });
});

View 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);

View File

@ -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()

View File

@ -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);

View File

@ -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
};

View File

@ -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)
}

View File

@ -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)

View File

@ -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>

View File

@ -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;">