b5d414d9af
Co-Authored-By: Claude <noreply@anthropic.com>
121 lines
6.8 KiB
Markdown
121 lines
6.8 KiB
Markdown
# 备货单车辆明细附件上传 设计
|
||
|
||
- 日期:2026-08-06
|
||
- 分支:feature/crm_pc
|
||
- 涉及前端:`src/views/crmpc/stocking/Add.vue`、`src/composables/useStockingForm.ts`
|
||
- 涉及后端:`stock_order_items.attachment`(已加字段)、备货单 store/update/show 接口
|
||
|
||
## 1. 背景
|
||
|
||
添加备货单(`Add.vue`)时,车辆明细(`items`,每行一辆车)目前只能录入车型 / 颜色 / 价格等字段,**没有附件**。业务需要每辆车能上传若干附件(如该车相关文件),随车辆一起保存。
|
||
|
||
后端 `stock_order_items` 表已新增 `attachment` 字段(本次由后端同事完成)。
|
||
|
||
## 2. 需求
|
||
|
||
- 添加备货单时,车辆明细**每辆车**可上传**多个**附件。
|
||
- UI 为**行内紧凑**形态:每行一个「上传附件」入口,已传文件以文件名标签列出、可删除,不撑高行高。
|
||
- 附件随车辆数据一起提交,存入 `stock_order_items.attachment`。
|
||
- 字段名统一用 `attachment`,值为 `file_name` 字符串数组(与备货单级 `contract`/`pi_doc`/`transaction_slip` 的存储模式一致)。
|
||
|
||
## 3. 现状(复用基础)
|
||
|
||
- 车辆明细 `items` 由 `useStockingForm(false)` 提供,每行 `item` 是 `ItemRow`。
|
||
- 备货单级文件已有成熟模式:前端维护 `FileItem[]`(`{name, url, file_name}`,带 url 供预览/下载),提交时映射成 `file_name[]` 存入 `baseForm.contract` 等。
|
||
- 上传走通用接口 `${VITE_API_URL}/ticket/upload?scene=stock_orders`,header 带 token(`useUploadHeaders`),成功返回 `{ data: { original_name, file_url, file_name } }`。**此接口无需改动。**
|
||
- 现成组件 `MultiFileUpload`(120px 大拖拽区)适合整页文件区,不适合塞进行内——故行内用 `el-upload` 直接内联。
|
||
|
||
## 4. 设计
|
||
|
||
### 4.1 数据层 `useStockingForm.ts`
|
||
|
||
- `ItemRow` 类型增加 `attachment_files: { name: string; url: string; file_name: string }[]`。
|
||
- `newItem()` 初始化 `attachment_files: []`。
|
||
- 提交映射 `mappedItems` 增加:
|
||
```ts
|
||
attachment: (it.attachment_files || []).map((f) => f.file_name)
|
||
```
|
||
- 详情回填 `populateFromDetail`:从后端返回的 `it.attachment` 反解为 `attachment_files`(格式见 4.4,Add 为新建不走回填,但 composable 与 edit 共享,一并支持)。
|
||
- `copyRow` 已是 `JSON.parse(JSON.stringify(...))`,自动带上附件,无需改。
|
||
- 草稿 `pickDraftPayload` 已含 `items: toRaw(items.value)`,附件自然入草稿,无需改。
|
||
- 新增车辆附件上传/删除方法(仿现有 `handleContractUpload` 等):
|
||
- `handleItemAttachmentUpload(response: any, idx: number)`:校验 `response.code===200`,把 `{ name: data.original_name, url: data.file_url, file_name: data.file_name }` push 进 `items.value[idx].attachment_files`。
|
||
- `removeItemAttachment(idx: number, fileIdx: number)`:从 `items.value[idx].attachment_files` 删除指定项。
|
||
|
||
### 4.2 UI 层 `Add.vue`
|
||
|
||
在车辆明细 `v-for="(item, idx) in items"` 的行内末尾增加一个 `ElCol`「附件」:
|
||
|
||
```html
|
||
<ElCol :sm="12" :md="12" :lg="6" :xl="4">
|
||
<ElFormItem label="附件" label-width="85px">
|
||
<el-upload
|
||
:action="`${VITE_API_URL}/ticket/upload?scene=stock_orders`"
|
||
:headers="uploadHeaders"
|
||
:show-file-list="false"
|
||
:before-upload="(file) => beforeItemAttachmentUpload(file)"
|
||
:on-success="(res) => handleItemAttachmentUpload(res, idx)"
|
||
multiple
|
||
:accept="itemAttachmentAccept"
|
||
>
|
||
<el-button size="small" type="primary" plain>上传附件</el-button>
|
||
</el-upload>
|
||
<div class="item-attachment-tags" v-if="item.attachment_files?.length">
|
||
<el-tag
|
||
v-for="(f, fIdx) in item.attachment_files"
|
||
:key="fIdx"
|
||
closable
|
||
@close="removeItemAttachment(idx, fIdx)"
|
||
style="margin: 2px 4px 2px 0"
|
||
>
|
||
{{ f.name }}
|
||
</el-tag>
|
||
</div>
|
||
</ElFormItem>
|
||
</ElCol>
|
||
```
|
||
|
||
- `VITE_API_URL`、`uploadHeaders`(`useUploadHeaders`)、`beforeItemAttachmentUpload`(类型/大小校验,复用 `MultiFileUpload` 的 accept 与 10MB 限制)、`handleItemAttachmentUpload`、`removeItemAttachment` 均由 `useStockingForm` return,模板解构使用,与现有 `contractFiles` 等用法一致。
|
||
|
||
### 4.3 后端配合
|
||
|
||
- `stock_order_items.attachment` 字段已加(DB 就绪)。
|
||
- 接口层需对接:
|
||
- **store / update**:接收 `items[].attachment`(`file_name` 数组),写入 `stock_order_items.attachment`(按 json/序列化存储)。
|
||
- **show / detail**:返回 `items[].attachment`(`file_name` 数组,或含 `original_name`/`file_url` 的对象数组——见 4.4 约定)。
|
||
|
||
### 4.4 回填格式约定(需与后端对齐)
|
||
|
||
详情接口返回的 `items[].attachment` 有两种可能,择一与后端确认:
|
||
- **A**:`file_name` 字符串数组(如 `["xxx.pdf"]`)——前端回填时 `url` 需另行拼接,预览受限。
|
||
- **B(推荐)**:对象数组(如 `[{ original_name, file_url, file_name }]`,与 `/ticket/upload` 返回一致)——前端直接还原成 `attachment_files`,预览/下载可用。
|
||
|
||
实现(plan)阶段以前端实际请求详情接口的返回为准;若为 A,前端按现有 `populateFromDetail` 中文件字段的拼 url 方式处理。
|
||
|
||
## 5. 安全 / 限制
|
||
|
||
- 复用 `beforeUpload`:类型限制 `.jpg,.jpeg,.png,.gif,.pdf,.doc,.docx,.xls,.xlsx,.txt`,单文件 ≤ 10MB。
|
||
- 上传 header 带 token(`useUploadHeaders`),鉴权由后端 `/ticket/upload` 既有机制处理。
|
||
- `attachment` 只存 `file_name`,不含敏感信息。
|
||
|
||
## 6. 改动清单
|
||
|
||
| 端 | 文件 | 改动 |
|
||
|---|---|---|
|
||
| 前端 | `src/composables/useStockingForm.ts` | `ItemRow` 加 `attachment_files`;`newItem` 初始化;`mappedItems` 映射 `attachment`;`populateFromDetail` 回填;新增 `handleItemAttachmentUpload`/`removeItemAttachment`/`beforeItemAttachmentUpload`/暴露 `uploadHeaders` |
|
||
| 前端 | `src/views/crmpc/stocking/Add.vue` | 车辆明细行末尾加「附件」`ElCol`(`el-upload` + 标签列表);解构新方法 |
|
||
| 后端 | store/update/show 接口 | 对接 `items[].attachment`(DB 字段已就绪) |
|
||
|
||
## 7. 验收标准
|
||
|
||
1. 添加备货单:每辆车行有「上传附件」按钮,点选多文件逐个上传成功后,文件名以标签列出。
|
||
2. 标签可点 × 删除;删除后提交不包含该文件。
|
||
3. 提交备货单,Network 里 `items[].attachment` 为对应 `file_name` 数组;后端 `stock_order_items.attachment` 落库正确。
|
||
4. (后端 show 返回后)进入编辑/详情,已存附件能回显为标签列表。
|
||
5. 超格式 / 超 10MB 文件被拦截并提示。
|
||
|
||
## 8. 未做项 / 后续
|
||
|
||
- 本次只改 `Add.vue` 模板;`stocking_edit.vue` 共用 `useStockingForm`,数据层已支持,如需在编辑页也展示附件上传,后续加同款 `ElCol` 即可。
|
||
- 终版(final_add/final_edit)是否需要同等能力,待业务确认。
|