Files
gen2d/docs/frontend.md
T
wonder cbcda82515 docs: 适配单机场景,简化存储与认证方案
- 数据库:MySQL → SQLite,去掉连接池配置和 golang-migrate
- 文件存储:OSS 对象存储 → 本地文件系统,统一 Gin 静态文件服务
- ID 生成:Sonyflake → 前缀+随机字符串
- 认证:Bearer Token → httpOnly Cookie,WebSocket 同步改为 Cookie 认证
- 质检重试:PromptBuilder 重试时注入 RejectReason 改进提示词
- 合并 multi-agent-pipeline.md 到 backend.md,消除文档重复
- 移除缓存管理 API(暴露内部实现细节)
2026-05-24 10:56:40 +08:00

9.9 KiB
Raw Blame History

前端工程

技术栈

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 模式
实时通信 原生 WebSocket 连接后端 /api/v1/tasks/:taskId/ws(Cookie 认证)
HTTP 请求 fetch + 封装层 统一错误处理、响应解包

目录结构

frontend/src/
├── main.tsx                  # 入口
├── App.tsx                   # 根组件 + 路由配置
├── api/                      # API 封装层
│   ├── client.ts             # fetch 封装:baseURL、统一错误处理、响应解包
│   ├── project.ts            # 工程 CRUD + 风格 GET/PUT
│   ├── generate.ts           # POST /generate、GET /tasks/:taskId、GET /tasks/:taskId/assets
│   └── types.ts              # API 请求/响应类型定义(Task、Asset、Style、PipelineProgress 等)
├── stores/                   # zustand stores
│   ├── project.ts            # 当前工程(id、风格、任务列表)
│   ├── task.ts               # 当前任务草稿(用户文本、素材类型、任务风格覆盖、技术参数)
│   └── generation.ts         # 生成状态(taskId、进度、阶段、结果、错误)
├── pages/                    # 页面级组件
│   ├── ProjectPage.tsx       # 工程首页:工程风格配置 + 任务列表
│   ├── GeneratePage.tsx      # 生成页:提示词构建 + 提交
│   └── ResultPage.tsx        # 结果页:素材预览 + 下载 + 元数据
├── components/               # 可复用组件
│   ├── StyleSelector.tsx     # 风格选择器
│   ├── PromptEditor.tsx      # 三段式提示词编辑器
│   ├── GenerateForm.tsx      # 生成表单
│   ├── ProgressBar.tsx       # 管线进度条(显示当前阶段 + 重试状态)
│   └── AssetPreview.tsx      # 素材预览(spritesheet 预览、单帧预览)
├── hooks/                    # 自定义 Hooks
│   ├── useGenerate.ts        # 提交生成任务 + WebSocket 订阅进度
│   └── useProjectStyle.ts    # 工程风格加载/保存
├── router/                   # 路由定义
│   └── index.tsx
└── utils/                    # 工具函数
    └── style.ts              # 风格合并逻辑(与后端保持一致的 merge 算法)

路由规划

路径 页面 说明
/ — 重定向到默认工程
/projects/:projectId ProjectPage 工程首页,配置工程风格,查看历史任务
/projects/:projectId/generate GeneratePage 提示词构建 + 提交生成
/projects/:projectId/tasks/:taskId ResultPage 任务结果页,预览素材、下载

组件树

graph TD
    App --> Router
    Router --> ProjectPage
    Router --> GeneratePage
    Router --> ResultPage

    ProjectPage --> StyleSelector["StyleSelector(工程风格编辑)"]
    ProjectPage --> TaskList["TaskList(历史任务列表)"]

    GeneratePage --> GenerateForm
    GenerateForm --> AssetTypePicker["AssetTypePicker"]
    GenerateForm --> StyleSelector2["StyleSelector(任务风格覆盖)"]
    GenerateForm --> PromptEditor["PromptEditor(三段式预览)"]
    GenerateForm --> ParamsForm["ParamsForm(分辨率、帧数)"]
    GeneratePage --> ProgressBar["ProgressBar(管线进度)"]

    ResultPage --> AssetPreview["AssetPreview(素材预览)"]
    ResultPage --> MetadataPanel["MetadataPanel(元数据)"]
    ResultPage --> DownloadButton["DownloadButton"]

核心交互流程

生成流程

sequenceDiagram
    actor User
    participant GP as GeneratePage
    participant API as 后端 API
    participant WS as WebSocket

    User->>GP: 填写 prompt、选择素材类型
    User->>GP: StyleSelector 点选任务风格覆盖
    GP->>GP: PromptEditor 实时预览三段式提示词
    User->>GP: 点击提交
    GP->>API: POST /api/v1/generate
    API-->>GP: 返回 taskId
    GP->>WS: ws://host/api/v1/tasks/:taskId/ws(Cookie 自动携带)
    loop 管线执行中
        WS-->>GP: PipelineProgress(stage + progress)
        GP->>GP: ProgressBar 更新阶段和进度
    end
    WS-->>GP: format_adapter completed + assets
    GP->>GP: 跳转 ResultPage

管线阶段对应 Eino Graph 节点,ProgressBar 展示:

