main.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. // main.js (ESM 版本)
  2. import { app, BrowserWindow, Menu, ipcMain, shell } from 'electron';
  3. import { fileURLToPath } from 'url';
  4. import { dirname, join } from 'path';
  5. import { exec } from 'child_process';
  6. import { promisify } from 'util';
  7. import { writeLog } from './src/log/log-writer.js';
  8. // -------------- 构造 ESM 版 __dirname --------------
  9. const __filename = fileURLToPath(import.meta.url);
  10. const __dirname = dirname(__filename);
  11. const execAsync = promisify(exec);
  12. let win=null;
  13. // -------------- 单实例检查 ------------
  14. // 1. 请求单实例锁
  15. const gotTheLock = app.requestSingleInstanceLock();
  16. if (!gotTheLock) {
  17. // 抢锁失败 -> 立即退出
  18. app.quit();
  19. }
  20. // 2. 抢锁成功 -> 监听后续启动事件
  21. app.on('second-instance', (ev, cmd, cwd, additionalData) => {
  22. // 把已存在的窗口提到最前
  23. if (win) {
  24. if (win.isMinimized()) win.restore();
  25. win.focus();
  26. }
  27. // todo 也可以在这里解析 cmd 中的参数,做打开文件等操作
  28. });
  29. // -------------- 创建窗口 --------------
  30. function createWindow() {
  31. const isMac = process.platform === 'darwin';
  32. win = new BrowserWindow({
  33. show: false,
  34. frame: false,
  35. titleBarStyle: 'hidden',
  36. webPreferences: {
  37. nodeIntegration: false,
  38. contextIsolation: true,
  39. preload: join(__dirname, 'preload.js'),
  40. },
  41. });
  42. // 去掉应用菜单栏
  43. Menu.setApplicationMenu(null);
  44. if (!isMac) win.removeMenu();
  45. win.maximize();
  46. // 加载外置 H5 页面
  47. win.loadFile(join(process.cwd(), 'h5/index.html'));
  48. win.once('ready-to-show', () => win.show());
  49. //渲染进程死了,纪录日志
  50. win.webContents.on('render-process-gone', (event, details) => {
  51. writeLog('error',`渲染进程崩溃 ${details}`);
  52. // writeLog('error', JSON.stringify(details));
  53. });
  54. }
  55. // -------------- 应用生命周期 --------------
  56. app.whenReady().then(createWindow);
  57. app.on('window-all-closed', () => {
  58. if (process.platform !== 'darwin') app.quit();
  59. });
  60. app.on('activate', () => {
  61. if (BrowserWindow.getAllWindows().length === 0) createWindow();
  62. });
  63. // -------------- 系统操作函数 --------------
  64. const getSystemCommands = () => {
  65. const platform = process.platform;
  66. switch (platform) {
  67. case 'win32': // Windows
  68. return {
  69. shutdown: 'shutdown /s /t 0'
  70. };
  71. case 'darwin': // macOS
  72. return {
  73. shutdown: 'sudo shutdown -h now'
  74. };
  75. case 'linux': // Linux
  76. return {
  77. shutdown: 'systemctl poweroff'
  78. };
  79. default:
  80. return {
  81. shutdown: null
  82. };
  83. }
  84. };
  85. const executeSystemCommand = async (command, requiresAdmin = false) => {
  86. try {
  87. if (!command) {
  88. throw new Error('当前平台不支持该操作');
  89. }
  90. // Windows平台的权限检查
  91. if (process.platform === 'win32' && requiresAdmin) {
  92. // 检查是否以管理员身份运行
  93. try {
  94. await execAsync('net session', { timeout: 5000 });
  95. } catch (adminCheckError) {
  96. writeLog('warn', '操作可能需要管理员权限');
  97. return {
  98. success: false,
  99. error: '该操作需要管理员权限,请以管理员身份运行程序后重试',
  100. requiresAdmin: true
  101. };
  102. }
  103. }
  104. const { stdout, stderr } = await execAsync(command, { timeout: 10000 });
  105. writeLog('info', `系统命令执行成功: ${command}`);
  106. if (stderr && stderr.trim()) {
  107. writeLog('warn', `系统命令有警告信息: ${stderr}`);
  108. }
  109. return { success: true, stdout, stderr };
  110. } catch (error) {
  111. let errorMessage = error.message;
  112. // 特定错误类型的处理
  113. if (error.code === 'EACCES') {
  114. errorMessage = '权限不足,请以管理员身份运行程序';
  115. } else if (error.code === 'ENOENT') {
  116. errorMessage = '系统命令不存在或路径错误';
  117. } else if (error.code === 'ETIMEDOUT') {
  118. errorMessage = '操作超时,请重试';
  119. }
  120. writeLog('error', `系统命令执行失败: ${command}, 错误: ${errorMessage}`);
  121. return { success: false, error: errorMessage };
  122. }
  123. };
  124. // -------------- IPC --------------
  125. ipcMain.handle('write-log', async (_, level, msg) => {
  126. writeLog(level, msg);
  127. });
  128. // 退出应用
  129. ipcMain.handle('exit-close', async () => {
  130. writeLog('info', '用户选择关闭应用程序');
  131. app.quit();
  132. return { success: true };
  133. });
  134. // 关机
  135. ipcMain.handle('exit-shutdown', async () => {
  136. writeLog('info', '用户选择关机');
  137. const commands = getSystemCommands();
  138. // 关机通常需要管理员权限
  139. const requiresAdmin = true;
  140. return await executeSystemCommand(commands.shutdown, requiresAdmin);
  141. });