mirror of https://github.com/jeecgboot/jeecg-boot
202 lines
6.1 KiB
Vue
202 lines
6.1 KiB
Vue
// 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);
|
||
}
|
||
},
|
||
},
|
||
]);
|
||
}
|