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

文章目录

  • 前言
    • 一、为什么需要封装 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.pswp.cn/bicheng/83195.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RustDesk 搭建自建服务器并设置服务自启动

目录 0. 介绍 1. 事前准备 1.1 有公网 ip 的云服务器一台 1.2 服务端部署包 1.3 客户端安装包 2. 部署 2.1 服务器环境准备 2.2 上传服务端部署包 2.3 运行 pm2 3. 客户端使用 3.1 安装 3.2 配置 3.2.1 解锁网络设置 3.2.2 ID / 中级服务器 3.3 启动效果 > …

基于Qt封装数据库基本增删改查操作,支持多线程,并实现SQLite数据库单例访问

抽出来的&#xff0c;直接用就行 头文件CPP文件使用示例 头文件 #ifndef DATABASECOMMON_H #define DATABASECOMMON_H/** 单例封装SQLite通用操作&#xff0c;支持多线程调用&#xff1b;可扩展兼容其他数据库&#xff0c;照着SysRunDatabase写&#xff0c;并且重载openDataba…

AI笔记 - 网络模型 - mobileNet

网络模型 mobileNet mobileNet V1网络结构深度可分离卷积空间可分![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/aff06377feac40b787cfc882be7c6e5d.png) 参考 mobileNet V1 网络结构 MobileNetV1可以理解为VGG中的标准卷积层换成深度可分离卷积 可分离卷积主要有…

第十五篇:MySQL 高级实战项目:构建高可用、可观测、性能优化一体化数据库平台

本篇聚焦于如何基于 MySQL 构建一个真正面向生产环境的数据库平台&#xff0c;集成高可用、可观测与性能调优三大核心能力&#xff0c;助力稳定、可扩展的系统运行。 一、项目背景与目标 在实际生产环境中&#xff0c;数据库系统需要应对以下挑战&#xff1a; 业务高速增长带来…

华为OD机试真题——文件目录大小(2025 A卷:100分)Java/python/JavaScript/C++/C语言/GO六种语言最佳实现

2025 A卷 100分 题型 本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析; 并提供Java、python、JavaScript、C++、C语言、GO六种语言的最佳实现方式! 2025华为OD真题目录+全流程解析/备考攻略/经验分享 华为OD机试真题《文件目录大小》: 目录 题…

qwen 2.5 并行计算机制:依靠 PyTorch 和 Transformers 库的分布式能力

qwen 2.5 并行计算机制:依靠 PyTorch 和 Transformers 库的分布式能力 完整可运行代码: import torch import torch.nn.functional as F from transformers

TIDB创建索引失败 mkdir /tmp/tidb/tmp_ddl-4000/1370: no such file or directory.

TIDB创建索引失败&#xff1a;解决“mkdir /tmp/tidb/tmp_ddl-4000/1370: no such file or directory”问题 在使用 TIDB 数据库时&#xff0c;我们有时会遇到创建索引失败的问题。常见的错误信息为&#xff1a; mkdir /tmp/tidb/tmp_ddl-4000/1370: no such file or directo…

华为OD机试真题—— 最少数量线段覆盖/多线段数据压缩(2025A卷:100分)Java/python/JavaScript/C++/C语言/GO六种最佳实现

2025 A卷 100分 题型 本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析; 并提供Java、python、JavaScript、C++、C语言、GO六种语言的最佳实现方式! 2025华为OD真题目录+全流程解析/备考攻略/经验分享 华为OD机试真题《最少数量线段覆盖/多线段数…

EasyRTC嵌入式音视频实时通话SDK助力AI与IoT智能硬件打造音视频交互多场景应用

一、引言​ 在数字化浪潮下&#xff0c;AI与IoT深度融合重塑智能硬件产业。实时音视频通信是智能硬件交互的核心&#xff0c;其性能关乎用户体验与场景拓展。EasyRTC嵌入式音视频实时通话SDK基于WebRTC技术&#xff0c;以轻量、易扩展的特性&#xff0c;为AI与IoT智能硬件融合…

第十四章 MQTT订阅

