import { app, BrowserWindow, protocol } from "electron"; import path from "path"; import fs from "fs"; import Module from "module"; // 加速 Electron 启动:跳过 GPU 信息收集,减少初始化耗时 app.commandLine.appendSwitch("disable-gpu-shader-disk-cache"); app.commandLine.appendSwitch("disable-features", "CalculateNativeWinOcclusion"); declare const __APP_VERSION__: string | undefined; /** * 将 extraResources 中的 data 目录复制到用户数据目录(跳过已存在的文件,保留用户修改) */ function getVersionFromUpdateJson(filePath: string): string | null { try { if (fs.existsSync(filePath)) { const data = JSON.parse(fs.readFileSync(filePath, "utf8")); return data.version ?? null; } } catch {} return null; } function copyDirForce(src: string, dest: string): void { if (!fs.existsSync(src)) return; if (fs.existsSync(dest)) { fs.rmSync(dest, { recursive: true, force: true }); } copyDirRecursive(src, dest); } function initializeData(): void { const srcDir = path.join(process.resourcesPath, "data"); const destDir = path.join(app.getPath("userData"), "data"); const updateJsonFile = path.join(destDir, "update.json"); const currentVersion = typeof __APP_VERSION__ !== "undefined" ? __APP_VERSION__ : "0.0.0"; const userVersion = getVersionFromUpdateJson(updateJsonFile); // 首次安装或无update.json,直接全量拷贝 if (!fs.existsSync(destDir) || !userVersion) { copyDirRecursive(srcDir, destDir); return; } // 版本号不同则覆盖 serve 和 web 目录 if (userVersion !== currentVersion) { copyDirForce(path.join(srcDir, "serve"), path.join(destDir, "serve")); copyDirForce(path.join(srcDir, "web"), path.join(destDir, "web")); } } function copyDirRecursive(src: string, dest: string): void { if (!fs.existsSync(src)) return; if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true }); for (const entry of fs.readdirSync(src, { withFileTypes: true })) { // 跳过 oss 文件夹和 db2.sqlite 文件 if (entry.isDirectory() && entry.name === "logs") continue; if (entry.isDirectory() && entry.name === "oss") continue; if (!entry.isDirectory() && entry.name === "db2.sqlite") continue; const srcPath = path.join(src, entry.name); const destPath = path.join(dest, entry.name); if (entry.isDirectory()) { copyDirRecursive(srcPath, destPath); } else if (!fs.existsSync(destPath)) { fs.copyFileSync(srcPath, destPath); } } } //获取全部依赖路径,优先从 unpacked 加载原生模块,其他模块从 asar 加载 function getNodeModulesPaths(): string[] { const paths: string[] = []; if (app.isPackaged) { // external 依赖(原生模块)在 unpacked 目录 const unpackedNodeModules = path.join(process.resourcesPath, "app.asar.unpacked", "node_modules"); if (fs.existsSync(unpackedNodeModules)) { paths.push(unpackedNodeModules); } // 普通依赖在 asar 内 const asarNodeModules = path.join(process.resourcesPath, "app.asar", "node_modules"); paths.push(asarNodeModules); } else { paths.push(path.join(process.cwd(), "node_modules")); } return paths; } //动态加载 function requireWithCustomPaths(modulePath: string): any { const appNodeModulesPaths = getNodeModulesPaths(); // 保存原始方法 const originalNodeModulePaths = (Module as any)._nodeModulePaths; // 临时修改模块路径解析 (Module as any)._nodeModulePaths = function (from: string): string[] { const paths = originalNodeModulePaths.call(this, from); // 将主程序的 node_modules 添加到前面 for (let i = appNodeModulesPaths.length - 1; i >= 0; i--) { const p = appNodeModulesPaths[i]; if (!paths.includes(p)) { paths.unshift(p); } } return paths; }; try { // 清除缓存确保加载最新 delete require.cache[require.resolve(modulePath)]; return require(modulePath); } finally { // 恢复原始方法 (Module as any)._nodeModulePaths = originalNodeModulePaths; } } let mainWindow: BrowserWindow | null = null; let loadingWindow: BrowserWindow | null = null; const loadingHtml = `data:text/html;charset=utf-8,${encodeURIComponent(`
正在启动服务…
`)}`; function showLoading(): void { loadingWindow = new BrowserWindow({ width: 1000, height: 700, minWidth: 800, minHeight: 500, frame: false, resizable: false, maximizable: false, minimizable: false, show: true, backgroundColor: "#ffffff", autoHideMenuBar: true, titleBarStyle: "hidden", titleBarOverlay: { color: "#ffffff", symbolColor: "#333333", height: 36, }, }); loadingWindow.setMenuBarVisibility(false); loadingWindow.removeMenu(); loadingWindow.on("closed", () => { loadingWindow = null; }); void loadingWindow.loadURL(loadingHtml); } function closeLoading(): void { if (loadingWindow && !loadingWindow.isDestroyed()) { loadingWindow.close(); loadingWindow = null; } } function createMainWindow(): Promise