123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 |
- 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<string> =>
- 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<DageUploadProps> = ({
- 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<DageFileAPIResponseType>(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) => (
- <DageUploadItemActions
- isPictureCard={isPictureCard}
- disabled={disabled}
- file={file}
- actions={actions}
- >
- {node}
- </DageUploadItemActions>
- ),
- showUploadList: {
- showPreviewIcon: false,
- showDownloadIcon: false,
- showRemoveIcon: false,
- },
- multiple: maxCount > 1,
- onPreview: handlePreview,
- beforeUpload: beforeUpload,
- onChange: handleChange,
- onDownload: handleDownload,
- ...rest,
- };
- return (
- <UploadContainer>
- {[DageUploadType.MODEL, DageUploadType.MOBILE_MODEL].includes(dType) ? (
- <AntdDragger style={{ padding: "0 24px", width: "320px" }} {...props}>
- <CloudUploadOutlined style={{ fontSize: "40px", color: "#1677ff" }} />
- <AntdDraggerText>
- 将文件拖到此处,或<span>点击上传</span>
- </AntdDraggerText>
- <UploadTips className="dage-upload__tips">{tips}</UploadTips>
- </AntdDragger>
- ) : (
- <>
- <AntdUpload {...props}>
- {isPictureCard ? (
- !disabled &&
- (!value || value.length < maxCount) && <PlusOutlined />
- ) : (
- <Button disabled={disabled} icon={<UploadOutlined />}>
- 上传
- </Button>
- )}
- </AntdUpload>
- {!!tips && (
- <UploadTips
- style={{ marginTop: isPictureCard ? 0 : "8px" }}
- className="dage-upload__tips"
- >
- {tips}
- </UploadTips>
- )}
- </>
- )}
- <Modal
- open={previewOpen}
- title={previewTitle}
- footer={null}
- onCancel={handleCancel}
- >
- <img alt="example" style={{ width: "100%" }} src={previewImage} />
- </Modal>
- </UploadContainer>
- );
- };
- export * from "./types";
- export * from "./context";
|