const { app, BrowserWindow, Tray, Menu, nativeImage } = require('electron') const path = require('path') const fs = require('fs') const express = require('express') const cors = require('cors') let mainWindow let tray let server const FRONTEND_DIR = path.join(__dirname, 'frontend') const BACKEND_DIR = path.join(__dirname, 'backend') const ICON_PATH = path.join(__dirname, 'icon.png') function startBackend() { return new Promise((resolve, reject) => { const serverApp = express() serverApp.use(cors()) serverApp.use(express.json()) const backendEntry = path.join(BACKEND_DIR, 'server.js') if (fs.existsSync(backendEntry)) { try { const userApi = require(backendEntry) if (typeof userApi === 'function') { userApi(serverApp) } else if (userApi && typeof userApi.setup === 'function') { userApi.setup(serverApp) } } catch (err) { console.error('后端加载失败:', err) } } server = serverApp.listen(0, () => { const port = server.address().port console.log(`后端服务运行于端口: ${port}`) resolve(port) }) }) } function createWindow(port) { const icon = fs.existsSync(ICON_PATH) ? nativeImage.createFromPath(ICON_PATH) : undefined mainWindow = new BrowserWindow({ width: 1200, height: 800, minWidth: 800, minHeight: 600, icon, webPreferences: { preload: path.join(__dirname, 'preload.js'), contextIsolation: true, nodeIntegration: false }, show: false }) const indexPath = path.join(FRONTEND_DIR, 'index.html') if (fs.existsSync(indexPath)) { mainWindow.loadFile(indexPath) } else { mainWindow.loadURL(`http://localhost:${port}`) } mainWindow.once('ready-to-show', () => { mainWindow.show() }) mainWindow.on('close', (event) => { if (tray) { event.preventDefault() mainWindow.hide() } }) } function setupTray() { const iconPath = path.join(__dirname, 'tray-icon.png') const icon = fs.existsSync(iconPath) ? nativeImage.createFromPath(iconPath).resize({ width: 16, height: 16 }) : nativeImage.createEmpty() tray = new Tray(icon) const contextMenu = Menu.buildFromTemplate([ { label: '显示', click: () => mainWindow.show() }, { label: '退出', click: () => { app.isQuitting = true; app.quit() } } ]) tray.setToolTip('Web2App Generated Application') tray.setContextMenu(contextMenu) tray.on('click', () => mainWindow.show()) } app.whenReady().then(async () => { try { const port = await startBackend() createWindow(port) setupTray() } catch (err) { console.error('启动失败:', err) createWindow(3000) setupTray() } app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow(3000) } }) }) app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit() }) app.on('before-quit', () => { app.isQuitting = true if (server) server.close() })