react+antd 导出excel文件(简单数据&多级表头)
需求:
在基于react+antd进行开发的页面中,实现导出excel报表的功能
实际场景:
1.简单数据:单层表头+数据
2.复杂数据:多层表头+数据
实现方式:
1.简单数据
简单数据的导出使用了js-export-excel插件,需要自己转换表头数据与表格数据,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | import { useState } from 'react' ; import { message, Button } from 'antd' ; import ExportJsonExcel from 'js-export-excel' ; import type { RequestData } from '@/utils/request' ; import type { ProjectInfoParams } from '@/services/summary' ; import type { PaginationProps } from 'antd' ; import { downloadChannelSettle } from '@/services/xxx' ; // 接口地址 interface DownloadEffortsProps { columnsData: any[]; totalList?: any[]; tableParams?: ProjectInfoParams & PaginationProps & { order?: string; sort?: string }; } const DownloadEfforts: React.FC<DownloadEffortsProps> = (props) => { const { columnsData, tableParams } = props; // 按钮loading const [downLoading, setDownLoading] = useState<boolean>( false ); // 下载结算申请报表 const downloadEffectExcel = async () => { // 请求所有数据 const downloadParams = { ...tableParams, current: 1, pageSize: 0, pageNum: 1 }; // 开启按钮loading setDownLoading( true ); try { const res: RequestData = await downloadChannelSettle(downloadParams); if (res.result) { const { tableList } = res.result; // 表头数组去掉操作栏 const newestHeaderList = columnsData.filter((arr: any) => arr.valueType !== 'option' ); // 导出报表的表头与数据的处理 const resultData: any[] = []; if (tableList) { tableList.forEach((item: any, index: number) => { const resultItem = {}; newestHeaderList.forEach((el: any, i: number) => { if (newestHeaderList[i].title) { resultItem[newestHeaderList[i].title] = tableList[index][newestHeaderList[i].dataIndex]; } }); resultData.push(resultItem); }); } const newHeader = newestHeaderList.map((item: any) => item.title); const option = { fileName: '导出数据' , // 文件名字 datas: [ { sheetData: resultData, // 数据 sheetName: 'sheet' , sheetFilter: newHeader, // 表头 sheetHeader: newHeader, // 表头 }, ], }; const toExcel = new ExportJsonExcel(option); toExcel.saveExcel(); } else { message.error( '下载失败!' ); } } catch (error) { // } finally { // 关闭按钮loading setDownLoading( false ); } }; return ( <Button key= "downloadExcel" type= "ghost" onClick={downloadEffectExcel} loading={downLoading}> 下载报表 </Button> ); }; export default DownloadEfforts; |
2.复杂数据
复杂数据,我只处理了多层表头的情况,table组件展示如图:
因为js-export-excel插件无法直接导出多层表头的数据(并且产品要求excel文件要与table展示布局相同),因此选用了react-html-table-to-excel插件进行开发,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | import { useState, useRef, useEffect } from 'react' ; import { isEmpty } from 'lodash' ; import { message, Table, Button } from 'antd' ; import ReactHTMLTableToExcel from 'react-html-table-to-excel' ; import type { RequestData } from '@/utils/request' ; import type { ParamsType } from '@ant-design/pro-provider' ; import type { PaginationProps } from 'antd' ; import { downloadChannelGoBackReport } from '@/services/xxx' ; import type { SettleApplicationParams } from '@/services/xxx' ; interface DownloadEffortsProps { columnsData: any[]; totalList?: any[]; tableParams?: ParamsType & PaginationProps & { order?: string; sort?: string; month?: string[] }; } const DownloadEfforts: React.FC<DownloadEffortsProps> = (props) => { const { columnsData, tableParams } = props; const buttonRef = useRef<any>( null ); const tableRef = useRef<any>( null ); const [tableData, setTableData] = useState<SettleApplicationParams[]>([]); // 按钮loading const [downLoading, setDownLoading] = useState<boolean>( false ); // 下载报表 const downloadEffectExcel = async (e: any) => { e.stopPropagation(); // 请求所有数据 const downloadParams = { ...tableParams, current: 1, pageSize: 0, pageNum: 1 }; if (downloadParams.month && !isEmpty(downloadParams.month)) { // 开启按钮loading setDownLoading( true ); try { const res: RequestData = await downloadChannelGoBackReport(downloadParams); if (res.result) { const { tableList } = res.result; setTableData(tableList); } else { message.error( '下载失败!' ); setDownLoading( false ); } } catch (error) { // } } else { message.error( '查询时间不能为空' ); } }; useEffect(() => { // 拿到本次请求的数据后,再进行下载 if (columnsData.length > 0 && tableData.length > 0) { const table = tableRef?.current?.querySelector( 'table' ); table.setAttribute( 'id' , 'table-to-xls' ); buttonRef.current.handleDownload(); // 关闭按钮loading setDownLoading( false ); // 下载完后,清除数据 setTimeout(() => { setTableData([]); }, 0); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [tableData]) useEffect(() => { // 禁止组件按钮的默认点击事件(因为ts默认用的是Element,需要声明为HTMLElement) const button = document.querySelector( '#test-table-xls-button' ) as HTMLElement; if (button) button.style[ 'pointer-events' ] = ( 'none' ); }, []); return (<> <Button key= "downloadExcel" type= "ghost" onClick={(e) => downloadEffectExcel(e)} loading={downLoading}> 下载报表 </Button> <span style={{ visibility: 'hidden' }}> <ReactHTMLTableToExcel ref={buttonRef} key= "downloadExcel" id= "test-table-xls-button" className= "ant-btn" table= "table-to-xls" filename= "报表" sheet= "报表" buttonText= "下载报表" /> </span> <div ref={tableRef} style={{ position: 'absolute' , zIndex: -1, top: 0, left: 0, width: 0, height: 0 }}> <Table rowKey= 'id' columns={columnsData.filter((arr: any) => arr.valueType !== 'option' )} dataSource={tableData} /> </div> </> ); }; export default DownloadEfforts; |
最终导出效果:
【ps】:因为YYYY-MM格式的表头数据会在导出时自动转格式(如上图),因此可以在表头处理时手动转为【YYYY年MM月】的格式避免此类情况。
参考文档:https://blog.csdn.net/weixin_44240581/article/details/115265810
这两个依赖的配置项很接近,肉眼可以明确属性名的含义,不做赘述。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具