feat: 完成 Web2App 核心功能实现
- Electron 主进程:窗口管理、IPC、项目读写、打包引擎 - Vue 3 前端:4 个视图页面、3 个组件、Pinia 状态管理 - 生成应用模板:Express 后端 + Electron 窗口 - 支持资源导入、应用配置、跨平台打包
This commit is contained in:
186
electron/project.js
Normal file
186
electron/project.js
Normal file
@@ -0,0 +1,186 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
function getProjectDir(projectPath) {
|
||||
return projectPath
|
||||
}
|
||||
|
||||
function getSrcDir(projectPath) {
|
||||
const dir = path.join(projectPath, '.web2app', 'src')
|
||||
fs.mkdirSync(dir, { recursive: true })
|
||||
return dir
|
||||
}
|
||||
|
||||
function getConfigPath(projectPath) {
|
||||
return path.join(projectPath, '.web2app', 'project.json')
|
||||
}
|
||||
|
||||
function getDistDir(projectPath) {
|
||||
const dir = path.join(projectPath, '.web2app', 'dist')
|
||||
fs.mkdirSync(dir, { recursive: true })
|
||||
return dir
|
||||
}
|
||||
|
||||
function createProject(name, dir) {
|
||||
const projectPath = path.join(dir, name)
|
||||
if (fs.existsSync(projectPath)) {
|
||||
throw new Error(`目录 ${projectPath} 已存在`)
|
||||
}
|
||||
fs.mkdirSync(path.join(projectPath, '.web2app', 'src'), { recursive: true })
|
||||
fs.mkdirSync(path.join(projectPath, '.web2app', 'dist'), { recursive: true })
|
||||
|
||||
const config = {
|
||||
name,
|
||||
version: '1.0.0',
|
||||
description: '',
|
||||
author: '',
|
||||
entry: 'index.html',
|
||||
backendEntry: '',
|
||||
windowWidth: 1200,
|
||||
windowHeight: 800,
|
||||
minWidth: 800,
|
||||
minHeight: 600,
|
||||
resizable: true,
|
||||
icon: '',
|
||||
copyright: '',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString()
|
||||
}
|
||||
|
||||
fs.writeFileSync(getConfigPath(projectPath), JSON.stringify(config, null, 2))
|
||||
return { projectPath, config }
|
||||
}
|
||||
|
||||
function loadProject(projectPath) {
|
||||
const configPath = getConfigPath(projectPath)
|
||||
if (!fs.existsSync(configPath)) {
|
||||
throw new Error('不是有效的 Web2App 项目')
|
||||
}
|
||||
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'))
|
||||
|
||||
const srcDir = getSrcDir(projectPath)
|
||||
const files = readFileTree(srcDir)
|
||||
|
||||
return { projectPath, config, files }
|
||||
}
|
||||
|
||||
function saveProject(projectPath, data) {
|
||||
const configPath = getConfigPath(projectPath)
|
||||
const existing = JSON.parse(fs.readFileSync(configPath, 'utf-8'))
|
||||
const updated = { ...existing, ...data, updatedAt: new Date().toISOString() }
|
||||
fs.writeFileSync(configPath, JSON.stringify(updated, null, 2))
|
||||
return updated
|
||||
}
|
||||
|
||||
function importFiles(projectPath, files) {
|
||||
const srcDir = getSrcDir(projectPath)
|
||||
const imported = []
|
||||
|
||||
for (const filePath of files) {
|
||||
const stat = fs.statSync(filePath)
|
||||
if (stat.isDirectory()) {
|
||||
const destDir = path.join(srcDir, path.basename(filePath))
|
||||
copyDirSync(filePath, destDir)
|
||||
imported.push({ source: filePath, dest: destDir })
|
||||
} else {
|
||||
const destPath = path.join(srcDir, path.basename(filePath))
|
||||
fs.copyFileSync(filePath, destPath)
|
||||
imported.push({ source: filePath, dest: destPath })
|
||||
}
|
||||
}
|
||||
|
||||
return readFileTree(srcDir)
|
||||
}
|
||||
|
||||
function removeFile(projectPath, relativePath) {
|
||||
const srcDir = getSrcDir(projectPath)
|
||||
const fullPath = path.join(srcDir, relativePath)
|
||||
|
||||
if (!fs.existsSync(fullPath)) {
|
||||
throw new Error(`文件不存在: ${relativePath}`)
|
||||
}
|
||||
|
||||
const stat = fs.statSync(fullPath)
|
||||
if (stat.isDirectory()) {
|
||||
fs.rmSync(fullPath, { recursive: true, force: true })
|
||||
} else {
|
||||
fs.unlinkSync(fullPath)
|
||||
}
|
||||
|
||||
return readFileTree(srcDir)
|
||||
}
|
||||
|
||||
function setProjectIcon(projectPath, iconPath) {
|
||||
const srcDir = getSrcDir(projectPath)
|
||||
const ext = path.extname(iconPath)
|
||||
const destName = `app-icon${ext}`
|
||||
const destPath = path.join(srcDir, destName)
|
||||
fs.copyFileSync(iconPath, destPath)
|
||||
|
||||
const config = JSON.parse(fs.readFileSync(getConfigPath(projectPath), 'utf-8'))
|
||||
config.icon = destName
|
||||
fs.writeFileSync(getConfigPath(projectPath), JSON.stringify(config, null, 2))
|
||||
return config
|
||||
}
|
||||
|
||||
function readFileTree(dirPath, basePath = '') {
|
||||
const items = []
|
||||
if (!fs.existsSync(dirPath)) return items
|
||||
|
||||
const entries = fs.readdirSync(dirPath, { withFileTypes: true })
|
||||
for (const entry of entries) {
|
||||
if (entry.name.startsWith('.')) continue
|
||||
const relativePath = basePath ? path.join(basePath, entry.name) : entry.name
|
||||
const fullPath = path.join(dirPath, entry.name)
|
||||
if (entry.isDirectory()) {
|
||||
items.push({
|
||||
name: entry.name,
|
||||
path: relativePath,
|
||||
type: 'directory',
|
||||
children: readFileTree(fullPath, relativePath)
|
||||
})
|
||||
} else {
|
||||
items.push({
|
||||
name: entry.name,
|
||||
path: relativePath,
|
||||
type: 'file',
|
||||
size: fs.statSync(fullPath).size,
|
||||
ext: path.extname(entry.name).toLowerCase()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
items.sort((a, b) => {
|
||||
if (a.type !== b.type) return a.type === 'directory' ? -1 : 1
|
||||
return a.name.localeCompare(b.name)
|
||||
})
|
||||
return items
|
||||
}
|
||||
|
||||
function copyDirSync(src, dest) {
|
||||
fs.mkdirSync(dest, { recursive: true })
|
||||
const entries = fs.readdirSync(src, { withFileTypes: true })
|
||||
for (const entry of entries) {
|
||||
if (entry.name.startsWith('.')) continue
|
||||
const srcPath = path.join(src, entry.name)
|
||||
const destPath = path.join(dest, entry.name)
|
||||
if (entry.isDirectory()) {
|
||||
copyDirSync(srcPath, destPath)
|
||||
} else {
|
||||
fs.copyFileSync(srcPath, destPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createProject,
|
||||
loadProject,
|
||||
saveProject,
|
||||
importFiles,
|
||||
removeFile,
|
||||
setProjectIcon,
|
||||
getSrcDir,
|
||||
getDistDir,
|
||||
getConfigPath,
|
||||
getProjectDir
|
||||
}
|
||||
Reference in New Issue
Block a user