index.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import { Button, Modal, Upload, message } from "antd";
  2. import { PlusOutlined } from "@ant-design/icons";
  3. import { FC, useMemo, useRef, useState } from "react";
  4. import { RcFile, UploadFile, UploadProps } from "antd/es/upload";
  5. import { getTokenInfo } from "@/utils";
  6. import { DageUploadProps, DageUploadType } from "./types";
  7. import { UploadOutlined } from "@ant-design/icons";
  8. import "./index.scss";
  9. const getBase64 = (file: RcFile): Promise<string> =>
  10. new Promise((resolve, reject) => {
  11. const reader = new FileReader();
  12. reader.readAsDataURL(file);
  13. reader.onload = () => resolve(reader.result as string);
  14. reader.onerror = (error) => reject(error);
  15. });
  16. export const DageUpload: FC<DageUploadProps> = ({
  17. action,
  18. value,
  19. type = DageUploadType.IMG,
  20. maxCount = 9,
  21. maxSize = 5,
  22. tips,
  23. disabled,
  24. onUploaded,
  25. onUploading,
  26. onChange,
  27. }) => {
  28. const [previewOpen, setPreviewOpen] = useState(false);
  29. const [previewImage, setPreviewImage] = useState("");
  30. const [previewTitle, setPreviewTitle] = useState("");
  31. const [uploading, setUploading] = useState(false);
  32. const uploadListType = useMemo(() => {
  33. switch (type) {
  34. case DageUploadType.IMG:
  35. return "picture-card";
  36. default:
  37. return "text";
  38. }
  39. }, [type]);
  40. const isPictureCard = uploadListType === "picture-card";
  41. const uploadingFileNum = useRef(0);
  42. const beforeUpload = (file: RcFile) => {
  43. let pass = false;
  44. let passFileType = false;
  45. // 校验文件类型
  46. switch (type) {
  47. case DageUploadType.IMG:
  48. passFileType = ["image/jpeg", "image/png", "image/gif"].includes(
  49. file.type
  50. );
  51. if (!passFileType) {
  52. message.error("只支持png、jpg、gif和jpeg格式!");
  53. }
  54. break;
  55. case DageUploadType.MODEL:
  56. passFileType = file.name.indexOf(".4dage") > -1;
  57. if (!passFileType) {
  58. message.error("只支持4dage格式的模型文件!");
  59. }
  60. break;
  61. case DageUploadType.VIDEO:
  62. passFileType = ["video/mp4"].includes(file.type);
  63. if (!passFileType) {
  64. message.error("只支持mp4格式!");
  65. }
  66. break;
  67. case DageUploadType.AUDIO:
  68. passFileType = ["audio/mpeg"].includes(file.type);
  69. if (!passFileType) {
  70. message.error("只支持mp3格式!");
  71. }
  72. break;
  73. }
  74. // 校验文件大小
  75. const isLtM = file.size / 1024 / 1024 < maxSize;
  76. if (!isLtM) {
  77. message.error(`最大支持 ${maxSize}M!`);
  78. }
  79. uploadingFileNum.current += 1;
  80. pass = passFileType && isLtM;
  81. return pass ? pass : Upload.LIST_IGNORE;
  82. };
  83. const handleChange: UploadProps["onChange"] = ({
  84. fileList: newFileList,
  85. file,
  86. }) => {
  87. if (file.status === "uploading") {
  88. setUploading(true);
  89. onUploading?.();
  90. }
  91. if (file.status === "done") {
  92. uploadingFileNum.current -= 1;
  93. if (uploading && !uploadingFileNum.current) {
  94. setUploading(false);
  95. onUploaded?.();
  96. }
  97. }
  98. onChange?.(
  99. newFileList.map((i) => ({
  100. ...i,
  101. dType: type,
  102. }))
  103. );
  104. };
  105. const handleCancel = () => setPreviewOpen(false);
  106. const handlePreview = async (file: UploadFile) => {
  107. if (!isPictureCard) return;
  108. if (!file.url && !file.preview) {
  109. file.preview = await getBase64(file.originFileObj as RcFile);
  110. }
  111. setPreviewImage(file.url || (file.preview as string));
  112. setPreviewOpen(true);
  113. setPreviewTitle(
  114. file.name || file.url!.substring(file.url!.lastIndexOf("/") + 1)
  115. );
  116. };
  117. return (
  118. <div className="dage-upload">
  119. <Upload
  120. disabled={disabled}
  121. headers={{
  122. token: getTokenInfo().token,
  123. }}
  124. fileList={value}
  125. withCredentials
  126. action={process.env.REACT_APP_BACKEND_URL + action}
  127. name="file"
  128. listType={uploadListType}
  129. data={{
  130. type,
  131. }}
  132. maxCount={maxCount}
  133. showUploadList={{
  134. showDownloadIcon: true,
  135. }}
  136. multiple={maxCount > 1}
  137. onPreview={handlePreview}
  138. beforeUpload={beforeUpload}
  139. onChange={handleChange}
  140. >
  141. {!disabled &&
  142. (isPictureCard ? (
  143. (!value || value.length < maxCount) && <PlusOutlined />
  144. ) : (
  145. <Button icon={<UploadOutlined />}>上传</Button>
  146. ))}
  147. </Upload>
  148. {!!tips && (
  149. <p
  150. style={{ marginTop: isPictureCard ? 0 : "8px" }}
  151. className="dage-upload__tips"
  152. >
  153. {tips}
  154. </p>
  155. )}
  156. <Modal
  157. open={previewOpen}
  158. title={previewTitle}
  159. footer={null}
  160. onCancel={handleCancel}
  161. >
  162. <img alt="example" style={{ width: "100%" }} src={previewImage} />
  163. </Modal>
  164. </div>
  165. );
  166. };
  167. export * from "./types";