Antd中Upload组件封装及使用:

1.Upload上传组件功能:

  •   文件校验 :

        文件格式校验/文件大小校验/上传文件总个数校验

  •   相关功能 :

         拖拽功能/上传到远程(七牛)/文件删除及下载

2.组件效果展示:

3.疑难点及解决方案:

  Promise.all多文件并行上传到远程(七牛云):

 (1)在beforeUpload钩子函数中获取token

 (2)循环fileList文件列表,使用fetch将所有文件上传到七牛,并将结果包装成Promise return出去

 (3)待所有文件上传成功后,通过Promise.all获取并存储结果,并通过useEffect及时将七牛返回的结果添加到fileList文件列表中。

 (4)注:由于一次上传多个文件时,beforeUpload钩子函数会执行多次,需要使用debounce进行防抖。

const [promiseAllResult, setPromiseAllResult] = useState<UploadFile[]>([]);

const beforeUpload: UploadProps["beforeUpload"] = (file, fileList) => {
		debouncedBeforeUpload(fileList);
		return false;
};

const debouncedBeforeUpload = debounce(async fileList => {
		...
		const res = await getQiniuTokenApi();
		const uploadPromises = fileList.map(async (file: any) => {
				return new Promise((resolve, reject) => {
					const formData = new FormData();
					formData.append("file", file);
					formData.append("token", res.data?.upToken || "");

					fetch("https://upload.qiniup.com/", {
						method: "POST",
						body: formData
					})
						.then(response => {
							if (response.status === 200) {
								return response
									.json()
									.then(data => {
										// 返回包含文件信息和响应数据的对象
										resolve({
											uid: file.uid,
											url: "https://" + res.data?.domain + "/" + data.key + "?attname=" + file.name,
											filePreviewUrl: "https://" + res.data?.domain + "/" + data.key
										});
									})
									.catch(() => {
										reject(new Error("Upload failed"));
									});
							} else {
								reject(new Error("Upload failed"));
							}
						})
						.catch(error => reject(error));
				});
		});
		Promise.all(uploadPromises)
				.then(res => {
					setPromiseAllResult(res);
					message.success("文件上传成功");
				})
				.catch(() => message.error("文件上传失败"));
});

useEffect(() => {
		if (promiseAllResult && promiseAllResult.length > 0 && fileList && fileList.length > 0) {
				fileList.forEach(item => {
					const findResult = promiseAllResult.find(file => file.uid === item.uid);
					if (findResult) {
						// @ts-ignore
						item.filePreviewUrl = findResult.filePreviewUrl;
						item.url = findResult.url;
					}
				});
		}
}, [promiseAllResult]);

4.完整代码:

  • 封装文件上传组件:

      src/component/Upload/index.tsx:

