文章目錄
- 前言
- 一、為什么需要封裝 Excel 組件?
- 二、技術選型
- 三、核心實現
- 1. 安裝依賴
- 2. 封裝Excel導出
- 3. 封裝導入組件 (UploadExcel)
- 總結
前言
在 React 項目中,處理 Excel 文件的導入和導出是常見的業務需求。無論是導出報表數據供用戶下載,還是讓用戶上傳 Excel 文件進行數據解析,一個高效、易用的組件都能極大提升開發效率和用戶體驗。本文將分享如何在 React 項目中封裝一個通用的 Excel 導入導出組件,涵蓋核心實現思路、代碼示例以及最佳實踐。
一、為什么需要封裝 Excel 組件?
- 統一處理邏輯:避免在多個頁面重復編寫 Excel 解析或生成代碼。
- 提升用戶體驗:通過統一的 UI 和交互,降低用戶學習成本。
- 減少錯誤:集中處理文件格式校驗、數據轉換等易錯環節。
- 可擴展性:支持自定義配置(如列映射、樣式調整等)。
二、技術選型
- 導出 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 框架提升交互體驗。
- 通過配置化(如列映射、泛型約束)滿足不同場景需求。