Files
ClassHelper/packages/desktop-client/src/main/tray.ts
T

76 lines
2.3 KiB
TypeScript
Raw Normal View History

import fs from 'node:fs';
import path from 'node:path';
import { Menu, Tray, app, nativeImage } from 'electron';
import { logger } from './logger.js';
/**
* 系统托盘(需求 5):关闭主窗口不退出程序,而是隐藏到托盘后台运行;
* 托盘图标可恢复窗口,并提供"退出"入口(退出前统一清理资源)。
*/
let tray: Tray | null = null;
export interface TrayHandlers {
onShow(): void;
onHide(): void;
onQuit(): void;
}
/** 托盘图标:优先用打包进 dist/renderer 的 tray.png,其次资源目录 */
function resolveTrayIcon(): string | null {
const candidates = [
path.join(__dirname, '../renderer/tray.png'),
path.join(process.resourcesPath ?? '', 'tray.png'),
];
for (const candidate of candidates) {
if (candidate && fs.existsSync(candidate)) return candidate;
}
return null;
}
export function createTray(handlers: TrayHandlers): Tray | null {
if (tray && !tray.isDestroyed()) return tray;
const iconPath = resolveTrayIcon();
const image = iconPath ? nativeImage.createFromPath(iconPath) : nativeImage.createEmpty();
tray = new Tray(image);
tray.setToolTip('班级小助手(后台运行中)');
tray.setContextMenu(
Menu.buildFromTemplate([
{ label: '显示主窗口', click: () => handlers.onShow() },
{ label: '隐藏到托盘', click: () => handlers.onHide() },
{ type: 'separator' },
{ label: '退出班级小助手', click: () => handlers.onQuit() },
]),
);
// 左键单击/双击托盘图标:恢复窗口(用户习惯)
tray.on('click', () => handlers.onShow());
tray.on('double-click', () => handlers.onShow());
logger.info(iconPath ? `系统托盘已就绪:${iconPath}` : '系统托盘已就绪(未找到图标文件,使用空图标)');
return tray;
}
export function destroyTray(): void {
if (!tray) return;
try {
tray.destroy();
} catch {
/* 忽略销毁异常 */
}
tray = null;
logger.info('系统托盘已销毁');
}
export function isTrayReady(): boolean {
return Boolean(tray && !tray.isDestroyed());
}
/** 供"关于/设置"展示(也可用于冒烟断言) */
export function getTrayState(): { ready: boolean; hasIcon: boolean; appPath: string } {
return {
ready: isTrayReady(),
hasIcon: Boolean(resolveTrayIcon()),
appPath: app.getPath('exe'),
};
}