main.ts 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. // 使用 CommonJS 格式
  2. const { app, BrowserWindow, ipcMain, shell, session, Menu, Tray, nativeImage } = require('electron');
  3. const { join } = require('path');
  4. const fs = require('fs');
  5. let mainWindow: typeof BrowserWindow.prototype | null = null;
  6. let tray: typeof Tray.prototype | null = null;
  7. let isQuitting = false;
  8. const VITE_DEV_SERVER_URL = process.env.VITE_DEV_SERVER_URL;
  9. // 获取图标路径
  10. function getIconPath() {
  11. return VITE_DEV_SERVER_URL
  12. ? join(__dirname, '../public/icons/icon-256.png')
  13. : join(__dirname, '../dist/icons/icon-256.png');
  14. }
  15. // 获取托盘图标路径
  16. function getTrayIconPath() {
  17. return VITE_DEV_SERVER_URL
  18. ? join(__dirname, '../public/icons/tray-icon.png')
  19. : join(__dirname, '../dist/icons/tray-icon.png');
  20. }
  21. // 创建托盘图标
  22. function createTrayIcon(): typeof nativeImage.prototype {
  23. const trayIconPath = getTrayIconPath();
  24. return nativeImage.createFromPath(trayIconPath);
  25. }
  26. // 创建系统托盘
  27. function createTray() {
  28. const trayIcon = createTrayIcon();
  29. tray = new Tray(trayIcon);
  30. const contextMenu = Menu.buildFromTemplate([
  31. {
  32. label: '显示主窗口',
  33. click: () => {
  34. if (mainWindow) {
  35. mainWindow.show();
  36. mainWindow.focus();
  37. }
  38. }
  39. },
  40. {
  41. label: '最小化到托盘',
  42. click: () => {
  43. mainWindow?.hide();
  44. }
  45. },
  46. { type: 'separator' },
  47. {
  48. label: '退出',
  49. click: () => {
  50. isQuitting = true;
  51. app.quit();
  52. }
  53. }
  54. ]);
  55. tray.setToolTip('多平台媒体管理系统');
  56. tray.setContextMenu(contextMenu);
  57. // 点击托盘图标显示窗口
  58. tray.on('click', () => {
  59. if (mainWindow) {
  60. if (mainWindow.isVisible()) {
  61. mainWindow.focus();
  62. } else {
  63. mainWindow.show();
  64. mainWindow.focus();
  65. }
  66. }
  67. });
  68. // 双击托盘图标显示窗口
  69. tray.on('double-click', () => {
  70. if (mainWindow) {
  71. mainWindow.show();
  72. mainWindow.focus();
  73. }
  74. });
  75. }
  76. function createWindow() {
  77. // 隐藏默认菜单栏
  78. Menu.setApplicationMenu(null);
  79. const iconPath = getIconPath();
  80. mainWindow = new BrowserWindow({
  81. width: 1400,
  82. height: 900,
  83. minWidth: 1200,
  84. minHeight: 700,
  85. icon: iconPath,
  86. webPreferences: {
  87. preload: join(__dirname, 'preload.js'),
  88. nodeIntegration: false,
  89. contextIsolation: true,
  90. webviewTag: true, // 启用 webview 标签
  91. },
  92. frame: false, // 无边框窗口,自定义标题栏
  93. transparent: false,
  94. backgroundColor: '#f0f2f5',
  95. show: false,
  96. });
  97. // 窗口准备好后再显示,避免白屏
  98. mainWindow.once('ready-to-show', () => {
  99. mainWindow?.show();
  100. setupWindowEvents();
  101. });
  102. // 加载页面
  103. if (VITE_DEV_SERVER_URL) {
  104. mainWindow.loadURL(VITE_DEV_SERVER_URL);
  105. mainWindow.webContents.openDevTools();
  106. } else {
  107. mainWindow.loadFile(join(__dirname, '../dist/index.html'));
  108. }
  109. // 处理外部链接
  110. mainWindow.webContents.setWindowOpenHandler(({ url }: { url: string }) => {
  111. shell.openExternal(url);
  112. return { action: 'deny' };
  113. });
  114. // 关闭按钮默认最小化到托盘
  115. mainWindow.on('close', (event: Event) => {
  116. if (!isQuitting) {
  117. event.preventDefault();
  118. mainWindow?.hide();
  119. // 显示托盘通知(仅首次)
  120. if (tray && !app.isPackaged) {
  121. // 开发模式下可以显示通知
  122. }
  123. }
  124. });
  125. mainWindow.on('closed', () => {
  126. mainWindow = null;
  127. });
  128. }
  129. // 单实例锁定
  130. const gotTheLock = app.requestSingleInstanceLock();
  131. if (!gotTheLock) {
  132. app.quit();
  133. } else {
  134. app.on('second-instance', () => {
  135. if (mainWindow) {
  136. mainWindow.show();
  137. if (mainWindow.isMinimized()) mainWindow.restore();
  138. mainWindow.focus();
  139. }
  140. });
  141. app.whenReady().then(() => {
  142. createTray();
  143. createWindow();
  144. app.on('activate', () => {
  145. if (BrowserWindow.getAllWindows().length === 0) {
  146. createWindow();
  147. } else if (mainWindow) {
  148. mainWindow.show();
  149. }
  150. });
  151. });
  152. }
  153. // 阻止默认的 window-all-closed 行为,保持托盘运行
  154. app.on('window-all-closed', () => {
  155. // 不退出应用,保持托盘运行
  156. // 只有在 isQuitting 为 true 时才真正退出
  157. });
  158. // 应用退出前清理托盘
  159. app.on('before-quit', () => {
  160. isQuitting = true;
  161. });
  162. app.on('quit', () => {
  163. if (tray) {
  164. tray.destroy();
  165. tray = null;
  166. }
  167. });
  168. // IPC 处理
  169. ipcMain.handle('get-app-version', () => {
  170. return app.getVersion();
  171. });
  172. ipcMain.handle('get-platform', () => {
  173. return process.platform;
  174. });
  175. // 窗口控制
  176. ipcMain.on('window-minimize', () => {
  177. mainWindow?.minimize();
  178. });
  179. ipcMain.on('window-maximize', () => {
  180. if (mainWindow?.isMaximized()) {
  181. mainWindow.unmaximize();
  182. } else {
  183. mainWindow?.maximize();
  184. }
  185. });
  186. // 关闭窗口(最小化到托盘)
  187. ipcMain.on('window-close', () => {
  188. mainWindow?.hide();
  189. });
  190. // 真正退出应用
  191. ipcMain.on('app-quit', () => {
  192. isQuitting = true;
  193. app.quit();
  194. });
  195. // 获取窗口最大化状态
  196. ipcMain.handle('window-is-maximized', () => {
  197. return mainWindow?.isMaximized() || false;
  198. });
  199. // 监听窗口最大化/还原事件,通知渲染进程
  200. function setupWindowEvents() {
  201. mainWindow?.on('maximize', () => {
  202. mainWindow?.webContents.send('window-maximized', true);
  203. });
  204. mainWindow?.on('unmaximize', () => {
  205. mainWindow?.webContents.send('window-maximized', false);
  206. });
  207. }
  208. // 获取 webview 的 cookies
  209. ipcMain.handle('get-webview-cookies', async (_event: unknown, partition: string, url: string) => {
  210. try {
  211. const ses = session.fromPartition(partition);
  212. const cookies = await ses.cookies.get({ url });
  213. return cookies;
  214. } catch (error) {
  215. console.error('获取 cookies 失败:', error);
  216. return [];
  217. }
  218. });
  219. // 清除 webview 的 cookies
  220. ipcMain.handle('clear-webview-cookies', async (_event: unknown, partition: string) => {
  221. try {
  222. const ses = session.fromPartition(partition);
  223. await ses.clearStorageData({ storages: ['cookies'] });
  224. return true;
  225. } catch (error) {
  226. console.error('清除 cookies 失败:', error);
  227. return false;
  228. }
  229. });