# 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 # 预览生产构建 ``` ## 技术栈 Vue 3 (Composition API) + Vite 7 + Pinia + Vue Router + Element Plus + Less + pnpm ## 架构概览 AI 绘画/视频生成前端操作平台,通过 HTTP 接口对接算力调度后端(suanli)和第三方 AI 平台(RunningHub),提交生成任务并轮询结果。 **Painting 和 Video 走两套不同的任务构造路径:** - **Painting(新架构)**:本地模型参数 schema → 动态表单 → 扁平 API body 提交 - **Video(旧架构)**:远程 workflow JSON → RunningHub Playload 适配器 → `{ workflowId, nodeInfoList }` body 提交 ### 关键目录 ``` src/ ├── main.js # 入口:创建 Vue 应用,安装 Pinia/Router/VueVirtualScroller ├── router/index.js # 路由定义 + token 验证守卫 ├── stores/ # Pinia 状态管理 │ ├── user.js # 用户认证、信息 │ └── display.js # 生成历史列表、UI 状态(滚动、画布等) ├── apis/ # HTTP API 层:纯请求封装,不含业务逻辑 │ ├── auth/ # 认证相关(登录、token 校验、用户信息) │ └── display/ # 任务创建/轮询/历史、平台模型、收藏/删除 ├── components/ │ ├── dialogBox/ # 生成参数输入面板(核心交互入口) │ │ ├── model/ # 模型选择器(painting 按 tag 分组,video 按 pattern 分组) │ │ ├── params/ # 动态参数控件(Painting 新架构):ProportionSelect、ResolutionSelect、SelectInput 等 │ │ ├── proportion/ # 比例选择器(仅 video.vue,Video 旧架构) │ │ ├── imageUploader/ # 图片上传(Painting) │ │ └── videoImageUploader/ # 视频图片上传(Video) │ ├── virtual-scroller/# 虚拟滚动列表组件(自定义实现,reverse 模式) │ └── canvas/ # 图片画布编辑(圆/矩形选区,局部重绘) ├── views/ # 页面(home、login) ├── utils/ │ ├── request.js # Axios 实例 + 拦截器:统一 Auth(不带 Bearer)+ 按前缀路由 baseURL │ ├── websocket.js # 任务生成入口:组装参数 → 调用 API → 20s 轮询直至完成/失败 │ ├── modelApi.js # 模型业务层:localStorage 每日缓存 + 模型名称→UUID 查找 + 平台编码映射 │ ├── createTask.js # 任务 body 构造:Painting 返回 modelParams,Video 走 Playload 适配器 │ ├── modelConfig.js # Video 旧架构:从远程 JSON 加载 workflow 配置(Video 专用) │ └── auth.ts # token 存取工具(localStorage) ├── config/ │ ├── index.js # 平台配置入口(目前仅导出 runninghub 供 Video 使用) │ ├── runninghub/ # RunningHub 平台适配器:Playload() 构造和 result() 解析(Video 专用) │ └── models/ # Painting 模型参数配置:每模型一个 JS 文件,定义 params schema ``` ### 模型参数配置(Painting 新架构) `src/config/models/` 下每个模型一个 JS 文件,定义该模型的 API 参数 schema: ```js export default { name: 'Flux 2', tag: '文生图', // 与 API 返回的 tag 对应,用于模型选择器分组 inputType: 'text', // 'text' | 'image' | 'both' — 控制是否显示图片上传 maxImages: 4, // 最大上传图片数(inputType 为 image/both 时有效) params: [ { name: 'prompt', // API 字段名 label: '提示词', type: 'string', // 'string' | 'number' | 'boolean' | 'select' | 'image' required: true, ui: 'textarea', // 渲染控件:'textarea' | 'proportion' | 'resolution' | 'select' | 'number' | 'switch' | 'imageUpload' default: '', options: [...], // select 类型的枚举值 showWhen: { aspectRatio: 'custom' }, // 条件显示(可选) }, ], } ``` - `src/config/models/index.js` 提供 `getModelConfig(modelName)` 查找函数 - `ui: 'textarea'` 的参数由 Sender 组件承载(prompt),`ui: 'imageUpload'` 由独立上传组件处理,其余渲染为 params/ 下的动态控件 - 模型选择器从 API(`fetchPlatformModels`)获取模型列表,按 `tag` 字段分组 ### API 层设计原则 - `src/apis/` 中的函数只做**纯 HTTP 调用**(`service.get/post/delete` 等),不包含缓存、localStorage、业务判断等逻辑 - 缓存、数据转换等业务逻辑放在 `src/utils/` 中,调用 apis 层的原始函数 示例:`utils/modelApi.js` 导入 `apis/display` 的原始 `fetchPlatformModels`,在其上叠加 localStorage 每日缓存和 `getModelId` 查找逻辑。 ### 核心数据流 **Painting(新架构):** 1. 用户在 `dialogBox` 中设置参数——模型选择器从 API 获取模型列表按 tag 分组,参数控件根据模型 config 动态渲染 2. 点击生成 → `dialogBox:handleStart()` 收集 `paramValues`(含 prompt)→ 组装 data(含 `modelParams` 扁平对象) 3. 调用 `websocket.js:generate(data, generateData)` 4. `generate()` 内部通过 `createTask(data)` → 因 `type === 'Painting'` 直接返回 `data.modelParams` 作为 body 5. 调用 `modelApi.getModelId(type, modelName)` 查找模型 UUID 6. 调用 `requestCreateTask(body, sessionId)` → POST `/suanli/v1/tasks`(`{ model_id, body: modelParams, request }`) 7. 返回 task_id → 轮询直至完成 **Video(旧架构,保留):** 1. 用户在 `dialogBox` 中设置参数(Pattern、videoModel、比例、时长) 2. 点击生成 → `dialogBox:handleStart()` 组装 data(含旧 params 数组) 3. `createTask(data)` → `runninghub.Playload(data)` → `fetchModelConfig()` 获取 workflow JSON → 返回 `{ workflowId, nodeInfoList }` 4. 后续同 Painting 的步骤 5-7 ### 接口速查 | 函数 | 端点 | 用途 | |------|------|------| | `requestCreateTask` | POST `/suanli/v1/tasks` | 创建生成任务 | | `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, name, tag, disabled?}`) | | `cancelOrCollect` | POST `/collect/toggle` | 收藏/取消收藏 | | `deleteGenerateHistory` | DELETE `/taskRecordHistory/delete` | 删除历史记录 | ### 请求拦截器路由 拦截器统一设置 `Authorization: `(不带 Bearer 前缀),根据 URL 前缀切换后端: | URL 前缀 | 环境变量 | 示例 target | |----------|----------|-------------| | `/suanli` | `VITE_API_TASK_TARGET` | `http://test.xueai.art` | | `/pay` | `VITE_API_PAY_TARGET` | `http://test.xueai.art` | | 其他 | `VITE_API_BASE_URL`(默认) | `http://test.xueai.art/newapi/api` | ### 响应格式兼容 响应拦截器同时识别 `code === 0` 和 `status === 0` 两种成功状态。用户信息接口兼容新旧两种格式:`data.userInfo` 嵌套(新)和 `data` 扁平(旧),在 store 的 `getInfo()` 中做了 `const u = res.data.userInfo || res.data` 的兼容处理。 ### 平台编码映射 | 类型 | 平台编码 | |------|----------| | Painting | `ai_painting_talk` | | Video | `ai_video_talk` | 映射函数 `getPlatformCode()` 位于 `utils/modelApi.js`。 ### 自动导入 - `unplugin-auto-import`:自动导入 Vue/Router/Pinia API,`.vue` 中无需手动 import - `unplugin-vue-components`:自动注册 `src/components/` 下的组件和 Element Plus 组件 - Element Plus 图标通过 `unplugin-icons` 按需加载 ### 路由守卫 `src/router/index.js` 的 `beforeEach` 守卫检查 token 存在性和有效性(调用 `POST /login/validateToken`),无效则跳转 `/login`。支持通过 URL query `?token=xxx` 传入 token。