- Electron 主进程:窗口管理、IPC、项目读写、打包引擎 - Vue 3 前端:4 个视图页面、3 个组件、Pinia 状态管理 - 生成应用模板:Express 后端 + Electron 窗口 - 支持资源导入、应用配置、跨平台打包
121 lines
3.0 KiB
JavaScript
121 lines
3.0 KiB
JavaScript
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()
|
|
})
|