refactor: 前端重构为项目+任务双核心模型
- 新增 HomePage 项目画廊页,支持新建/删除工程 - 新增 ProjectDetailPage 三栏工作台(配置+生成+任务列表) - 新增 CustomTagsEditor 支持自定义风格标签(custom: 前缀存入 kvPairs) - 重构 project store 为 draft/commit 模式,支持手动保存/取消 - 新增 projectList store 管理工程列表 - 扩展 mock.ts 为多工程数据结构 - 扩展 api/project.ts 新增项目 CRUD API(mock-gated) - 简化 ResultPage,仅展示素材预览 - 删除 GeneratePage、ProjectPage 及未使用的 hooks - 更新 docs/ 文档(frontend.md、api.md、style-keys.md、_index.md)
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
import { useState } from 'react'
|
||||
import StyleSelector from './StyleSelector'
|
||||
import CustomTagsEditor from './CustomTagsEditor'
|
||||
|
||||
interface CreateProjectModalProps {
|
||||
open: boolean
|
||||
onClose: () => void
|
||||
onSubmit: (name: string, style: Record<string, string>) => Promise<void>
|
||||
}
|
||||
|
||||
export default function CreateProjectModal({
|
||||
open,
|
||||
onClose,
|
||||
onSubmit,
|
||||
}: CreateProjectModalProps) {
|
||||
const [name, setName] = useState('')
|
||||
const [style, setStyle] = useState<Record<string, string>>({})
|
||||
const [submitting, setSubmitting] = useState(false)
|
||||
|
||||
if (!open) return null
|
||||
|
||||
const handleStyleChange = (key: string, value: string) => {
|
||||
setStyle(prev =>
|
||||
prev[key] === value
|
||||
? (() => {
|
||||
const { [key]: _, ...rest } = prev
|
||||
return rest
|
||||
})()
|
||||
: { ...prev, [key]: value }
|
||||
)
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!name.trim()) return
|
||||
setSubmitting(true)
|
||||
try {
|
||||
await onSubmit(name.trim(), style)
|
||||
setName('')
|
||||
setStyle({})
|
||||
onClose()
|
||||
} finally {
|
||||
setSubmitting(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
inset: 0,
|
||||
zIndex: 1000,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
onClick={onClose}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
background: 'rgba(0, 0, 0, 0.5)',
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className="card"
|
||||
style={{
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
maxWidth: 520,
|
||||
maxHeight: '80vh',
|
||||
overflow: 'auto',
|
||||
zIndex: 1,
|
||||
}}
|
||||
onClick={e => e.stopPropagation()}
|
||||
>
|
||||
<h2 style={{ fontSize: 18, marginBottom: 20 }}>新建工程</h2>
|
||||
|
||||
<label style={{ display: 'block', marginBottom: 16 }}>
|
||||
<span style={{ display: 'block', fontSize: 13, fontWeight: 600, marginBottom: 6, color: 'var(--text-secondary)' }}>
|
||||
工程名称
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={e => setName(e.target.value)}
|
||||
placeholder="输入工程名称"
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<span style={{ display: 'block', fontSize: 13, fontWeight: 600, marginBottom: 8, color: 'var(--text-secondary)' }}>
|
||||
风格配置
|
||||
</span>
|
||||
<StyleSelector value={style} onChange={handleStyleChange} compact />
|
||||
<CustomTagsEditor style={style} onChange={setStyle} />
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: 12 }}>
|
||||
<button className="btn-secondary" onClick={onClose} disabled={submitting}>
|
||||
取消
|
||||
</button>
|
||||
<button
|
||||
className="btn-primary"
|
||||
onClick={handleSubmit}
|
||||
disabled={!name.trim() || submitting}
|
||||
>
|
||||
{submitting ? '创建中...' : '创建'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user