exam11/background/main.js
2025-09-09 22:06:02 +08:00

325 lines
9.4 KiB
JavaScript
Raw 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";
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() {
console.log('开始创建应用窗口...');
// 创建浏览器窗口
mainWindow = new BrowserWindow({
width: 800,
height: 600,
// 确保在Windows 7上能正常显示
show: false,
webPreferences: {
// 使用Node.js 12兼容的路径解析方式
preload: path.join(__dirname, '../src/preload.js'),
nodeIntegration: false,
contextIsolation: true,
}
});
// 设置隐藏菜单栏
mainWindow.setMenu(null);
// 添加窗口准备好显示时的事件
mainWindow.once('ready-to-show', () => {
console.log('窗口准备就绪,开始显示...');
try {
// 在Windows 7上使用try-catch确保最大化操作不失败
try {
mainWindow.maximize();
console.log('窗口已最大化');
} catch (e) {
console.error('窗口最大化失败:', e);
}
// 显示窗口
mainWindow.show();
console.log('窗口已显示');
} catch (error) {
console.error('显示窗口时出错:', error);
// 作为备用方案,再次尝试显示窗口
mainWindow.show();
}
});
// 添加窗口显示事件
mainWindow.on('show', () => {
console.log('窗口已成功显示');
});
// 添加webContents加载完成事件
mainWindow.webContents.on('did-finish-load', () => {
console.log('页面加载完成');
});
// 添加窗口关闭事件监听 - 保持原有逻辑
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) {
// 开发模式加载开发服务器URL
await mainWindow.loadURL(process.env.WEBPACK_DEV_SERVER_URL);
if (!process.env.IS_TEST) mainWindow.webContents.openDevTools();
} else {
createProtocol("app");
// 生产模式加载本地HTML文件
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处理器
// 这些处理器应该已经在各个service的init函数中被注册了
// 检测是否为便携模式运行
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();