162 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
		
			
		
	
	
			162 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
|  | import { PlusOutlined } from "@ant-design/icons"; | |||
|  | import { Upload as AntUpload, Button, message, Modal } from "antd"; | |||
|  | import { useState } from "react"; | |||
|  | 
 | |||
|  | /** | |||
|  |  * 文件上传组件 | |||
|  |  */ | |||
|  | const Upload = (props) => { | |||
|  |   const { | |||
|  |     value = [], | |||
|  |     onChange, | |||
|  |     onPreview, | |||
|  |     maxCount = 1, | |||
|  |     listType = "text", | |||
|  |     accept = "", | |||
|  |     ratio = "", | |||
|  |     showTip = true, | |||
|  |     multiple = true, | |||
|  |     size = 0, | |||
|  |     tipContent, | |||
|  |     uploadButtonText = "点击选择文件上传", | |||
|  |     ...restProps | |||
|  |   } = props; | |||
|  | 
 | |||
|  |   const [previewVisible, setPreviewVisible] = useState(false); | |||
|  |   const [previewImage, setPreviewImage] = useState(""); | |||
|  | 
 | |||
|  |   // 生成提示信息
 | |||
|  |   const getTipText = () => { | |||
|  |     if (tipContent) | |||
|  |       return tipContent; | |||
|  | 
 | |||
|  |     const tips = [ | |||
|  |       `最多上传${maxCount}个文件`, | |||
|  |       accept | |||
|  |         ? `并且只能上传${accept | |||
|  |           .replace(/\./g, "") | |||
|  |           .split(",") | |||
|  |           .join("、")}格式的文件`
 | |||
|  |         : "可以上传任意格式的文件", | |||
|  |       size ? `文件大小不能超过${size}M` : "", | |||
|  |       ratio ? `只能上传${ratio}分辨率的图片` : "", | |||
|  |     ].filter(Boolean); | |||
|  | 
 | |||
|  |     return `${tips.join(",")}。`; | |||
|  |   }; | |||
|  | 
 | |||
|  |   const handleBeforeUpload = () => { | |||
|  |     return false; | |||
|  |   }; | |||
|  | 
 | |||
|  |   // 文件状态改变
 | |||
|  |   const handleChange = ({ file, fileList }) => { | |||
|  |     const acceptList = accept ? accept.split(",") : []; | |||
|  |     const ratioArr = ratio ? ratio.split("*") : []; | |||
|  |     const suffix = file.name.substring( | |||
|  |       file.name.lastIndexOf("."), | |||
|  |       file.name.length, | |||
|  |     ); | |||
|  |     const maxSize = size * 1024 * 1024; | |||
|  | 
 | |||
|  |     // 验证文件格式
 | |||
|  |     if (acceptList.length > 0 && !acceptList.includes(suffix)) { | |||
|  |       message.warning(`只能上传${accept}格式的文件`); | |||
|  |       return; | |||
|  |     } | |||
|  | 
 | |||
|  |     // 验证文件大小
 | |||
|  |     if (maxSize && file.size > maxSize) { | |||
|  |       message.warning(`文件大小不能超过${size}M`); | |||
|  |       return; | |||
|  |     } | |||
|  | 
 | |||
|  |     // 验证图片分辨率
 | |||
|  |     if (ratioArr.length === 2 && file.type?.startsWith("image/")) { | |||
|  |       const img = new Image(); | |||
|  |       img.src = file.url || file.thumbUrl; | |||
|  |       img.onload = () => { | |||
|  |         if (img.width !== +ratioArr[0] || img.height !== +ratioArr[1]) { | |||
|  |           message.warning(`只能上传${ratio}分辨率的图片`); | |||
|  |           const filtered = fileList.filter(item => item.uid !== file.uid); | |||
|  |           onChange?.(filtered); | |||
|  |           return; | |||
|  |         } | |||
|  |         onChange?.(fileList); | |||
|  |       }; | |||
|  |     } | |||
|  |     else { | |||
|  |       onChange?.(fileList); | |||
|  |     } | |||
|  |   }; | |||
|  | 
 | |||
|  |   // 预览文件
 | |||
|  |   const handlePreview = (file) => { | |||
|  |     if (["picture-card", "picture-circle", "picture"].includes(listType)) { | |||
|  |       setPreviewImage(file.url || file.thumbUrl); | |||
|  |       setPreviewVisible(true); | |||
|  |     } | |||
|  |     onPreview?.(file); | |||
|  |   }; | |||
|  | 
 | |||
|  |   // 关闭预览
 | |||
|  |   const handleCancel = () => { | |||
|  |     setPreviewVisible(false); | |||
|  |   }; | |||
|  | 
 | |||
|  |   // 上传按钮
 | |||
|  |   const uploadButton | |||
|  |     = ["picture-card", "picture-circle", "picture"].includes(listType) | |||
|  |       ? ( | |||
|  |           <div> | |||
|  |             <PlusOutlined style={{ fontSize: 32 }} /> | |||
|  |           </div> | |||
|  |         ) | |||
|  |       : ( | |||
|  |           <Button type="primary">{uploadButtonText}</Button> | |||
|  |         ); | |||
|  | 
 | |||
|  |   return ( | |||
|  |     <> | |||
|  |       <AntUpload | |||
|  |         fileList={value} | |||
|  |         multiple={multiple} | |||
|  |         maxCount={maxCount} | |||
|  |         listType={listType} | |||
|  |         accept={accept} | |||
|  |         onChange={handleChange} | |||
|  |         onPreview={handlePreview} | |||
|  |         beforeUpload={handleBeforeUpload} | |||
|  |         {...restProps} | |||
|  |       > | |||
|  |         {value.length >= maxCount ? null : uploadButton} | |||
|  |       </AntUpload> | |||
|  |       { | |||
|  |         showTip | |||
|  |           ? (tipContent || getTipText()) && ( | |||
|  |               <div style={{ marginTop: 10, color: "#ff4d4f" }}> | |||
|  |                 {tipContent || getTipText()} | |||
|  |               </div> | |||
|  |             ) | |||
|  |           : null | |||
|  |       } | |||
|  |       <Modal | |||
|  |         open={previewVisible} | |||
|  |         title="查看图片" | |||
|  |         footer={null} | |||
|  |         onCancel={handleCancel} | |||
|  |       > | |||
|  |         <img | |||
|  |           alt="preview" | |||
|  |           style={{ width: "100%", objectFit: "scale-down" }} | |||
|  |           src={previewImage} | |||
|  |         /> | |||
|  |       </Modal> | |||
|  |     </> | |||
|  |   ); | |||
|  | }; | |||
|  | 
 | |||
|  | Upload.displayName = "Upload"; | |||
|  | 
 | |||
|  | export default Upload; |