相关参考
- FormData:https://developer.mozilla.org/zh-CN/docs/Web/API/FormData
- FileReader:https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader
前言
antdesign的upload组件:https://ant.design/components/upload-cn/#components-upload-demo-basic
需要一个接口直接传给服务端,针对一些简单的场景可以使用,但如果是一个交互多点场景,可能有点不适合,所以下面进入正文视觉稿
代码实现
具体交互细节以实际交互为准
import { useState, useEffect } from "react";
import { Modal, Button, message } from "antd";
import { LoadingOutlined, PaperClipOutlined } from "@ant-design/icons";
import { UploadFile } from "../components";
import * as PartnerAction from "../api/request/partner";
import "@css/components/upload-partner.less";
export default ({ fetchList }: any) => {
const [visible, setVisible] = useState<boolean>(false);
const [btnLoading, setBtnLoading] = useState<boolean>(false);
const [uploadDisabled, setUploadDisabled] = useState<boolean>(true);
const [uploadFileInfo, setUploadFileInfo] = useState<any>({});
// 确认上传
const onFinish = async () => {
try {
const formData = new FormData();
formData.append("file", uploadFileInfo.file);
setBtnLoading(true);
const { success }: any = await PartnerAction.uploadFile(formData);
setBtnLoading(false);
if (!success) return;
await fetchList();
setVisible(false);
message.success("导入成功");
onUploadFileChange({
status: "none",
});
} catch (error) {
console.log(error);
}
};
// 【X】关闭按钮
const onModalClose = (): void => {
if (btnLoading) return;
setUploadDisabled(true);
setVisible(false);
setUploadFileInfo({
status: "none",
});
};
const onUploadFileChange = (item: any) => {
setUploadFileInfo(item);
};
useEffect(() => {
if (
uploadFileInfo.status === "none" ||
uploadFileInfo.status === "progress"
) {
setUploadDisabled(true);
}
if (uploadFileInfo.status === "done") {
setUploadDisabled(false);
}
}, [uploadFileInfo]);
return (
<>
<Button onClick={() => setVisible(true)}>文件上传</Button>
<Modal
title="文件上传"
centered
visible={visible}
maskClosable={false}
keyboard={false}
onCancel={onModalClose}
okButtonProps={{ disabled: uploadDisabled, loading: btnLoading }}
cancelButtonProps={{ disabled: uploadDisabled }}
onOk={onFinish}
>
<UploadFile
uploadDisabled={btnLoading}
uploadData={uploadFileInfo}
onUploadFileChange={onUploadFileChange}
/>
<>
{uploadFileInfo.status === "none" ? (
<span className="file_prefix">支持扩展名:.xls .xlsx</span>
) : (
<p className="upload_file">
{uploadFileInfo.status === "progress" && (
<LoadingOutlined className="upload_icon" />
)}
{uploadFileInfo.status === "done" && (
<PaperClipOutlined className="upload_icon" />
)}
{(uploadFileInfo.status === "done" ||
uploadFileInfo.status === "progress") &&
`${uploadFileInfo?.file?.name}`}
</p>
)}
</>
</Modal>
</>
);
};
import { useState, useEffect, useRef } from "react";
import { UploadOutlined } from "@ant-design/icons";
import { Button, message } from "antd";
import { beforeUploadFile } from "../utils";
import "@css/components/upload-file.less";
export default ({ uploadDisabled = false, onUploadFileChange }: any) => {
const uploadFileRef = useRef<any>({});
const [disabled, setDisabled] = useState<boolean>(uploadDisabled);
const [fileData, setFileData] = useState<any>({
status: "none",
});
const onUploadFile = (e: any) => {
const file = e.target.files[0];
if (!beforeUploadFile(file)) return;
const fileReader: any = new FileReader();
fileReader.onload = () => {
const data = {
...fileData,
file,
status: "done",
};
setFileData(data);
setDisabled(false);
onUploadFileChange(data);
};
fileReader.onerror = () => {
message.error("文件失败,请重新上传:", fileReader.error);
onUploadFileChange({
status: "none",
});
setDisabled(false);
};
fileReader.onprogress = () => {
setFileData({
status: "progress",
});
onUploadFileChange({
status: "progress",
});
setDisabled(true);
};
fileReader.readAsArrayBuffer(file);
};
const onUploadClick = () => {
uploadFileRef.current.click();
};
// 重置input的value为空
/**
* faq:使用input[type=file] 实现文件上传功能,通过onchange事件触发js代码,这个时候第一次上传是完全没问题的,当你第二次上传文件时,如果是不同于上一次上传文件的话是可以正常上传的,不过如果你选择的还是上一个文件,也就是两次上传的文件重复了,那么就会上传失败。
* input是通过onchange事件来触发js代码的,由于两次文件是重复的,所以这个时候onchange事件是没有触发到的。
* how:读取文件后,记得把input的value重新设置为空即e.target.value=''
*/
const onResetInputValue = (e: any) => {
(e.target as HTMLInputElement).value = "";
};
useEffect(() => {
onUploadFileChange({
status: "none",
});
}, []);
return (
<>
<div className="upload_container">
<Button disabled={disabled} onClick={onUploadClick}>
<UploadOutlined />
选择文件
</Button>
<input
ref={uploadFileRef}
type="file"
onClick={(e) => onResetInputValue(e)}
className="upload_input"
onChange={onUploadFile}
/>
</div>
</>
);
};
export const EXCEL_SUFFIX_REG = /(xls|xlsx)(\?.*)?/;
// 文件上传校验
export const beforeUploadFile = (file: any): boolean | undefined => {
const temp = file?.name?.split(".");
const suffix = temp[temp.length - 1].toLowerCase();
if (!EXCEL_SUFFIX_REG.test(suffix)) {
message.warning("仅支持导入.xls .xlsx扩展名文件");
return false;
}
return true;
};