Files
Web2App/electron/main.js
gmh01 8dc102a7cb fix: 重构新建项目逻辑,选定文件夹即项目
- 移除 prompt() 及 InputDialog 组件依赖
- 选定文件夹直接创建 .web2app 项目结构
- 默认以文件夹名作为项目名称
- 在编辑器中可自由修改名称、版本、图标、入口等配置
- 增强导入资源入口的文案提示
2026-07-23 20:40:45 +08:00

191 lines
5.2 KiB
JavaScript

const { app, BrowserWindow, ipcMain, dialog } = require('electron')
const path = require('path')
const fs = require('fs')
const { createProject, loadProject, saveProject, importFiles, removeFile, setProjectIcon } = require('./project')
const { startPack, cancelPack } = require('./builder')
let mainWindow
function createWindow() {
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
minWidth: 900,
minHeight: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false
},
title: 'Web2App',
show: false
})
if (process.env.NODE_ENV === 'development' || process.argv.includes('--dev')) {
mainWindow.loadURL('http://localhost:5173')
mainWindow.webContents.openDevTools()
} else {
mainWindow.loadFile(path.join(__dirname, '..', 'dist', 'index.html'))
}
mainWindow.once('ready-to-show', () => {
mainWindow.show()
})
}
ipcMain.handle('dialog:selectDirectory', async () => {
const result = await dialog.showOpenDialog(mainWindow, {
properties: ['openDirectory']
})
if (!result.canceled && result.filePaths.length > 0) {
return result.filePaths[0]
}
return null
})
ipcMain.handle('dialog:selectFile', async (_event, options) => {
const result = await dialog.showOpenDialog(mainWindow, {
properties: ['openFile', 'multiSelections'],
filters: options?.filters || [
{ name: '所有文件', extensions: ['*'] }
]
})
if (!result.canceled) {
return result.filePaths
}
return []
})
ipcMain.handle('dialog:selectIcon', async () => {
const result = await dialog.showOpenDialog(mainWindow, {
properties: ['openFile'],
filters: [
{ name: '图标文件', extensions: ['ico', 'png', 'icns'] }
]
})
if (!result.canceled && result.filePaths.length > 0) {
return result.filePaths[0]
}
return null
})
ipcMain.handle('dialog:saveFile', async (_event, options) => {
const defaultFilters = process.platform === 'win32'
? [{ name: '安装包', extensions: ['exe'] }]
: process.platform === 'darwin'
? [{ name: '安装包', extensions: ['dmg'] }]
: [{ name: '安装包', extensions: ['AppImage', 'deb'] }]
const result = await dialog.showSaveDialog(mainWindow, {
defaultPath: options?.defaultPath,
filters: options?.filters || defaultFilters
})
if (!result.canceled && result.filePath) {
return result.filePath
}
return null
})
ipcMain.handle('dialog:showMessage', async (_event, options) => {
const result = await dialog.showMessageBox(mainWindow, {
type: options?.type || 'info',
buttons: options?.buttons || ['确定'],
title: options?.title || 'Web2App',
message: options?.message || ''
})
return result.response
})
ipcMain.handle('dialog:showConfirm', async (_event, options) => {
const result = await dialog.showMessageBox(mainWindow, {
type: 'question',
buttons: ['取消', '确定'],
defaultId: 1,
cancelId: 0,
title: options?.title || 'Web2App',
message: options?.message || '确认?'
})
return result.response === 1
})
ipcMain.handle('dir:readTree', async (_event, dirPath) => {
return readDirectoryTree(dirPath)
})
ipcMain.handle('project:create', async (_event, projectPath) => {
return createProject(projectPath)
})
ipcMain.handle('project:load', async (_event, projectPath) => {
return loadProject(projectPath)
})
ipcMain.handle('project:save', async (_event, projectPath, data) => {
return saveProject(projectPath, data)
})
ipcMain.handle('project:importFiles', async (_event, projectPath, files) => {
return importFiles(projectPath, files)
})
ipcMain.handle('project:removeFile', async (_event, projectPath, relativePath) => {
return removeFile(projectPath, relativePath)
})
ipcMain.handle('project:setIcon', async (_event, projectPath, iconPath) => {
return setProjectIcon(projectPath, iconPath)
})
ipcMain.handle('pack:start', async (_event, projectPath, outputPath) => {
const result = await startPack(projectPath, outputPath, (progress) => {
if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.webContents.send('pack:progress', progress)
}
})
return result
})
ipcMain.handle('pack:cancel', () => {
cancelPack()
return true
})
function readDirectoryTree(dirPath) {
const items = fs.readdirSync(dirPath, { withFileTypes: true })
const result = []
for (const item of items) {
if (item.name.startsWith('.')) continue
const fullPath = path.join(dirPath, item.name)
if (item.isDirectory()) {
result.push({
name: item.name,
path: fullPath,
type: 'directory',
children: readDirectoryTree(fullPath)
})
} else {
result.push({
name: item.name,
path: fullPath,
type: 'file',
size: fs.statSync(fullPath).size,
ext: path.extname(item.name).toLowerCase()
})
}
}
return result.sort((a, b) => {
if (a.type !== b.type) return a.type === 'directory' ? -1 : 1
return a.name.localeCompare(b.name)
})
}
app.whenReady().then(() => {
createWindow()
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})