# 平台化架构设计 ## 目标 将 Painting / Video 两套硬编码分支重构为统一的平台描述符架构,使加入新平台只需新建一个文件夹并实现标准接口,零改动 dialogBox。 ## 背景 当前 `dialogBox/index.vue`(792 行)通过 `v-if="type === 'Painting'"` / `v-if="type === 'Video'"` 承载两套完全不同的逻辑: - **模型列表**:Painting 走后端 API,Video 走静态 JSON - **参数 schema**:Painting 走本地 JS 文件,Video 走远程 workflow JSON - **UI 控件**:Painting 有 proportion/dimension/quality/quantity,Video 有 pattern/proportion/time - **任务 body**:Painting 扁平 modelParams,Video `{ workflowId, nodeInfoList }` 后续模型参数后端化后,所有平台将统一为"API 获取模型列表 + API 获取参数 schema"模式。 ## 核心设计:平台描述符模式 每个平台封装为一个文件夹,导出 `definePlatform()` 工厂函数,返回标准接口对象。dialogBox 退化为纯渲染引擎。 ### 平台接口 ```js // src/platforms//index.js export function definePlatform() { // 响应式状态(各平台自定义) const model = ref(defaultValue) const modelType = ref(defaultValue) const state = reactive({ ... }) // 模型选择器组件 const ModelSelector = markRaw(Component) // 参数控件列表(有序,决定渲染顺序) const controls = [ { name: 'proportion', component: markRaw(ProportionComponent), show: (config) => config?.params?.some(p => p.ui === 'proportion'), props: (config) => ({ /* 额外 props */ }), }, // ... ] // 图片上传器(可选) const ImageUploader = markRaw(Component) | null return { id: 'painting', // 平台标识 label: 'AI绘画2026', // 显示标题 ModelSelector, // 模型选择器组件 controls, // 有序控件列表 ImageUploader, // 图片上传器组件(可选) state, // 平台自定义响应式状态 model, // 当前模型 ref modelType, // 当前模型类型 ref async loadModels() { }, // 获取模型列表 async loadConfig(modelName) { }, // 获取模型参数配置 buildTaskBody(state) { }, // 构造请求 body getDefaultModel() { }, // 默认模型名称 isImageRequired(state) { }, // 是否必须上传图片 } } ``` ### 控件绑定约定 dialogBox 渲染控件时自动处理 `name` 与 `state` 的 v-model 绑定: - `modelValue` → `state[name]` - `onUpdate:modelValue` → `state[name] = v` 控件 descriptor 仅需定义 `show` 条件(基于 modelConfig 上下文)和额外 `props`。 ### 平台注册表 ```js // src/platforms/registry.js import { definePaintingPlatform } from './painting/index.js' import { defineVideoPlatform } from './video/index.js' const registry = { Painting, Video } export function createPlatform(type) { const factory = registry[type] if (!factory) throw new Error(`未找到平台: ${type}`) return factory() } ``` ### dialogBox 角色变化 dialogBox 接收 `type` prop → 调用 `createPlatform(type)` → 获得 descriptor → 据 descriptor 渲染一切: 1. 渲染 `` 2. 遍历 `platform.controls`,`show` 返回 true 的渲染 `` 3. `handleStart()` 委托给 `platform.buildTaskBody(state)` → 调用 `taskPolling.generate(body)` 4. `watch(model)` 委托给 `platform.loadConfig(name)` ## 数据流 ```mermaid flowchart TD subgraph dialogBox["dialogBox 编排层"] A["platform.loadModels()"] --> B[模型选择器渲染] B --> C["watch(model) → platform.loadConfig(name)"] C --> D[计算 visibleControls] D --> E["v-for 渲染 controls(自动 v-model)"] E --> F["handleStart → platform.buildTaskBody()"] F --> G["taskPolling.generate(body)"] end subgraph platform["platform 包"] H["loadModels() → API"] I["loadConfig(name) → API"] J["buildTaskBody() → 扁平 body"] end G --> K[POST /suanli/v1/tasks] K --> L[轮询 → displayStore 更新虚拟滚动列表] ``` ## 目录结构 ``` src/ ├── platforms/ # 平台包(新增) │ ├── painting/ │ │ ├── index.js # definePlatform() │ │ ├── modelSelector.vue │ │ ├── imageUploader.vue │ │ └── controls/ │ │ ├── proportion.vue │ │ ├── dimension.vue │ │ ├── quality.vue │ │ └── quantity.vue │ ├── video/ │ │ ├── index.js │ │ ├── modelSelector.vue │ │ ├── imageUploader.vue │ │ └── controls/ │ │ ├── pattern.vue │ │ ├── proportion.vue │ │ └── time.vue │ └── registry.js │ ├── components/ │ ├── dialogBox/index.vue # 精简后 ~200 行 │ ├── Popover/ # 共享基础组件(不变) │ ├── Select/ # 共享基础组件(不变) │ ├── Img/ # 共享基础组件(不变) │ └── virtual-scroller/ # 共享基础组件(不变) │ ├── apis/ # API 层(不变) ├── utils/ │ ├── taskPolling.js # 任务轮询(不变) │ ├── request.js # Axios(不变) │ └── modelApi.js # 平台 API 封装 │ ├── config/ │ ├── models/ # 逐步废弃 │ ├── runninghub/ # 逐步废弃 │ └── plugins.js # 不变 ``` ### 迁移路径 | 步骤 | 内容 | 影响范围 | |------|------|----------| | 1 | 新建 `src/platforms/` + `registry.js`,不删旧代码 | 纯新增 | | 2 | Painting 迁入 descriptor,dialogBox 切换读取路径 | dialogBox 精简 | | 3 | Video 迁入 descriptor | dialogBox 继续精简 | | 4 | 删除旧代码:`config/models/`、`config/runninghub/`、`modelConfig.js` | 清理 | | 5 | 后端化:各平台 `loadConfig()` 改为调 API | 仅改 descriptor 内部 | 每步独立提交,方便回滚。 ## 不变更的部分 - `taskPolling.js`:任务创建和轮询逻辑通用,不变 - `displayStore`:虚拟滚动列表状态通用,不变 - `Popover`、`Select`、`Img`、`virtual-scroller`:共享 UI 组件 - `home/index.vue`:仍然传 `type` prop,不变 - `apis/`、`request.js`:HTTP 层不变 - `config/plugins.js`、router、stores:不变