325 lines
9.4 KiB
JavaScript
325 lines
9.4 KiB
JavaScript
"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: {
|
||
// 更新preload路径,使用__dirname确保路径正确
|
||
preload: path.join(__dirname, '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();
|