# 前端工程 ## 技术栈 Vite 6 + React 18 + TypeScript + zustand + react-router-dom | 类别 | 选型 | 说明 | |------|------|------| | 构建 | Vite 6 | 开发服务器端口 3000,`/api` 代理到 `localhost:8080` | | UI 框架 | React 18 | 函数组件 + Hooks | | 状态管理 | zustand | 轻量,支持 devtools 中间件 | | 路由 | react-router-dom v6 | SPA 模式 | | 实时通信 | HTTP 轮询 | 每 1.5s 轮询任务状态(WebSocket 待后端实现) | | HTTP 请求 | fetch + 封装层 | 统一错误处理、响应解包 | ## 目录结构 ``` frontend/src/ ├── main.tsx # 入口 ├── App.tsx # 根组件 ├── api/ # API 封装层 │ ├── client.ts # fetch 封装:统一错误处理、响应解包(get/post/put/del) │ ├── auth.ts # 注册/登录(真实后端调用) │ ├── project.ts # 工程 CRUD + 风格 + 任务列表(mock 切换) │ ├── generate.ts # POST /generate、GET /tasks/:taskId、GET /tasks/:taskId/assets │ ├── prompt.ts # 提示词优化 + extractTags │ ├── mock.ts # 多工程 Mock 数据 + 模拟 WebSocket 管线进度 │ └── types.ts # API 类型定义 ├── stores/ # zustand stores │ ├── auth.ts # 用户认证状态 │ ├── project.ts # 当前工程详情(含 draft/commit 风格编辑) │ ├── projectList.ts # 工程列表(画廊页) │ ├── task.ts # 任务草稿(生成表单状态) │ ├── generation.ts # 生成状态(提交、轮询、结果) │ ├── theme.ts # 主题切换(light/dark) │ └── toast.ts # Toast 通知 ├── pages/ # 页面级组件 │ ├── LoginPage.tsx # 登录页 │ ├── RegisterPage.tsx # 注册页 │ ├── HomePage.tsx # 项目画廊(工程列表 + 新建弹窗) │ ├── ProjectDetailPage.tsx # 三栏工作台(配置 + 生成 + 任务列表) │ └── ResultPage.tsx # 结果页(素材预览 + 下载) ├── components/ # 可复用组件 │ ├── Layout.tsx # 导航栏 + Outlet + ToastContainer │ ├── StyleSelector.tsx # 预设风格选择器(pill 按钮) │ ├── CustomTagsEditor.tsx # 自定义标签编辑器(可添加/删除) │ ├── PromptEditor.tsx # 三段式提示词编辑器 │ ├── GenerateForm.tsx # 生成表单(素材类型 + 风格覆盖 + 提示词 + 参数) │ ├── ProgressBar.tsx # 管线进度条 │ ├── AssetPreview.tsx # 素材预览 │ ├── ProjectCard.tsx # 画廊卡片 │ ├── CreateProjectModal.tsx # 新建工程弹窗 │ ├── ProjectConfigPanel.tsx # 左栏:工程配置编辑面板 │ ├── TaskListPanel.tsx # 右栏:任务列表面板 │ ├── StatusBadge.tsx # 状态徽标组件 │ ├── EmptyState.tsx # 空状态占位 │ ├── Skeleton.tsx # 加载骨架屏 │ └── Toast.tsx # Toast 通知容器 ├── router/ # 路由定义 │ └── index.tsx ├── styles/ # 全局样式 │ └── global.css # 暗色/亮色主题 CSS 变量 + reset └── utils/ # 工具函数 └── style.ts # 风格合并、预设分类、自定义标签工具 ``` ## 实现状态 | 模块 | 状态 | 说明 | |------|------|------| | API 封装层 | [x] | client.ts 统一 fetch 封装,auth.ts 真实调用,project/generate 使用 mock | | Mock 层 | [x] | mock.ts 提供多工程 mock 数据 + 模拟 WebSocket 管线进度 | | 认证流程 | [x] | 注册/登录/退出/token 持久化,真实后端对接 | | zustand stores | [x] | auth/project/projectList/task/generation/theme/toast 七个 store | | 页面 | [x] | 5 个页面(Login/Register/Home/ProjectDetail/Result) | | 组件 | [x] | StyleSelector/CustomTagsEditor/PromptEditor/GenerateForm/ProgressBar/AssetPreview/ProjectCard/CreateProjectModal/ProjectConfigPanel/TaskListPanel/StatusBadge 等 | | 路由 | [x] | 含 ProtectedRoute 守卫 | | 自定义标签 | [x] | `custom:` 前缀存入 kvPairs,零后端改动 | ## 路由规划 | 路径 | 页面 | 说明 | |------|------|------| | `/` | HomePage | 项目画廊,展示所有工程 | | `/projects/:projectId` | ProjectDetailPage | 三栏工作台(配置 + 生成 + 任务列表) | | `/projects/:projectId/tasks/:taskId` | ResultPage | 素材预览 + 下载 | ## 组件树 ```mermaid graph TD App --> Router Router --> HomePage Router --> ProjectDetailPage Router --> ResultPage HomePage --> ProjectCard["ProjectCard(工程卡片)"] HomePage --> CreateProjectModal["CreateProjectModal(新建弹窗)"] CreateProjectModal --> StyleSelector CreateProjectModal --> CustomTagsEditor ProjectDetailPage --> ProjectConfigPanel["ProjectConfigPanel(左栏配置)"] ProjectDetailPage --> GenerateForm["GenerateForm(中间上半)"] ProjectDetailPage --> ProgressBar["ProgressBar(中间下半)"] ProjectDetailPage --> TaskListPanel["TaskListPanel(右栏任务)"] ProjectConfigPanel --> StyleSelector2["StyleSelector"] ProjectConfigPanel --> CustomTagsEditor2["CustomTagsEditor"] TaskListPanel --> StatusBadge["StatusBadge"] GenerateForm --> PromptEditor["PromptEditor"] GenerateForm --> StyleSelector3["StyleSelector(任务覆盖)"] ResultPage --> AssetPreview["AssetPreview"] ``` ## 三栏工作台布局 ProjectDetailPage 采用 CSS Grid 三栏布局: ``` grid-template-columns: 260px 1fr 320px ``` | 区域 | 宽度 | 内容 | |------|------|------| | 左栏 | 260px | 工程名(可编辑)、预设风格选择器、自定义标签编辑器、保存/取消按钮 | | 中间 | flex-1 | 上半:生成表单;下半:当前任务进度条(提交后显示) | | 右栏 | 320px | 任务列表(所有状态),15 秒自动刷新 | ## 核心交互流程 ### 项目管理流程 ```mermaid sequenceDiagram actor User participant HP as HomePage participant API as 后端 API User->>HP: 进入首页 HP->>API: GET /api/v1/projects API-->>HP: 工程列表 HP->>HP: 画廊展示 ProjectCard User->>HP: 点击「新建工程」 HP->>HP: 弹出 CreateProjectModal User->>HP: 输入名称、选择风格、添加自定义标签 HP->>API: POST /api/v1/projects API-->>HP: 新工程 HP->>HP: 跳转到 ProjectDetailPage ``` ### 生成流程 ```mermaid sequenceDiagram actor User participant PDP as ProjectDetailPage participant API as 后端 API User->>PDP: 填写 prompt、选择素材类型 User->>PDP: 点击提交 PDP->>API: POST /api/v1/generate API-->>PDP: 返回 taskId PDP->>PDP: ProgressBar 显示进度 loop 轮询任务状态(1.5s) PDP->>API: GET /api/v1/tasks/:taskId API-->>PDP: 任务状态 + 进度 PDP->>PDP: 更新 ProgressBar end API-->>PDP: status = completed PDP->>API: GET /api/v1/tasks/:taskId/assets API-->>PDP: 素材列表 PDP->>PDP: 显示「查看结果」按钮 ``` ### 风格编辑流程(手动保存) ```mermaid sequenceDiagram actor User participant PCP as ProjectConfigPanel participant Store as project store participant API as 后端 API User->>PCP: 进入工程页 PCP->>Store: startEditing() — 复制 style 到 draftStyle User->>PCP: 修改预设风格 / 添加自定义标签 PCP->>Store: updateDraft(key, value) — 更新 draftStyle Store->>Store: hasUnsavedChanges = true alt 点击保存 User->>PCP: 点击「保存」 PCP->>Store: commitDraft() Store->>API: PUT /api/v1/projects/:id/style Store->>API: PUT /api/v1/projects/:id(如名称变更) Store->>Store: style = draftStyle, hasUnsavedChanges = false else 点击取消 User->>PCP: 点击「取消」 PCP->>Store: discardDraft() Store->>Store: draftStyle = style, hasUnsavedChanges = false end ``` ## 自定义标签 在预设风格分类之外,用户可添加自定义标签。标签存储在工程风格的 `kvPairs` 中,使用 `custom:` 前缀区分: ```json { "artStyle": "pixel", "palette": "warm", "custom:cel-shading": "true", "custom:glow-effects": "true" } ``` 工具函数(`utils/style.ts`): - `isCustomKey(key)` — 判断是否为自定义标签 - `getCustomTags(style)` — 提取自定义标签列表 - `addCustomTag(style, tag)` — 添加标签 - `removeCustomTag(style, tag)` — 删除标签 `extractTags()` 函数在提取标签时自动包含自定义标签值。 ## 状态管理 ### zustand stores **projectList store** — 工程列表(HomePage) ```typescript interface ProjectListState { projects: Project[]; loading: boolean; loadProjects: () => Promise; createProject: (name: string, style?: Record) => Promise; deleteProject: (projectId: string) => Promise; } ``` **project store** — 当前工程详情(ProjectDetailPage) ```typescript interface ProjectState { projectId: string; name: string; style: Record; draftStyle: Record; draftName: string; hasUnsavedChanges: boolean; loading: boolean; loadProject: (projectId: string) => Promise; loadStyle: (projectId: string) => Promise; startEditing: () => void; updateDraft: (key: string, value: string) => void; setDraftStyle: (style: Record) => void; setDraftName: (name: string) => void; discardDraft: () => void; commitDraft: () => Promise; } ``` **task store** — 任务草稿 ```typescript interface TaskState { prompt: string; assetType: 'sprite' | 'background' | 'ui' | 'animation'; taskStyle: Record; params: TaskParams; enableAI: boolean; optimizedPrompt: string | null; // ... setters, runOptimize, reset } ``` **generation store** — 生成状态 ```typescript interface GenerationState { taskId: string | null; stage: PipelineStage | null; progress: number; status: 'idle' | 'submitting' | 'running' | 'completed' | 'failed'; retryCount: number; rejectReason: string | null; assets: Asset[]; error: string | null; submit: (req: GenerateRequest) => Promise; reset: () => void; } ``` ## API 类型定义(api/types.ts) ```typescript interface Project { id: string; name: string; style: { kvPairs: Record }; createdAt: string; taskCount?: number; lastActivityAt?: string; } interface CreateProjectRequest { name: string; style?: Record; } interface Task { id: string; projectId: string; prompt: string; assetType: string; status: 'pending' | 'submitted' | 'running' | 'completed' | 'failed'; stage?: PipelineStage; progress?: number; retryCount?: number; error?: string | null; createdAt: string; updatedAt: string; } interface Asset { id: string; key: string; url: string; format: string; width: number; height: number; metadata: { frameWidth?: number; frameHeight?: number; frameCount?: number; directions?: number; }; } ``` ## 预设风格键分类 前端 StyleSelector 以分类标签组织,完整键值表见 [预设风格键](style-keys.md)。 StyleSelector 两种使用场景: 1. **工程风格**(ProjectConfigPanel / CreateProjectModal):全量编辑,保存到后端 2. **任务覆盖**(GenerateForm):基于工程风格展示,仅记录差异