Appearance
Data Model
以下数据模型覆盖当前 MVP 范围。后续功能的字段将在对应迭代中扩展。
核心实体
画布 (Canvas)
typescript
interface Canvas {
id: string
name: string
nodes: CanvasNode[]
edges: CanvasEdge[]
viewport: { x: number, y: number, zoom: number }
createdAt: string // ISO 8601
updatedAt: string // ISO 8601
}- 存储:localStorage(MVP 阶段)
- 主键:
id(UUID)
节点 (Node)
typescript
type NodeType = 'text' | 'image' | 'video'
interface BaseNode<T = Record<string, unknown>> {
id: string
type: NodeType
position: { x: number, y: number }
data: T
}
type CanvasNode = TextNode | ImageNode | VideoNode
interface TextNode extends BaseNode<TextNodeData> {
type: 'text'
}
interface ImageNode extends BaseNode<ImageNodeData> {
type: 'image'
}
interface VideoNode extends BaseNode<VideoNodeData> {
type: 'video'
}TextNodeData
typescript
interface TextNodeData {
title: string
content: string // 富文本内容 (HTML)
prompt: string // 用户输入的提示词
model: string // 选择的 AI 模型
variations: 1 | 2 | 4 // 变体数量
status: NodeStatus
result?: string // 生成结果文本
}ImageNodeData
typescript
interface ImageNodeData {
title: string
imageUrl?: string // 上传/生成的图片 URL
prompt: string
model: string
variations: 1 | 2 | 4 // 变体数量
status: NodeStatus
results?: string[] // 生成的图片 URL 列表(多变体时)
}VideoNodeData(不含 variations)
typescript
interface VideoNodeData {
title: string
videoUrl?: string // 上传/生成的视频 URL
duration?: number // 时长(秒)
prompt: string
model: string
status: NodeStatus
result?: string // 生成的视频 URL
}节点状态
typescript
type NodeStatus = 'idle' | 'generating' | 'done' | 'error'连线 (Edge)
序列化存储用独立接口,与 Vue Flow 的 Edge 类型解耦。加载画布时转换为 Vue Flow Edge,保存时转换回 CanvasEdge。
typescript
interface CanvasEdge {
id: string
source: string // 源节点 ID
target: string // 目标节点 ID
sourceHandle?: string // 源连接点 ID
targetHandle?: string // 目标连接点 ID
}状态结构
Pinia Stores
canvasStore
├── canvases: Canvas[] // 所有画布列表(Workspace 用)
├── currentCanvas: Canvas | null // 当前编辑的画布
└── actions
├── loadAllCanvases() // 从 localStorage 加载所有画布
├── createCanvas(name?) // 创建画布,自动分配 UUID
├── deleteCanvas(id)
├── loadCanvas(id) // structuredClone 深拷贝加载
└── saveCanvas() // 保存 currentCanvas 到 localStorage
flowStore (与 Vue Flow 集成)
├── nodes / edges // 来自 useVueFlow 的响应式引用
├── _syncing: boolean // 防止双重触发同步的标志位
├── event listeners // onNodeDragStop/onMoveEnd/onEdgesChange/onNodesChange → syncToCanvas
└── actions
├── addNode(type, position) // 创建节点 + 默认数据 + syncToCanvas
├── removeNode(id) // 设置 _syncing + removeNodes + syncToCanvas
├── updateNodeData(id, data) // 更新部分数据 + syncToCanvas
├── addEdge(edge) // validateEdge → addEdges + syncToCanvas
├── removeEdge(id) // 设置 _syncing + removeEdges + syncToCanvas
├── loadFromCanvas() // canvasStore → setNodes/setEdges/setViewport
├── syncToCanvas() // nodes/edges/viewport → canvasStore.saveCanvas()
└── validateEdge(edge) // 委托 flowValidation 纯函数校验
flowValidation (纯函数,无 store 依赖)
├── wouldCreateCycle(edges, source, target) // BFS 环路检测
└── validateEdge(edges, newEdge) // 自连接 → 循环 → 重复,返回 { valid, reason? }AI 服务接口
已接入真实后端 API(见 ADR-004):
| 操作 | API Endpoint | 返回值 |
|---|---|---|
| 文本生成 | POST /ability/v1/texts/action/generate | string(生成文本) |
| 图片生成 | POST /ability/v1/images/action/generate | string[](图片 URL 列表) |
| 视频生成 | POST /ability/v1/videos/action/generate | string(视频 URL) |
| 模型列表 | GET /resource/v1/models | AiModelInfo[] |
详见 AI Service 模块契约。
modelsStore
modelsStore
├── cache: Partial<Record<ModelModality, AiModelInfo[]>>
├── loading: Partial<Record<ModelModality, boolean>>
├── errors: Partial<Record<ModelModality, string>>
└── actions
├── fetchModels(modality) // 带缓存 + 防重复请求
├── getModels(modality) // 返回 AiModelInfo[]
├── getAiModels(modality) // 返回 AiModel[](简化视图)
├── getDefaultModelKey(modality) // 返回第一个模型 name
├── isLoading(modality)
└── getError(modality)