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

12 KiB
Raw Blame History

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

常用命令

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 模型列表从远程静态 JSONVITE_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)。

模型选择器从 APIfetchPlatformModels)获取模型列表,按 API 返回的 tags 数组字段分组(text→生成模型,edit→编辑模型,vision→视觉理解模型)。

displayNameMap 机制

src/config/models/index.jsdisplayNameMap 负责将 API 返回的 display_name 映射到 config key。因为同一模型在不同 tag 下可能共用一个 display_name(如 GPT-Image-2GPT-image-2 分别对应编辑/生成config key 采用内部中文名区分。

已知 bugdisplayNameMap 存在重复 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 计算 showProportionshowDimensionshowQualityshowQuantity(各自检查对应 ui 字段是否存在);hasCustomSizeproportion options 含 customdimConfig(自动识别 combined/split 模式)
  • 状态管理dimWidth/dimHeight 通过 v-model:width/v-model:heightDimensionInput 双向绑定;qualityValue 通过 v-model 与 Select 组件双向绑定
  • 参数回填fillParamsFromResult() 供历史记录重编辑使用
  • 任务发起handleStart() 收集所有参数 → 构造 data → 调用 taskPolling.js:generate()

$attrs 穿透注意

向子组件传递的 prop 如果子组件未声明,会通过 $attrs 穿透到根元素。例如 dialogBoxpaintingProportion 传递 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.jsfetchPlatformModels 使用 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 删除历史记录

任务响应格式

// 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 按需加载