From 657d8df9fceb6747d704f9924ca978b83166ec27 Mon Sep 17 00:00:00 2001 From: chenqiang Date: Tue, 9 Sep 2025 02:52:00 +0800 Subject: [PATCH] win7 --- package-windows-portable.js | 51 ++++---- src/background/main.js | 240 +++++++++++++++++++----------------- vue.config.js | 87 ++++++------- 3 files changed, 195 insertions(+), 183 deletions(-) diff --git a/package-windows-portable.js b/package-windows-portable.js index 8260c78..4dd3a18 100644 --- a/package-windows-portable.js +++ b/package-windows-portable.js @@ -1,6 +1,6 @@ -const { execSync } = require('child_process'); -const fs = require('fs'); -const path = require('path'); +const { execSync } = require("child_process"); +const fs = require("fs"); +const path = require("path"); // 创建一个兼容Node.js 12的删除文件夹函数 function deleteFolderRecursive(dir) { @@ -24,64 +24,65 @@ function deleteFolderRecursive(dir) { // 创建打包前的准备工作 function prepareForBuild() { - console.log('开始准备Windows 7便携应用打包...'); + console.log("开始准备Windows 7便携应用打包..."); try { // 清理之前的构建文件 - 使用兼容Node.js 12的方法 - const buildDir = path.join(__dirname, 'dist_electron'); + const buildDir = path.join(__dirname, "dist_electron"); if (fs.existsSync(buildDir)) { - console.log('清理之前的构建文件...'); + console.log("清理之前的构建文件..."); deleteFolderRecursive(buildDir); } // 确保data目录存在(便携应用需要) - const dataDir = path.join(__dirname, 'data'); + const dataDir = path.join(__dirname, "data"); if (!fs.existsSync(dataDir)) { fs.mkdirSync(dataDir); - fs.writeFileSync(path.join(dataDir, '.gitignore'), '*\n!.gitignore'); - console.log('创建data目录完成'); + fs.writeFileSync(path.join(dataDir, ".gitignore"), "*\n!.gitignore"); + console.log("创建data目录完成"); } - console.log('准备工作完成'); + console.log("准备工作完成"); } catch (error) { - console.error('准备工作失败:', error); + console.error("准备工作失败:", error); process.exit(1); } } // 执行打包命令 function buildPortableApp() { - console.log('开始构建Windows 7便携应用...'); + console.log("开始构建Windows 7便携应用..."); try { // 设置构建环境变量 - process.env.WIN_CSC_LINK = ''; - process.env.WIN_CSC_KEY_PASSWORD = ''; + process.env.WIN_CSC_LINK = ""; + process.env.WIN_CSC_KEY_PASSWORD = ""; - // 执行构建命令 - 特别针对Windows 7优化,兼容Electron 11 + // 执行构建命令 - 移除不兼容的--no-compress选项 + // Electron Builder 22.9.1版本不支持--no-compress命令行参数 execSync( - 'npm run electron:build -- --win portable --ia32 --x64 --no-compress --publish never', - { stdio: 'inherit' } + "npm run electron:build -- --win portable --ia32 --x64 --publish never", + { stdio: "inherit" } ); - console.log('Windows 7便携应用构建完成!'); - console.log('构建产物位于 dist_electron 目录'); + console.log("Windows 7便携应用构建完成!"); + console.log("构建产物位于 dist_electron 目录"); } catch (error) { - console.error('构建失败:', error); + console.error("构建失败:", error); process.exit(1); } } // 主函数 function main() { - console.log('==== Windows 7便携应用打包工具 ===='); - console.log('Node.js版本:', process.version); - console.log('Electron版本: 11.5.0'); - console.log('Vue版本: 2.6.11'); + console.log("==== Windows 7便携应用打包工具 ===="); + console.log("Node.js版本:", process.version); + console.log("Electron版本: 11.5.0"); + console.log("Vue版本: 2.6.11"); prepareForBuild(); buildPortableApp(); } // 执行主函数 -main(); \ No newline at end of file +main(); diff --git a/src/background/main.js b/src/background/main.js index 72fbd83..df7fa08 100644 --- a/src/background/main.js +++ b/src/background/main.js @@ -1,54 +1,58 @@ -'use strict' +"use strict"; -import { app, protocol, BrowserWindow, ipcMain, dialog } from 'electron' -import { createProtocol } from 'vue-cli-plugin-electron-builder/lib' +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' +const bcrypt = require("bcryptjs"); +const isDevelopment = process.env.NODE_ENV !== "production"; // 导入fs模块用于文件操作 -const fs = require('fs') -const path = require('path') +const fs = require("fs"); +const path = require("path"); // 导入数据库相关函数 -const { checkDatabaseInitialized, initializeDatabase, initializeUserDatabase } = require('./db/index.js'); +const { + checkDatabaseInitialized, + initializeDatabase, + initializeUserDatabase, +} = require("./db/index.js"); // 导入数据库路径函数 -const { getUserDbPath } = require('./db/path.js'); +const { getUserDbPath } = require("./db/path.js"); // 导入配置服务 -const { initConfigIpc } = require('./service/configService.js'); +const { initConfigIpc } = require("./service/configService.js"); // 导入字典服务 -const { initDictIpc } = require('./service/dictService.js'); +const { initDictIpc } = require("./service/dictService.js"); // 导入试题服务 -const { initQuestionIpc } = require('./service/questionService.js'); +const { initQuestionIpc } = require("./service/questionService.js"); // 导入考试服务 -const { initExamIpc } = require('./service/examService.js'); +const { initExamIpc } = require("./service/examService.js"); // 导入考生服务 -const { initExamineeIpc } = require('./service/examineeService.js'); +const { initExamineeIpc } = require("./service/examineeService.js"); // 导入考生考试服务 -const { initExamingIpc } = require('./service/examingService.js'); +const { initExamingIpc } = require("./service/examingService.js"); // 导入文件服务 -const { initFileIpc } = require('./service/fileService.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 } } -]) + { scheme: "app", privileges: { secure: true, standard: true } }, +]); // 添加全局变量保存窗口引用 -let mainWindow = null +let mainWindow = null; async function createWindow() { // Create the browser window. mainWindow = new BrowserWindow({ - width: 800, // 默认宽度(实际会被最大化覆盖) + width: 800, // 默认宽度(实际会被最大化覆盖) height: 600, // 默认高度(实际会被最大化覆盖) show: false, // 先隐藏窗口,避免闪烁 webPreferences: { // 改为使用绝对路径解析 - preload: require('path').join(process.cwd(), 'src/preload.js'), + preload: require("path").join(process.cwd(), "src/preload.js"), nodeIntegration: false, - contextIsolation: true - } - }) + contextIsolation: true, + }, + }); // 设置隐藏菜单栏 mainWindow.setMenu(null); @@ -59,76 +63,76 @@ async function createWindow() { mainWindow.show(); // 添加窗口关闭事件监听 - mainWindow.on('close', (event) => { - console.log('检测到窗口关闭事件'); + mainWindow.on("close", (event) => { + console.log("检测到窗口关闭事件"); // 阻止默认的关闭行为 event.preventDefault(); - console.log('已阻止默认关闭行为'); + console.log("已阻止默认关闭行为"); // 使用同步方式显示对话框(Electron 11.5支持的方式) try { // 直接显示对话框并获取结果 const response = dialog.showMessageBoxSync(mainWindow, { - type: 'warning', - title: '确认关闭', - message: '确认要退出软件吗?退出后,未完成的考试将不会被保存。', - buttons: ['取消', '确认关闭'], + type: "warning", + title: "确认关闭", + message: "确认要退出软件吗?退出后,未完成的考试将不会被保存。", + buttons: ["取消", "确认关闭"], defaultId: 0, - cancelId: 0 + cancelId: 0, }); - console.log('用户选择了response:', response); + console.log("用户选择了response:", response); // 检查用户选择的按钮索引 if (response === 1) { - console.log('用户确认关闭,准备退出应用'); + console.log("用户确认关闭,准备退出应用"); // 移除所有事件监听器以防止任何干扰 mainWindow.removeAllListeners(); // 强制关闭窗口 mainWindow.destroy(); // 然后退出应用 setTimeout(() => { - console.log('强制退出应用'); + console.log("强制退出应用"); app.quit(); }, 100); } else { - console.log('用户取消关闭'); + console.log("用户取消关闭"); } } catch (error) { - console.error('显示对话框时出错:', 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() + await mainWindow.loadURL(process.env.WEBPACK_DEV_SERVER_URL); + if (!process.env.IS_TEST) mainWindow.webContents.openDevTools(); } else { - createProtocol('app') + createProtocol("app"); // Load the index.html when not in development - mainWindow.loadURL('app://./index.html') + mainWindow.loadURL("app://./index.html"); } } // Quit when all windows are closed. -app.on('window-all-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() + if (process.platform !== "darwin") { + app.quit(); } -}) +}); -app.on('activate', () => { +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() -}) + if (BrowserWindow.getAllWindows().length === 0) createWindow(); +}); // 添加退出确认逻辑 -app.on('before-quit', (event) => { - console.log('检测到应用退出事件'); +app.on("before-quit", (event) => { + console.log("检测到应用退出事件"); // 注释掉这部分代码,因为我们已经在窗口的close事件中处理了确认逻辑 /* // 如果是MacOS系统,让系统默认处理 @@ -161,16 +165,16 @@ app.on('before-quit', (event) => { 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(); +const sqlite3 = require("sqlite3").verbose(); // 在app.on('ready')事件中添加 -app.on('ready', async () => { +app.on("ready", async () => { // 禁用Vue DevTools扩展 /* if (isDevelopment && !process.env.IS_TEST) { @@ -205,119 +209,125 @@ app.on('ready', async () => { await initializeDatabase(); } } catch (error) { - console.error('数据库初始化失败:', error); + console.error("数据库初始化失败:", error); } - createWindow() -}) + 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() + if (process.platform === "win32") { + process.on("message", (data) => { + if (data === "graceful-exit") { + app.quit(); } - }) + }); } else { - process.on('SIGTERM', () => { - app.quit() - }) + process.on("SIGTERM", () => { + app.quit(); + }); } } // 实现hashTest接口(替换原来的argon2Test) -ipcMain.handle('hashTest', async (event, inputString) => { +ipcMain.handle("hashTest", async (event, inputString) => { try { // 使用bcrypt.hash方法计算哈希值 - const salt = await bcrypt.genSalt(10) - const hash = await bcrypt.hash(inputString, salt) - return 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 + console.error("Error in hashTest calculation:", error); + throw error; } -}) +}); // 数据库相关IPC接口 -ipcMain.handle('check-database-initialized', async () => { +ipcMain.handle("check-database-initialized", async () => { try { - return await checkDatabaseInitialized() + return await checkDatabaseInitialized(); } catch (error) { - console.error('Failed to check database initialization:', error) - return false + console.error("Failed to check database initialization:", error); + return false; } -}) +}); -ipcMain.handle('initialize-database', async () => { +ipcMain.handle("initialize-database", async () => { try { - return await initializeDatabase() + return await initializeDatabase(); } catch (error) { - console.error('Failed to initialize database:', error) - return false + console.error("Failed to initialize database:", error); + return false; } -}) +}); // 检查user.db是否存在 -ipcMain.handle('checkUserDbExists', async () => { +ipcMain.handle("checkUserDbExists", async () => { try { - const userDbPath = getUserDbPath() - return fs.existsSync(userDbPath) + const userDbPath = getUserDbPath(); + return fs.existsSync(userDbPath); } catch (error) { - console.error('检查user.db文件是否存在失败:', error) - return false + console.error("检查user.db文件是否存在失败:", error); + return false; } -}) +}); // 静默初始化用户数据库 -ipcMain.handle('initializeUserDatabaseSilently', async () => { +ipcMain.handle("initializeUserDatabaseSilently", async () => { try { - console.log('开始静默初始化用户数据库...') - const result = await initializeUserDatabase() - console.log('静默初始化用户数据库完成:', result) - return { success: true, result } + console.log("开始静默初始化用户数据库..."); + const result = await initializeUserDatabase(); + console.log("静默初始化用户数据库完成:", result); + return { success: true, result }; } catch (error) { - console.error('静默初始化用户数据库失败:', error) - return { success: false, error: error.message } + console.error("静默初始化用户数据库失败:", error); + return { success: false, error: error.message }; } -}) - +}); // 检测是否为便携模式运行 -let isPortableMode = false -let appDataPath = '' +let isPortableMode = false; +let appDataPath = ""; -// 初始化应用路径 - 兼容Node.js 12 +// 初始化应用路径 - 兼容Node.js 12和Windows 7 function initializeAppPaths() { // 检查应用根目录是否存在data文件夹,如果存在则认为是便携模式 - const portableDataPath = path.join(process.cwd(), 'data') - const defaultDataPath = path.join(app.getPath('userData'), '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) + isPortableMode = true; + appDataPath = portableDataPath; + console.log("检测到便携模式,使用当前目录的data文件夹:", appDataPath); } else { - isPortableMode = false - appDataPath = defaultDataPath - console.log('使用默认数据目录:', appDataPath) + isPortableMode = false; + appDataPath = defaultDataPath; + console.log("使用默认数据目录:", appDataPath); // 确保默认数据目录存在 if (!fs.existsSync(appDataPath)) { - fs.mkdirSync(appDataPath, { recursive: true }) + // 递归创建目录,兼容Windows路径 + try { + fs.mkdirSync(appDataPath, { recursive: true }); + } catch (error) { + console.error("创建默认数据目录失败:", error); + } } } } // 添加全局函数获取数据目录 -global.getAppDataPath = function() { - return appDataPath -} +global.getAppDataPath = function () { + return appDataPath; +}; -global.isPortableMode = function() { - return isPortableMode -} +global.isPortableMode = function () { + return isPortableMode; +}; // 在应用ready事件前初始化路径 -initializeAppPaths() +initializeAppPaths(); diff --git a/vue.config.js b/vue.config.js index 5030dca..e84eb10 100644 --- a/vue.config.js +++ b/vue.config.js @@ -1,77 +1,78 @@ module.exports = { // 添加应用标题配置 - chainWebpack: config => { - config - .plugin('html') - .tap(args => { - args[0].title = '统计技能考试系统' - return args - }) + chainWebpack: (config) => { + config.plugin("html").tap((args) => { + args[0].title = "统计技能考试系统"; + return args; + }); }, pluginOptions: { electronBuilder: { nodeIntegration: false, contextIsolation: true, - preload: 'src/preload.js', - mainProcessFile: 'src/background/main.js', + preload: "src/preload.js", + mainProcessFile: "src/background/main.js", lintPreloadFiles: false, // 将externals改为数组格式 - externals: ['fontkit', 'pdfkit'], + externals: ["fontkit", "pdfkit"], chainWebpackMainProcess: (config) => { config.module - .rule('babel') + .rule("babel") .test(/\.js$/) - .use('babel-loader') - .loader('babel-loader') + .use("babel-loader") + .loader("babel-loader") .options({ - presets: ['@babel/preset-env'], + presets: ["@babel/preset-env"], plugins: [ - '@babel/plugin-proposal-optional-chaining', - '@babel/plugin-proposal-class-properties' - ] + "@babel/plugin-proposal-optional-chaining", + "@babel/plugin-proposal-class-properties", + ], }) - .end() + .end(); // 添加对mjs文件的处理 - 兼容Electron 11 config.module - .rule('mjs') + .rule("mjs") .test(/\.mjs$/) - .include - .add(/node_modules/) + .include.add(/node_modules/) .end() - .type('javascript/auto') + .type("javascript/auto"); }, - // Windows 7便携应用配置 - 兼容Electron 11 + // Windows 7便携应用配置 - 兼容Electron 11和electron-builder 22.9.1 win: { target: [ { - target: 'portable', - arch: ['ia32', 'x64'] - } + target: "portable", + arch: ["ia32", "x64"], + }, ], - icon: 'public/favicon.ico', - // 关闭压缩以提高在Windows 7上的兼容性 - compression: 'store', + icon: "public/favicon.ico", + // 在electron-builder 22.9.1中,compression选项应该在这里设置 + // 删除了之前的错误配置 // 针对Windows 7的特殊配置 extraResources: [ { - from: 'src/background/font', - to: 'font', - filter: ['**/*'] + from: "src/background/font", + to: "font", + filter: ["**/*"], }, { - from: 'data', - to: 'data', - filter: ['.gitignore'] - } - ] + from: "data", + to: "data", + filter: [".gitignore"], + }, + ], }, // 便携应用配置 - 兼容Electron 11 portable: { - artifactName: '统计技能考试系统_便携版_${version}_${arch}.exe', + artifactName: "统计技能考试系统_便携版_${version}_${arch}.exe", // 启用便携模式标志 - target: 'portable' - } - } - } -} \ No newline at end of file + target: "portable", + }, + // 在electron-builder 22.9.1中,压缩配置应该在build选项中设置 + builderOptions: { + compression: "store", // 等同于--no-compress,不压缩应用 + }, + }, + }, +};