202 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			TypeScript
		
	
	
		
		
			
		
	
	
			202 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			TypeScript
		
	
	
| 
								 | 
							
								// tray = 系统托盘
							 | 
						|||
| 
								 | 
							
								import path from 'path';
							 | 
						|||
| 
								 | 
							
								import { Tray, Menu, app, dialog, nativeImage, BrowserWindow, Notification, ipcMain } from 'electron';
							 | 
						|||
| 
								 | 
							
								import type { IpcMainInvokeEvent } from 'electron';
							 | 
						|||
| 
								 | 
							
								import {_PATHS} from '../paths';
							 | 
						|||
| 
								 | 
							
								import {$env, isDev} from '../env';
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								const TrayIcons = {
							 | 
						|||
| 
								 | 
							
								  // update-begin--author:liaozhiyang---date:20250725---for:【JHHB-13】桌面应用消息通知
							 | 
						|||
| 
								 | 
							
								  normal: nativeImage.createFromPath(
							 | 
						|||
| 
								 | 
							
								    process.platform === 'win32'
							 | 
						|||
| 
								 | 
							
								      ? path.join(_PATHS.publicRoot, 'logo.png')
							 | 
						|||
| 
								 | 
							
								      : path.join(_PATHS.electronRoot, './icons/mac/tray-icon.png').replace(/[\\/]dist[\\/]/, '/')
							 | 
						|||
| 
								 | 
							
								  ),
							 | 
						|||
| 
								 | 
							
								  // update-end--author:liaozhiyang---date:20250725---for:【JHHB-13】桌面应用消息通知
							 | 
						|||
| 
								 | 
							
								  empty: nativeImage.createEmpty(),
							 | 
						|||
| 
								 | 
							
								};
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								// 创建托盘图标
							 | 
						|||
