Pure front-end implementation of importing and exporting Excel

Recently, I often do irregular Excelimports, or some common Excelexports. At present, the above mentioned are pure front-end implementations; let’s talk about the frequently used Excel export and import implementation schemes. This article implements the technology stack with Vue2 + JS as an example

Import categories:

  1. Calling APIThe data is completely parsed and cleaned by the backend, and the frontend is only responsible for calling API;
  2. The front end parses Excel, cleans the data, and processes the corresponding data into the APIrequired JSON; (this article mainly introduces this)

Export categories:

  1. The call APIis completely generated by the backend Excel, and the frontend obtains APIthe returned file name and downloads it;
  2. The front end is generated based on JSON data , and then downloaded Excelusing a third-party library ; (this article mainly introduces this)file-saver

Import Excelneeds to use xlsxthis npm library

Export Excelneeds to be used exceljs, file-saverthese two

Just npm install the corresponding library directly;

1. Import Excel and process data

1.1 Requirement Example

insert image description here

If I now have an Excel of this kind that needs to be imported, the front end is responsible for parsing the Excel and cleaning the data, and the API only needs 4-5 useful fields

1.2 Specific implementation – html part

<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>
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 Specific implementation – single sheet

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 Specific implementation – multiple sheets

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 Related parameters

file read type

type expected input
base64 Base64 encoded type string
binary binary string (byte n is data.charCodeAt(n))
string JS string (only for UTF-8 text format)
buffer The buffer type of nodejs
array array
file The path to the file that will be read (nodejs only)

common method

  • sheet_to_*The function accepts a worksheet and an optional options object, mainly to convert the excel file into the corresponding data format, generally used when importing the excel file
  • *_to_sheetThe function accepts a data object and an optional options object, mainly to convert the data format into an excel file, which is generally used when exporting files
  • sheet_add_*Function accepts worksheet, data and optional options. The main purpose is to update an existing worksheet object

2. According to the existing data, export Excel as needed

1.1 Requirement Example

insert image description here

If I now have a query table of this kind that needs to be exported, because all the data is in the table, it APIcan be implemented without calling

1.2 Specific implementation

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;
}

If 大数据的量导出it is recommended to implement it on the backend, the frontend should websocketbe optimized to avoid bad user experience caused by long-time loading

Guess you like

Origin blog.csdn.net/weixin_56650035/article/details/132666602