fix: 重构新建项目逻辑,选定文件夹即项目

- 移除 prompt() 及 InputDialog 组件依赖
- 选定文件夹直接创建 .web2app 项目结构
- 默认以文件夹名作为项目名称
- 在编辑器中可自由修改名称、版本、图标、入口等配置
- 增强导入资源入口的文案提示
This commit is contained in:
gmh01
2026-07-23 20:40:45 +08:00
parent 686d94e178
commit 8dc102a7cb
8 changed files with 34 additions and 42 deletions

2
.gitignore vendored
View File

@@ -2,6 +2,8 @@ node_modules/
dist/
.web2app/
release/
installer/
__appImage-x64/
# Binaries & executables
*.exe

View File

@@ -111,8 +111,8 @@ ipcMain.handle('dir:readTree', async (_event, dirPath) => {
return readDirectoryTree(dirPath)
})
ipcMain.handle('project:create', async (_event, name, dir) => {
return createProject(name, dir)
ipcMain.handle('project:create', async (_event, projectPath) => {
return createProject(projectPath)
})
ipcMain.handle('project:load', async (_event, projectPath) => {

View File

@@ -13,7 +13,7 @@ contextBridge.exposeInMainWorld('api', {
readTree: (dirPath) => ipcRenderer.invoke('dir:readTree', dirPath)
},
project: {
create: (name, dir) => ipcRenderer.invoke('project:create', name, dir),
create: (projectPath) => ipcRenderer.invoke('project:create', projectPath),
load: (projectPath) => ipcRenderer.invoke('project:load', projectPath),
save: (projectPath, data) => ipcRenderer.invoke('project:save', projectPath, data),
importFiles: (projectPath, files) => ipcRenderer.invoke('project:importFiles', projectPath, files),

View File

@@ -21,16 +21,21 @@ function getDistDir(projectPath) {
return dir
}
function createProject(name, dir) {
const projectPath = path.join(dir, name)
if (fs.existsSync(projectPath)) {
throw new Error(`目录 ${projectPath} 已存在`)
function createProject(projectPath) {
if (!fs.existsSync(projectPath)) {
fs.mkdirSync(projectPath, { recursive: true })
}
const configPath = getConfigPath(projectPath)
if (fs.existsSync(configPath)) {
throw new Error(`该目录已是 Web2App 项目`)
}
fs.mkdirSync(path.join(projectPath, '.web2app', 'src'), { recursive: true })
fs.mkdirSync(path.join(projectPath, '.web2app', 'dist'), { recursive: true })
const config = {
name,
name: path.basename(projectPath),
version: '1.0.0',
description: '',
author: '',
@@ -47,7 +52,7 @@ function createProject(name, dir) {
updatedAt: new Date().toISOString()
}
fs.writeFileSync(getConfigPath(projectPath), JSON.stringify(config, null, 2))
fs.writeFileSync(configPath, JSON.stringify(config, null, 2))
return { projectPath, config }
}

View File

@@ -21,7 +21,7 @@
"appId": "com.web2app.app",
"productName": "Web2App",
"directories": {
"output": "release"
"output": "installer"
},
"files": [
"electron/**/*",

View File

@@ -28,8 +28,8 @@ export const useProjectStore = defineStore('project', {
},
actions: {
async createProject(name, dir) {
const result = await window.api.project.create(name, dir)
async createProject(projectPath) {
const result = await window.api.project.create(projectPath)
this.projectPath = result.projectPath
this.config = result.config
this.files = []

View File

@@ -12,13 +12,13 @@
<div class="action-card" @click="createNewProject">
<span class="action-icon">+</span>
<span class="action-title">新建项目</span>
<span class="action-desc">创建新的 Web2App 项目</span>
<span class="action-desc">选择文件夹快速创建项目</span>
</div>
<div class="action-card" @click="openExistingProject">
<span class="action-icon">&#9776;</span>
<span class="action-title">打开项目</span>
<span class="action-desc">打开已有的 .web2app 项目</span>
<span class="action-desc">打开已有的 Web2App 项目</span>
</div>
<div class="action-card" @click="goHelp">
@@ -42,15 +42,6 @@
</div>
</div>
</div>
<InputDialog
:visible="showInput"
title="新建项目"
message="请输入项目名称"
placeholder="例如MyApp"
@confirm="onNameConfirmed"
@cancel="onNameCancelled"
/>
</div>
</template>
@@ -58,26 +49,17 @@
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { useProjectStore } from '../stores/project'
import InputDialog from '../components/InputDialog.vue'
const router = useRouter()
const store = useProjectStore()
const recentProjects = ref(loadRecentProjects())
const showInput = ref(false)
const pendingDir = ref('')
async function createNewProject() {
const dir = await window.api.dialog.selectDirectory()
if (!dir) return
pendingDir.value = dir
showInput.value = true
}
async function onNameConfirmed(name) {
showInput.value = false
try {
await store.createProject(name, pendingDir.value)
addRecentProject(pendingDir.value, name)
await store.createProject(dir)
addRecentProject(dir, store.config.name)
router.push('/editor')
} catch (err) {
await window.api.dialog.showMessage({
@@ -88,10 +70,6 @@ async function onNameConfirmed(name) {
}
}
function onNameCancelled() {
showInput.value = false
}
async function openExistingProject() {
const dir = await window.api.dialog.selectDirectory()
if (!dir) return

View File

@@ -21,8 +21,8 @@
<div class="sidebar-header">
<h3>资源文件</h3>
<div class="sidebar-actions">
<button class="btn btn-sm btn-secondary" @click="importFromDir" title="导入文件夹">&#128193;</button>
<button class="btn btn-sm btn-secondary" @click="importFromFiles" title="导入文件">&#128196;</button>
<button class="btn btn-sm btn-secondary" @click="importFromDir" title="文件夹导入整个网站">&#128193; 路径</button>
<button class="btn btn-sm btn-secondary" @click="importFromFiles" title="选择单个文件导入">&#128196; 文件</button>
</div>
</div>
<div class="file-tree-container">
@@ -32,8 +32,9 @@
@delete="handleDelete"
/>
<div v-else class="empty-state">
<p>暂无资源</p>
<p class="hint">点击上方按钮导入网站文件</p>
<div class="empty-icon">&#128193;</div>
<p>尚未设置资源路径</p>
<p class="hint">点击上方路径按钮选择你的网站目录导入</p>
</div>
</div>
</aside>
@@ -186,6 +187,12 @@ onBeforeUnmount(() => {
padding: 8px;
}
.empty-icon {
font-size: 48px;
opacity: 0.3;
margin-bottom: 8px;
}
.hint {
font-size: 12px;
}