"use strict"; import { app, protocol, BrowserWindow, ipcMain, dialog } from "electron"; import { createProtocol } from "vue-cli-plugin-electron-builder/lib"; // 替换argon2为bcryptjs const bcrypt = require("bcryptjs"); const isDevelopment = process.env.NODE_ENV !== "production"; // 导入fs模块用于文件操作 const fs = require("fs"); const path = require("path"); // 导入数据库相关函数 const { checkDatabaseInitialized, initializeDatabase, initializeUserDatabase, } = require("./db/index.js"); // 导入数据库路径函数 const { getUserDbPath } = require("./db/path.js"); // 导入配置服务 const { initConfigIpc } = require("./service/configService.js"); // 导入字典服务 const { initDictIpc } = require("./service/dictService.js"); // 导入试题服务 const { initQuestionIpc } = require("./service/questionService.js"); // 导入考试服务 const { initExamIpc } = require("./service/examService.js"); // 导入考生服务 const { initExamineeIpc } = require("./service/examineeService.js"); // 导入考生考试服务 const { initExamingIpc } = require("./service/examingService.js"); // 导入文件服务 const { initFileIpc } = require("./service/fileService.js"); // Scheme must be registered before the app is ready protocol.registerSchemesAsPrivileged([ { scheme: "app", privileges: { secure: true, standard: true } }, ]); // 添加全局变量保存窗口引用 let mainWindow = null; async function createWindow() { // Create the browser window. mainWindow = new BrowserWindow({ width: 800, // 默认宽度(实际会被最大化覆盖) height: 600, // 默认高度(实际会被最大化覆盖) show: false, // 先隐藏窗口,避免闪烁 webPreferences: { // 更新preload路径为background目录下的preload.js preload: require("path").join(process.cwd(), "background/preload.js"), nodeIntegration: false, contextIsolation: true, }, }); // 设置隐藏菜单栏 mainWindow.setMenu(null); // 在窗口显示前设置最大化 mainWindow.maximize(); // 然后显示窗口 mainWindow.show(); // 添加窗口关闭事件监听 mainWindow.on("close", (event) => { console.log("检测到窗口关闭事件"); // 阻止默认的关闭行为 event.preventDefault(); console.log("已阻止默认关闭行为"); // 使用同步方式显示对话框(Electron 11.5支持的方式) try { // 直接显示对话框并获取结果 const response = dialog.showMessageBoxSync(mainWindow, { type: "warning", title: "确认关闭", message: "确认要退出软件吗?退出后,未完成的考试将不会被保存。", buttons: ["取消", "确认关闭"], defaultId: 0, cancelId: 0, }); console.log("用户选择了response:", response); // 检查用户选择的按钮索引 if (response === 1) { console.log("用户确认关闭,准备退出应用"); // 移除所有事件监听器以防止任何干扰 mainWindow.removeAllListeners(); // 强制关闭窗口 mainWindow.destroy(); // 然后退出应用 setTimeout(() => { console.log("强制退出应用"); app.quit(); }, 100); } else { console.log("用户取消关闭"); } } catch (error) { console.error("显示对话框时出错:", error); } }); if (process.env.WEBPACK_DEV_SERVER_URL) { // Load the url of the dev server if in development mode await mainWindow.loadURL(process.env.WEBPACK_DEV_SERVER_URL); if (!process.env.IS_TEST) mainWindow.webContents.openDevTools(); } else { createProtocol("app"); // Load the index.html when not in development mainWindow.loadURL("app://./index.html"); } } // Quit when all windows are closed. app.on("window-all-closed", () => { // On macOS it is common for applications and their menu bar // to stay active until the user quits explicitly with Cmd + Q if (process.platform !== "darwin") { app.quit(); } }); app.on("activate", () => { // On macOS it's common to re-create a window in the app when the // dock icon is clicked and there are no other windows open. if (BrowserWindow.getAllWindows().length === 0) createWindow(); }); // 添加退出确认逻辑 app.on("before-quit", (event) => { console.log("检测到应用退出事件"); // 注释掉这部分代码,因为我们已经在窗口的close事件中处理了确认逻辑 /* // 如果是MacOS系统,让系统默认处理 if (process.platform === 'darwin') { return } // 如果没有窗口,直接退出 if (!mainWindow) { return } // 阻止默认的退出行为 event.preventDefault() // 显示确认对话框 dialog.showMessageBox(mainWindow, { type: 'warning', title: '确认关闭', message: '确认要退出软件吗?退出后,未完成的考试将不会被保存。', buttons: ['取消', '确认关闭'], defaultId: 0, cancelId: 0 }).then((result) => { // 如果用户确认关闭,则退出应用 if (result.response === 1) { app.exit(0) } }).catch((err) => { console.error('显示退出确认对话框失败:', err) }) */ }); // This method will be called when Electron has finished // initialization and is ready to create browser windows. // Some APIs can only be used after this event occurs. // 在文件顶部添加 const sqlite3 = require("sqlite3").verbose(); // 在app.on('ready')事件中添加 app.on("ready", async () => { // 禁用Vue DevTools扩展 /* if (isDevelopment && !process.env.IS_TEST) { // Install Vue Devtools try { await installExtension(VUEJS_DEVTOOLS) } catch (e) { console.error('Vue Devtools failed to install:', e.toString()) } } */ // 初始化配置相关的IPC处理程序 initConfigIpc(ipcMain); // 初始化字典相关的IPC处理程序 initDictIpc(ipcMain); // 初始化试题相关的IPC处理程序 initQuestionIpc(ipcMain); // 初始化考试相关的IPC处理程序 initExamIpc(ipcMain); // 初始化考生相关的IPC处理程序 initExamineeIpc(ipcMain); // 初始化考生考试相关的IPC处理程序 initExamingIpc(ipcMain); // 初始化文件相关的IPC处理程序 initFileIpc(ipcMain); // 检查数据库是否初始化 try { const isInitialized = await checkDatabaseInitialized(); if (!isInitialized) { await initializeDatabase(); } } catch (error) { console.error("数据库初始化失败:", error); } createWindow(); }); // Exit cleanly on request from parent process in development mode. if (isDevelopment) { if (process.platform === "win32") { process.on("message", (data) => { if (data === "graceful-exit") { app.quit(); } }); } else { process.on("SIGTERM", () => { app.quit(); }); } } // 实现hashTest接口(替换原来的argon2Test) ipcMain.handle("hashTest", async (event, inputString) => { try { // 使用bcrypt.hash方法计算哈希值 const salt = await bcrypt.genSalt(10); const hash = await bcrypt.hash(inputString, salt); return hash; } catch (error) { console.error("Error in hashTest calculation:", error); throw error; } }); // 数据库相关IPC接口 ipcMain.handle("check-database-initialized", async () => { try { return await checkDatabaseInitialized(); } catch (error) { console.error("Failed to check database initialization:", error); return false; } }); ipcMain.handle("initialize-database", async () => { try { return await initializeDatabase(); } catch (error) { console.error("Failed to initialize database:", error); return false; } }); // 检查user.db是否存在 ipcMain.handle("checkUserDbExists", async () => { try { const userDbPath = getUserDbPath(); return fs.existsSync(userDbPath); } catch (error) { console.error("检查user.db文件是否存在失败:", error); return false; } }); // 静默初始化用户数据库 ipcMain.handle("initializeUserDatabaseSilently", async () => { try { console.log("开始静默初始化用户数据库..."); const result = await initializeUserDatabase(); console.log("静默初始化用户数据库完成:", result); return { success: true, result }; } catch (error) { console.error("静默初始化用户数据库失败:", error); return { success: false, error: error.message }; } }); // 检测是否为便携模式运行 let isPortableMode = false; let appDataPath = ""; // 初始化应用路径 - 兼容Node.js 12和Windows 7 function initializeAppPaths() { // 检查应用根目录是否存在data文件夹,如果存在则认为是便携模式 // Windows路径兼容处理 const appRoot = process.cwd(); const portableDataPath = path.join(appRoot, "data"); const defaultDataPath = path.join(app.getPath("userData"), "data"); // 检查是否为便携模式 if (fs.existsSync(portableDataPath)) { isPortableMode = true; appDataPath = portableDataPath; console.log("检测到便携模式,使用当前目录的data文件夹:", appDataPath); } else { isPortableMode = false; appDataPath = defaultDataPath; console.log("使用默认数据目录:", appDataPath); // 确保默认数据目录存在 if (!fs.existsSync(appDataPath)) { // 递归创建目录,兼容Windows路径 try { fs.mkdirSync(appDataPath, { recursive: true }); } catch (error) { console.error("创建默认数据目录失败:", error); } } } } // 添加全局函数获取数据目录 global.getAppDataPath = function () { return appDataPath; }; global.isPortableMode = function () { return isPortableMode; }; // 在应用ready事件前初始化路径 initializeAppPaths();