阶段 说明 ProgressBar 展示
prompt_builder 合并风格,生成三段式提示词 第 1 步
asset_generator 调用 AI 推理 API 出图 第 2 步
quality_supervisor 视觉模型质检 第 3 步(可能回退到第 1 步)
format_adapter 格式转换、spritesheet 打包 第 4 步

质检重试时,ProgressBar 显示回退动画和重试次数。

风格编辑流程

sequenceDiagram
    actor User
    participant SP as StyleSelector
    participant API as 后端 API

    User->>SP: 进入 ProjectPage
    SP->>API: GET /api/v1/projects/:id/style
    API-->>SP: 返回 kvPairs
    SP->>SP: 按分类展示风格键值对
    User->>SP: 点选/修改风格
    SP->>SP: 实时展示当前配置
    User->>SP: 点击保存
    SP->>API: PUT /api/v1/projects/:id/style
    API-->>SP: 保存成功

错误处理

API 请求错误

  • api/client.ts 统一拦截 code !== 0 的响应,抛出业务异常
  • 401:跳转到登录页(Token 已过期或无效,Cookie 会被服务端清除)
  • 403:提示无权访问,不自动跳转
  • 429:提示请求过于频繁,稍后重试
  • 500:展示通用错误提示

WebSocket 断连

  • 连接断开后自动重连(指数退避,最大间隔 10 秒)
  • 重连失败超过 3 次后,降级为轮询模式(GET /api/v1/tasks/:taskId,间隔 2-3 秒)
  • 连接恢复后自动切回 WebSocket

加载状态

  • 页面级加载(如 ProjectPage 进入时):展示骨架屏或 Loading 指示器
  • 操作级提交(如保存风格、提交生成):按钮置为 loading 态,防止重复提交

状态管理

zustand stores

project store — 当前工程

interface ProjectStore {
  projectId: string;
  name: string;
  style: Record<string, string>;  // kvPairs
  loading: boolean;
  loadProject: (projectId: string) => Promise<void>;
  loadStyle: (projectId: string) => Promise<void>;
  updateStyle: (kvPairs: Record<string, string>) => void;
  saveStyle: () => Promise<void>;
}

task store — 任务草稿(对应后端 PipelineInput)

interface TaskStore {
  prompt: string;
  assetType: 'sprite' | 'background' | 'ui' | 'animation';
  taskStyle: Record<string, string>;  // 仅覆盖的键
  params: {
    resolution: number;
    frames?: { directions: number; framesPerDirection: number };
    format: 'spritesheet' | 'individual';
  };
  setPrompt: (text: string) => void;
  setAssetType: (type: TaskStore['assetType']) => void;
  toggleTaskStyle: (key: string, value: string) => void;
  setParams: (params: Partial<TaskStore['params']>) => void;
  reset: () => void;
}

generation store — 生成状态

interface GenerationStore {
  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: (projectId: string, task: TaskStore) => Promise<void>;
  reset: () => void;
}

type PipelineStage =
  | 'prompt_builder'
  | 'asset_generator'
  | 'quality_supervisor'
  | 'format_adapter';

API 封装

api/client.ts 统一封装:

  • baseURL 从环境变量读取,开发模式默认 /api/v1
  • 所有 fetch 请求设置 credentials: 'include',自动携带 Cookie
  • 所有响应按 { code, message, data } 解包,code !== 0 时抛错
  • 统一 401/403/500 错误处理
  • WebSocket 连接封装为 createTaskSocket(taskId) 返回可订阅对象(浏览器自动携带 Cookie)

API 类型定义(api/types.ts)

// 请求
interface CreateProjectRequest {
  name: string;
}

interface GenerateRequest {
  projectId: string;
  prompt: string;
  assetType: 'sprite' | 'background' | 'ui' | 'animation';
  taskStyle?: Record<string, string>;
  params?: {
    resolution?: number;
    frames?: { directions?: number; framesPerDirection?: number };
    format?: 'spritesheet' | 'individual';
  };
}

interface UpdateStyleRequest {
  kvPairs: Record<string, string>;
}

// 响应
interface Project {
  id: string;
  name: string;
  style: { kvPairs: Record<string, string> };
  createdAt: string;
}

interface Task {
  id: string;
  projectId: string;
  prompt: string;
  assetType: string;
  status: 'pending' | 'running' | 'completed' | 'failed';
  stage?: PipelineStage;
  progress?: number;
  retryCount?: number;
  error?: string | null;
  createdAt: string;
  updatedAt: string;
}

interface Asset {
  id: string;
  url: string;
  format: string;
  width: number;
  height: number;
  metadata: {
    frameWidth?: number;
    frameHeight?: number;
    frameCount?: number;
    directions?: number;
  };
}

// WebSocket 消息
interface PipelineProgress {
  stage: PipelineStage;
  status: 'running' | 'completed' | 'failed';
  progress: number;
  message?: string;
  retryCount?: number;
  rejectReason?: string;
  result?: { assets: Asset[] };
  error?: string;
}

预设风格键分类

前端 StyleSelector 以分类标签组织,完整键值表见 预设风格键。

StyleSelector 两种使用场景:

  1. 工程风格(ProjectPage):全量编辑,保存到后端
  2. 任务覆盖(GeneratePage):基于工程风格展示,高亮已覆盖的键,仅记录差异