前端实现Excel批量导入用户数据

4,286 阅读2分钟

前言

在我们日常开发中肯定会遇到批量导入这样的功能实现。

后端:"小凌,你批量导出做的这么优秀,批量导入也做了呗?"。

我:"为啥?这一般都是后端做的呀?"

后端:"你想一下,让用户上传一个文件岂不是要300-400K?如果你前端直接转成JSON数据给我岂不是减少了很多数据传输量?(主要是后端想直接存数据,不用写逻辑)"

01.png

秉承着解决问题提升自己的我决定接下这个任务。

流程图

02.png

组件设计

安装依赖

npm install -S xlsx

上传的组件

ExcelImport.vue


<template>
  <div class="excelImport">
    <input type="file" ref="upload" v-show="false" :accept="accept" />
  </div>
</template>
<script>
import XLSX from "xlsx";
export default {
  name: "excelImport",
  props: {
    accept: {
      // 允许上传的文件格式
      type: String,
      default: ".xls,.xlsx",
    },
    dataRow: {
      // 数据开始列 这个注意excel一定要有表头
      type: Number,
      default: 0,
      validator: (val) => {
        return val >= 0;
      },
    },
    valueMatch: {
      // 数据列 必须与excel中的表列顺序相对应
      type: Array,
    },
  },
  data() {
    return {};
  },
  methods: {
    cleanUpload() {
      // 清空input值
      this.$refs.upload.value = null;
    },
    // 文件上传事件
    handleFileChange(event) {
      // 这边也可以封装成多个文件上传的形式 目前是单个文件
      this.handleReadExcel(event.target.files[0]);
    },
    // (重点)把文件按照二进制读取
    handleReadExcel(file) {
      const fileReader = new FileReader();
      fileReader.onload = (ev) => {
        try {
          const fileData = ev.target.result;
          const workbook = XLSX.read(fileData, {
            type: "binary",
          });
          const wsname = workbook.SheetNames[0]; // 取第一张表(也可以取多个sheet)
          const snArr = XLSX.utils.sheet_to_json(workbook.Sheets[wsname]); // 生成json表格内容
          this.excelDataToJson(snArr);
        } catch (e) {
          console.log(e);
          return false;
        }
      };
      fileReader.readAsBinaryString(file);
    },
    // 表格数据转换
    excelDataToJson(snArr) {
      let retn = [];
      // 如果数据不在第一列 则到相应列去取数据
      if (this.dataRow) {
        snArr = snArr.slice(this.dataRow, snArr.length);
      }
      // 如果不用匹配数据则直接返回原始数据
      if (!this.valueMatch || this.valueMatch.length === 0) {
        // 输出原始数据自己在组件外进行处理
        this.$emit("changeExcel", snArr);
      }
      // 获取一个元素的key值(excel表格原始的key)
      let snKeys = Object.keys(snArr[0]);
      snArr.forEach((snItem) => {
        let pushItem = {};
        // 匹配我们传入的新属性
        this.valueMatch.forEach((item, index) => {
          pushItem[item] = snItem[snKeys[index]];
        });
        retn.push(pushItem);
      });
      // 输出匹配后的数据
      this.$emit("changeExcel", retn);
    },
    // 打开上传
    open() {
      this.$nextTick(() => {
        this.$refs.upload.click();
      });
    },
  },
  mounted() {
    // 绑定更改事件监听
    this.$nextTick(() => {
      this.$refs.upload.removeEventListener("change", this.handleFileChange);
      this.$refs.upload.addEventListener("change", this.handleFileChange);
    });
  },
  beforeDestroy() {
    // 移除更改事件监听
    this.$refs.upload.removeEventListener("change", this.handleFileChange);
  },
};
</script>

使用样例

ExcelImportPage.vue

<template>
  <div class="excelImport">
    <button @click="toImport">导入Excel</button>
    <excel-import
      ref="uploadExamFile"
      @changeExcel="uploadExamFile"
      accept=".xls,.xlsx"
      :dataRow="1"
      :valueMatch="valueMatch"
    ></excel-import>
    <!-- 展示数据 -->
    <table border v-if="dataList && dataList.length > 0">
      <!-- 表头 -->
      <tr>
        <th v-for="hItem in valueTitle" :key="hItem">{{ hItem }}</th>
      </tr>
      <!-- 数据 -->
      <tr v-for="(item, index) in dataList" :key="index">
        <td v-for="jItem in valueMatch" :key="jItem">{{ item[jItem] }}</td>
      </tr>
    </table>
  </div>
</template>
<script>
import ExcelImport from "./ExcelImport";
export default {
  name: "excelImportPage",
  data() {
    return {
      valueMatch: ["name", "sex", "age", "phone", "department", "position"], // 数据列 必须与excel中的表列顺序相对应
      valueTitle: ["姓名", "性别", "年龄", "联系方式", "部门", "职位"],
      dataList: [],
    };
  },
  methods: {
    // 获取数据
    uploadExamFile(data) {
      console.log("导出的数据", data);
      this.dataList = data;
    },
    toImport() {
      this.$refs.uploadExamFile.open();
    },
  },
  components: {
    ExcelImport,
  },
};
</script>

使用效果

03.png

gif2.gif

使用注意

1.表头必须

导入的表格表头必须!导入的表格表头必须!导入的表格表头必须!

04.png

表格的第一行必须有,不然数据会少一行

2.dataRow的定义

以下情况下输入参数为0(默认)

05.png

以下情况下为1

06.png

组件文档

输入参数

参数作用默认值
accept控制上传的文件类型.xls,.xlsx
valueMatch表列匹配的参数名称如['name','age']null
dataRow数据开始行0

绑定方法

方法名作用输出参数
changeExcelexcel解析完成后触发解析完成的数据

主动触发事件

方法名作用输入参数
open打开上传文件
cleanUpload清除文件上传的内容

结合业务

用户批量上传带图片的商品信息

我们可以结合之前我们实现的导出功能。

导出文件参考文章:前端导出Excel,让后端刮目相看

1.上传静态资源到OSS。

2.OSS保存静态图片地址列表到后端。

3.导出静态资源列表为Excel。

4.添加商品名称、价格等其他字段。

5.导入商品信息Excel解析成JSON格式。

6.将数据通过接口上传给后端。

xx.png

项目地址

项目地址:github.com/FireSmallPa…

excel导入组件地址:导入Excel组件数据