import { forwardRef, useImperativeHandle, useState, useEffect } from "react";
import { Upload, message } from "antd";
import { InboxOutlined } from "@ant-design/icons";
import type { UploadFile, UploadProps } from "antd";
import { getQiniuTokenApi } from "@/api/modules/assetManagement";
import { debounce } from "lodash";
const { Dragger } = Upload;
interface UploadComType {
	maxCount?: number;
	accept?: string[];
	size?: number;
	multiple?: boolean;
}
const UploadCom = forwardRef(
	(
		{
			maxCount = 3,
			accept = [
				".doc",
				".docx",
				".xml",
				"application/msword",
				"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
				"application/pdf",
				"image/png",
				"image/jpeg"
			],
			size = 2,
			multiple = true
		}: UploadComType,
		ref: any
	) => {
		const [fileList, setFileList] = useState<UploadFile[]>([]);
		const [promiseAllResult, setPromiseAllResult] = useState<UploadFile[]>([]);
		useEffect(() => {
			if (promiseAllResult && promiseAllResult.length > 0 && fileList && fileList.length > 0) {
				fileList.forEach(item => {
					const findResult = promiseAllResult.find(file => file.uid === item.uid);
					if (findResult) {
						// @ts-ignore
						item.filePreviewUrl = findResult.filePreviewUrl;
						item.url = findResult.url;
					}
				});
			}
		}, [promiseAllResult]);
		useImperativeHandle(ref, () => ({
			getFileList: () => {
				return fileList;
			},
			parentSetList: (list: UploadFile[]) => {
				setFileList(list);
			}
		}));
		const onRemove = (file: UploadFile) => {
			const list = fileList.filter(item => item.uid !== file.uid);
			setFileList(list);
		};
		const beforeUpload: UploadProps["beforeUpload"] = (file, fileList) => {
			debouncedBeforeUpload(fileList);
			return false;
		};
		const debouncedBeforeUpload = debounce(async fileList => {
			let newFileList = fileList.filter((file: any) => {
				// 上传中的文件不进行校验
				if (file.status === "uploading") return true;

				// 校验文件类型
				const isFileTypeValid = accept.includes(file.type || "");
				if (!isFileTypeValid) {
					message.error(`${file.name} 不是允许的文件类型`);
					return false;
				}
				// 校验文件大小
				const isFileSizeValid = (file.size || 0) <= size * 1024 * 1024;
				if (!isFileSizeValid) {
					message.error(`${file.name} 超过最大文件大小限制 (${size}MB)`);
					return false;
				}
				return true;
			});
			if (newFileList.length === 0) {
				message.warning("没有符合要求的文件可上传");
				return false;
			}

			const res = await getQiniuTokenApi();
			const uploadPromises = newFileList.map(async (file: any) => {
				return new Promise((resolve, reject) => {
					const formData = new FormData();
					formData.append("file", file);
					formData.append("token", res.data?.upToken || "");

					fetch("https://upload.qiniup.com/", {
						method: "POST",
						body: formData
					})
						.then(response => {
							if (response.status === 200) {
								return response
									.json()
									.then(data => {
										// 返回包含文件信息和响应数据的对象
										resolve({
											name: file.name,
											size: file.size,
											uid: file.uid,
											type: file.type,
											status: file.status,
											url: "https://" + res.data?.domain + "/" + data.key + "?attname=" + file.name,
											filePreviewUrl: "https://" + res.data?.domain + "/" + data.key
										});
									})
									.catch(() => {
										reject(new Error("Upload failed"));
									});
							} else {
								reject(new Error("Upload failed"));
							}
						})
						.catch(error => reject(error));
				});
			});
			Promise.all(uploadPromises)
				.then(res => {
					setPromiseAllResult(res);
					message.success("文件上传成功");
				})
				.catch(() => message.error("文件上传失败"));
		});
		const handleChange: UploadProps["onChange"] = ({ fileList }) => {
			let newFileList = fileList;
			if (newFileList.length > maxCount) {
				message.warning(`最多可上传${maxCount}个文件`);
				newFileList = newFileList.slice(-maxCount);
			}
			setFileList(newFileList);
		};

		const uploadProps: UploadProps = {
			name: "file",
			onRemove,
			beforeUpload: beforeUpload,
			multiple: multiple,
			onChange: handleChange,
			accept: accept.join(",")
		};

		return (
			<div>
				<Dragger {...uploadProps} fileList={fileList}>
					<p className="ant-upload-drag-icon">
						<InboxOutlined />
					</p>
					<p className="ant-upload-text">Click or drag file to this area to upload</p>
					<p className="ant-upload-hint">
						Support for a single or bulk upload. Strictly prohibited from uploading company data or other banned files.
					</p>
				</Dragger>
			</div>
		);
	}
);
export default UploadCom;
  • 使用文件上传组件:
import UploadCom from "@/components/Upload/index";

const uploadComRef = useRef<any>(null);

<UploadCom ref={uploadComRef} />

//获取组件中的文件
const file = uploadComRef.current?.getFileList();

//给组件中的文件赋初始值
uploadComRef.current?.parentSetList(files);

antd 是一个基于 Ant Design 设计语言的 React UI 组件库,它提供了一套丰富的组件来帮助开发者快速构建高质量的 Web 应用。如果你想改写 antd 中的 Upload 组件的样式,可以通过以下几种方法实现: 1. **使用 className 或者 classNamePrefix:** antd组件都支持通过 className 属性添加自定义的样式类,或者使用 classNamePrefix 来为内部所有样式类添加一个前缀,从而实现样式的覆盖。例如: ```jsx <Upload className="custom-upload" // 或者 // classNamePrefix="custom-upload" > <Button>点击上传</Button> </Upload> ``` 然后在你的 CSS 文件中: ```css .custom-upload .ant-upload { /* 自定义上传区域的样式 */ } ``` 2. **使用 CSS-in-JS:** 如果你使用 CSS-in-JS 的方式来编写样式,可以直接在组件使用样式对象来定义样式。例如使用 styled-components 或 emotion 这类库: ```jsx import styled from 'styled-components'; const StyledUpload = styled(Upload)` .ant-upload { /* 自定义样式 */ } `; <StyledUpload> <Button>点击上传</Button> </StyledUpload> ``` 3. **修改 Less 变量:** antd 默认使用 Less 作为样式预处理器,它提供了一套默认的主题变量。你可以通过覆写这些变量来全局改变组件的样式。具体方法是创建一个自定义的主题样式文件,并通过配置导入它: ```less // custom-antd.less @import '~antd/dist/antd.less'; // 引入 antd 默认样式 // 自定义 Upload 样式 .ant-upload { .ant-upload-select { /* 自定义上传按钮的样式 */ } } ``` 然后在你的应用中配置: ```jsx // 引入自定义样式文件 import './custom-antd.less'; ``` 4. **直接修改组件内部 CSS:** 直接通过 CSS 选择器找到 Upload 组件的内部元素并进行样式覆盖。这种方法虽然直接,但不推荐使用,因为它违背了封装原则,可能会在未来的版本中因组件内部实现的改变而导致样式失效。 ```css .ant-upload.ant-upload-select { /* 自定义上传按钮的样式 */ } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值