纯前端实现 导入 与 导出 Excel

最近经常在做 不规则Excel的导入,或者一些普通Excel的导出,当前以上说的都是纯前端来实现;下面我们来聊聊经常用到的Excel导出与导入的实现方案,本文实现技术栈以 Vue2 + JS 为例
导入分类:

  1. 调用 API 完全由后端来解析数据,清洗数据,前端只负责调用 API

  2. 前端解析 Excel ,清洗数据,把对应的数据处理成 API需要的 JSON;(本文主要介绍这个)
    导出分类:

  3. 调用API 完全由后端来生成Excel,前端获得 API 返回的文件名,下载即可;

  4. 前端根据 JSON 数据来生成 Excel, 然后利用第三方库 file-saver 进行下载;(本文主要介绍这个)

导入 Excel 需要用到 xlsx 这个 npm 库

导出 Excel 需要用到 exceljs , file-saver 这两个

直接 npm install 对应库即可;

1. 导入Excel,处理数据

1.1 需求示例

假如我现在有一个这种 Excel 需要导入,前端负责解析 Excel,清洗数据,API 只需要 4-5 个有用的字段

1.2 具体实现 -- html 部分

html 复制代码
<section>
    <el-button @click="handleUpload" size="mini" type="primary">{{l("ChooseFile")}}</el-button>
    <input v-show="false" @change="handleFileChange" ref="inputFile" type="file" />
    <el-alert type="warning" :closable="false" style="margin-top:6px;">
      {{'Please Upload (xls | xlsx) Type File'}}
    </el-alert>
</section>
javascript 复制代码
import XLSX from "xlsx";

handleUpload() {
  if (!this.importResult) {
    this.$refs["inputFile"].click();
  }
},
handleFileChange(e) {
      const file = e.target.files[0];
      const fileName = file.name.substring(file.name.lastIndexOf(".") + 1);
      if (fileName !== "xlsx" && fileName !== "xls") {
        this.$message.error(this.l("FileTypeError,PleaseTryAgain"));
        return;
      }
      const reader = new FileReader();
      reader.readAsBinaryString(file);
      reader.onload = (e) => {
        const result = e.target.result;
        if (!result) {
          this.errorMsg = this.l("NoData");
          this.step = 1;
          return;
        }
        if (this.importType === 1) {
          this.handleSinglePageExcel(result);
        } else {
          this.handleMultiplePageExcel(result);
        }
      };
      reader.onerror = (err) => {
        throw new Error("UpLoadError: " + err.stack);
      };
    },

1.3 具体实现 -- 单个 sheet

javascript 复制代码
handleSinglePageExcel(data) {
  const wb = XLSX.read(data, {
    type: "binary",
    cellDates: true,
  });
  const sheet = wb.SheetNames[0];
  const importData = XLSX.utils.sheet_to_json(wb.Sheets[sheet], {
    range: -1,
  });
  const arr = [];
  for (let i = 3; i < importData.length; i++) {
    // 处理业务逻辑
  }
  this.importResult = arr;
},

1.4 具体实现 -- 多个 sheet

javascript 复制代码
handleMultiplePageExcel(data) {
  const wb = XLSX.read(data, {
    type: "binary",
    cellDates: true,
  });
  const sheetList = wb.SheetNames;
  const arrMap = {}; // 多 Sheet 页数据;
  sheetList.forEach((t) => {
    const importData = XLSX.utils.sheet_to_json(wb.Sheets[t], {
      range: 2,
    });
    arrMap[t] = importData;
  });
  const arr = [];
  for (let t in arrMap) {
    const importData = arrMap[t];
    // importData : 代表每个 Sheet 页的 Excel 数据
  }
  this.importResult = arr;
},

1.4 相关参数

文件读取类型

类型 预期输入
base64 Base64编码类型字符串
binary 二进制字符串(字节n是data.charCodeAt(n))
string JS字符串(仅适用于UTF-8文本格式)
buffer nodejs的buffer类型
array 数组
file 将被读取的文件路径(仅限nodejs)

常用方法

  • sheet_to_* 函数接受一个工作表和一个可选的options对象,主要是将excel文件转化为对应的数据格式,一般导入excel文件的时候使用
  • *_to_sheet 函数接受一个数据对象和一个可选的options对象,主要是将数据格式转化为excel文件,一般导出文件的时候使用
  • sheet_add_* 函数接受工作表、数据和可选选项。主要用途是更新一个现有的工作表对象

2. 根据已有数据,按需导出Excel

1.1 需求示例

假如我现在有一个这种查询表格需要导出,因为所有的数据都在表格中,所以不需要调用API也可以实现

1.2 具体实现

javascript 复制代码
import { Workbook } from "exceljs";
import { saveAs } from "file-saver";

try {
  this.loading = true;
  // 创建一个工作簿
  const workbook = new Workbook();
  // columns 需要生成的Excel列 { prop, label, width, sheetName | Detail }
  // sheetName 需要生成的 Sheet 页, 如果只生成一个 Sheet Excel 不用考虑这里
  const sheets = _.uniq(this.columns.map((t) => t.sheetName || "Detail"));
  for (let i = 0; i < sheets.length; i++) {
    const columns = this.columns.filter(
      (t) => (t.sheetName || "Detail") === sheets[i]
    );
    // addWorksheet 添加一个 Sheet 页
    const worksheet = workbook.addWorksheet(sheets[i]);
    worksheet.columns = columns.map((t) => {
      // 需求处理
      const label = t.label ? t.label : this.propToLabel(t.prop);
      return {
        header: this.l(label), // Excel 第一行标题
        key: t.prop,
        width: label.length * 2, // Excel 列的宽度
      };
    });
    // this.list -> 当前 table 数据 
    this.list.forEach((t) => {
      const row = [];
      columns.forEach((x) => {
        row.push(t[x.prop] || "");
      });
      // 生成的 Excel Sheet 添加数据
      worksheet.addRow(row);
    });
    // 第一行 Header 行添加自定义样式
    worksheet.getRow(1).eachCell((cell, colNumber) => {
      cell.fill = {
        type: "pattern",
        pattern: "solid",
        fgColor: {
          argb: "cccccc",
        },
        bgColor: {
          argb: "#96C8FB",
        },
      };
    });
  }
  // 导出的文件名
  const code = this.exportTemple.code || new Date().getTime();
  workbook.xlsx.writeBuffer().then((buffer) => {
    // 调用 第三方库 下载刚生成好的Excel
    saveAs(
      new Blob([buffer], {
        type: "application/octet-stream",
      }),
      code + "." + "xlsx"
    );
    this.loading = false;
  });
} catch (e) {
  console.error("clinet export error", e);
} finally {
  this.loading = false;
}

如果大数据的量导出建议还是后端来实现,前端要用 websocket 做优化,避免长时间 loading 带来不好的用户体验

相关推荐
恋猫de小郭36 分钟前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅8 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅9 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊9 小时前
jwt介绍
前端