/** * 自然写互动课堂PC端应用软件 V1.0 * * main.ts - Electron主进程入口 * * 功能说明: * - Electron应用生命周期管理 * - 主窗口创建与配置 * - 系统托盘与菜单 * - IPC通信注册 * - 自动更新检测 * - 单实例锁定 * - 全局异常处理 */ import { app, BrowserWindow, Menu, Tray, ipcMain, dialog, shell } from 'electron'; import * as path from 'path'; import * as fs from 'fs'; /* ======================== 应用配置 ======================== */ /** 应用版本号 */ const APP_VERSION = '1.0.0'; /** 应用名称 */ const APP_NAME = '自然写互动课堂'; /** 窗口默认尺寸 */ const DEFAULT_WIDTH = 1440; const DEFAULT_HEIGHT = 900; /** 最小窗口尺寸 */ const MIN_WIDTH = 1024; const MIN_HEIGHT = 680; /** 开发模式标志 */ const IS_DEV = process.env.NODE_ENV === 'development'; /* ======================== 全局变量 ======================== */ /** 主窗口实例 */ let mainWindow: BrowserWindow | null = null; /** 系统托盘实例 */ let tray: Tray | null = null; /** 窗口状态保存路径 */ const windowStatePath = path.join(app.getPath('userData'), 'window-state.json'); /* ======================== 窗口状态管理 ======================== */ /** 保存的窗口状态 */ interface WindowState { x?: number; y?: number; width: number; height: number; isMaximized: boolean; } /** * 加载上次保存的窗口状态 */ function loadWindowState(): WindowState { try { if (fs.existsSync(windowStatePath)) { const data = fs.readFileSync(windowStatePath, 'utf-8'); return JSON.parse(data); } } catch (err) { console.error('[主进程] 加载窗口状态失败:', err); } return { width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT, isMaximized: false }; } /** * 保存当前窗口状态 */ function saveWindowState(win: BrowserWindow): void { const bounds = win.getBounds(); const state: WindowState = { x: bounds.x, y: bounds.y, width: bounds.width, height: bounds.height, isMaximized: win.isMaximized() }; try { fs.writeFileSync(windowStatePath, JSON.stringify(state, null, 2)); } catch (err) { console.error('[主进程] 保存窗口状态失败:', err); } } /* ======================== 窗口创建 ======================== */ /** * 创建主窗口 * 配置安全选项、预加载脚本和窗口参数 */ function createMainWindow(): void { const savedState = loadWindowState(); mainWindow = new BrowserWindow({ title: APP_NAME, width: savedState.width, height: savedState.height, x: savedState.x, y: savedState.y, minWidth: MIN_WIDTH, minHeight: MIN_HEIGHT, show: false, frame: true, backgroundColor: '#ffffff', webPreferences: { /* 安全选项:渲染进程沙箱化 */ nodeIntegration: false, contextIsolation: true, sandbox: true, /* 预加载脚本路径 */ preload: path.join(__dirname, 'preload.js'), /* 禁用远程模块 */ webSecurity: true, /* 禁止打开新窗口 */ allowRunningInsecureContent: false } }); /* 加载渲染进程页面 */ if (IS_DEV) { mainWindow.loadURL('http://localhost:5173'); mainWindow.webContents.openDevTools(); } else { mainWindow.loadFile(path.join(__dirname, '../renderer/index.html')); } /* 窗口就绪后显示(避免白屏闪烁) */ mainWindow.once('ready-to-show', () => { if (savedState.isMaximized) { mainWindow?.maximize(); } mainWindow?.show(); console.log('[主进程] 主窗口已显示'); }); /* 窗口关闭前保存状态 */ mainWindow.on('close', (event) => { if (mainWindow) { saveWindowState(mainWindow); } }); mainWindow.on('closed', () => { mainWindow = null; }); /* 拦截外部链接在系统浏览器打开 */ mainWindow.webContents.setWindowOpenHandler(({ url }) => { shell.openExternal(url); return { action: 'deny' }; }); console.log(`[主进程] 窗口创建完成: ${savedState.width}x${savedState.height}`); } /* ======================== 系统托盘 ======================== */ /** * 创建系统托盘图标和菜单 */ function createTray(): void { const iconPath = path.join(__dirname, '../assets/tray-icon.png'); tray = new Tray(iconPath); tray.setToolTip(APP_NAME); const contextMenu = Menu.buildFromTemplate([ { label: '显示主窗口', click: () => mainWindow?.show() }, { type: 'separator' }, { label: '设备管理', click: () => sendToRenderer('navigate', '/devices') }, { label: '设置', click: () => sendToRenderer('navigate', '/settings') }, { type: 'separator' }, { label: `版本 ${APP_VERSION}`, enabled: false }, { label: '退出', click: () => app.quit() } ]); tray.setContextMenu(contextMenu); tray.on('click', () => mainWindow?.show()); } /* ======================== IPC通信处理 ======================== */ /** * 向渲染进程发送消息 */ function sendToRenderer(channel: string, data: any): void { mainWindow?.webContents.send(channel, data); } /** * 注册IPC通信处理器 * 渲染进程通过IPC调用主进程的系统API */ function setupIpcHandlers(): void { /* 获取应用信息 */ ipcMain.handle('app:getInfo', () => ({ version: APP_VERSION, name: APP_NAME, platform: process.platform, arch: process.arch, userDataPath: app.getPath('userData') })); /* 文件选择对话框 */ ipcMain.handle('dialog:openFile', async (_, options) => { const result = await dialog.showOpenDialog(mainWindow!, { title: options.title || '选择文件', filters: options.filters || [{ name: '所有文件', extensions: ['*'] }], properties: options.properties || ['openFile'] }); return result.filePaths; }); /* 保存文件对话框 */ ipcMain.handle('dialog:saveFile', async (_, options) => { const result = await dialog.showSaveDialog(mainWindow!, { title: options.title || '保存文件', defaultPath: options.defaultPath, filters: options.filters || [{ name: '所有文件', extensions: ['*'] }] }); return result.filePath; }); /* 文件读取 */ ipcMain.handle('fs:readFile', async (_, filePath: string) => { return fs.readFileSync(filePath, 'utf-8'); }); /* 文件写入 */ ipcMain.handle('fs:writeFile', async (_, filePath: string, content: string) => { fs.writeFileSync(filePath, content, 'utf-8'); return true; }); /* 打印功能 */ ipcMain.handle('print:start', async (_, options) => { mainWindow?.webContents.print({ silent: options.silent || false, printBackground: true, copies: options.copies || 1, pageSize: options.pageSize || 'A4' }); }); /* 窗口控制 */ ipcMain.on('window:minimize', () => mainWindow?.minimize()); ipcMain.on('window:maximize', () => { if (mainWindow?.isMaximized()) { mainWindow.unmaximize(); } else { mainWindow?.maximize(); } }); ipcMain.on('window:close', () => mainWindow?.close()); console.log('[主进程] IPC处理器注册完成'); } /* ======================== 自动更新 ======================== */ /** * 检查应用更新 * 使用electron-updater检查并安装更新 */ function checkForUpdates(): void { if (IS_DEV) return; console.log('[主进程] 检查应用更新...'); /* autoUpdater.checkForUpdatesAndNotify() .then(result => { ... }) .catch(err => { ... }); */ /* autoUpdater.on('update-available', (info) => { sendToRenderer('update:available', info); }); autoUpdater.on('download-progress', (progress) => { sendToRenderer('update:progress', progress); }); autoUpdater.on('update-downloaded', (info) => { sendToRenderer('update:downloaded', info); }); */ } /* ======================== 应用生命周期 ======================== */ /** 确保单实例运行 */ const gotLock = app.requestSingleInstanceLock(); if (!gotLock) { console.log('[主进程] 已有实例运行,退出'); app.quit(); } app.on('second-instance', () => { /* 用户尝试打开第二个实例时,聚焦已有窗口 */ if (mainWindow) { if (mainWindow.isMinimized()) mainWindow.restore(); mainWindow.focus(); } }); /* 应用就绪 */ app.whenReady().then(() => { console.log(`[主进程] ${APP_NAME} v${APP_VERSION} 启动`); createMainWindow(); createTray(); setupIpcHandlers(); /* 延迟检查更新 */ setTimeout(checkForUpdates, 5000); }); /* macOS特殊处理:所有窗口关闭后重新创建 */ app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { createMainWindow(); } }); /* 所有窗口关闭时退出(macOS除外) */ app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit(); } }); /* 全局异常处理 */ process.on('uncaughtException', (error) => { console.error('[主进程] 未捕获异常:', error); dialog.showErrorBox('应用错误', `发生未预期的错误:\n${error.message}`); });