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:
@@ -5,17 +5,28 @@ interface ProjectState {
|
||||
projectId: string
|
||||
name: string
|
||||
style: Record<string, string>
|
||||
draftStyle: Record<string, string>
|
||||
draftName: string
|
||||
hasUnsavedChanges: boolean
|
||||
loading: boolean
|
||||
loadProject: (projectId: string) => Promise<void>
|
||||
loadStyle: (projectId: string) => Promise<void>
|
||||
updateStyle: (key: string, value: string) => void
|
||||
saveStyle: () => Promise<void>
|
||||
startEditing: () => void
|
||||
updateDraft: (key: string, value: string) => void
|
||||
setDraftStyle: (style: Record<string, string>) => void
|
||||
setDraftName: (name: string) => void
|
||||
discardDraft: () => void
|
||||
commitDraft: () => Promise<void>
|
||||
updateName: (name: string) => Promise<void>
|
||||
}
|
||||
|
||||
export const useProjectStore = create<ProjectState>((set, get) => ({
|
||||
projectId: '',
|
||||
name: '',
|
||||
style: {},
|
||||
draftStyle: {},
|
||||
draftName: '',
|
||||
hasUnsavedChanges: false,
|
||||
loading: false,
|
||||
|
||||
loadProject: async (projectId) => {
|
||||
@@ -25,7 +36,10 @@ export const useProjectStore = create<ProjectState>((set, get) => ({
|
||||
set({
|
||||
projectId: project.id,
|
||||
name: project.name,
|
||||
draftName: project.name,
|
||||
style: project.style.kvPairs,
|
||||
draftStyle: { ...project.style.kvPairs },
|
||||
hasUnsavedChanges: false,
|
||||
loading: false,
|
||||
})
|
||||
} catch {
|
||||
@@ -35,15 +49,56 @@ export const useProjectStore = create<ProjectState>((set, get) => ({
|
||||
|
||||
loadStyle: async (projectId) => {
|
||||
const kvPairs = await projectApi.getStyle(projectId)
|
||||
set({ style: kvPairs })
|
||||
set({ style: kvPairs, draftStyle: { ...kvPairs }, hasUnsavedChanges: false })
|
||||
},
|
||||
|
||||
updateStyle: (key, value) => {
|
||||
set(state => ({ style: { ...state.style, [key]: value } }))
|
||||
startEditing: () => {
|
||||
const { style, name } = get()
|
||||
set({ draftStyle: { ...style }, draftName: name, hasUnsavedChanges: false })
|
||||
},
|
||||
|
||||
saveStyle: async () => {
|
||||
const { projectId, style } = get()
|
||||
await projectApi.saveStyle(projectId, style)
|
||||
updateDraft: (key, value) => {
|
||||
set(state => {
|
||||
const next = { ...state.draftStyle }
|
||||
if (next[key] === value) {
|
||||
delete next[key]
|
||||
} else {
|
||||
next[key] = value
|
||||
}
|
||||
return {
|
||||
draftStyle: next,
|
||||
hasUnsavedChanges: true,
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
setDraftStyle: (style) => set({ draftStyle: style, hasUnsavedChanges: true }),
|
||||
|
||||
setDraftName: (name) => set({ draftName: name, hasUnsavedChanges: true }),
|
||||
|
||||
discardDraft: () => {
|
||||
const { style, name } = get()
|
||||
set({ draftStyle: { ...style }, draftName: name, hasUnsavedChanges: false })
|
||||
},
|
||||
|
||||
commitDraft: async () => {
|
||||
const { projectId, draftStyle, draftName, name } = get()
|
||||
// save style
|
||||
await projectApi.saveStyle(projectId, draftStyle)
|
||||
// save name if changed
|
||||
if (draftName !== name) {
|
||||
await projectApi.updateProject(projectId, { name: draftName })
|
||||
}
|
||||
set({
|
||||
style: { ...draftStyle },
|
||||
name: draftName,
|
||||
hasUnsavedChanges: false,
|
||||
})
|
||||
},
|
||||
|
||||
updateName: async (name) => {
|
||||
const { projectId } = get()
|
||||
await projectApi.updateProject(projectId, { name })
|
||||
set({ name, draftName: name })
|
||||
},
|
||||
}))
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { create } from 'zustand'
|
||||
import type { Project } from '../api/types'
|
||||
import * as projectApi from '../api/project'
|
||||
|
||||
interface ProjectListState {
|
||||
projects: Project[]
|
||||
loading: boolean
|
||||
loadProjects: () => Promise<void>
|
||||
createProject: (name: string, style?: Record<string, string>) => Promise<Project>
|
||||
deleteProject: (projectId: string) => Promise<void>
|
||||
}
|
||||
|
||||
export const useProjectListStore = create<ProjectListState>((set, get) => ({
|
||||
projects: [],
|
||||
loading: false,
|
||||
|
||||
loadProjects: async () => {
|
||||
set({ loading: true })
|
||||
try {
|
||||
const projects = await projectApi.listProjects()
|
||||
set({ projects, loading: false })
|
||||
} catch {
|
||||
set({ loading: false })
|
||||
}
|
||||
},
|
||||
|
||||
createProject: async (name, style) => {
|
||||
const project = await projectApi.createProject(name, style)
|
||||
set({ projects: [project, ...get().projects] })
|
||||
return project
|
||||
},
|
||||
|
||||
deleteProject: async (projectId) => {
|
||||
await projectApi.deleteProject(projectId)
|
||||
set({ projects: get().projects.filter(p => p.id !== projectId) })
|
||||
},
|
||||
}))
|
||||
Reference in New Issue
Block a user