177 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
			
		
		
	
	
			177 lines
		
	
	
		
			4.5 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 = "picture-card",
 | 
						||
    accept = ["picture-card", "picture-circle", "picture"].includes(listType) ? ".jpg,.jpeg,.png" : "",
 | 
						||
    ratio = "",
 | 
						||
    showTip = true,
 | 
						||
    multiple = true,
 | 
						||
    size = 0,
 | 
						||
    tipContent,
 | 
						||
    uploadButtonText = "点击选择文件上传",
 | 
						||
    formValues,
 | 
						||
    ...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 validateImageResolution = (imageUrl) => {
 | 
						||
        const img = new Image();
 | 
						||
        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);
 | 
						||
        };
 | 
						||
        img.src = imageUrl;
 | 
						||
      };
 | 
						||
 | 
						||
      // 如果有现成的URL则直接使用,否则使用FileReader读取本地文件
 | 
						||
      if (file.url) {
 | 
						||
        validateImageResolution(file.url);
 | 
						||
      }
 | 
						||
      else {
 | 
						||
        const reader = new FileReader();
 | 
						||
        reader.onload = (e) => {
 | 
						||
          validateImageResolution(e.target.result);
 | 
						||
        };
 | 
						||
        reader.readAsDataURL(file);
 | 
						||
      }
 | 
						||
    }
 | 
						||
    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;
 |