diff --git a/electron-vue-template/electron-builder.json b/electron-vue-template/electron-builder.json index 2a225a7..dc8013d 100644 --- a/electron-vue-template/electron-builder.json +++ b/electron-vue-template/electron-builder.json @@ -15,6 +15,7 @@ "directories": { "output": "dist" }, + "electronLanguages": ["zh-CN", "en-US"], "nsis": { "oneClick": false, "perMachine": false, @@ -22,7 +23,7 @@ "shortcutName": "erpClient" }, "win": { - "target": "nsis", + "target": "dir", "icon": "public/icon/icon.png" }, "files": [ @@ -53,23 +54,10 @@ "config/**/*", "!erp_client_sb-*.jar", "!data/**/*", - "!jre/bin/jab*.exe", - "!jre/bin/jac*.exe", - "!jre/bin/jar*.exe", - "!jre/bin/jc*.exe", - "!jre/bin/jd*.exe", - "!jre/bin/jf*.exe", - "!jre/bin/jh*.exe", - "!jre/bin/ji*.exe", - "!jre/bin/jl*.exe", - "!jre/bin/jm*.exe", - "!jre/bin/jp*.exe", - "!jre/bin/jr*.exe", - "!jre/bin/jsh*.exe", - "!jre/bin/jst*.exe", - "!jre/bin/k*.exe", - "!jre/bin/rmi*.exe", - "!jre/bin/serial*.exe", + "!jre/bin/*.exe", + "jre/bin/java.exe", + "jre/bin/javaw.exe", + "jre/bin/keytool.exe", "!jre/include/**", "!jre/lib/src.zip", "!jre/lib/ct.sym", diff --git a/electron-vue-template/package.json b/electron-vue-template/package.json index 83e9155..38f571f 100644 --- a/electron-vue-template/package.json +++ b/electron-vue-template/package.json @@ -5,10 +5,10 @@ "main": "main/main.js", "scripts": { "dev": "node scripts/dev-server.js", - "build": "node scripts/build.js && electron-builder", - "build:win": "node scripts/build.js && electron-builder --win", - "build:mac": "node scripts/build.js && electron-builder --mac", - "build:linux": "node scripts/build.js && electron-builder --linux" + "build": "node scripts/build.js && electron-builder --dir", + "build:win": "node scripts/build.js && electron-builder --win --dir", + "build:mac": "node scripts/build.js && electron-builder --mac --dir", + "build:linux": "node scripts/build.js && electron-builder --linux --dir" }, "repository": "https://github.com/deluze/electron-vue-template", "author": { diff --git a/electron-vue-template/src/main/main.ts b/electron-vue-template/src/main/main.ts index a86038d..4a730c6 100644 --- a/electron-vue-template/src/main/main.ts +++ b/electron-vue-template/src/main/main.ts @@ -23,23 +23,27 @@ function openAppIfNotOpened() { if (appOpened) return; appOpened = true; if (mainWindow && !mainWindow.isDestroyed()) { + isDev + ? mainWindow.loadURL(`http://localhost:${process.argv[2] || 8083}`) + : mainWindow.loadFile(join(__dirname, '../renderer/index.html')); + mainWindow.webContents.once('did-finish-load', () => { setTimeout(() => { if (mainWindow && !mainWindow.isDestroyed()) { - mainWindow.show(); - mainWindow.focus(); + const config = loadConfig(); + const shouldMinimize = config.launchMinimized || false; + if (!shouldMinimize) { + mainWindow.show(); + mainWindow.focus(); + } if (isDev) mainWindow.webContents.openDevTools(); } if (splashWindow && !splashWindow.isDestroyed()) { splashWindow.close(); splashWindow = null; } - }, 1000); + }, 500); }); - - isDev - ? mainWindow.loadURL(`http://localhost:${process.argv[2] || 8083}`) - : mainWindow.loadFile(join(__dirname, '../renderer/index.html')); } } @@ -78,11 +82,17 @@ function getDataDirectoryPath(): string { return dataDir; } +interface AppConfig { + closeAction?: 'quit' | 'minimize' | 'tray'; + autoLaunch?: boolean; + launchMinimized?: boolean; +} + function getConfigPath(): string { return join(app.getPath('userData'), 'config.json'); } -function loadConfig(): { closeAction?: 'quit' | 'minimize' | 'tray' } { +function loadConfig(): AppConfig { try { const configPath = getConfigPath(); if (existsSync(configPath)) { @@ -94,7 +104,7 @@ function loadConfig(): { closeAction?: 'quit' | 'minimize' | 'tray' } { return {}; } -function saveConfig(config: any) { +function saveConfig(config: AppConfig) { try { const configPath = getConfigPath(); require('fs').writeFileSync(configPath, JSON.stringify(config, null, 2)); @@ -138,7 +148,6 @@ function startSpringBoot() { } try { - // Spring Boot启动参数配置 const springArgs = [ '-jar', jarPath, `--spring.datasource.url=jdbc:sqlite:${dataDir}/erp-cache.db`, @@ -146,32 +155,20 @@ function startSpringBoot() { `--logging.config=file:${logbackConfigPath}` ]; - // 工作目录设为数据目录,这样Spring Boot会在数据目录下创建临时文件 springProcess = spawn(javaPath, springArgs, { cwd: dataDir, - detached: false + detached: false, + stdio: ['ignore', 'pipe', 'pipe'] }); let startupCompleted = false; springProcess.stdout?.on('data', (data) => { - const output = data.toString(); - console.log('[Spring Boot]', output.trim()); - - if (!startupCompleted && (output.includes('Started Success') || output.includes('Started ErpClientSbApplication'))) { - startupCompleted = true; - openAppIfNotOpened(); - } + console.log('[Spring Boot]', data.toString().trim()); }); springProcess.stderr?.on('data', (data) => { - const output = data.toString(); - console.log('[Spring Boot]', output.trim()); - - if (!startupCompleted && (output.includes('Started Success') || output.includes('Started ErpClientSbApplication'))) { - startupCompleted = true; - openAppIfNotOpened(); - } + console.log('[Spring Boot]', data.toString().trim()); }); springProcess.on('close', (code) => { @@ -187,8 +184,25 @@ function startSpringBoot() { app.quit(); }); + const checkHealth = () => { + if (startupCompleted) return; + + http.get('http://127.0.0.1:8081', (res) => { + if (!startupCompleted) { + startupCompleted = true; + console.log('[Spring Boot] 服务已就绪'); + openAppIfNotOpened(); + } + }).on('error', () => { + setTimeout(checkHealth, 200); + }); + }; + + setTimeout(checkHealth, 1000); + setTimeout(() => { if (!startupCompleted) { + console.log('[Spring Boot] 启动超时,强制打开窗口'); openAppIfNotOpened(); } }, 15000); @@ -199,7 +213,7 @@ function startSpringBoot() { } } -startSpringBoot(); +// startSpringBoot(); function stopSpringBoot() { if (!springProcess) return; @@ -225,9 +239,10 @@ function createWindow() { mainWindow = new BrowserWindow({ width: 1280, height: 800, - show: false, + show: false, // autoHideMenuBar: true, icon: getIconPath(), + backgroundColor: '#f5f5f5', webPreferences: { preload: join(__dirname, 'preload.js'), nodeIntegration: false, @@ -266,38 +281,52 @@ function createWindow() { } app.whenReady().then(() => { + // 应用开机自启动配置 + const config = loadConfig(); + const shouldMinimize = config.launchMinimized || false; + + if (config.autoLaunch !== undefined) { + app.setLoginItemSettings({ + openAtLogin: config.autoLaunch, + openAsHidden: shouldMinimize + }); + } + createWindow(); createTray(mainWindow); - splashWindow = new BrowserWindow({ - width: 1200, - height: 675, - frame: false, - transparent: false, - resizable: false, - alwaysOnTop: false, - show: true, - center: true, - icon: getIconPath(), - webPreferences: { - nodeIntegration: false, - contextIsolation: true, + // 只有在不需要最小化启动时才显示 splash 窗口 + if (!shouldMinimize) { + splashWindow = new BrowserWindow({ + width: 1200, + height: 675, + frame: false, + transparent: false, + resizable: false, + alwaysOnTop: false, + show: true, + center: true, + icon: getIconPath(), + webPreferences: { + nodeIntegration: false, + contextIsolation: true, + } + }); + + // 监听启动窗口关闭事件 + splashWindow.on('closed', () => { + splashWindow = null; + }); + + const splashPath = getSplashPath(); + if (existsSync(splashPath)) { + splashWindow.loadFile(splashPath); } - }); - - // 监听启动窗口关闭事件 - splashWindow.on('closed', () => { - splashWindow = null; - }); - - const splashPath = getSplashPath(); - if (existsSync(splashPath)) { - splashWindow.loadFile(splashPath); } - // setTimeout(() => { - // openAppIfNotOpened(); - // }, 2000); + setTimeout(() => { + openAppIfNotOpened(); + }, 2000); app.on('activate', () => { if (mainWindow && !mainWindow.isDestroyed()) { @@ -709,6 +738,46 @@ ipcMain.handle('set-close-action', (event, action: 'quit' | 'minimize' | 'tray') return { success: true }; }); +// 清理缓存 +ipcMain.handle('clear-cache', async () => { + try { + const response = await fetch('http://127.0.0.1:8081/api/system/cache/clear', { + method: 'POST' + }); + const data = await response.json(); + return data; + } catch (error: any) { + console.error('清理缓存失败:', error); + return { success: false, error: error.message }; + } +}); + +// 获取启动配置 +ipcMain.handle('get-launch-config', () => { + const config = loadConfig(); + const loginSettings = app.getLoginItemSettings(); + return { + autoLaunch: config.autoLaunch !== undefined ? config.autoLaunch : loginSettings.openAtLogin, + launchMinimized: config.launchMinimized || false + }; +}); + +// 设置启动配置 +ipcMain.handle('set-launch-config', (event, launchConfig: { autoLaunch: boolean; launchMinimized: boolean }) => { + const config = loadConfig(); + config.autoLaunch = launchConfig.autoLaunch; + config.launchMinimized = launchConfig.launchMinimized; + saveConfig(config); + + // 立即应用开机自启动设置 + app.setLoginItemSettings({ + openAtLogin: launchConfig.autoLaunch, + openAsHidden: launchConfig.launchMinimized + }); + + return { success: true }; +}); + async function getFileSize(url: string): Promise { return new Promise((resolve) => { diff --git a/electron-vue-template/src/main/preload.ts b/electron-vue-template/src/main/preload.ts index 71c81f8..3f5f4b3 100644 --- a/electron-vue-template/src/main/preload.ts +++ b/electron-vue-template/src/main/preload.ts @@ -27,6 +27,13 @@ const electronAPI = { getCloseAction: () => ipcRenderer.invoke('get-close-action'), setCloseAction: (action: 'quit' | 'minimize' | 'tray') => ipcRenderer.invoke('set-close-action', action), + // 缓存管理 API + clearCache: () => ipcRenderer.invoke('clear-cache'), + + // 启动配置 API + getLaunchConfig: () => ipcRenderer.invoke('get-launch-config'), + setLaunchConfig: (config: { autoLaunch: boolean; launchMinimized: boolean }) => ipcRenderer.invoke('set-launch-config', config), + onDownloadProgress: (callback: (progress: any) => void) => { ipcRenderer.removeAllListeners('download-progress') ipcRenderer.on('download-progress', (event, progress) => callback(progress)) diff --git a/electron-vue-template/src/renderer/App.vue b/electron-vue-template/src/renderer/App.vue index 1a068b6..0050cf6 100644 --- a/electron-vue-template/src/renderer/App.vue +++ b/electron-vue-template/src/renderer/App.vue @@ -1,5 +1,5 @@