| | |
| | | |
| | | import { IconifyIcon } from '@vben/icons'; |
| | | import { $t } from '@vben/locales'; |
| | | import { checkFileType, isFunction, isObject, isString } from '@vben/utils'; |
| | | import { checkFileType, isFunction, isImage, isObject, isString } from '@vben/utils'; |
| | | |
| | | import { Button, message, Upload } from 'ant-design-vue'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | import { FilePreviewModal, useFilePreview } from '#/components/file-preview'; |
| | | |
| | | import { UploadResultStatus } from './typing'; |
| | | import { useUpload, useUploadType } from './use-upload'; |
| | |
| | | } else { |
| | | value.push(v); |
| | | } |
| | | const existingMap = new Map<string | number, UploadFile>(); |
| | | for (const f of fileList.value ?? []) { |
| | | const id = (f as any)?.response?.[props.valueKey] ?? (f as any)?.[props.valueKey] ?? f?.uid; |
| | | if (id != null) existingMap.set(id, f); |
| | | } |
| | | fileList.value = value |
| | | .map((item, i) => { |
| | | if (item && isString(item)) { |
| | | return { |
| | | return existingMap.get(item) ?? { |
| | | uid: `${-i}`, |
| | | name: item.slice(Math.max(0, item.lastIndexOf('/') + 1)), |
| | | status: UploadResultStatus.DONE, |
| | | url: item, |
| | | }; |
| | | } else if (item && isObject(item)) { |
| | | return item; |
| | | const key = item[props.valueKey] ?? item.uid; |
| | | return (key != null && existingMap.get(key)) || { ...item, url: String((item as any).url ?? '') }; |
| | | } else if (item && (typeof item === 'number' || typeof item === 'string')) { |
| | | return { |
| | | return existingMap.get(item) ?? { |
| | | uid: `${-i}`, |
| | | name: `文件 ${String(item)}`, |
| | | status: UploadResultStatus.DONE, |
| | | url: item, |
| | | url: String(item), |
| | | response: { [props.valueKey]: item }, |
| | | } as UploadFile; |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | const { openPreview, previewState } = useFilePreview(); |
| | | |
| | | /** 处理文件预览 */ |
| | | function handlePreview(file: UploadFile) { |
| | | emit('preview', file); |
| | | async function handlePreview(file: UploadFile) { |
| | | const url = (file as any)?.url || (file as any)?.response?.url || (file as any)?.response; |
| | | if (!url) { |
| | | message.warning('文件地址不存在,无法预览'); |
| | | return; |
| | | } |
| | | const name = file.name || (file as any)?.fileName || ''; |
| | | if (isImage(name || url)) { |
| | | window.open(url, '_blank'); |
| | | return; |
| | | } |
| | | try { |
| | | // 将绝对 URL 转为相对路径,走 Vite 代理避免跨域 |
| | | const path = typeof url === 'string' ? url.replace(/^https?:\/\/[^/]+/, '') : url; |
| | | const blob = await requestClient.download(path); |
| | | const blobUrl = URL.createObjectURL(blob as Blob); |
| | | openPreview(blobUrl, name); |
| | | emit('preview', file); |
| | | } catch { |
| | | message.error('文件加载失败'); |
| | | } |
| | | } |
| | | |
| | | /** 处理文件数量超限 */ |
| | |
| | | fileList.value?.splice(index!, 1); |
| | | } |
| | | |
| | | const fileUrl = res?.url || res?.data || res; |
| | | const fileUrl = res?.url || res?.data || (typeof res === 'string' ? res : ''); |
| | | uploadList.value.push({ |
| | | name: res?.name || file.name, |
| | | url: fileUrl, |
| | |
| | | 格式文件 |
| | | </div> |
| | | </Upload> |
| | | <FilePreviewModal |
| | | v-model:visible="previewState.visible.value" |
| | | :file-url="previewState.fileUrl.value" |
| | | :file-name="previewState.fileName.value" |
| | | /> |
| | | </div> |
| | | </template> |
| | | |