React + Ant Design 导出按钮组件
该笔记记录了如何创建一个 通用的导出按钮组件,它能够调用后台 API 导出数据并提供下载。此组件在点击时会弹出确认框,确认后调用 API 获取导出数据并下载文件。
功能概述
- 按钮展示:展示一个 Ant Design
Button
组件,配合下载图标(DownloadOutlined
)。 - 二次确认:点击按钮时会弹出一个确认框,用户确认后才开始导出操作。
- API 调用:通过传入
api
和params
,动态获取数据,api
是导出数据的 API 方法,params
是获取导出 API 所需参数的函数。 - 文件导出:成功获取数据后,会以指定格式导出文件,默认导出为
.xlsx
格式。
组件代码
import React, { useState } from 'react';
import { Button, Modal } from 'antd'; // 使用 Ant Design 的 Button 组件
import { DownloadOutlined } from '@ant-design/icons'; // 下载图标
import { useTranslation } from "react-i18next";
import moment from 'moment';
import PermissionButton from '@/components/Basic/PermissionButton';
/**
* 通用的导出按钮组件
* @param {Object} props - 组件的传递参数
* @param {Function} props.api - 导出数据的 API 方法,必须返回 Promise
* @param {Function} props.params - 获取导出 API 所需的参数的函数
* @param {React.ReactNode} [props.children='导出'] - 按钮文本,可以自定义
* @param {string} [props.fileName='导出文件名'] - 导出的文件名(不含后缀)
* @param {string} [props.suffixName='.xlsx'] - 导出文件的后缀名,默认为 `.xlsx`
* @param {Boolean} [props.isConfirm=true] - 是否需要二次确认,默认为 true
* @param {Boolean} [props.isTimeStamp=false] - 是否需要添加时间戳,默认为 false
* @param {string} [props.fileType='application/vnd.ms-excel'] - 文件的 MIME 类型,默认是 Excel 格式
* @returns {JSX.Element} 返回渲染的导出按钮
*/
const ExportButton = ({
api,
params,
children = '',
fileName = '',
suffixName = '.xlsx',
fileType = 'application/vnd.ms-excel',
isConfirm = true, // 是否需要二次确认
isTimeStamp = false, // 是否需要添加时间戳
...props
}) => {
const [loading, setLoading] = useState(false);
const { t } = useTranslation();
/**
* 显示确认导出数据的二次确认框
* @returns {void}
*/
const handleConfirm = async () => {
if (isConfirm) {
Modal.confirm({
title: t('global.export.exprotTis'),
onOk: handleExport,
});
} else {
await handleExport()
}
};
const errConfirm = (text = '') => {
Modal.error({
title: text,
});
}
/**
* 处理文件导出逻辑,调用 API 并导出文件
* @returns {Promise<void>}
*/
const handleExport = async () => {
setLoading(true);
try {
const res = await api(params()); // 调用 API 获取数据
// 处理 Blob 对象
const blobRes = new Blob([res], { type: fileType });
// 如果 Blob 里面包含 JSON 格式数据,可以这样处理:
const text = await blobRes.text(); // 获取文本内容
try {
const json = JSON.parse(text); // 尝试解析成 JSON 对象
console.log('Parsed JSON: ', json); // 打印解析后的 JSON 对象
return errConfirm(json.message);
} catch (error) {
console.error('无法解析 Blob 为 JSON:', error);
}
if (res instanceof Blob) {
exportFile(res, fileName, suffixName, fileType);
} else {
const contentDisposition = res.headers['content-disposition']
let apiFilename
if (contentDisposition) {
const match = contentDisposition.match(/filename="?([^";]*)"?/);
if (match && match[1]) {
apiFilename = decodeURIComponent(match[1]);
}
}
exportFile(res.data, fileName || apiFilename, fileName ? suffixName : '', fileType); // 导出文件
}
} catch (error) {
console.error(t('global.export.exprotFail'), error);
} finally {
setLoading(false);
}
};
/**
* 导出文件到本地
* @param {BlobPart} blobData - 要导出的二进制数据
* @param {string} fileName - 文件名(不含后缀)
* @param {string} suffixName - 文件的后缀名
* @param {string} fileType - 文件类型(MIME)
* @returns {void}
*/
const exportFile = (blobData, fileName, suffixName, fileType) => {
const time = moment().format('YYYYMMDDHHmmss')
const blob = new Blob([blobData], { type: fileType }); // 创建 Blob 对象
const downloadElement = document.createElement('a'); // 创建下载链接
const href = window.URL.createObjectURL(blob); // 创建下载链接的 URL
downloadElement.href = href;
downloadElement.download = isTimeStamp ? `${fileName}${time}${suffixName}` : `${fileName}${suffixName}`; // 设置下载文件的名称和后缀
document.body.appendChild(downloadElement); // 将链接添加到 DOM
downloadElement.click(); // 模拟点击触发下载
document.body.removeChild(downloadElement); // 下载完成后移除链接
};
return (
<PermissionButton
type="primary"
loading={loading}
icon={<DownloadOutlined />} // 使用下载图标
onClick={handleConfirm} // 点击触发二次确认弹框
{...props}
>
{children} {/* 按钮文本 */}
</PermissionButton>
);
};
export default ExportButton;
PermissionButton 参考 权限按钮
使用说明
-
传入 Props 参数:
api
: 必须传入一个返回Promise
的 API 方法,该方法会调用并获取要导出的数据。params
: 一个函数,返回传递给api
的参数。children
: 按钮文本(可选,默认为'导出'
)。fileName
: 导出的文件名称(不含后缀,默认为空字符串)。suffixName
: 导出的文件后缀名(默认为.xlsx
)。fileType
: 文件 MIME 类型(默认为application/vnd.ms-excel
)。
-
使用示例:
import React from 'react';
import { message } from 'antd';
import ExportButton from './ExportButton';
// 假设有一个 API 获取数据
const fetchData = async (params) => {
// 模拟 API 请求,返回 Blob 数据
return new Promise((resolve) => {
setTimeout(() => {
const blobData = new Blob([JSON.stringify({ data: 'example data' })], {
type: 'application/json',
});
resolve(blobData);
}, 1000);
});
};
// 获取参数的函数
const getExportParams = () => {
return { page: 1, size: 20 }; // 示例参数
};
const ExampleComponent = () => {
return (
<div>
<ExportButton
api={fetchData} // 传入 API 方法
params={getExportParams} // 传入获取参数的函数
fileName="example" // 设置导出文件名
suffixName=".json" // 设置文件后缀
fileType="application/json" // 设置文件类型
>
导出示例数据
</ExportButton>
</div>
);
};
export default ExampleComponent;
组件解析
- handleConfirm: 弹出确认框,当用户点击“确定”按钮时,会调用
handleExport
方法。 - handleExport: 执行数据导出操作,首先打印导出的参数,接着调用
api
方法获取数据,并通过exportFile
导出文件。 - exportFile: 使用
Blob
创建文件,并通过动态生成的<a>
标签进行文件下载。
总结
该组件适用于各种后台管理系统中的数据导出需求,用户只需提供对应的 API 和参数获取函数,组件会处理导出逻辑并支持弹框确认。通过自定义文件名、文件类型等参数,能够适应不同的导出需求,且具有较强的复用性和扩展性。