zy-react-library/components/Upload/index.js

276 lines
6.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import { PlusOutlined, UploadOutlined, VideoCameraAddOutlined } from "@ant-design/icons";
import { Upload as AntUpload, Button, message, Modal } from "antd";
import { useState } from "react";
/**
* 文件上传组件
*/
const Upload = (props) => {
const {
value = [],
onChange,
onPreview,
onRemove,
onGetRemoveFile,
beforeUpload,
maxCount: externalMaxCount,
listType: externalListType,
accept: externalAccept,
ratio = "",
showTip = true,
multiple = true,
size: externalSize,
tipContent,
uploadButtonText: externalUploadButtonText,
fileType: externalFileType,
...restProps
} = props;
const [previewVisible, setPreviewVisible] = useState(false);
const [previewImage, setPreviewImage] = useState("");
// 预设的文件格式
const imageAccept = ".jpg,.jpeg,.png";
const documentAccept = ".pdf,.doc,.docx";
const videoAccept = ".mp4";
// 根据accept自动判断文件类型
const getAutoFileType = () => {
if (externalAccept) {
if (externalAccept === "*")
return "document";
const acceptList = externalAccept.split(",");
if (acceptList.some(format => videoAccept.split(",").includes(format)))
return "video";
if (acceptList.some(format => documentAccept.split(",").includes(format)))
return "document";
if (acceptList.some(format => imageAccept.split(",").includes(format)))
return "image";
return "document";
}
return "image";
};
const fileType = externalFileType || getAutoFileType();
// 文件类型判断
const isImageType = fileType === "image";
const isVideoType = fileType === "video";
const isDocumentType = fileType === "document";
// 获取listType
const getListType = () => {
if (externalListType)
return externalListType;
if (externalAccept === "*")
return "text";
if (fileType === "image")
return "picture-card";
return "text";
};
const listType = getListType();
// 获取文件格式
const getAccept = () => {
if (externalAccept)
return externalAccept === "*" ? "" : externalAccept;
if (isImageType)
return imageAccept;
if (isVideoType)
return videoAccept;
if (isDocumentType)
return documentAccept;
return imageAccept;
};
const accept = getAccept();
// 获取默认上传数量
const getMaxCount = () => {
if (externalMaxCount)
return externalMaxCount;
if (isVideoType)
return 1;
if (isImageType)
return 4;
if (isDocumentType)
return 4;
return 1;
};
const maxCount = getMaxCount();
// 获取默认文件大小
const getSize = () => {
if (externalSize)
return externalSize;
if (isVideoType)
return 100;
return 0;
};
const size = getSize();
// 上传按钮文字
const uploadButtonText = externalUploadButtonText || (isVideoType ? "上传视频" : "上传附件");
// 文件格式提示
const acceptTip = accept.replace(/\./g, "").split(",").join("、");
// 生成提示信息
const getTipText = () => {
if (tipContent)
return tipContent;
const tips = [
`最多上传${maxCount}个文件`,
accept
? `并且只能上传${acceptTip}格式的文件`
: "可以上传任意格式的文件",
size ? `文件大小不能超过${size}M` : "",
ratio ? `只能上传${ratio}分辨率的图片` : "",
].filter(Boolean);
return `${tips.join("")}`;
};
const handleBeforeUpload = (file, fileList) => {
if (beforeUpload)
return beforeUpload(file, fileList)
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(`只能上传${acceptTip}格式的文件`);
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 handleRemove = (file) => {
if (file.originFileObj)
onGetRemoveFile?.(file);
return onRemove?.(file);
}
// 预览文件
const handlePreview = (file) => {
if (isImageType) {
setPreviewImage(file.url || file.thumbUrl);
setPreviewVisible(true);
}
onPreview?.(file);
};
// 关闭预览
const handleCancel = () => {
setPreviewVisible(false);
};
// 上传按钮
const uploadButton
= isImageType
? (
<div>
<PlusOutlined style={{ fontSize: 32 }} />
</div>
)
: (
<Button
type="primary"
icon={isVideoType ? <VideoCameraAddOutlined /> : <UploadOutlined />}
>
{uploadButtonText}
</Button>
);
return (
<>
<AntUpload
fileList={value}
multiple={multiple}
maxCount={maxCount}
listType={listType}
accept={accept}
onChange={handleChange}
onPreview={handlePreview}
onRemove={handleRemove}
beforeUpload={handleBeforeUpload}
{...restProps}
>
{value.length >= maxCount ? null : uploadButton}
</AntUpload>
{
showTip
? (getTipText()) && (
<div style={{ marginTop: 10, color: "#ff4d4f" }}>
{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;