diff --git a/background/db/path.js b/background/db/path.js index 845f42f..46d723c 100644 --- a/background/db/path.js +++ b/background/db/path.js @@ -1,5 +1,5 @@ -const path = require('path'); const fs = require('fs'); +const path = require('path'); // 添加缺失的path模块导入 const { app } = require('electron'); // 判断是否为开发环境 @@ -7,14 +7,27 @@ const isDev = process.env.NODE_ENV === 'development' || !app.isPackaged; // 检测是否为便携模式 let isPortable = false; -const exePath = app.getPath('exe'); -const appDir = path.dirname(exePath); -const portableFlagPath = path.join(appDir, 'portable.txt'); +let appDir; +let portableFlagPath; -// 如果应用目录中存在portable.txt文件,则启用便携模式 -if (!isDev && fs.existsSync(portableFlagPath)) { - isPortable = true; - console.log('启用便携模式'); +// 确保app已经初始化后再获取路径 +if (app && app.getPath) { + try { + const exePath = app.getPath('exe'); + appDir = path.dirname(exePath); + portableFlagPath = path.join(appDir, 'portable.txt'); + + // 如果应用目录中存在portable.txt文件,则启用便携模式 + if (!isDev && fs.existsSync(portableFlagPath)) { + isPortable = true; + console.log('启用便携模式'); + } + } catch (error) { + console.error('获取应用路径时出错:', error); + appDir = process.cwd(); + } +} else { + appDir = process.cwd(); } // 根据模式选择数据目录 @@ -49,5 +62,7 @@ function getUserDbPath() { // 导出函数供其他模块使用 module.exports = { getSystemDbPath, - getUserDbPath + getUserDbPath, + dataDir, // 导出数据目录路径,方便其他地方使用 + isPortable // 导出便携模式状态 }; \ No newline at end of file diff --git a/background/main.js b/background/main.js index d9183b6..e4c7c79 100644 --- a/background/main.js +++ b/background/main.js @@ -41,77 +41,97 @@ protocol.registerSchemesAsPrivileged([ let mainWindow = null; async function createWindow() { - // Create the browser window. - mainWindow = new BrowserWindow({ - width: 800, // 默认宽度(实际会被最大化覆盖) - height: 600, // 默认高度(实际会被最大化覆盖) - show: false, // 先隐藏窗口,避免闪烁 - webPreferences: { - // 改为使用绝对路径解析 - preload: require("path").join(process.cwd(), "src/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("用户取消关闭"); + try { + // Create the browser window. + mainWindow = new BrowserWindow({ + width: 800, + height: 600, + show: true, // 直接显示窗口,避免隐藏后显示可能导致的问题 + webPreferences: { + // 修复preload路径,使用更可靠的解析方式 + preload: path.join(__dirname, '..', 'src', 'preload.js'), + nodeIntegration: false, + contextIsolation: true, } - } 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"); + // 设置隐藏菜单栏 + mainWindow.setMenu(null); + + // 设置窗口最大化(放在窗口创建后,确保窗口已经初始化) + try { + mainWindow.maximize(); + } catch (error) { + console.warn('窗口最大化失败:', error); + } + + // 加载URL + 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"); + } + + // 添加窗口关闭事件监听 + 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); + } + }); + + // 添加错误监听 + mainWindow.webContents.on('did-fail-load', (event, errorCode, errorDescription) => { + console.error(`页面加载失败: ${errorCode} - ${errorDescription}`); + }); + + // 添加崩溃监听 + mainWindow.webContents.on('crashed', (event, killed) => { + console.error(`渲染进程崩溃: killed=${killed}`); + }); + + } catch (error) { + console.error('创建窗口时出错:', error); + // 如果创建窗口失败,显示错误对话框 + dialog.showErrorBox('应用启动失败', `无法创建应用窗口: ${error.message}`); } } @@ -175,92 +195,104 @@ 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(); + // 禁用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); + } + + // 添加数据库初始化相关的IPC处理 + ipcMain.handle('initialize-database', async (event) => { + try { + console.log('收到初始化数据库请求'); + await initializeDatabase(); + console.log('数据库初始化成功'); + return { success: true }; + } catch (error) { + console.error('数据库初始化失败:', error); + return { success: false, error: error.message }; + } + }); + + ipcMain.handle('check-database-initialized', async (event) => { + try { + return await checkDatabaseInitialized(); + } catch (error) { + console.error('检查数据库初始化状态失败:', error); + return false; + } + }); + + // 检查user.db是否存在的IPC处理 + ipcMain.handle('checkUserDbExists', async (event) => { + try { + const userDbPath = getUserDbPath(); + const exists = fs.existsSync(userDbPath); + console.log(`用户数据库检查结果: ${exists ? '存在' : '不存在'}`); + return exists; + } catch (error) { + console.error('检查用户数据库失败:', error); + return false; + } + }); + + // 静默初始化用户数据库的IPC处理 + ipcMain.handle('initializeUserDatabaseSilently', async (event) => { + try { + await initializeUserDatabase(); + console.log('用户数据库静默初始化成功'); + return { success: true }; + } catch (error) { + console.error('用户数据库静默初始化失败:', error); + return { success: false, error: error.message }; + } + }); + + // 检测是否为便携模式运行 + try { + const isInitialized = await checkDatabaseInitialized(); + console.log(`应用启动 - 数据库状态: ${isInitialized ? '已初始化' : '未初始化'}`); + createWindow(); + } catch (error) { + console.error('检查数据库状态时出错:', error); + createWindow(); } } catch (error) { - console.error("数据库初始化失败:", error); + console.error('应用初始化失败:', error); + dialog.showErrorBox('应用初始化失败', `无法初始化应用: ${error.message}`); } - - // 添加数据库初始化相关的IPC处理 - ipcMain.handle('initialize-database', async (event) => { - try { - console.log('收到初始化数据库请求'); - await initializeDatabase(); - console.log('数据库初始化成功'); - return { success: true }; - } catch (error) { - console.error('数据库初始化失败:', error); - return { success: false, error: error.message }; - } - }); - - ipcMain.handle('check-database-initialized', async (event) => { - try { - return await checkDatabaseInitialized(); - } catch (error) { - console.error('检查数据库初始化状态失败:', error); - return false; - } - }); - - // 检查user.db是否存在的IPC处理 - ipcMain.handle('checkUserDbExists', async (event) => { - try { - const userDbPath = getUserDbPath(); - const exists = fs.existsSync(userDbPath); - console.log(`用户数据库检查结果: ${exists ? '存在' : '不存在'}`); - return exists; - } catch (error) { - console.error('检查用户数据库失败:', error); - return false; - } - }); - - // 静默初始化用户数据库的IPC处理 - ipcMain.handle('initializeUserDatabaseSilently', async (event) => { - try { - await initializeUserDatabase(); - console.log('用户数据库静默初始化成功'); - return { success: true }; - } catch (error) { - console.error('用户数据库静默初始化失败:', error); - return { success: false, error: error.message }; - } - }); - - // 检测是否为便携模式运行 - createWindow(); }); // Exit cleanly on request from parent process in development mode. diff --git a/package-windows-portable.js b/package-windows-portable.js index 50f96d4..1a5b45d 100644 --- a/package-windows-portable.js +++ b/package-windows-portable.js @@ -1,4 +1,4 @@ -const fs = require('fs'); +const fs = require('fs'); // 添加缺失的fs模块导入 const path = require('path'); const { execSync } = require('child_process'); @@ -23,7 +23,6 @@ function deleteFolderRecursive(dir) { } // 创建打包前的准备工作 -// 在prepareForBuild函数中添加 function prepareForBuild() { console.log('开始准备Windows 7便携应用打包...'); @@ -69,20 +68,64 @@ function buildPortableApp() { process.env.ELECTRON_BUILDER_SKIP_NOTARIZATION = 'true'; process.env.ELECTRON_SKIP_NOTARIZE = 'true'; - // 使用最基本的命令行参数,移除不支持的--no-sign + // 使用最基本的命令行参数 execSync( 'npm run electron:build -- --win portable --ia32 --x64 --publish never', { stdio: 'inherit' } ); + // 构建完成后,创建最终的便携应用结构 + console.log('创建便携应用最终结构...'); + createPortableStructure(); + console.log('Windows 7便携应用构建完成!'); - console.log('构建产物位于 dist_electron 目录'); + console.log('构建产物位于 dist_electron 目录下的 portable-app 文件夹'); } catch (error) { console.error('构建失败:', error); process.exit(1); } } +// 创建便携应用最终结构:可执行文件和data目录在同一级 +function createPortableStructure() { + const distDir = path.join(__dirname, 'dist_electron'); + const portableAppDir = path.join(distDir, 'portable-app'); + + // 创建目标目录 + if (fs.existsSync(portableAppDir)) { + deleteFolderRecursive(portableAppDir); + } + fs.mkdirSync(portableAppDir, { recursive: true }); + + // 复制可执行文件 + const exeFiles = fs.readdirSync(distDir).filter(file => file.endsWith('.exe')); + if (exeFiles.length > 0) { + const exeFile = exeFiles[0]; + fs.copyFileSync(path.join(distDir, exeFile), path.join(portableAppDir, exeFile)); + console.log(`已复制可执行文件: ${exeFile}`); + } + + // 创建data目录并复制数据库文件 + const srcDataDir = path.join(__dirname, 'data'); + const destDataDir = path.join(portableAppDir, 'data'); + fs.mkdirSync(destDataDir, { recursive: true }); + + // 复制data目录下的所有文件 + if (fs.existsSync(srcDataDir)) { + const dataFiles = fs.readdirSync(srcDataDir); + dataFiles.forEach(file => { + const srcPath = path.join(srcDataDir, file); + const destPath = path.join(destDataDir, file); + fs.copyFileSync(srcPath, destPath); + console.log(`已复制数据文件: ${file}`); + }); + } + + // 确保portable.txt文件在可执行文件同级目录 + fs.copyFileSync(path.join(__dirname, 'portable.txt'), path.join(portableAppDir, 'portable.txt')); + console.log('已复制便携模式标识文件'); +} + // 主函数 function main() { console.log('==== Windows 7便携应用打包工具 ===='); diff --git a/vue.config.js b/vue.config.js index a6814fc..0e24778 100644 --- a/vue.config.js +++ b/vue.config.js @@ -86,7 +86,11 @@ module.exports = { { from: "data", to: "data", - filter: ["**/*"], // 修改为包含所有文件 + filter: ["**/*"], // 确保包含data目录下的所有文件 + }, + { + from: "portable.txt", + to: ".", // 确保portable.txt文件放在根目录 }, ], },