思路
1、下载列表定时请求数据,首次加载组件,例如登录,开启定时任务请求
2、当任务已下载执行完成后,清除定时任务
3、当触发导出表格任务时,再次开启定时任务
也就是在任务下载中的时候,才去执行请求调用,其他情况下避免了不必要的请求
关键代码
isOpenFetchFileList//是否开启定时任务
useEffect(() => {
if (isOpenFetchFileList) {
if (!timer) {
console.log('开启定时任务');
const t = setInterval(() => {
dispatch({
type: 'downloadTasks/fetchFileList',
});
}, 5000);
dispatch({
type: 'downloadTasks/overrideStateProps',
payload: {
timer: t,
},
});
}
} else if (timer) {
console.log('关闭定时任务---');
clearInterval(timer);
dispatch({
type: 'downloadTasks/overrideStateProps',
payload: {
timer: null,
isOpenFetchFileList: false,
},
});
}
}, [isOpenFetchFileList, timer, dispatch]);
model
import { message } from 'antd';
import DownloadTasksApi from '@/services/DownloadTasks.js';
import moment from 'moment';
import qs from 'qs';
import { downloadFile } from '@/utils/utils';
const namespace = 'downloadTasks';
// const selectState = state => state[namespace];
const SUCCESS = DownloadTasksApi.SUCCESS; // 完成
const DOWNLOADIND = DownloadTasksApi.DOWNLOADIND; // 下载中
export default {
namespace,
state: {
isOpenFetchFileList: true,
timer: null,
fetchFileList: [],
isSomeDownloading: false,
downloadLoading: false,
},
reducers: {
overrideStateProps(state, { payload }) {
return {
...state,
...payload,
};
},
updateStateProps(state, { payload }) {
const { name, value } = payload;
return {
...state,
...{ [name]: { ...state[name], ...value } },
};
},
},
effects: {
/**
* 触发任务下载
* @param {*} listParams 参数列表
* @param {*} url 请求地址
*/
*triggerFileExport({ payload }, { put, call }) {
const { listParams, url } = payload;
yield put({
type: 'overrideStateProps',
payload: {
downloadLoading: true,
},
});
const response = yield call(DownloadTasksApi.triggerFileExport, { listParams, url });
switch (response.XCmdrCode) {
case 0:
message.info('下载中,请进入下载任务列表查看下载进度');
yield put({
type: 'overrideStateProps',
payload: {
isOpenFetchFileList: true,
},
});
yield put({
type: 'fetchFileList',
});
break;
case 342:
message.warn(`${response?.XCmdrMessage || '当前已有下载任务,请稍后再点击'}`);
break;
default:
break;
}
yield put({
type: 'overrideStateProps',
payload: {
downloadLoading: false,
},
});
},
*fetchFileList({ payload }, { put, call, select }) {
const { token } = yield select(store => store.user);
const handleDownload = item => {
const { task_id, type, created_at } = item;
const params = {
type,
task_id,
};
const downloadExcelUrl = `https://admin-api.zhgcloud.com/export-task/export-excel?${qs.stringify(
params,
)}&token=${token}`;
downloadFile(
downloadExcelUrl,
`${DownloadTasksApi?.DOWNLOADTASKS[type]?.name || ''}${created_at ||
moment().format('YYYY-MM-DD')}.xls`,
);
};
const response = yield call(DownloadTasksApi.getExportTaskTxportTaskList);
const list = response?.XCmdrResult || [];
const changeList = list?.map(item => {
const { is_frontend_export, status } = item;
if (status === SUCCESS) {
if (is_frontend_export === 0) {
handleDownload(item);
}
}
return { ...item };
});
const isSomeDownloading = changeList?.some(item => item?.status === DOWNLOADIND);
switch (response.XCmdrCode) {
case 0:
if (!isSomeDownloading) {
yield put({
type: 'overrideStateProps',
payload: {
isOpenFetchFileList: false,
isSomeDownloading: false,
},
});
}
yield put({
type: 'overrideStateProps',
payload: {
fetchFileList: changeList,
isSomeDownloading,
},
});
break;
default:
yield put({
type: 'overrideStateProps',
payload: {
isOpenFetchFileList: false,
},
});
break;
}
},
/**
* 重新触发任务下载
*/
*handleReExport({ payload }, { put, call }) {
const { task_id, type, params } = payload;
const response = yield call(DownloadTasksApi.handleReDownload, { task_id, type, params });
switch (response.XCmdrCode) {
case 0:
yield put({
type: 'overrideStateProps',
payload: {
isOpenFetchFileList: true,
},
});
yield put({
type: 'fetchFileList',
});
break;
case 342:
message.warn(`${response?.XCmdrMessage || '当前已有下载任务,请稍后再点击'}`);
break;
default:
break;
}
},
},
};
下载组件
import { Dropdown, List } from 'antd';
import React, { useMemo, useEffect } from 'react';
import InfiniteScroll from 'react-infinite-scroller';
import moment from 'moment';
import { useSelector, useDispatch } from 'umi';
import qs from 'qs';
import DownloadTasks from '@/services/DownloadTasks';
import useToken from '@/hooks/useToken';
import download_task_green from '@/assets/download_task_green.png';
import download_task_grey from '@/assets/download_task_grey.png';
import download_task_red from '@/assets/download_task_red.png';
import download_task_yellow from '@/assets/download_task_yellow.png';
import no_pass_ic from '@/assets/no_pass_ic.png';
import styles from './FileDownloadList.less';
const ListItem = List.Item;
const SUCCESS = DownloadTasks.SUCCESS; // 完成
const DOWNLOADIND = DownloadTasks.DOWNLOADIND; // 下载中
const FAILED = DownloadTasks.FAILED; // 下载失败
const getType = type => {
return DownloadTasks?.DOWNLOADTASKS[type]?.name || '';
};
export default () => {
const token = useToken();
const dispatch = useDispatch();
const { isOpenFetchFileList, timer, fetchFileList, isSomeDownloading } = useSelector(state => state.downloadTasks);
useEffect(() => {
if (isOpenFetchFileList) {
if (!timer) {
console.log('开启定时任务');
const t = setInterval(() => {
dispatch({
type: 'downloadTasks/fetchFileList',
});
}, 5000);
dispatch({
type: 'downloadTasks/overrideStateProps',
payload: {
timer: t,
},
});
}
} else if (timer) {
console.log('关闭定时任务---');
clearInterval(timer);
dispatch({
type: 'downloadTasks/overrideStateProps',
payload: {
timer: null,
isOpenFetchFileList: false,
},
});
}
}, [isOpenFetchFileList, timer, dispatch]);
const getDowloadSatus = item => {
const { rows, completed_rows, status } = item;
switch (status) {
case DOWNLOADIND:
return (
<div className={styles.list_item_download_ing}>
<img src={download_task_yellow} alt="" style={{ marginLeft: 2 }} />
<span style={{ marginLeft: 2, color: 'rgb(253, 173, 77)' }}>下载中</span>
</div>
);
case SUCCESS:
return (
<div className={styles.list_item_download_ok}>
<img src={download_task_green} alt="" />
<span style={{ marginLeft: 2, color: 'rgb(13, 207, 151)' }}>完成</span>
</div>
);
case FAILED:
return (
<div className={styles.list_item_download_failed}>
<img src={no_pass_ic} alt="" style={{ marginLeft: 2 }} />
<span style={{ marginLeft: 3, color: 'rgb(251, 92, 124)' }}>失败</span>
</div>
);
default:
if (rows === completed_rows) {
return (
<div className={styles.list_item_download_ok}>
<img src={download_task_green} alt="" />
<span style={{ marginLeft: 2, color: 'rgb(13, 207, 151)' }}>完成</span>
</div>
);
} else {
return (
<div className={styles.list_item_download_ing}>
<img src={download_task_yellow} alt="" style={{ marginLeft: 2 }} />
<span style={{ marginLeft: 2, color: 'rgb(253, 173, 77)' }}>下载中</span>
</div>
);
}
}
};
const fliesList = useMemo(() => {
const handleDownload = item => {
const { task_id, type, created_at } = item;
const params = {
type,
task_id,
};
const downloadExcelUrl = `https://admin-api.zhgcloud.com/export-task/export-excel?${qs.stringify(
params,
)}&token=${token}`;
downloadFile(downloadExcelUrl, `${getType(type)}${created_at || moment().format('YYYY-MM-DD')}.xls`);
};
// 重新下载
const handleReDownload = item => {
const { task_id, type, params } = item;
const fetParams = {
task_id,
params,
type,
};
DownloadTasks.handleReDownload(fetParams).then(res => {
if (res.XCmdrCode === 0) {
//
}
});
};
const handleDowloadButton = item => {
const { status } = item;
switch (status) {
case SUCCESS:
return (
<span className="curp txt-primary" onClick={() => handleDownload(item)}>
下载
</span>
);
case FAILED:
return (
<span
className="curp txt-primary"
style={{ marginLeft: 2 }}
onClick={() => handleReDownload(item)}
>
重新下载
</span>
);
default:
return null;
}
};
const fliesDiv = (
<>
<div className={styles.fliesListContainer}>
<div className={styles.flies_list_title}>
<div className={styles.flies_list_title_lable}>下载任务列表</div>
{/* <div className={styles.flies_list_close} onClick={handleClose}>x</div> */}
</div>
<div className={styles.infiniteScroll_container}>
<InfiniteScroll>
<List
dataSource={fetchFileList}
renderItem={item => (
<ListItem key={item?.task_id} style={{ alignItems: 'self-start' }}>
<div className={styles.list_item_wrap}>
<div className={styles.list_item_type}>{getType(item.type)}</div>
<div className={styles.list_item_time}>{item.created_at}</div>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
}}
>
{getDowloadSatus(item)}
<div style={{ minWidth: '50px', textAlignLast: 'right' }}>
{handleDowloadButton(item)}
</div>
</div>
</div>
</ListItem>
)}
/>
</InfiniteScroll>
</div>
</div>
</>
);
return fliesDiv;
}, [fetchFileList, token]);
return (
<div style={{ display: 'inline-block' }}>
<Dropdown overlay={fliesList} trigger={['click']}>
<img
src={isSomeDownloading ? download_task_red : download_task_grey}
className={styles.download_task_icon}
alt=""
/>
</Dropdown>
</div>
);
};
下载按钮组件
导出按钮可以使用防抖处理,这里选用loading
防抖处理
import React, { useMemo, useState } from 'react';
import { Button } from 'antd';
import { useDispatch, useSelector } from 'react-redux';
import Authorized from '@/utils/Authorized';
import DownloadTasks from '@/services/DownloadTasks';
import debounce from 'lodash/debounce';
export default ({ authority, listParams = {}, type = '', name = '导出表格', isDisabled = false }) => {
const dispatch = useDispatch();
const { downloadLoading } = useSelector(state => state.downloadTasks);
const [exportAbled, setExportAbled] = useState(false);
const exportButton = useMemo(() => {
const handleEexport = () => {
const url = DownloadTasks?.DOWNLOADTASKS[type]?.url;
dispatch({
type: `${DownloadTasks.EXPORTTASKMODEL}`,
payload: { listParams, url },
});
if (isDisabled) {
setExportAbled(true);
setTimeout(() => {
setExportAbled(false);
}, 2000);
}
};
return (
<Authorized authority={[authority]} noMatch={null}>
<Button
disabled={exportAbled}
onClick={debounce(handleEexport, 1000, { leading: true })}
style={{ marginLeft: 10 }}
loading={downloadLoading}
>
{name}
</Button>
</Authorized>
);
}, [authority, dispatch, exportAbled, listParams, type, name, isDisabled, downloadLoading]);
return exportButton;
};
loading处理
import React, { useMemo, useState } from 'react';
import { Button } from 'antd';
import { useDispatch, useSelector } from 'react-redux';
import Authorized from '@/utils/Authorized';
import DownloadTasks from '@/services/DownloadTasks';
export default ({ authority, listParams = {}, type = '', name = '导出表格', isDisabled = false }) => {
const dispatch = useDispatch();
const { downloadLoading } = useSelector(state => state.downloadTasks);
const [exportAbled, setExportAbled] = useState(false);
const exportButton = useMemo(() => {
const handleEexport = () => {
const url = DownloadTasks?.DOWNLOADTASKS[type]?.url;
dispatch({
type: `${DownloadTasks.EXPORTTASKMODEL}`,
payload: { listParams, url },
});
if (isDisabled) {
setExportAbled(true);
setTimeout(() => {
setExportAbled(false);
}, 2000);
}
};
return (
<Authorized authority={[authority]} noMatch={null}>
<Button
disabled={exportAbled}
onClick={handleEexport}
style={{ marginLeft: 10 }}
loading={downloadLoading}
>
{name}
</Button>
</Authorized>
);
}, [authority, dispatch, exportAbled, listParams, type, name, isDisabled, downloadLoading]);
return exportButton;
};
下载组件使用
<DownloadTaskExport
authority="admin_contract_management_export"
type="admin_contracts"
listParams={listParams}
name="导出表格"
/>
serve
import request from '@/utils/request';
import { apiHostMap } from './apiHostMap';
import Model from './Model';
export default class DownloadTasks extends Model {
static EXPORTTASKMODEL = 'downloadTasks/triggerFileExport';
// model
static SUCCESS = 'success';
// 完成
static DOWNLOADIND = 'downloading';
// 下载中
static FAILED = 'failed'; // 下载失败
static DOWNLOADTASKS = {
client: {
name: '客户档案',
type: 'client',
url: 'client/export-task?type=client',
},
client_high_sea: {
name: '客户公海',
type: ' client_high_sea',
url: 'client/high-sea-export-task',
},
admin_contracts: {
name: '销售合同',
type: 'admin_contracts',
url: 'admin_contracts/export-task?type=admin_contracts',
},
};
// 获取下载任务列表
static getExportTaskTxportTaskList(params) {
return request(`${apiHostMap.HOST}/export-task/export-task-list`, {
params,
});
}
// 任务列表导出
static exportTaskListDownload(params) {
return request(`${apiHostMap.HOST}/export-task/export-excel`, { params });
}
// 触发任务下载
static triggerFileExport(params) {
const { listParams, url } = params;
return request(`${apiHostMap.HOST}/${url}`, { params: listParams });
}
// 重新触发任务下载
static handleReDownload(item) {
const { task_id, type, params } = item;
return request(`${apiHostMap.HOST}/export-task/reload-export-task?task_id=${task_id}&&type=${type}&&${params}`);
}
}