import { app, Menu, nativeImage, Tray } from 'electron'; import log from 'electron-log/main'; import { getAutoLaunchEnabled, setAutoLaunchEnabled } from './auto-launch'; import { promptAndClearCache } from './cache'; import { getResourcePath } from './config'; import { checkForUpdates } from './updater'; import { getMainWindow, markQuitting, showMainWindow } from './window'; /** * 系统托盘:应用常驻入口 * - Windows 单击托盘图标恢复主窗口;mac 单击弹出菜单(跟随平台惯例) * - 菜单:显示主窗口 / 开机自启 / 重新启动 / 切换账号 / 清除缓存 / 检查更新 / 退出 * 注意:托盘实例必须保持模块级引用,否则会被 GC 导致图标消失(Electron 经典坑) */ let tray: Tray | null = null; /** 重新启动整个 Electron 进程(保留启动参数,常用于卡死/配置变更后自愈) */ function restartApp(): void { markQuitting(); app.relaunch(); app.exit(0); } /** 唤起主窗口并通知渲染端打开「切换账号」弹窗(未登录时弹窗自行失败即可) */ function openSwitchAccount(): void { showMainWindow(); getMainWindow()?.webContents.send('desktop:open-switch-account'); } /** 构建托盘菜单:开机自启是动态勾选项,每次状态变化后需重建菜单 */ function buildTrayMenu(): Menu { return Menu.buildFromTemplate([ { label: '显示主窗口', click: () => showMainWindow(), }, { type: 'separator' }, { label: '开机自启', type: 'checkbox', checked: getAutoLaunchEnabled(), // 开发模式登录项无意义,置灰避免误操作 enabled: app.isPackaged, click: (menuItem) => { setAutoLaunchEnabled(menuItem.checked); refreshTrayMenu(); }, }, { label: '重新启动', click: () => restartApp(), }, { label: '切换账号', click: () => openSwitchAccount(), }, { label: '清除缓存', click: () => { void promptAndClearCache(); }, }, { label: '检查更新', click: () => checkForUpdates(true), }, { type: 'separator' }, { label: '退出', click: () => { // 先标记退出,放行主窗口 close 拦截,再走正常退出流程 markQuitting(); app.quit(); }, }, ]); } /** 刷新托盘菜单(开机自启状态从渲染端 IPC 修改后也要同步勾选态) */ export function refreshTrayMenu(): void { tray?.setContextMenu(buildTrayMenu()); } /** * 创建系统托盘 * 图标用打包的应用图标缩放(mac 菜单栏建议 18px、Windows 托盘 16px 的倍数由系统处理) * TODO(待确认):正式图标到位后,mac 建议换单色 Template 图以适配深浅菜单栏 */ export function createTray(): void { try { const icon = nativeImage.createFromPath(getResourcePath('icon.png')); const trayIcon = process.platform === 'darwin' ? icon.resize({ width: 18, height: 18 }) : icon.resize({ width: 16, height: 16 }); tray = new Tray(trayIcon); tray.setToolTip('萧康云医'); tray.setContextMenu(buildTrayMenu()); // Windows 单击直接唤起主窗口;mac 上设置了 contextMenu 后单击默认弹菜单,不重复绑定 if (process.platform === 'win32') { tray.on('click', () => showMainWindow()); } } catch (error) { // 托盘创建失败(图标缺失等)不阻塞应用启动,仅损失常驻入口 log.error('创建系统托盘失败', error); } }