React 项目中封装 Excel 导入导出组件:技术分享与实践

article/2025/7/27 18:49:46

文章目录

  • 前言
    • 一、为什么需要封装 Excel 组件?
    • 二、技术选型
    • 三、核心实现
      • 1. 安装依赖
      • 2. 封装Excel导出
      • 3. 封装导入组件 (UploadExcel)
  • 总结


前言

在 React 项目中,处理 Excel 文件的导入和导出是常见的业务需求。无论是导出报表数据供用户下载,还是让用户上传 Excel 文件进行数据解析,一个高效、易用的组件都能极大提升开发效率和用户体验。本文将分享如何在 React 项目中封装一个通用的 Excel 导入导出组件,涵盖核心实现思路、代码示例以及最佳实践。


一、为什么需要封装 Excel 组件?

  1. 统一处理逻辑:避免在多个页面重复编写 Excel 解析或生成代码。
  2. 提升用户体验:通过统一的 UI 和交互,降低用户学习成本。
  3. 减少错误:集中处理文件格式校验、数据转换等易错环节。
  4. 可扩展性:支持自定义配置(如列映射、样式调整等)。

二、技术选型

  • 导出 Excel:使用 xlsx 库 & file-saver
  • 导入 Excel:使用 xlsx 库解析文件内容。
  • UI 框架:基于 Ant Design 的 Upload 组件或自定义按钮。

三、核心实现

1. 安装依赖

	npm install xlsx file-saver @ant-design/icons# 或使用 yarnyarn add xlsx file-saver @ant-design/icons

2. 封装Excel导出

根据泛型传入不同的data数据类型和动态传递header表头

import * as XLSX from "xlsx";
import { saveAs } from "file-saver";/***  导出Excel* @param data* @param header*/
export function exportExcel<T>(data: T[], header: string[]) {const worksheet = XLSX.utils.json_to_sheet(data, { header });// 创建工作簿const workbook = XLSX.utils.book_new();// 将工作表添加到工作簿XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1");// 将工作簿转换为二进制数据const excelBuffer = XLSX.write(workbook, { bookType: "xlsx", type: "array" });// 创建 Blob 对象const blob = new Blob([excelBuffer], { type: "application/octet-stream" });// 使用 file-saver 库保存文件saveAs(blob, "exported_data.xlsx");
}

使用示例

import {useState} from 'react'
import { exportExcel } from "@/utils/exprotExcel";
// 这个是antd的table组件的表格多选框 selectedRowKeys, setSelectedRowKeys要绑定的状态值
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]); 
const [selectRows, setSelectRows] = useState<IBill[]>([]); 
const header = ["accountNo","status","roomNo","carNo","tel","costName1","costName2","costName3","startDate","endDate","preferential","money","pay",
];// 多选const onSelectChange = (selectedRowKeys: React.Key[],selectedRows: IBill[]) => {setSelectedRowKeys(selectedRowKeys);setSelectRows(selectedRows);};// 多选框配置项const rowSelection = {selectedRowKeys,onChange: onSelectChange,preserveSelectedRowKeys: true,};// 这下面是结构
<Tableloading={loading}dataSource={billList}columns={columns}pagination={false}rowKey={(record) => record.accountNo}scroll={{ x: 1200 }}rowSelection={rowSelection}/><Button// 使用封装好的Excel导出组件type="primary"onClick={() => exportExcel(selectRows, header)}icon={<DownloadOutlined />}disabled={disabled}>导出为Excel
</Button>

在这里插入图片描述
在这里插入图片描述


3. 封装导入组件 (UploadExcel)