| 
								 | 
							
								export function createTray(win: BrowserWindow) {
							 | 
						|||
| 
								 | 
							
								  const tray = new Tray(TrayIcons.normal);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								  const TrayUtils = useTray(tray, win);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								  tray.setToolTip($env.VITE_GLOB_APP_TITLE! + (isDev ? ' (开发环境)' : ''));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								  // 左键托盘图标显示主窗口
							 | 
						|||
| 
								 | 
							
								  tray.on('click', () => TrayUtils.showMainWindow());
							 | 
						|||
| 
								 | 
							
								  // 右键托盘图标显示托盘菜单
							 | 
						|||
| 
								 | 
							
								  tray.on('right-click', () => showTrayContextMenu());
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								  function showTrayContextMenu() {
							 | 
						|||
| 
								 | 
							
								    const trayContextMenu = getTrayMenus(win, TrayUtils);
							 | 
						|||
| 
								 | 
							
								    // 弹出托盘菜单,不使用 setContextMenu 方法是因为要实时更新菜单内容
							 | 
						|||
| 
								 | 
							
								    tray.popUpContextMenu(trayContextMenu);
							 | 
						|||
| 
								 | 
							
								  }
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								export function useTray(tray: Tray, win: BrowserWindow) {
							 | 
						|||
| 
								 | 
							
								  let isBlinking = false;
							 | 
						|||
| 
								 | 
							
								  let blinkTimer: NodeJS.Timeout | null = null;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								  function showMainWindow() {
							 | 
						|||
| 
								 | 
							
								    win.show();
							 | 
						|||
| 
								 | 
							
								  }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								  // 开始闪动
							 | 
						|||
| 
								 | 
							
								  function startBlink() {
							 | 
						|||
| 
								 | 
							
								    isBlinking = true;
							 | 
						|||
| 
								 | 
							
								    tray.setImage(TrayIcons.empty);
							 | 
						|||
| 
								 | 
							
								    blinkTimer = setTimeout(() => {
							 | 
						|||
| 
								 | 
							
								      tray.setImage(TrayIcons.normal);
							 | 
						|||
| 
								 | 
							
								      setTimeout(() => {
							 | 
						|||
| 
								 | 
							
								        if (isBlinking) {
							 | 
						|||
| 
								 | 
							
								          startBlink();
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								      }, 500);
							 | 
						|||
| 
								 | 
							
								    }, 500);
							 | 
						|||
| 
								 | 
							
								  }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								  // 结束闪动
							 | 
						|||
| 
								 | 
							
								  function stopBlink() {
							 | 
						|||
| 
								 | 
							
								    isBlinking = false;
							 | 
						|||
| 
								 | 
							
								    if (blinkTimer) {
							 | 
						|||
| 
								 | 
							
								      clearTimeout(blinkTimer);
							 | 
						|||
| 
								 | 
							
								      blinkTimer = null;
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								    tray.setImage(TrayIcons.normal);
							 | 
						|||
| 
								 | 
							
								  }
							 | 
						|||
| 
								 | 
							
								  ipcMain.on('tray-flash', (event: IpcMainInvokeEvent) => {
							 | 
						|||
| 
								 | 
							
								    // 仅在 Windows 系统中闪烁
							 | 
						|||
| 
								 | 
							
								    if (process.platform === 'win32') {
							 | 
						|||
| 
								 | 
							
								      startBlink();
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								  });
							 | 
						|||
| 
								 | 
							
								  ipcMain.on('tray-flash-stop', (event: IpcMainInvokeEvent) => {
							 | 
						|||
| 
								 | 
							
								    // 仅在 Windows 系统中停止闪烁
							 | 
						|||
| 
								 | 
							
								    if (process.platform === 'win32') {
							 | 
						|||
| 
								 | 
							
								      stopBlink();
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								  });
							 | 
						|||
| 
								 | 
							
								  win.on('focus', () => {
							 | 
						|||
| 
								 | 
							
								    stopBlink();
							 | 
						|||
| 
								 | 
							
								  });
							 | 
						|||
| 
								 | 
							
								  // 发送桌面通知
							 | 
						|||
| 
								 | 
							
								  function sendDesktopNotice() {
							 | 
						|||
| 
								 | 
							
								    // 判断是否支持桌面通知
							 | 
						|||
| 
								 | 
							
								    if (!Notification.isSupported()) {
							 | 
						|||
| 
								 | 
							
								      // todo 实际开发中不需要提示,直接返回或者换一种提示方式
							 | 
						|||
| 
								 | 
							
								      dialog.showMessageBoxSync(win, {
							 | 
						|||
| 
								 | 
							
								        type: 'error',
							 | 
						|||
| 
								 | 
							
								        title: '错误',
							 | 
						|||
| 
								 | 
							
								        message: '当前系统不支持桌面通知',
							 | 
						|||
| 
								 | 
							
								      });
							 | 
						|||
| 
								 | 
							
								      return;
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								    const ins = new Notification({
							 | 
						|||
| 
								 | 
							
								      title: '通知标题',
							 | 
						|||
| 
								 | 
							
								      body: '通知内容第一行\n通知内容第二行',
							 | 
						|||
| 
								 | 
							
								      // icon: TrayIcons.normal.resize({width: 32, height: 32}),
							 | 
						|||
| 
								 | 
							
								    });
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    ins.on('click', () => {
							 | 
						|||
| 
								 | 
							
								      dialog.showMessageBoxSync(win, {
							 | 
						|||
| 
								 | 
							
								        type: 'info',
							 | 
						|||
| 
								 | 
							
								        title: '提示',
							 | 
						|||
| 
								 | 
							
								        message: '通知被点击',
							 | 
						|||
| 
								 | 
							
								      });
							 | 
						|||
| 
								 | 
							
								    });
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    ins.show();
							 | 
						|||
| 
								 | 
							
								  }
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								  return {
							 | 
						|||
| 
								 | 
							
								    showMainWindow,
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    startBlink,
							 | 
						|||
| 
								 | 
							
								    stopBlink,
							 | 
						|||
| 
								 | 
							
								    isBlinking: () => isBlinking,
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    sendDesktopNotice,
							 | 
						|||
| 
								 | 
							
								  };
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								const MenuIcon = {
							 | 
						|||
| 
								 | 
							
								  exit: nativeImage
							 | 
						|||
| 
								 | 
							
								    .createFromDataURL(
							 | 
						|||
| 
								 | 
							
								      'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAA7EAAAOxAGVKw4bAAACJ0lEQVR4nH1TzWvUQBRP7fpxsWqVXsSLiAevRWhhN28msRJo981kay4WRBCF/QdEFJpbaUHw4kFBQTwUKX4gKh48KPiBBcGLJ1F0uzPZ7ibWXf0DIjObielS+mDIm/fxm9/85sWyBixN06E0CIaV3wB2XhC8puOWNZSG4Y7B+k2mi7Kl9l2n9rHnzvbWJoLRYn7r5jTViQjwzM8ynlC+AFyVgN2NU8G+Rnn6QETx3FfP223A/jeHfWqCsAUJ7Hlryh9Te0nYqiDsz9rE6VHVIABvNwEf/ADYk4OsZPeVFbwiCHtcZBVR9k4CJhJmDuUxwEVJ8H4fINOkC9Vjbeq/UTR1IgPturX3f93Z35+B7ddxgJL6dih/skF9zE9KCJ//5bDLpii1+npIuzolKTubC5gBxzarJo6vWWjrUP+etFlF+ds9lRFOXalN+NPEmxvRDS3KH34v8+PFIgNmTh0EahH+InGCwzoQEbYcuTMnlR8aYbaxGHFvRNiznssP6sA65UsxrdU1+hYnFhlpAGAkdvzlPLFu88mY8pcrVjCsxcqGapC2eYW249/tUH4xS4QaVQLeigi/YWJqPl4DlNRSrAwzSaoXIspeWUYrI9qXINglgT1qAt5JPG+kkNN5BSAJuyoJfhAVdmST4PlPBFASNs6rIgnspqC8HlF+SQAuRQTfKpYiEy6fwuIdP42P71T+t0l/TBKcE8AXm4DXBfB6w50+apgUhf4HZ5j+Z5+zNTAAAAAASUVORK5CYII='
							 | 
						|||
| 
								 | 
							
								    )
							 | 
						|||
| 
								 | 
							
								    .resize({
							 | 
						|||
| 
								 | 
							
								      width: 16,
							 | 
						|||
| 
								 | 
							
								      height: 16,
							 | 
						|||
| 
								 | 
							
								    }),
							 | 
						|||
| 
								 | 
							
								};
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								// 设置托盘菜单
							 | 
						|||
| 
								 | 
							
								function getTrayMenus(win: BrowserWindow, TrayUtils: ReturnType<typeof useTray>) {
							 | 
						|||
| 
								 | 
							
								  const {startBlink, stopBlink, sendDesktopNotice} = TrayUtils;
							 | 
						|||
| 
								 | 
							
								  const isBlinking = TrayUtils.isBlinking();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								  return Menu.buildFromTemplate([
							 | 
						|||
| 
								 | 
							
								    ...(isDev
							 | 
						|||
| 
								 | 
							
								      ? [
							 | 
						|||
| 
								 | 
							
								        {
							 | 
						|||
| 
								 | 
							
								          label: '开发工具',
							 | 
						|||
| 
								 | 
							
								          submenu: [
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								              label: '以下菜单仅显示在开发环境',
							 | 
						|||
| 
								 | 
							
								              sublabel: '当前为开发环境',
							 | 
						|||
| 
								 | 
							
								              enabled: false,
							 | 
						|||
| 
								 | 
							
								            },
							 | 
						|||
| 
								 | 
							
								            {type: 'separator'},
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								              label: '切换 DevTools',
							 | 
						|||
| 
								 | 
							
								              click: () => win.webContents.toggleDevTools(),
							 | 
						|||
| 
								 | 
							
								            },
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								              label: `托盘图标${isBlinking ? '停止' : '开始'}闪烁`,
							 | 
						|||
| 
								 | 
							
								              sublabel: '模拟新消息提醒',
							 | 
						|||
| 
								 | 
							
								              click: () => (isBlinking ? stopBlink() : startBlink()),
							 | 
						|||
| 
								 | 
							
								            },
							 | 
						|||
| 
								 | 
							
								            {
							 | 
						|||
| 
								 | 
							
								              label: '发送桌面通知示例',
							 | 
						|||
| 
								 | 
							
								              click: () => sendDesktopNotice(),
							 | 
						|||
| 
								 | 
							
								            },
							 | 
						|||
| 
								 | 
							
								          ],
							 | 
						|||
| 
								 | 
							
								        },
							 | 
						|||
| 
								 | 
							
								        {type: 'separator'},
							 | 
						|||
| 
								 | 
							
								      ]
							 | 
						|||
| 
								 | 
							
								      : ([] as any)),
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								      label: '显示主窗口',
							 | 
						|||
| 
								 | 
							
								      // 文件图标
							 | 
						|||
| 
								 | 
							
								      icon: TrayIcons.normal.resize({width: 16, height: 16}),
							 | 
						|||
| 
								 | 
							
								      click: () => win.show(),
							 | 
						|||
| 
								 | 
							
								    },
							 | 
						|||
| 
								 | 
							
								    {type: 'separator'},
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								      label: '退出',
							 | 
						|||
| 
								 | 
							
								      // base64图标
							 | 
						|||
| 
								 | 
							
								      icon: MenuIcon.exit,
							 | 
						|||
| 
								 | 
							
								      click: () => {
							 | 
						|||
| 
								 | 
							
								        // 弹出是否确认退出提示框
							 | 
						|||
| 
								 | 
							
								        const choice = dialog.showMessageBoxSync(win, {
							 | 
						|||
| 
								 | 
							
								          type: 'question',
							 | 
						|||
| 
								 | 
							
								          title: '提示',
							 | 
						|||
| 
								 | 
							
								          message: '确定要退出应用吗?',
							 | 
						|||
| 
								 | 
							
								          buttons: ['退出', '取消'],
							 | 
						|||
| 
								 | 
							
								          defaultId: 1,
							 | 
						|||
| 
								 | 
							
								          cancelId: 1,
							 | 
						|||
| 
								 | 
							
								          noLink: true,
							 | 
						|||
| 
								 | 
							
								        });
							 | 
						|||
| 
								 | 
							
								        // 用户选择了退出,直接 exit
							 | 
						|||
| 
								 | 
							
								        if (choice === 0) {
							 | 
						|||
| 
								 | 
							
								          // global.isQuitting = true;
							 | 
						|||
| 
								 | 
							
								          app.exit(0);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								      },
							 | 
						|||
| 
								 | 
							
								    },
							 | 
						|||
| 
								 | 
							
								  ]);
							 | 
						|||
| 
								 | 
							
								}
							 |