系列文章目录 系列文章目录 第一章 总体概述 第二章 在实体机上安装ubuntu 第三章 Windows远程连接ubuntu 第四章 使用Docker安装和运行EMQX 第五章 Docker卸载EMQX 第六章 EMQX客户端MQTTX Desktop的安装与使用 第七章 EMQX客户端MQTTX CLI的安装与使用 第八章 Wireshark工具…

【第4章 图像与视频】4.4 离屏 canvas

文章目录 前言为什么要使用 offscreenCanvas为什么要使用 OffscreenCanvas如何使用 OffscreenCanvas第一种使用方式第二种使用方式 计算时长超过多长时间适合用Web Worker 前言 在 Canvas 开发中&#xff0c;我们经常需要处理复杂的图形和动画&#xff0c;这些操作可能会影响页…

Go语言事件总线EventBus本地事件总线系统的完整实现框架

在Go语言中&#xff0c;EventBus是一种非常有用的工具&#xff0c;它通过事件驱动的编程方式&#xff0c;帮助开发者实现组件之间的解耦&#xff0c;提高代码的可维护性和扩展性。 背景 软件架构的发展需求&#xff1a;随着软件系统的规模和复杂度不断增大&#xff0c;传统的紧…

Go语言接口:灵活多态的核心机制

引言 Go语言的接口系统是其​​面向对象编程​​的核心&#xff0c;它摒弃了传统语言的类继承体系&#xff0c;采用独特的​​隐式实现​​和​​鸭子类型​​设计。这种设计使得Go接口既灵活又强大&#xff0c;成为构建松耦合系统的关键工具。本文将深入剖析Go接口的实现机制…

DeviceNET转EtherCAT网关:医院药房自动化的智能升级神经中枢

在现代医院药房自动化系统中&#xff0c;高效、精准、可靠的设备通信是保障患者用药安全与效率的核心。当面临既有支持DeviceNET协议的传感器、执行器&#xff08;如药盒状态传感器、机械臂限位开关&#xff09;需接入先进EtherCAT高速实时网络时&#xff0c;JH-DVN-ECT疆鸿智能…

android实现使用RecyclerView详细

显示页面代码&#xff1a;activity_category_inventory.xml代码&#xff1a; <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android" xmlns:app"http://schemas.and…

【SpringBoot实战】优雅关闭服务

文章目录 一、什么是优雅关闭&#xff1f;二、优雅关闭的核心步骤三、SpringBoot优雅关闭实现四、关键注意事项1. 超时时间必须配置2. 信号支持局限性3. 特殊请求处理 五、底层实现原理六、总结 一、什么是优雅关闭&#xff1f; 优雅关闭&#xff08;Graceful Shutdown&#x…

C++哈希表:unordered系列容器详解

本节目标 1.unordered系列关联式容器 2.底层结构 3.模拟实现 4.哈希的应用 5.海量数据处理面试题 unordered系列关联式容器 在c98中&#xff0c;STL提供了底层为红黑树结构的一系列关联式容器&#xff0c;在查询时效率可以达到logN&#xff0c;即最差的情况下需要比较红…

java操作服务器文件(把解析过的文件迁移到历史文件夹地下)

第一步导出依赖 <dependency><groupId>org.apache.sshd</groupId><artifactId>sshd-core</artifactId><version>2.13.0</version></dependency> 第二步写代码 public void moveFile( List<HmAnalysisFiles> hmAnalys…

Oracle OCP认证的技术定位怎么样?

一、引言&#xff1a;Oracle OCP认证的技术定位​ Oracle Certified Professional&#xff08;OCP&#xff09;认证是数据库领域含金量最高的国际认证之一&#xff0c;其核心价值在于培养具备企业级数据库全生命周期管理能力的专业人才。随着数字化转型加速&#xff0c;OCP认证…

TK海外抢单源码/指定卡单

​ 抢单源码&#xff0c;有指定派单&#xff0c;打针&#xff0c;这套二改过充值跳转客服 前端vue 后端php 两端分离 可二开 可以指定卡第几单&#xff0c;金额多少&#xff0c; 前后端开源 PHP7.2 MySQL5.6 前端要www.域名&#xff0c;后端要admin.域名 前端直接静态 伪静…