1、引入方式
import BatchImport from '@/components/Modal/batchImport';
<Button type="primary" onClick={batchImport}>
批量导入
</Button>
const batchImport = () => {
BatchImport.show({
datatype: dataName,
taskId: taskId,
projectName: detailsData?.projectName,
projectCode: projectCode,
isAlone: 'false',
sceneType: 2,
processId: isNopass === 'true' ? processId : '',
relationFlag: isNopass === 'true' ? true : false,
onOk(resData) {
// console.log('【resData】:', resData);
// 这里是弹窗组件method过来的值
},
title: '批量导入' + dataName + '数据',
});
};
2、import弹窗组件内容
import React, { useState, useEffect, useRef, NamedExoticComponent } from 'react';
import { Modal, Button, Upload, Radio, message, Progress, Col } from 'antd';
import env from '@/utils/env';
import axios from 'axios';
import exportExcel from '@/models/excel';
import publics from '@/utils/public';
import { onloadExcel } from '@/utils/exportExcel';
import Icons from '@/components/SvgIcon';
import _ from 'lodash';
import BatchImportApi from '@/services/batchImport';
import { taskEntityNameFunc, radioOptions } from '@/pages/TaskManage/fastDelivery/components/fastStatusType';
interface Payload {
onOk?: (l: any[], m: string) => void;
onCancel?: () => void;
[others: string]: any;
}
interface IProps extends NamedExoticComponent {
show: (payload: Payload) => void;
}
const { Dragger } = Upload;
const FastBatchImport: IProps = React.memo((props) => {
const [visible, setVisible] = useState(false);
const payloadRef = useRef<Payload>({});
const [value, setValue] = useState('2');
const [file, setFile] = useState('');
const [fileName, setFileName] = useState('');
const [imgColor, setImgColor] = useState(false);
const [progressLine, setProgressLine] = useState(1);
const [percent, setPercent] = useState(0);
const [fastImportTypes, setFastImportTypes] = useState<any[]>([]);
const [errorData, setErrorData] = useState<any[]>([]);
const [importNumber, setImportNumber] = useState('');
useEffect(() => {
const lastShow = FastBatchImport.show;
FastBatchImport.show = (payload: Payload) => {
sessionStorage.removeItem('batchNumber2');
setFastImportTypes([]);
setVisible(true);
setValue('2');
payloadRef.current = payload;
};
return () => {
FastBatchImport.show = lastShow;
};
}, [visible]);
const wrapWithClose = (method?: (l: any[]) => void) => async () => {
let arr: any[] = [];
method?.(arr);
setVisible(false);
setProgressLine(1);
setImgColor(false);
setFileName('');
setPercent(0);
};
const onCancel = () => {
setVisible(false);
setProgressLine(1);
setImgColor(false);
setFileName('');
setPercent(0);
sessionStorage.removeItem('batchNumber2');
};
const onloadExcelData = () => {
let name = '';
name = `批量导入${taskEntityNameFunc(value)}新增模板.xlsx`;
let url = '';
url = env.VITE_APP_ROOT + '/authorization/api/template/download?inFileName=' + name;
onloadExcel({ url, method: 'get', name, params: '', callbacks: () => {} });
};
const onChange = (e: any) => {
setValue(e.target.value);
setImgColor(false);
setFileName('');
setFile('');
setProgressLine(1);
};
function handleImport(e: any) {
setPercent(0);
e.stopPropagation();
// e.nativeEvent.stopImmediatePropagation();
let str = 0;
let ds_percent = setInterval(() => {
if (str < 99) {
str = str + 1;
setPercent(str + 1);
} else {
setPercent(100);
clearInterval(ds_percent);
}
}, 1500);
setProgressLine(2);
const fd = new FormData();
fd.append('file', file);
fd.append('type', value);
fd.append('optType', '1');
fd.append('flag', true);
fd.append('sceneType', payloadRef.current.sceneType);
if (sessionStorage.getItem('batchNumber2')) {
fd.append('processId', sessionStorage.getItem('batchNumber2') || '');
} else {
if (payloadRef.current.processId) {
fd.append('processId', payloadRef.current.processId);
fd.append('relationFlag', true);
}
}
fd.append('taskId', payloadRef.current.taskId);
fd.append('projectName', payloadRef.current.projectName);
fd.append('projectCode', payloadRef.current.projectCode);
let configs = {
headers: { 'Content-Type': 'multipart/form-data', token: localStorage.getItem('token') || '' },
withCredentials: true,
onUploadProgress: (progress: any) => {
console.log(progress);
console.log(Math.round((progress.loaded / progress.total) * 100));
},
};
axios
.post(env.VITE_APP_ROOT + '/authorization/api/data/manage/dataManageBatchImportExcel', fd, configs)
.then((res: any) => {
clearInterval(ds_percent);
setPercent(100);
if (res.data.code === 200) {
if (res.data.message) {
let concatarr: any[] = [];
concatarr = [...new Set(fastImportTypes.concat([value]))].sort();
setFastImportTypes(concatarr);
sessionStorage.setItem('batchNumber2', res.data.message.split('-')[0]);
setImportNumber(res.data.message.split('-')[1] || res.data.message.split('--')[1]);
}
setTimeout(() => {
setProgressLine(3);
}, 1000);
} else {
const arr: any[] = [];
if (res.data.message.indexOf(';') !== -1) {
const A = res.data.message.split(';');
let B = '';
let E = '';
A.forEach((item: any, index: any) => {
if (item !== '') {
if (item.indexOf('行') !== -1 && A[index - 1] && A[index - 1].indexOf('行') !== -1) {
const I = item.indexOf('行') + 1;
const T = item.slice(0, I); // 截取当前行数
const Q = A[index - 1].slice(0, I); // 截取上一行数
if (T === Q) {
B = B + item.replace(`${T}`, ',');
} else {
B = B + ';' + item;
}
} else {
B = B + ';' + item;
}
}
});
B.split(';').forEach((item) => {
if (item !== '') {
if (item.indexOf(',请上传页面项目相同的项目名称') !== -1) {
const I = item.indexOf('行') + 1;
const T = item.slice(0, I); // 截取当前行数
const C = item.replace(',请上传页面项目相同的项目名称', '');
const D = T + '请上传页面项目相同的项目名称,' + C.replace(`${T}`, '');
E = E + ';' + D;
} else {
E = E + ';' + item;
}
}
});
E.split(';').forEach((item, index) => {
if (item !== '') {
arr.push({ number: index, reason: item });
}
});
} else {
arr.push({ number: 1, reason: res.data.message });
}
setErrorData(arr);
setTimeout(() => {
setProgressLine(4);
}, 1000);
}
});
}
function cancelExcel(e: any) {
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
setImgColor(false);
setFileName('');
setProgressLine(1);
setPercent(0);
}
function deleteExcel() {
Modal.confirm({
title: '提示',
content: `确定删除excel导入的${taskEntityNameFunc(value)}数据吗?`,
onOk() {
let params = {
type: value,
processId: sessionStorage.getItem('batchNumber2'),
};
BatchImportApi.delTempDate(params).then((res: any) => {
if (res && res.code === 200) {
setImgColor(false);
setFileName('');
setProgressLine(1);
setPercent(0);
let concatArr = fastImportTypes.filter((item: any) => {
return item !== value;
});
setFastImportTypes([...new Set(concatArr)].sort());
message.success('删除成功');
} else {
message.error(res.message);
}
});
},
});
}
const uploadProps = {
beforeUpload: (res: any) => {
if (res.size > 10485760 * 2) {
setImgColor(false);
message.error('上传文件不能大于20兆!');
return;
}
if (
((value === '2' && res.name.includes('导入楼栋')) ||
(value === '3' && res.name.includes('导入房屋')) ||
(value === '4' && res.name.includes('导入车位')) ||
(value === '5' && res.name.includes('导入虚拟楼栋')) ||
(value === '6' && res.name.includes('导入虚拟房屋'))) &&
res.name.includes('新增')
) {
if (res) {
setImgColor(true);
setFile(res);
setFileName(res.name);
} else {
message.error(`上传excel表格失败,请重新上传`);
setImgColor(false);
setFileName('');
}
} else {
message.error(`上传excel表格名称与所选导入数据类型不符,请检查导入数据类型`);
}
return false;
},
};
function showError() {
const heard = {
number: '序号',
reason: '导入失败原因',
};
exportExcel(heard, errorData, `${taskEntityNameFunc(value)}新增导入失败原因表`);
}
function publishData() {
setPercent(0);
if (fastImportTypes) {
publics.openNewTabs(
`modification/index/${value}/${payloadRef.current.isAlone}?processId=${sessionStorage.getItem(
'batchNumber2',
)}&taskId=${payloadRef.current.taskId}&operateType=1&sceneType=${
payloadRef.current.sceneType
}&fastTypeNumber=${fastImportTypes?.toString()}`,
);
}
}
return (
<Modal
title={payloadRef.current.title}
width={750}
zIndex={100}
visible={visible}
onOk={wrapWithClose(payloadRef.current.onOk)}
onCancel={onCancel}
forceRender={true}
destroyOnClose={true}
maskClosable={false}
footer=""
>
<div className="overflow-y-auto max-h-140">
{progressLine === 3 && (
<div className="absolute top-0 left-0 bg-white h-13 px-5 z-index-101 w-full">
<div className="text-16px leading-13 float-left">批量导入{taskEntityNameFunc(value)}数据</div>
<div onClick={cancelExcel} className="text-right cursor-pointer float-right pt-4 px-2">
X
</div>
</div>
)}
{progressLine !== 3 && (
<div>
<div className="flex items-center mb-4 change_radio">
<Radio.Group
onChange={onChange}
options={radioOptions}
value={value}
optionType="button"
buttonStyle="solid"
/>
</div>
<div className="flex items-center justify-between h-16 px-4 mb-4 border-blue-300 border-solid rounded bg-blue-50 border-1 template-style">
<span>下载{taskEntityNameFunc(value)}新增数据导入模板</span>
<Button onClick={onloadExcelData}>下载模板</Button>
</div>
<div className="mb-4 text-xs leading-5 tracking-widest text-red-500">
温馨提示:请严格按照导入模板要求导入数据。若导入报错,请IT报事,谢谢。
</div>
</div>
)}
<Dragger {...uploadProps} className="relative z-index-1" showUploadList={false} accept=".xls, .xlsx">
<div className="overflow-hidden h-60" />
<div className="absolute top-0 bottom-0 left-0 right-0 pt-0 auto z-index-900 cursor-text">
{progressLine === 1 ? (
<div>
<div className="relative overflow-hidden">
{imgColor ? (
<div
className="pt-8 overflow-hidden h-68"
onClick={(e: any) => {
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
}}
>
<Icons className="mx-auto mb-2 mt-7" type="dragGreen" size="70px" />
<div className="absolute text-xs cursor-pointer top-12 right-77" onClick={cancelExcel}>
X
</div>
<div className="mb-4 text-xs leading-4 text-gray-300">{fileName}</div>
<Button type="primary" onClick={handleImport}>
导入数据
</Button>
</div>
) : (
<div className="pt-8 overflow-hidden h-68">
<div
className="absolute top-0 w-full h-45 z-index-999"
onClick={(e: any) => {
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
}}
/>
<div
className="absolute w-full top-53 h-15 z-index-999"
onClick={(e: any) => {
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
}}
/>
<div
className="absolute left-0 h-8 top-45 w-76 z-index-999"
onClick={(e: any) => {
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
}}
/>
<div
className="absolute right-0 h-8 top-45 w-76 z-index-999"
onClick={(e: any) => {
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
}}
/>
<Icons className="mx-auto mb-2 mt-7" type="drag" size="60px" />
<div className="mx-auto mb-4 text-center">
<div className="h-4 text-xs leading-4 text-gray-300">将文件拖拽至此区域</div>
<div className="h-4 mb-5 text-xs leading-4 text-gray-300">支持xls、xlsx格式</div>
<Button className="mb-3">上传文件</Button>
</div>
</div>
)}
</div>
</div>
) : progressLine === 2 ? (
<div
className="overflow-hidden h-68"
onClick={(e: any) => {
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
}}
>
<Progress percent={percent} className="!px-10 !pt-26" />
<div className="flex justify-center my-4 text-gray-400">
导入{taskEntityNameFunc(value)}新增的数据中,请稍后...
</div>
</div>
) : progressLine === 3 ? (
<div
className="overflow-hidden h-68"
onClick={(e: any) => {
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
}}
>
<div className="h-30">
<Icons className="mx-auto mb-2 mt-7" type="dragGreen" size="70px" />
{importNumber && (
<div className="absolute text-xs cursor-pointer top-12 right-77" onClick={deleteExcel}>
X
</div>
)}
<div className="mb-5 text-xs leading-4 text-gray-300">{fileName}</div>
</div>
<div className="mt-4 mb-2 text-2xl">导入成功</div>
<div className="mb-4 text-gray-400">
成功导入{importNumber}条{taskEntityNameFunc(value)}新增数据,请尽快发布数据
</div>
<div>
<Button onClick={cancelExcel} className="mr-2">
重新导入
</Button>
<Button type="primary" onClick={publishData}>
发布数据
</Button>
</div>
</div>
) : (
<div
className="overflow-hidden h-68"
onClick={(e: any) => {
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
}}
>
<Icons className="mx-auto mb-2 mt-7" type="importFail" size="80px" />
<div className="mt-5 mb-2 text-2xl">导入失败</div>
<div className="mb-4 text-gray-400">你可以下载查看失败数据,修改后重新导入</div>
<div>
<Button type="primary" onClick={showError} className="mr-2">
点击查看导入失败原因
</Button>
<Button onClick={cancelExcel}>重新导入</Button>
</div>
</div>
)}
</div>
</Dragger>
</div>
</Modal>
);
}) as any;
FastBatchImport.show = (payload: Payload) => console.log('BatchImport is not mounted.');
export default FastBatchImport;
该代码段展示了在React应用中实现批量导入功能的逻辑,包括使用AntDesign的Modal和Upload组件处理文件上传,调用Axios进行API交互,以及处理导入后的数据验证和错误反馈。功能涵盖了从用户界面到服务端通信的完整流程。

被折叠的 条评论
为什么被折叠?



