# 本地文件上传 README
本文档基于以下实现整理:
- `src/components/AttachmentUpload/file/index.vue`
- `src/components/AttachmentUpload/image/index.vue`
- `src/components/AttachmentPreview/image/index.vue`
- `src/components/Dialog/FileList.vue`
- `src/api/basicData/common.js`
- `src/api/publicApi/commonFile.js`
相关组件已在 `src/main.js` 中注册为全局组件,可直接在页面中使用:
- `FileUpload`
- `ImageUpload`
- `ImagePreview`
- `FileListDialog`
## 1. 功能概览
当前这套上传能力主要分为 4 部分:
1. `FileUpload`:普通文件上传,支持拖拽、批量上传、预览、删除
2. `ImageUpload`:图片上传,支持图片墙展示、预览、删除
3. `ImagePreview`:图片列表预览展示
4. `FileListDialog`:业务附件弹窗,支持查询、上传、删除、下载
上传底层统一走接口:
- `POST /common/upload`
对应方法在 `src/api/basicData/common.js`:
```js
uploadFile(data)
```
## 2. 上传接口说明
### 2.1 通用上传接口
文件上传组件和图片上传组件都调用了:
```js
import { uploadFile } from '@/api/basicData/common'
```
接口特征:
- 请求方式:`POST`
- 地址:`/common/upload`
- 请求类型:`multipart/form-data`
- 支持 `FormData` 批量上传
- 默认字段名:`files`
组件内部会这样组装参数:
```js
const formData = new FormData()
validFiles.forEach((file) => {
formData.append(props.uploadFieldName, file.raw)
})
```
### 2.2 上传返回值要求
上传成功后,组件会尝试从以下结构中提取数组:
- `response`
- `response.data`
- `response.data.data`
- `response.payload`
- `response.payload.data`
- `response.rows`
- `response.result`
因此后端返回数组时,上面任意一种结构都可以被识别。
组件展示时常用到的字段有:
- 文件名:`name` / `originalFilename` / `fileName` / `uidFilename`
- 文件地址:`url` / `downloadURL`
- 图片地址:`url` / `previewURL` / `previewUrl`
- 主键:`id`
建议上传接口返回的单项对象尽量包含:
```js
{
id: 1,
originalFilename: 'demo.pdf',
downloadURL: 'https://xxx/demo.pdf',
previewURL: 'https://xxx/demo.png'
}
```
## 3. FileUpload 文件上传组件
组件路径:
`src/components/AttachmentUpload/file/index.vue`
### 3.1 基础用法
```vue
```
### 3.2 常用属性
| 属性 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| `fileList` | 绑定文件列表 | `Array` | `[]` |
| `limit` | 最大上传数量 | `Number` | `10` |
| `fileSize` | 单个文件大小限制,单位 MB | `Number` | `50` |
| `fileType` | 允许上传的文件类型,如 `['pdf', 'docx']` | `Array` | `[]` |
| `buttonText` | 上传提示文案 | `String` | `单击选择文件` |
| `disabled` | 是否禁用 | `Boolean` | `false` |
| `uploadFieldName` | `FormData` 字段名 | `String` | `files` |
| `index` | 表格/列表行模式下的当前行索引 | `Number` | `-1` |
| `childrenKey` | 行内挂载字段名 | `String` | `files` |
### 3.3 事件
| 事件 | 说明 |
| --- | --- |
| `update:fileList` | 文件列表变化时触发 |
| `change` | 文件列表变化时触发,返回最新列表 |
### 3.4 限制规则
组件内已实现:
- 文件数量限制
- 文件大小限制
- 文件类型校验
- 上传中状态锁定
- 失败后自动清空当前选择队列
例如限制 PDF/Word:
```vue
```
### 3.5 返回数据格式建议
`FileUpload` 更适合接收这样的列表:
```js
[
{
id: 1,
originalFilename: '合同.pdf',
downloadURL: 'https://xxx/contract.pdf'
}
]
```
因为组件打开文件时会优先读取:
```js
url || downloadURL || previewURL || previewUrl
```
### 3.6 行内嵌套模式
如果上传组件放在表格某一行中,可配合 `index` 和 `childrenKey` 使用:
```vue
```
此时组件会自动读写:
```js
tableData[scope.$index].files
```
## 4. ImageUpload 图片上传组件
组件路径:
`src/components/AttachmentUpload/image/index.vue`
### 4.1 基础用法
```vue
```
### 4.2 默认行为
图片上传组件默认:
- 最多上传 `10` 张
- 单张不超过 `10MB`
- 默认支持格式:`png / jpg / jpeg / webp`
- 使用 `picture-card` 风格展示
- 点击缩略图可预览大图
### 4.3 常用示例
```vue
```
### 4.4 返回数据格式建议
`ImageUpload` 展示图片时优先读取:
```js
url || previewURL || previewUrl
```
建议后端返回:
```js
[
{
id: 1,
originalFilename: '现场图片.jpg',
previewURL: 'https://xxx/image.jpg'
}
]
```
### 4.5 行内嵌套模式
图片组件同样支持行内字段写回:
```vue
```
默认写回字段为 `images`。
## 5. ImagePreview 图片预览组件
组件路径:
`src/components/AttachmentPreview/image/index.vue`
### 5.1 基础用法
```vue
```
### 5.2 可配置项
| 属性 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| `fileList` | 图片列表 | `Array` | `[]` |
| `thumbSize` | 缩略图大小 | `Number` | `72` |
| `gap` | 缩略图间距 | `Number` | `10` |
### 5.3 数据要求
组件会过滤没有 `previewURL` 的项,因此如果要正常显示,建议至少包含:
```js
[
{
previewURL: 'https://xxx/image.jpg',
originalFilename: '图片1.jpg'
}
]
```
如果列表为空,组件显示“暂无图片”。
## 6. FileListDialog 附件弹窗组件
组件路径:
`src/components/Dialog/FileList.vue`
这个组件适合业务表单或详情页里的“附件管理”场景,能力包括:
- 根据业务主键查询附件列表
- 打开弹窗查看附件
- 在弹窗中继续上传附件
- 删除附件
- 下载附件
### 6.1 组件属性
| 属性 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| `visible` | 是否显示弹窗 | `Boolean` | 必传 |
| `recordType` | 业务类型 | `String` | `''` |
| `recordId` | 业务主键 | `Number` | `0` |
| `title` | 弹窗标题 | `String` | `附件` |
| `width` | 弹窗宽度 | `String` | `50%` |
| `showActions` | 是否显示下载/删除操作列 | `Boolean` | `true` |
### 6.2 基础用法
```vue
查看附件
```
### 6.3 组件内部依赖的接口
`FileListDialog` 本身不直接调用 `commonFile.js`,而是依赖:
- `attachmentList`
- `createAttachment`
- `deleteAttachment`
处理逻辑为:
1. 打开弹窗后根据 `recordType + recordId` 查询附件
2. 点击“上传附件”后,内部使用 `AttachmentUpload/file` 先上传到 `/common/upload`
3. 上传成功后,将返回的文件对象和已有列表一起提交给 `createAttachment`
4. 删除时调用 `deleteAttachment`
5. 下载时直接 `window.open(downloadURL, '_blank')`
因此这里要特别注意:
- `recordType` 和 `recordId` 必须是有效业务标识
- 上传成功返回的数据,需要能被 `createAttachment` 直接接收
- 列表中的下载地址字段应为 `downloadURL`
## 7. commonFile.js 说明
文件路径:
`src/api/publicApi/commonFile.js`
当前文件提供的是公共文件删除接口:
```js
delCommonFile(ids)
delCommonFileInvoiceLedger(ids)
```
对应接口:
- `/commonFile/delCommonFile`
- `/invoiceLedger/delFile`
这两个方法更适合已经和具体业务绑定后的“删除已保存附件”场景,不负责上传文件本身。
示例:
```js
import { delCommonFile } from '@/api/publicApi/commonFile'
await delCommonFile([1, 2, 3])
```
## 8. 推荐使用方式
### 8.1 普通业务表单上传附件
```vue
```
提交表单时直接带上:
```js
{
...form,
storageBlobDTOs: form.storageBlobDTOs
}
```
### 8.2 图片类业务
```vue
```
### 8.3 已落库附件管理
```vue
```
适合详情页、审批页、台账页这类“查看并维护当前业务附件”的场景。
## 9. 注意事项
1. `FileUpload` 和 `ImageUpload` 只是负责把文件先传到 `/common/upload`,不等于已经和业务数据绑定。
2. 如果业务需要持久化附件关系,仍需要在保存表单时把返回的文件对象提交给业务接口。
3. `ImagePreview` 当前只识别 `previewURL`,如果后端只返回 `url`,预览组件将不会展示,最好统一补齐 `previewURL`。
4. `FileListDialog` 依赖 `recordType`、`recordId` 查询和保存附件关系,新增业务时要先确认后端关联接口可用。
5. 删除本地列表项和删除已保存附件是两件事:
- 上传组件里的删除:只会从当前前端绑定数组中移除
- `commonFile.js` / `deleteAttachment`:才是真正调用后端删除
## 10. 一套最常见的页面写法
```vue
```
如果你的目标是“先上传,再跟业务一起保存”,这套写法可以直接作为基础模板使用。