exam11/background/main.js
2025-09-14 16:08:36 +08:00

422 lines
13 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use strict";
const {
app,
protocol,
BrowserWindow,
ipcMain,
dialog,
shell,
} = require("electron");
const { createProtocol } = require("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 { getSystemDbPath, 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");
// 试卷
const { initPaperIpc } = require("./service/paperService.js");
// 导入 package.json 获取系统信息
const packageInfo = require("../package.json");
// 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路径避免依赖process.cwd()
preload: path.join(__dirname, "preload.js"),
nodeIntegration: false,
contextIsolation: true,
},
});
// 设置隐藏菜单栏
mainWindow.setMenu(null);
// 在窗口显示前设置最大化
mainWindow.maximize();
// 然后显示窗口
mainWindow.show();
// 移除之前的便携模式检测逻辑,直接打开开发者工具
// 这样在任何模式下都能查看控制台报错信息
try {
// 只在开发环境下打开开发者工具
if (isDevelopment && !process.env.IS_TEST) {
console.log("打开开发者工具");
mainWindow.webContents.openDevTools();
}
} catch (error) {
console.error("打开开发者工具时出错:", error);
}
// 添加窗口关闭事件监听
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')事件中添加获取package.json的IPC处理程序
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())
}
}
*/
// 添加获取package.json的IPC处理程序
ipcMain.handle("get-package-info", async () => {
try {
return {
success: true,
data: packageInfo,
};
} catch (error) {
console.error("获取package.json信息失败:", error);
return {
success: false,
message: "获取package.json信息失败",
data: null,
};
}
});
// 实现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();
// 新增添加获取路径信息的IPC处理程序
ipcMain.handle("get-exe-path", (event) => {
try {
const exePath = app.getPath("exe");
console.log("获取exe路径:", exePath);
return exePath;
} catch (error) {
console.error("获取exe路径失败:", error);
return null;
}
});
ipcMain.handle("get-database-paths", (event) => {
try {
const systemDbPath = getSystemDbPath();
const userDbPath = getUserDbPath();
console.log("获取数据库路径:");
console.log("system.db:", systemDbPath);
console.log("user.db:", userDbPath);
return { systemDbPath, userDbPath };
} catch (error) {
console.error("获取数据库路径失败:", error);
return { systemDbPath: "获取失败", userDbPath: "获取失败" };
}
});
// 在现有的IPC处理程序后面添加
ipcMain.handle("file-open-file", async (event, filePath) => {
try {
console.log("尝试打开文件:", filePath);
// 使用shell.openPath打开文件这会使用系统默认应用打开指定文件
const result = await shell.openPath(filePath);
// 在Windows上result是打开的文件路径在macOS和Linux上成功时返回空字符串
if (process.platform === "win32" || result === "") {
console.log("文件打开成功:", filePath);
return { success: true, message: "文件打开成功" };
} else {
console.error("文件打开失败:", result);
return { success: false, message: result };
}
} catch (error) {
console.error("打开文件时发生错误:", error);
return { success: false, message: error.message };
}
});
// 初始化配置相关的IPC处理程序
initConfigIpc(ipcMain);
// 初始化字典相关的IPC处理程序
initDictIpc(ipcMain);
// 初始化试题相关的IPC处理程序
initQuestionIpc(ipcMain);
// 初始化考试相关的IPC处理程序
initExamIpc(ipcMain);
// 初始化考生相关的IPC处理程序
initExamineeIpc(ipcMain);
// 初始化考生考试相关的IPC处理程序
initExamingIpc(ipcMain);
// 初始化文件相关的IPC处理程序
initFileIpc(ipcMain);
// 初始化试卷相关的IPC处理程序
initPaperIpc(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();
});
}
}