# 本地文件上传 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 ``` 如果你的目标是“先上传,再跟业务一起保存”,这套写法可以直接作为基础模板使用。