feat: 完成 Web2App 核心功能实现

- Electron 主进程:窗口管理、IPC、项目读写、打包引擎
- Vue 3 前端:4 个视图页面、3 个组件、Pinia 状态管理
- 生成应用模板:Express 后端 + Electron 窗口
- 支持资源导入、应用配置、跨平台打包
This commit is contained in:
gmh01
2026-07-23 18:25:35 +08:00
parent b67e27beb1
commit 2f3ec636e0
26 changed files with 8455 additions and 0 deletions

120
template/main.js Normal file
View File

@@ -0,0 +1,120 @@
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()
})

20
template/package.json Normal file
View File

@@ -0,0 +1,20 @@
{
"name": "generated-app",
"version": "1.0.0",
"description": "Generated by Web2App",
"main": "main.js",
"scripts": {
"start": "electron .",
"build": "electron-builder",
"postinstall": "electron-builder install-app-deps"
},
"dependencies": {
"express": "^4.18.0",
"cors": "^2.8.5",
"better-sqlite3": "^11.0.0"
},
"devDependencies": {
"electron": "^28.0.0",
"electron-builder": "^24.9.0"
}
}

9
template/preload.js Normal file
View File

@@ -0,0 +1,9 @@
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('api', {
platform: process.platform,
versions: {
node: process.versions.node,
electron: process.versions.electron
}
})