--- name: upload-component-first description: MallBase Vben Admin 上传组件与字段契约规则;实现图片、视频或文件上传,以及处理 FileInfo 回填和提交值时使用。 --- # Vben 规则:上传组件与字段契约 ## 适用范围 `frontend/admin/apps/web-antd` 中的图片、视频和文件上传交互,以及编辑回填、预览和提交转换。 ## 强制规则 1. 优先使用 `#/components/upload/index.vue`,禁止在业务页面重复实现上传或素材选择逻辑。 2. UI 状态使用 `FileInfo`;`url` 保存可提交值,`full_url` 仅用于预览,`asset_id` 保存素材 ID。 3. 新素材契约优先提交数值型 `asset_id`;只有真实后端接口明确接受存储路径时,才回退提交 `url` 字符串。 4. 后端字段类型是最终依据。商品媒体字段遵循 `GoodsApi.MediaValue = number | string`,其他模块不得未经核对直接照搬。 5. `full_url`、`*_full_url` 和前端拼接的完整地址不得作为持久化提交值。 6. 编辑回填使用后端返回的素材值与完整预览地址构造 `FileInfo`,不在页面硬编码域名。 7. 多图、排序或关联对象必须保留接口要求的数组/对象结构,不能统一粗暴转换成字符串数组。 8. 批量编辑若覆盖媒体字段,必须同时处理回填、预览和提交转换。 ## 引入方式 ```vue import type { FileInfo } from '#/components/upload'; import Upload from '#/components/upload/index.vue'; ``` ## 用法示例 ```vue ``` - `v-model:value` 单文件绑定类型通常为 `FileInfo | string | undefined` - `v-model:value` 多文件绑定类型通常为 `FileInfo[]` - `module` 指定上传模块(如 `"goods"`、`"user"` 等) ## 契约边界示例 ```typescript function toMediaValue(file?: FileInfo | string): number | string { if (!file) return ''; if (typeof file === 'string') { return /^\d+$/.test(file) ? Number(file) : file; } if (file.asset_id) return file.asset_id; return /^\d+$/.test(file.url) ? Number(file.url) : file.url; } ``` 该示例只适用于后端接受 `number | string` 的媒体字段。提交前仍需核对对应的 `backend/route/api/admin/*.php`、Controller/Service 校验和前端 API 类型。 ## 禁止 - ❌ 使用 `` 让用户手动输入图片 URL - ❌ 手动实现图片上传逻辑 - ❌ 把 `full_url` 提交到只接受素材 ID 或存储路径的字段 - ❌ 看到 `FileInfo.url` 就假设它一定是文件路径 ## 参考位置 - `frontend/admin/apps/web-antd/src/components/upload/index.vue` - `frontend/admin/apps/web-antd/src/components/upload/index.ts` - `frontend/admin/apps/web-antd/src/views/goods/goods/composables/useGoodsEdit.ts` - `frontend/admin/apps/web-antd/src/api/goods/goods.ts` ## 自检清单 - [ ] 上传逻辑未重复造轮子。 - [ ] 回填对象包含可靠的提交值、预览地址和文件名。 - [ ] 素材 ID、兼容路径、单图/多图结构与真实后端契约一致。 - [ ] 提交数据不包含 `full_url`,保存后二次进入仍能稳定回显。