AI_Painting_V2.0/CLAUDE.md
WangLeo a1134d85ad 新增 DimensionInput 共享组件,修复多个模型参数 UI 渲染缺陷,补充后端化方案文档
- 新增 DimensionInput 组件(Popover + W/H 数字输入 + 比例锁),支持 combined(单字段 W*H)和 split(独立 width/height)两种模式
- 修复 jimeng/qwen/qwen-edit 尺寸参数不显示:改用 dimension/dimensionWidth/dimensionHeight 替代 number/select
- 修复 GPT-Image-2/GPT-Image-2 I2I quality 选择器不显示:通过 Select 组件承载 ui: 'select'
- 修复 jimeng/GPT-Image-2 误显示 quantity:showQuantity 移除 fallback,仅匹配 ui: 'quantity'
- 新增 docs/模型参数后端化方案.md:API 设计、数据库设计、前后端迁移步骤
- 更新 CLAUDE.md:补充新 UI 类型映射、dimension 模式说明、displayNameMap bug 标注
- 删除废弃文件 Vidu Q3-T2V.json、modelConfig 空目录
2026-06-08 18:36:53 +08:00

198 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## 常用命令
```bash
pnpm dev # 启动 Vite 开发服务器
pnpm build # 生产构建
pnpm preview # 预览生产构建
npx eslint . # 代码检查(@antfu/eslint-configVue 支持,无 TypeScript
```
## 技术栈
Vue 3 (Composition API) + Vite 7 + Pinia + Vue Router + Element Plus + `vue-element-plus-x`(多媒体编辑) + Less + pnpm
## 架构概览
AI 绘画/视频生成前端操作平台,通过 HTTP 接口对接算力调度后端suanli和第三方 AI 平台RunningHub提交生成任务并轮询结果。
**Painting 和 Video 走两套不同的任务构造路径:**
- **Painting新架构**:本地模型参数 schema → 专用控件 + 动态表单 → `X-Session-Id` header + 扁平 API body
- **Video旧架构**:远程 workflow JSON → RunningHub Playload 适配器 → `{ workflowId, nodeInfoList }` body。Video 模型列表从远程静态 JSON`VITE_API_MODEL_RESOURCE/.../video.json`获取workflow 配置按日缓存于 localStorage
### 关键目录
```
src/
├── main.js # 入口:创建 Vue 应用,安装 Pinia/Router/VueVirtualScroller
├── router/index.js # 路由定义 + token 验证守卫
├── stores/ # Pinia 状态管理
│ ├── user.js # 用户认证、信息(含 sessionId使用 pinia persist 持久化 token
│ ├── display.js # 生成历史列表、UI 状态(滚动、画布等)
│ └── param.js # 参数 store当前为空
├── apis/ # HTTP API 层:纯请求封装,不含业务逻辑
│ ├── auth/ # 认证相关登录、token 校验、用户信息、验证码)
│ └── display/ # 任务创建/轮询/历史、平台模型、收藏/删除
├── components/
│ ├── dialogBox/ # 生成参数输入面板(核心交互入口)
│ │ ├── index.vue # 编排中心:组装所有控件,处理 handleStart()、模型配置加载、参数回填
│ │ ├── model/ # 模型选择器(按 API 返回的 tags 分组value 编码为 tag::display_name
│ │ ├── proportion/ # 比例/分辨率选择器painting.vue 用于 Paintingvideo.vue 用于 Video
│ │ ├── dimension/ # 尺寸输入 PopoverW/H 数字输入 + 比例锁,支持 split/combined 两种模式)
│ │ ├── imageUploader/ # 图片上传Painting
│ │ ├── videoImageUploader/ # 视频图片上传Video
│ │ ├── quantity/ # 生成数量选择器(支持 1-6上限由模型配置派生
│ │ ├── Time/ # 视频时长选择器
│ │ └── pattern/ # 视频模式选择器
│ ├── Popover/ # 自定义弹出层Teleport to bodyposition:fixed + fit-content 宽度)
│ ├── Select/ # 自定义下拉选择器(分组选项,与 Popover 协调互斥开关)
│ ├── Img/ # 图片包装组件点击全屏查看Teleport 实现)
│ ├── virtual-scroller/# 虚拟滚动列表组件自定义实现reverse 模式)
│ └── canvas/ # 图片画布编辑(圆/矩形选区局部重绘undo/redo
├── views/ # 页面home、login
├── utils/
│ ├── request.js # Axios 实例 + 拦截器:统一 Auth不带 Bearer+ 按前缀路由 baseURL
│ ├── taskPolling.js # 任务生成入口:组装参数 → POST 创建任务 → 20s HTTP 轮询直至完成/失败
│ ├── modelApi.js # 模型业务层localStorage 30s 缓存 + pendingRequests 并发去重 + 平台编码映射
│ ├── createTask.js # 任务 body 构造Painting 返回 modelParamsVideo 走 Playload 适配器
│ ├── modelConfig.js # Video 旧架构:从远程 JSON 加载 workflow 配置(含 localStorage 每日缓存)
│ ├── downloadImage.js # 图片/视频下载fetch → Blob → 自动文件名下载
│ ├── tokenError.js # 认证失败处理:提示后 5 秒刷新页面
│ ├── encrypt.ts # 加密工具Base64/MD5/RSA/AES依赖 crypto-js、jsencrypt
│ └── auth.ts # token 存取工具localStorage
├── config/
│ ├── plugins.js # Vite 插件配置unplugin-auto-import + unplugin-vue-components
│ ├── index.js # 平台配置入口(目前仅导出 runninghub 供 Video 使用)
│ ├── runninghub/ # RunningHub 平台适配器Playload() 构造和 result() 解析Video 专用)
│ └── models/ # Painting 模型参数 schema每模型一个 JS 文件,定义 params 和各字段的 ui 类型
```
### 模型参数配置Painting 新架构)
`src/config/models/` 下每个模型一个 JS 文件,参数通过 `ui` 字段映射到不同 UI 组件:
| `ui` 值 | 组件 | 说明 |
|---------|------|------|
| `textarea` | Sender 内置 textarea | prompt 输入框 |
| `proportion` | `paintingProportion` | 比例选择 Popoveroptions 含 `custom` 时可自定义 W/H |
| `resolution` | `paintingProportion` 内部 | 分辨率子选项,与 proportion 共用一个 Popover |
| `dimension` | `DimensionInput` | **组合模式**:单个 `"W*H"` 字符串参数,通过 `dimension.parse/format` 序列化 |
| `dimensionWidth` + `dimensionHeight` | `DimensionInput` | **拆分模式**:两个独立 number 参数,共享同一个 Popover 和比例锁 |
| `select` | `Select` | 通用下拉选择(如 quality |
| `quantity` | `Quantity` | 生成张数(上限由 `options` 最大值派生) |
| `imageUpload` | `ImageUploader` | 参考图上传 |
| `hidden` | 无 | 静默写入默认值 |
**dimension 模式区分**dialogBox 通过 `dimConfig` computed 自动检测 —— 找 `ui: 'dimension'`(组合)或 `ui: 'dimensionWidth'`(拆分),两种模式共用同一个 `DimensionInput` 组件。
**条件显示**`showWhen: { aspectRatio: 'custom' }` 使参数仅在 proportion 选 `custom` 时显示(如 `customWidth`/`customHight`)。
模型选择器从 API`fetchPlatformModels`)获取模型列表,按 API 返回的 `tags` 数组字段分组(`text`→生成模型,`edit`→编辑模型,`vision`→视觉理解模型)。
### `displayNameMap` 机制
`src/config/models/index.js``displayNameMap` 负责将 API 返回的 `display_name` 映射到 config key。因为同一模型在不同 tag 下可能共用一个 `display_name`(如 `GPT-Image-2``GPT-image-2` 分别对应编辑/生成config key 采用内部中文名区分。
**已知 bug**`displayNameMap` 存在重复 key `'GPT-Image-2'`,第二条会覆盖第一条,导致文字生图版 GPT-Image-2 查找走 `displayNameMap` 时映射到 I2I 版的 config。当前因 `model.value` 已经是中文 config key直达 `configs[]` 而非走 map暂时不触发。若后续改为按 `display_name` 查找,需修复此重复 key。
### dialogBox 编排中心
`src/components/dialogBox/index.vue` 是核心编排组件,负责:
- **模型选择切换**:监听 `model` + `modelType` 变化,调用 `loadModelConfig()` 加载模型参数 schema
- **派生 UI 配置**:从 model config 计算 `showProportion`、`showDimension`、`showQuality`、`showQuantity`(各自检查对应 `ui` 字段是否存在);`hasCustomSize`proportion options 含 `custom``dimConfig`(自动识别 combined/split 模式)
- **状态管理**`dimWidth`/`dimHeight` 通过 `v-model:width`/`v-model:height` 与 `DimensionInput` 双向绑定;`qualityValue` 通过 `v-model` 与 Select 组件双向绑定
- **参数回填**`fillParamsFromResult()` 供历史记录重编辑使用
- **任务发起**`handleStart()` 收集所有参数 → 构造 `data` → 调用 `taskPolling.js:generate()`
### `$attrs` 穿透注意
向子组件传递的 prop 如果子组件未声明,会通过 `$attrs` 穿透到根元素。例如 `dialogBox``paintingProportion` 传递 `v-model:width="customWidth"`,若 `paintingProportion` 未声明 `width` prop值会穿透到 `<Popover>``width` prop导致异常宽度。**所有通过 `v-model` 传递的值,子组件必须声明对应的 prop。**
### API 层设计原则
- `src/apis/` 只做纯 HTTP 调用(`service.get/post/delete`不含缓存、localStorage、业务逻辑
- 缓存、数据转换等业务逻辑放在 `src/utils/`
### 核心数据流
**Painting新架构**
1. 用户设置参数 → `dialogBox` 从 model config 派生 proportion/resolution 选项、hasCustomSize、quantityMax
2. `handleStart()` 收集参数 → 组装 `{ modelParams, request }`
3. `taskPolling.js:generate()``createTask(data)` → Painting 直接返回 `data.modelParams`
4. `getModelId(type, modelName)` 查找 UUID内部调用 `fetchPlatformModels` 走缓存)
5. `requestCreateTask(body, sessionId)` → POST `/suanli/v1/tasks`,携带 `X-Session-Id` header
6. 返回 task_id → 20s 间隔轮询直至完成/失败
**Video旧架构保留**
1. 用户设置 Pattern、videoModel、比例、时长
2. `createTask(data)``runninghub.Playload(data)``fetchModelConfig()` 获取 workflow JSON → 返回 `{ workflowId, nodeInfoList }`
3. 后续同 Painting 步骤 4-6
### 关键注意事项
- **`sessionId`** 来自登录接口返回的 `userInfo.sessionId`,存储在 `useUserStore().userInfo` 中。`taskPolling.js` 必须使用该值,禁止随机生成。
- **`X-Session-Id`** 自定义 header 需要 nginx 在 `/suanli/` location 的 `Access-Control-Allow-Headers` 中加入,否则 POST 请求会触发 CORS 预检失败。
- **模型列表缓存**`modelApi.js` 中 `fetchPlatformModels` 使用 localStorage 30 秒 TTL + `pendingRequests` Map 并发去重,避免重复请求。
- **页面加载预请求**:平台模型列表在 `dialogBox onMounted` 时预请求,避免首次点击"发送"时才触发。
### 接口速查
| 函数 | 端点 | 用途 |
|------|------|------|
| `requestCreateTask` | POST `/suanli/v1/tasks` | 创建任务(带 `X-Session-Id` header |
| `requestTaskStatus` | GET `/suanli/v1/tasks/:id` | 查询单个任务状态 |
| `requestTaskHistory` | GET `/suanli/v1/tasks/history` | 历史任务列表(支持 `user_id`/`platform_code`/`page`/`pageSize` |
| `fetchPlatformModels` | GET `/suanli/v1/platforms/:code/models` | 获取平台模型列表(返回 `{id, display_name, tags, disabled?}` |
| `cancelOrCollect` | POST `/collect/toggle` | 收藏/取消收藏 |
| `deleteGenerateHistory` | DELETE `/taskRecordHistory/delete` | 删除历史记录 |
### 任务响应格式
```json
// GET /suanli/v1/tasks/:id 返回结构
{
"code": 0,
"data": {
"task_id": "uuid",
"status": "completed", // queued | processing | completed | failed
"outputs": [ // ⚠️ 扁平数组,不是 { images: [...] }
{ "url": "https://...", "type": "png" }
],
"vendor_error": "..." // 仅 failed 时有值
}
}
```
### 请求拦截器路由
拦截器统一设置 `Authorization: <token>`(不带 Bearer 前缀),根据 URL 前缀切换后端:
| URL 前缀 | 环境变量 |
|----------|----------|
| `/suanli` | `VITE_API_TASK_TARGET` |
| `/pay` | `VITE_API_PAY_TARGET` |
| `/aigc` | `VITE_API_AIGC_TARGET` |
| 其他 | `VITE_API_BASE_URL`(默认) |
### 平台编码映射
| 类型 | 平台编码 |
|------|----------|
| Painting | `ai_painting_talk` |
| Video | `ai_video_talk` |
映射函数 `getPlatformCode()` 位于 `utils/modelApi.js`
### 自动导入
- `unplugin-auto-import`:自动导入 Vue/Router/Pinia API
- `unplugin-vue-components`:自动注册 `src/components/` 下的组件和 Element Plus 组件
- Element Plus 图标通过 `unplugin-icons` 按需加载