import { Button, Modal, Upload, message } from "antd"; import type { UploadRequestOption as RcCustomRequestOptions } from "rc-upload/lib/interface"; import { CloudUploadOutlined, PlusOutlined, UploadOutlined, } from "@ant-design/icons"; import { FC, useContext, useMemo, useState } from "react"; import { requestByGet, requestByPost } from "@dage/service"; import { RcFile, UploadFile, UploadProps } from "antd/es/upload"; import { DageFileAPIResponseType, DageUploadProps, DageUploadType, } from "./types"; import { AntdDragger, AntdDraggerText, AntdUpload, UploadContainer, UploadTips, } from "./style"; import { DageUploadContext } from "./context"; import { DageUploadItemActions } from "./ItemActions"; import { getImageSize } from "./utils"; const getBase64 = (file: RcFile): Promise => new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = () => resolve(reader.result as string); reader.onerror = (error) => reject(error); }); export const DageUpload: FC = ({ action, value, dType = DageUploadType.IMG, maxCount = 9, maxSize = 5, tips, disabled, name = "file", onChange, ...rest }) => { const context = useContext(DageUploadContext); const [previewOpen, setPreviewOpen] = useState(false); const [previewImage, setPreviewImage] = useState(""); const [previewTitle, setPreviewTitle] = useState(""); const uploadListType = useMemo(() => { switch (dType) { case DageUploadType.IMG: return "picture-card"; default: return "text"; } }, [dType]); const isPictureCard = uploadListType === "picture-card"; // 可选文件类型 const accept = useMemo(() => { switch (dType) { case DageUploadType.IMG: return ".jpg,.jpeg,.png,.gif"; case DageUploadType.MOBILE_MODEL: return ".zip"; case DageUploadType.MODEL: return ".4dage"; case DageUploadType.VIDEO: return ".mp4"; case DageUploadType.AUDIO: return ".mp3"; default: return "*"; } }, [dType]); const beforeUpload = (file: RcFile) => { let pass = false; let passFileType = false; // 校验文件类型 const fileName = file.name.toLowerCase(); const fileExtension = fileName.substring(fileName.lastIndexOf(".")); passFileType = accept.split(",").includes(fileExtension); if (!passFileType) { message.error(tips || "选择的文件类型不正确!"); } // 校验文件大小 const isLtM = file.size / 1024 / 1024 < maxSize; if (!isLtM) { message.error(`最大支持 ${maxSize}M!`); } pass = passFileType && isLtM; pass && context?.handleUploadingFileNum("add"); return pass ? pass : Upload.LIST_IGNORE; }; const onUpload = (option: RcCustomRequestOptions) => { const formData = new FormData(); const headers = option.headers || {}; formData.append("type", dType); if (option.file instanceof Blob) { formData.append(name, option.file, (option.file as any).name); } else { formData.append(name, option.file); } option.data && Object.keys(option.data).forEach((key) => { const value = option.data?.[key]; if (Array.isArray(value)) { value.forEach((item) => { formData.append(`${key}[]`, item); }); return; } formData.append(key, value as string | Blob); }); requestByPost(action, formData, { // @ts-ignore withCredentials: option.withCredentials, headers, // @ts-ignore onUploadProgress: ({ total, loaded }) => { if (!total || !loaded) return; option.onProgress?.({ percent: Math.round((loaded / total) * 100) }); }, }) .then((data) => { option.onSuccess?.(data); }) .catch((err) => { context?.handleUploadingFileNum("uploaded"); option.onError?.(err); }); }; const handleChange: UploadProps["onChange"] = async ({ fileList: newFileList, file, }) => { if (file.status === "done") { if (typeof file.response !== "object" || !file.response.id) { // 上传失败 file.status = "error"; } else if (isPictureCard && file.originFileObj) { // 如果是图片获取图片宽高 // @ts-ignore file.imgAttrs = await getImageSize(file.originFileObj); } context?.handleUploadingFileNum("uploaded"); } onChange?.( newFileList.map((i) => ({ ...i, dType, })), { ...file, dType, } ); }; const handleCancel = () => setPreviewOpen(false); const handlePreview = async (file: UploadFile) => { if (!isPictureCard) return; if (!file.url && !file.preview) { file.preview = await getBase64(file.originFileObj as RcFile); } setPreviewImage(file.url || (file.preview as string)); setPreviewOpen(true); setPreviewTitle( file.name || file.url!.substring(file.url!.lastIndexOf("/") + 1) ); }; const handleDownload = async (file: UploadFile) => { if (!file.url) return; const res: BlobPart = await requestByGet(file.url, "", { meta: { responseType: "arrayBuffer", }, }); const blob = new Blob([res]); const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.target = "_blank"; link.download = file.name; link.style.display = "none"; document.body.appendChild(link); link.click(); document.body.removeChild(link); }; const props: UploadProps = { fileList: value, withCredentials: true, customRequest: onUpload, listType: uploadListType, maxCount, accept, disabled, itemRender: (node, file, fileList, actions) => ( {node} ), showUploadList: { showPreviewIcon: false, showDownloadIcon: false, showRemoveIcon: false, }, multiple: maxCount > 1, onPreview: handlePreview, beforeUpload: beforeUpload, onChange: handleChange, onDownload: handleDownload, ...rest, }; return ( {[DageUploadType.MODEL, DageUploadType.MOBILE_MODEL].includes(dType) ? ( 将文件拖到此处,或点击上传 {tips} ) : ( <> {isPictureCard ? ( !disabled && (!value || value.length < maxCount) && ) : ( )} {!!tips && ( {tips} )} )} example ); }; export * from "./types"; export * from "./context";