import { Upload, message } from "antd";
import type { UploadProps } from "antd";
import { InboxOutlined } from "@ant-design/icons";
const { Dragger } = Upload;import * as XLSX from "xlsx";interface ExcelRowItem {[key: number]: string; // 索引签名,允许任意数字键
}interface IParms<U> {setStaffList: (staffList: (prev: U[]) => U[]) => void;headers: (keyof U)[];
}
// 处理导入的Excel数据
function handleImpotedJson<T>(jsonArr: ExcelRowItem[],headers: (keyof T)[] // 将 headers 明确为 T 的键数组
) {// 去掉表头jsonArr.splice(0, 1);const jsonArrData: T[] = jsonArr.map((item: ExcelRowItem) => {console.log(item, "item");// 这样做可以避免不用初始化对象,不用硬编码Partialconst jsonObj: Partial<T> = {};// const jsonObj: T = {//   key: Math.floor(Math.random() * 10000000),//   name: item[0],//   region: item[1],//   role: item[2],//   phone: item[3],// };// 动态生成表头headers.forEach((header, index) => {const value = item[index];if (value !== undefined) {jsonObj[header] = value as T[keyof T];}});return jsonObj as T;});console.log(jsonArrData, "jsonArrData");return jsonArrData;
}/***  Excel上传组件* @param param0  传参需要:1.useState表格数据列表 2.表头信息 3.传表头的泛型* @returns*/
function UploadExcel<U>({ setStaffList, headers }: IParms<U>) {const props: UploadProps = {name: "file",multiple: true,accept: ".xls,.xlsx", // 只允许上传 Excel 文件action: "https://www.demo.com/import",onChange(info) {console.log("我同时触发了onChange和onDrop");const { status } = info.file;if (status !== "uploading") {const file = info.file.originFileObj; //if (file) {const reader = new FileReader();reader.onload = (e) => {const target = e.target?.result as ArrayBuffer;if (target) {const data = target; // 安全访问const workbook = XLSX.read(data, { type: "binary" });const first_worksheet = workbook.Sheets[workbook.SheetNames[0]];const jsonArr = XLSX.utils.sheet_to_json(first_worksheet, {header: 1,});const res = handleImpotedJson<U>(jsonArr as ExcelRowItem[],headers);console.log(res, "我是最后的结果");setStaffList((prevStaffList) => {return [...prevStaffList, ...res];});// 添加这一行,开始读取文件reader.readAsArrayBuffer(file);}}if (status === "done") {message.success(`${info.file.name} 文件已成功上传.`);} else if (status === "error") {message.error(`${info.file.name} 文件上传失败.`);}},onDrop(e) {console.log("我开始拖拽了", e.dataTransfer.files);},};return (//  这个是antd的上传组件<><Dragger {...props}><p className="ant-upload-drag-icon"><InboxOutlined /></p><p className="ant-upload-text">点击或拖拽上传文件哦!!!</p><p className="ant-upload-hint">支持单个文件或批量上传。严禁上传公司数据或其他受禁文件。</p></Dragger></>);
}export default UploadExcel;

使用示例:
也是引入组件,传表头,和你的修改状态的setState

import UploadExcel from "@/components/UploadExcel";
import {useState} from 'react'const [staffList, setStaffList] = useState<IStaff[]>([]);const header: StaffKeys[] = ["name", "region", "role", "phone"];<UploadExcel<StaffMemberData>headers={header}setStaffList={setStaffList}/>

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总结

通过封装 Excel 导入导出组件,我们可以将重复的逻辑抽象为可复用的模块,提升开发效率和代码质量。关键点包括:

  • 使用 xlsx 库处理核心逻辑。
  • 结合 Ant Design 等 UI 框架提升交互体验。
  • 通过配置化(如列映射、泛型约束)满足不同场景需求。

http://www.hkcw.cn/article/dFPVxrQYHd.shtml

相关文章

用calibredrv提取版图中指定类型cell,保留位置信息并输出新的gds

我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧? 拾陆楼知识星球入口 现在有一个gds,其中的bump位置信息是我们需要的,如何从现有的gds中提取我们需要的部分呢? 需要用到工具calibredrv,如果数量少,可以用图形界面操作,方法如下: 01 打开gds calibredrv -m inp…

iOS 使用CocoaPods 添加Alamofire 提示错误的问题

Sandbox: rsync(59817) deny(1) file-write-create /Users/aaa/Library/Developer/Xcode/DerivedData/myApp-bpwnzikesjzmbadkbokxllvexrrl/Build/Products/Debug-iphoneos/myApp.app/Frameworks/Alamofire.framework/Alamofire.bundle把这个改成 no 2 设置配置文件

Python基本运算符

White graces&#xff1a;个人主页 &#x1f439;今日诗词:相恨不如潮有信&#xff0c;相思始觉海非深&#x1f439; ⛳️点赞 ☀️收藏⭐️关注&#x1f4ac;卑微小博主&#x1f64f; ⛳️点赞 ☀️收藏⭐️关注&#x1f4ac;卑微小博主&#x1f64f; 目录 &#x1f9ee; Pyt…

nginx: [emerg] bind() to 0.0.0.0:80 failed (10013: 80端口被占用

Nginx启动报错&#xff1a;nginx: [emerg] bind() to 0.0.0.0:80 failed (10013: An attempt was made to access a socket in a way forbidden by its access permissions) 这个报错代表80端口被占用 先查看占用80的端口 netstat -aon | findstr :80 把它杀掉&#xff0c;强…

vscode命令行debug

vscode命令行debug 一般命令行debug会在远程连服务器的时候用上&#xff0c;命令行debug的本质是在执行时暴露一个监听端口&#xff0c;通过进入这个端口&#xff0c;像本地调试一样进行。 这里提供两种方式&#xff1a; 直接在命令行中添加debugpy&#xff0c;适用于python…

(笔记+作业)第五期书生大模型实战营---L1G2000 OpenCompass 评测书生大模型实践

学员闯关手册&#xff1a;https://aicarrier.feishu.cn/wiki/QdhEwaIINietCak3Y1dcdbLJn3e 课程视频&#xff1a;https://www.bilibili.com/video/BV13U1VYmEUr/ 课程文档&#xff1a;https://github.com/InternLM/Tutorial/tree/camp4/docs/L0/Python 关卡作业&#xff1a;htt…

激光雷达的强度像和距离像误差与噪声分析(1)2025.5.30

激光雷达的强度像和距离像在测量过程中可能受到多种误差和噪声的影响&#xff0c;这些因素既包括硬件本身的物理特性&#xff0c;也涉及环境条件和算法处理等外部因素。以下是主要误差类型、噪声来源及其关键影响因素的综合分析&#xff1a; 一、强度像的误差与噪声 能量信号…

uboot移植之IOMUX介绍

本章节主要讲&#xff0c;如何将NXP官方i.MX6ULL EVK评估板的uboot源码移植适配到ELF 1开发板。本身uboot的作用就是启动内核&#xff0c;只要能成功启动内核&#xff0c;uboot使命便已完成。但是从开发调试的角度来讲&#xff0c;有时候我们需要在uboot阶段使用一些外设接口方…

3DMAX+Photoshop教程:将树木和人物添加到户外建筑场景中的方法

在本教程中&#xff0c;我将向您展示如何制作室外场景。我不会详细解释每一个细节&#xff0c;而是想快速概述一下我的方法。 在本教程中&#xff0c;我使用了一个相对简单的3D模型&#xff0c;并向您展示了在一些高质量纹理的帮助下可以做什么。此外&#xff0c;我将向您展示…

n8n 中文系列教程_25.在n8n中调用外部Python库

在n8n中使用Python处理复杂任务时&#xff0c;内置的Code节点由于运行在沙盒环境中&#xff0c;无法直接调用外部Python库&#xff08;如pandas、requests等&#xff09;&#xff0c;限制了工作流的扩展能力。本文将介绍一种持久化解决方案&#xff1a;通过Docker挂载目录虚拟环…

STM32单片机简介

1.基本情况 STM32单片机正如其名是32位微控制器&#xff0c;相较于51单片机的8位微控制器&#xff0c;性能会更好&#xff0c;但学习难度也会提高。 在stm32单片机中内核时核心部分&#xff0c;是ARM公司设计的&#xff0c;其在stm32单片机中占据极为重要的地位。(程序指令的…

安全帽目标检测

安全帽数据集 这里我们使用的安全帽数据集是HelmentDetection&#xff0c;这是一个公开数据集&#xff0c;里面包含5000张voc标注格式的图像&#xff0c;分为三个类别&#xff0c;分别是 0: head 1: helmet 2: person 安全帽数据集下载地址、 我们将数据集下载后&#xff0c…

气镇阀是什么?

01、阀门介绍&#xff1a; 油封机械真空泵的压缩室上开一小孔&#xff0c;并装上调节阀&#xff0c;当打开阀并调节入气量&#xff0c;转子转到某一位置&#xff0c;空气就通过此孔掺入压缩室以降低压缩比&#xff0c;从而使大部分蒸汽不致凝结而和掺入的气体一起被排除泵外起此…

1,QT的编译教程

目录 整体流程&#xff1a; 1&#xff0c;新建project文件 2,编写源代码 3&#xff0c;打开QT的命令行窗口 4&#xff0c;生成工程文件&#xff08;QT_demo.pro&#xff09; 5&#xff0c;生成Make file 6&#xff0c;编译工程 7&#xff0c;运行编译好的可执行文件 整体…

Linux操作系统 使用共享内存实现进程通信和同步

共享内存使用 //main.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <assert.h> #include <sys/shm.h> #include <string.h> int main() {int shmidshmget((key_t)1234,256,IPC_CREAT|0600);assert(shmid!-1);…

力扣HOT100之动态规划:322. 零钱兑换

这道题和上一道题279.完全平方数的套路是完全一样的&#xff0c;但是这道题不需要我们自己生成物品列表&#xff0c;函数的输入中已经给出了&#xff0c;但是这道题有一个坑&#xff0c;就是我们在初始化dp数组的时候&#xff0c;所有的位置不应该赋值为INT_MAX&#xff0c;因为…

工厂方法模式(Factory Method)深度解析:从原理到实战优化

作者简介 我是摘星&#xff0c;一名全栈开发者&#xff0c;专注 Java后端开发、AI工程化 与 云计算架构 领域&#xff0c;擅长Python技术栈。热衷于探索前沿技术&#xff0c;包括大模型应用、云原生解决方案及自动化工具开发。日常深耕技术实践&#xff0c;乐于分享实战经验与…

π0-FAST-针对VLA模型的高效动作token化技术-2025.1.16-开源

0. 前言 2025年2月4日&#xff0c;π0 和 π0-FAST 一并开源&#xff0c;这个系列许多研究者、企业人士认为落地潜力很大 项目页 论文页 GitHub页 之前已经做了 π0 论文的详解&#xff1a;π0-通用VLA模型-2024.11.13-开源 本文来详解一下 π0-FAST 1. π0-FAST&#xff1…

正点原子Z20 ZYNQ ​​​开发板​​发布!板载FMC LPC、LVDS LCD和WIFI蓝牙等接口,资料丰富!

正点原子Z20 ZYNQ ​​​开发板​​发布&#xff01;板载FMC LPC、LVDS LCD和WIFI&蓝牙等接口&#xff0c;资料丰富&#xff01; 正点原子新品Z20 ZYNQ开发板来啦&#xff01;核心板全工业级设计&#xff0c;主控芯片型号是XC7Z020CLG484-2I。开发板由核心板底板组成&…

SPL做量化----SRMI(动量修正指标)

MI 修正指标&#xff0c;用法参考 MI 动力指数用法。 例如计算浦发银行 2024 年的 SRMI 指标&#xff0c;N 取 9。 代码示例&#xff1a; A160000022024-01-0132024-12-314call("adjustprice.splx", "", call("loadkday.splx", A1, A2,A3) )5…