main.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. // electron/main.ts - Electron 主进程入口
  2. import { app, BrowserWindow, Menu } from 'electron'
  3. import * as path from 'path'
  4. import * as os from 'os'
  5. import { registerHandlers, removeHandlers } from './modules/ipc-handlers'
  6. import { logger } from './modules/logger'
  7. import { initConfig, getWindowBounds, saveWindowBounds } from './modules/config'
  8. import { initAutoUpdater } from './modules/updater'
  9. // 全局窗口实例
  10. let mainWindow: BrowserWindow | null = null
  11. // 开发环境判断
  12. const isDev = process.env.NODE_ENV === 'development' || !app.isPackaged
  13. // 根据平台获取图标路径
  14. function getIconPath(): string {
  15. const platform = os.platform()
  16. // 开发环境使用 public 目录,生产环境使用打包后的路径
  17. const basePath = isDev
  18. ? path.join(__dirname, '../public/icons')
  19. : path.join(process.resourcesPath, 'icons')
  20. if (platform === 'win32') {
  21. return path.join(basePath, 'win/icon.ico')
  22. } else if (platform === 'darwin') {
  23. return path.join(basePath, 'mac/icon.icns')
  24. } else {
  25. return path.join(basePath, 'png/256x256.png')
  26. }
  27. }
  28. /**
  29. * 创建主窗口
  30. */
  31. function createWindow(): void {
  32. // 获取保存的窗口位置
  33. const savedBounds = getWindowBounds()
  34. mainWindow = new BrowserWindow({
  35. width: savedBounds?.width ?? 1050,
  36. height: savedBounds?.height ?? 810,
  37. x: savedBounds?.x,
  38. y: savedBounds?.y,
  39. minWidth: 800,
  40. minHeight: 810,
  41. resizable: true, // 允许调整大小
  42. frame: false, // 无边框窗口
  43. titleBarStyle: 'hidden', // 隐藏标题栏
  44. icon: getIconPath(),
  45. webPreferences: {
  46. preload: path.join(__dirname, 'preload.js'),
  47. contextIsolation: true,
  48. nodeIntegration: false,
  49. sandbox: true
  50. }
  51. })
  52. // 开发环境加载 Vite 开发服务器
  53. if (isDev) {
  54. mainWindow.loadURL('http://localhost:5173')
  55. mainWindow.webContents.openDevTools({ mode: 'detach' })
  56. } else {
  57. // 生产环境加载打包后的文件
  58. mainWindow.loadFile(path.join(__dirname, '../dist/index.html'))
  59. }
  60. // 窗口移动或调整大小时保存位置
  61. mainWindow.on('moved', () => {
  62. if (mainWindow) {
  63. saveWindowBounds(mainWindow.getBounds())
  64. }
  65. })
  66. mainWindow.on('resized', () => {
  67. if (mainWindow) {
  68. saveWindowBounds(mainWindow.getBounds())
  69. }
  70. })
  71. mainWindow.on('closed', () => {
  72. mainWindow = null
  73. })
  74. logger.info('主窗口已创建')
  75. }
  76. /**
  77. * 应用初始化
  78. */
  79. async function initialize(): Promise<void> {
  80. // 初始化日志模块
  81. logger.init()
  82. // 写入安装日志,标记程序启动
  83. logger.installInfo(`========== 程序启动 ${new Date().toLocaleString()} ==========`)
  84. logger.info('应用启动', {
  85. platform: os.platform(),
  86. arch: os.arch(),
  87. nodeVersion: process.version,
  88. electronVersion: process.versions.electron,
  89. isDev
  90. })
  91. // 初始化配置
  92. await initConfig()
  93. // 注册 IPC 处理器
  94. registerHandlers()
  95. // 隐藏菜单栏
  96. Menu.setApplicationMenu(null)
  97. // 创建窗口
  98. createWindow()
  99. // 生产环境初始化自动更新
  100. if (!isDev && mainWindow) {
  101. initAutoUpdater(mainWindow)
  102. }
  103. // 注册窗口级别的 F12 快捷键切换开发者工具
  104. mainWindow?.webContents.on('before-input-event', (event, input) => {
  105. if (input.key === 'F12' && input.type === 'keyDown') {
  106. if (mainWindow?.webContents.isDevToolsOpened()) {
  107. mainWindow.webContents.closeDevTools()
  108. } else {
  109. mainWindow?.webContents.openDevTools()
  110. }
  111. event.preventDefault()
  112. }
  113. })
  114. }
  115. // 应用就绪
  116. app.whenReady().then(initialize)
  117. // 所有窗口关闭
  118. app.on('window-all-closed', () => {
  119. logger.info('所有窗口已关闭')
  120. if (os.platform() !== 'darwin') {
  121. app.quit()
  122. }
  123. })
  124. // macOS 激活
  125. app.on('activate', () => {
  126. if (BrowserWindow.getAllWindows().length === 0) {
  127. createWindow()
  128. }
  129. })
  130. // 应用退出前
  131. app.on('before-quit', () => {
  132. logger.info('应用即将退出')
  133. removeHandlers()
  134. logger.close()
  135. })
  136. // 未捕获的异常
  137. process.on('uncaughtException', (error) => {
  138. logger.error('未捕获的异常', error)
  139. })
  140. // 未处理的 Promise 拒绝
  141. process.on('unhandledRejection', (reason, promise) => {
  142. logger.error('未处理的 Promise 拒绝', { reason, promise: String(promise) })
  143. })