Files
Web2App/src/views/ProjectEditor.vue
gmh01 686d94e178 fix: 修复新建项目无响应问题
- 替换 prompt() 为 InputDialog Vue 模态组件
- 替换 alert()/confirm() 为 Electron showMessageBox IPC
- 添加 dialog:showMessage / dialog:showConfirm IPC 通道
2026-07-23 19:44:04 +08:00

199 lines
4.0 KiB
Vue

<template>
<div class="editor">
<header class="editor-header">
<div class="header-left">
<button class="btn btn-secondary btn-sm" @click="goHome">&larr; 返回</button>
<h2>{{ store.projectName }}</h2>
<span v-if="store.isDirty" class="dirty-badge">未保存</span>
</div>
<div class="header-right">
<button class="btn btn-secondary btn-sm" @click="saveProject" :disabled="!store.isDirty">
保存
</button>
<button class="btn btn-primary" @click="goPack">
打包应用 &rarr;
</button>
</div>
</header>
<div class="editor-body">
<aside class="sidebar">
<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>
</div>
</div>
<div class="file-tree-container">
<ResourceTree
v-if="store.files.length > 0"
:files="store.files"
@delete="handleDelete"
/>
<div v-else class="empty-state">
<p>暂无资源</p>
<p class="hint">点击上方按钮导入网站文件</p>
</div>
</div>
</aside>
<main class="main-content">
<ConfigPanel />
</main>
</div>
</div>
</template>
<script setup>
import { onBeforeUnmount } from 'vue'
import { useRouter } from 'vue-router'
import { useProjectStore } from '../stores/project'
import ResourceTree from '../components/ResourceTree.vue'
import ConfigPanel from '../components/ConfigPanel.vue'
const router = useRouter()
const store = useProjectStore()
if (!store.hasProject) {
router.replace('/')
}
async function saveProject() {
await store.saveProject()
}
async function importFromDir() {
const count = await store.importFromDirectory()
if (count > 0) store.isDirty = true
}
async function importFromFiles() {
const count = await store.importFromFiles()
if (count > 0) store.isDirty = true
}
async function handleDelete(relativePath) {
const ok = await window.api.dialog.showConfirm({
title: '删除确认',
message: `确定要删除 "${relativePath}" 吗?`
})
if (ok) {
await store.removeFile(relativePath)
}
}
function goHome() {
if (store.isDirty) {
store.saveProject()
}
router.push('/')
}
function goPack() {
if (store.isDirty) {
store.saveProject()
}
router.push('/pack')
}
onBeforeUnmount(() => {
if (store.isDirty) {
store.saveProject()
}
})
</script>
<style scoped>
.editor {
height: 100%;
display: flex;
flex-direction: column;
}
.editor-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 20px;
background: var(--surface);
border-bottom: 1px solid var(--border);
}
.header-left {
display: flex;
align-items: center;
gap: 12px;
}
.header-left h2 {
font-size: 16px;
font-weight: 600;
}
.dirty-badge {
font-size: 11px;
background: var(--warning);
color: white;
padding: 2px 8px;
border-radius: 10px;
}
.header-right {
display: flex;
gap: 8px;
}
.editor-body {
flex: 1;
display: flex;
overflow: hidden;
}
.sidebar {
width: 280px;
min-width: 280px;
border-right: 1px solid var(--border);
background: var(--surface);
display: flex;
flex-direction: column;
}
.sidebar-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
border-bottom: 1px solid var(--border);
}
.sidebar-header h3 {
font-size: 13px;
font-weight: 600;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.sidebar-actions {
display: flex;
gap: 4px;
}
.file-tree-container {
flex: 1;
overflow-y: auto;
padding: 8px;
}
.hint {
font-size: 12px;
}
.main-content {
flex: 1;
overflow-y: auto;
padding: 20px;
}
</style>