使用xlsx前端导入/导出excel文件

586 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情

日常需求中,可能存在业务场景需要前端做以下处理

  1. 校验导入的Excel文件是否有数据
  2. 对Excel文件转化为Json格式
  3. Json转化为Excel文件

xlsx插件提供了以上问题的解决方案,以下以小demo为例简单说明实现:

一、使用npm安装xlsx

npm i xlsx

二、前端获取Excel文件数据

1、封装从 excel 文件读取数据

传参需要

  • excelRcFileBuffer: excel 文件
  • index: excel 文件中的第几张表
  • moduleName: 表格中表头映射对应的key值的模板name

通过函数parseAndServeChange见步骤3进行数据格式转换

转换前

转换后

import * as xlsx from 'xlsx';
import type { WorkBook } from 'xlsx';

/**
 * 从 excel 文件读取数据
 * @param excelRcFileBuffer excel 文件
 * @param index 第几张表
 * @param moduleName 映射模板name
 */
export function importExcelFromBuffer<Item = any>(excelRcFileBuffer: ArrayBuffer, index:number = 0, moduleName: string): Item[] {
  if (moduleName === '') {
    console.log('请输入对应的映射模板name');
    return []
  }
  // 读取表格对象
  const workbook = xlsx.read(excelRcFileBuffer, {type: 'buffer'});
  // 找到对应index表
  const sheetNames = workbook.SheetNames;
  const sheet = workbook.Sheets[sheetNames[index]];
  let outdata: Item[] = xlsx.utils.sheet_to_json(sheet);
  outdata = parseAndServeChange(outdata, fieldMapping[moduleName]);
  console.log('data========================', outdata);
  if (outdata.length === 0) {
    console.log('请检查模板是否正确/内容是否为空');
  }
  // 读取内容
  return outdata;
}

2、封装映射模板fieldMapping

表格中表头对应后端映射的key值

const UserInfo = {
	name: "姓名", 
	phone: "联系电话", 
	age: "年龄", 
	local: "籍贯"
}
export {
	UserInfo,
}

3、处理读取出来数据转换为服务器可读取的数据/将服务器数据转换为Excel表头对应字段数据

即[{姓名: "张三",联系电话:13800013800,年龄: "28",籍贯:"广东广州"}]和[{name:"张三", phone: 13800013800, 年龄: 18, local: "广东广州"}]数据根据对应类型相互转换

把读取出来得数据变为组后可以传递给服务器的数据,传参需要:

  • data: excel解析数据
  • revertChart: 字段映射

返回值:

  • returns: 返回对应格式数据
import * as fieldMapping from './fieldMapping';
/**
 * @description 把数据进行不同类型转换
 * @param data excel解析数据
 * @param revertChart 字段映射 
 * @param type 字段映射 type为out则为数据变为组后可以传递给服务器的数据, 否则服务器的数据转换为Excel表头对应字段数据
 * @returns 返回对应格式数据
 */
const parseAndServeChange = (data: any, revertChart: any, type: string = 'out') => {
  const arr: any = [];
  data.forEach((item: any) => {
    const obj = {};
    Object.keys(revertChart).forEach(key => {
      let _key = type === 'out' ? key : revertChart[key]
      let _val = type === 'out' ? item[revertChart[key]] : item[key]
      obj[_key] = _val;
    })
    arr.push(obj);
  });
  return arr;
};

4、在组件中使用

import { importExcelFromBuffer } from '@/utils/xlsx/utils'
const excelMimeType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
import { Button, Upload } from 'antd';
...
const Index: React.FC = () => {
  const [excelData, setExcelData] = useState<any[]>([]);
  // 获取文件并解析
  const localExcelToData = async (options: any) => {
    const {file, onError, onSuccess} = options;
    try {
      // xlsx 导入 excel
      const excelData = importExcelFromBuffer<any>(await (file).arrayBuffer(), 0, 'UserInfo');
      // 设置 data
      setExcelData(excelData);
      if (onSuccess) onSuccess(excelData, new XMLHttpRequest());
    } catch (e) {
      if (onError) onError(e)
    }
  }
  return (
    <PageContainer>
      <Upload accept={excelMimeType} customRequest={localExcelToData}>
        <Button type="primary">前端Excel转Data</Button>
      </Upload>
    </PageContainer>
  );
}

三、前端导出Excel文件

1、封装从 json 转 excel 文件

传参需要

  • dataSource: JSON 数组
  • moduleName: 映射模板name
  • sheetName: 表名
  • fileName: 文件名
import * as xlsx from 'xlsx';
import type { WorkBook } from 'xlsx';
import * as fieldMapping from './fieldMapping';
/**
 * 导出 excel 文件
 * @param dataSource JSON 数组
 * @param moduleName 映射模板name
 * @param sheetName 表名
 * @param fileName 文件名
 */
 export function exportExcelFile(dataSource: any[], moduleName: string, sheetName = '表1', fileName = 'example.xlsx') {
  const inputData = parseAndServeChange(dataSource, fieldMapping[moduleName], 'input')
  const jsonWorkSheet = xlsx.utils.json_to_sheet(inputData);
  const workBook: WorkBook = {
    SheetNames: [sheetName],
    Sheets: {
      [sheetName]: jsonWorkSheet,
    }
  };
  return xlsx.writeFile(workBook, fileName);
}

2、在组件中使用

import { exportExcelFile } from '@/utils/xlsx/utils'
import { Button } from 'antd';
...
const Index: React.FC = () => {
  const [excelData, setExcelData] = useState<any[]>([]);
  return (
    <PageContainer>
      <Button
        disabled={excelData.length === 0}
        onClick={() => exportExcelFile(excelData, 'UserInfo')}
        type="primary"
      >
        前端Data转Excel
      </Button>
    </PageContainer>
  );
}