基于Vue实现excel导入导出

5,326 阅读4分钟

导入excel表:

导入excel导入功能vue-element-admin已经提供了,只要基于源码改造即可。

(1):excel导入功能需要使用npm包xlsx,所以需要安装xlsx插件, 使用 npm i xlsx 下载组件。

(2):将vue-element-admin提供的导入功能,新建成一个组件,组件源码地址(cv过去就好了):

https://github.com/PanJiaChen/vue-element-admin/blob/master/src/components/UploadExcel/index.vue

得到类似于这样的页面(多少会有点差异,我修改了一下样式):

图片.png

(3):使用Vue全局注册的方式注册并导入 excel 组件

import UploadExcel from './UploadExcel'
export default {
install(Vue) {
  Vue.component('UploadExcel', UploadExcel) // 注册导入excel组件
  }
}

(4):使用一个中间组件导入,注册在全局的excel组件(为什么要使用一个中间模块呢?因为在excel组件源码中需要传递两个props且为函数,需要使用一个中间组件传参,这是最方便以及简单的办法了)

props: {
    beforeUpload: Function, // eslint-disable-line
    onSuccess: Function// eslint-disable-line
  },

(5):从源码中我们可以看到打开文件按钮其实打开的其实是隐藏域input type 为 file类型的文件域,其中 @change="handleClick 是用来判断文件域中有无数据,从而决定是否调用upload 函数, upload 函数中判断使用props传过来的参数 beforeUpload 函数是否有值,有值就直接执行并拿到返回值 before ,如果传递了函数 beforeUpload,并且没有 return 则结束导入,导入失败,如果函数 beforeUpload 有返回值,或者没有传 beforeUpload 则调用函数 readerData 并且把 excel 文件作为形参传递过去,在函数 readerData 中我们可以看到 读取文件是基于原生 FileReader 构造函数,并且返回值是一个Promise对象,在 reader.onload 事件中我们可以看到,header 和 results 这两个返回值被传入到 generateData 函数中,generateData 函数拿到结果后,判断我们是否传递参数 onSuccess ,如果传递,则调用函数传递结果 header 和 results

以上我们可以看到使用该组件非常的简单,只需要传递 onSuccess 函数, 如果在 readerData 读取excel表之前还需要做什么操作时就要传递 beforeUpload 函数(一定要有返回值, 否则上传文件失败)。

<input ref="excel-upload-input" class="excel-upload-input" type="file" accept=".xlsx, .xls" @change="handleClick">

<div class="drop" @drop="handleDrop" @dragover="handleDragover" @dragenter="handleDragover">

<el-button :loading="loading" style="margin-left:16px;" size="mini" type="primary" @click="handleUpload">打开文件</el-button>
handleUpload() {
      this.$refs['excel-upload-input'].click();
    },

handleClick(e) {
      const files = e.target.files;
      const rawFile = files[0]; // only use files[0]
      if (!rawFile) return;
      this.upload(rawFile);
    },
upload(rawFile) {
      this.$refs['excel-upload-input'].value = null; // fix can't select the same excel
      if (!this.beforeUpload) {
        this.readerData(rawFile);
        return;
      }
      const before = this.beforeUpload(rawFile);
      if (before) {
        this.readerData(rawFile);
      }
    },
readerData(rawFile) {
      this.loading = true;
      return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = e => {
          const data = e.target.result;
          const workbook = XLSX.read(data, { type: 'array' });
          const firstSheetName = workbook.SheetNames[0];
          const worksheet = workbook.Sheets[firstSheetName];
          const header = this.getHeaderRow(worksheet);
          const results = XLSX.utils.sheet_to_json(worksheet);
          this.generateData({ header, results });
          this.loading = false;
          resolve();
        };

        reader.readAsArrayBuffer(rawFile);
      });
    },
generateData({ header, results }) {
      this.excelData.header = header;
      this.excelData.results = results;
      this.onSuccess && this.onSuccess(this.excelData);
    },

(6):拿到 headerresults 结果后结果类似于下图:

图片.png

根据自己的需要的格式进行格式转换,然后调用后台接口就基本完成了,但是excel表中有一个时间问题需要自己转换一下时间格式,该函数我已经写出来并贴在代码区了(这里就不细说了):

    formatDate(numb, format) {
      const time = new Date((numb - 1) * 24 * 3600000 + 1)
      time.setYear(time.getFullYear() - 70)
      const year = time.getFullYear() + ''
      const month = time.getMonth() + 1 + ''
      const date = time.getDate() - 1 + ''
      if (format && format.length === 1) {
        return year + format + month + format + date
      }
      return year + (month < 10 ? '0' + month : month) + (date < 10 ? '0' + date : date)
    }

导出excel表:

1、Excel 的导入导出都是依赖于js-xlsx来实现的, 在 js-xlsx的基础上又封装了Export2Excel.js来方便导出数据。

2、由于 Export2Excel不仅依赖js-xlsx还依赖file-saver和script-loader,所以开始前需要下载xlsx file-saverscript-loader 两个包:

npm install xlsx file-saver -S
npm install script-loader -S -D

(1):由于js-xlsx体积还是很大的,导出功能也不是一个非常常用的功能,所以使用的时候建议使用懒加载。使用方法如下:

import('@/vendor/Export2Excel').then(excel => {
  excel.export_json_to_excel({
    header: tHeader, //表头 必填
    data, //具体数据 必填
    filename: 'excel-list', //非必填
    autoWidth: true, //非必填
    bookType: 'xlsx' //非必填
  })
})

(2):excel导出参数的介绍:

参数说明类型可选值默认值
header导出数据的表头Array/[]
data导出的具体数据Array/[[]]
filename导出文件名String/excel-list
autoWidth单元格是否要自适应宽度Booleantrue / falsetrue
bookType导出文件类型Stringxlsx, csv, txt, morexlsx

(3):我们最重要的一件事,就是把表头和数据进行相应的对应填上 headerdata ,一个生成表头,一个生成表体,将数据转换好数据结构之后直接传入即可。

(4):我们从上文中说道excel表有时间问题,其excel时间与js时间对应不上,我们存入的时候做了时间转换,所以现在拿出也要做时间转换,代码如下:

function formatDate(date, fmt = 'yyyy-MM-dd') {
  if (!(date instanceof Array)) {
    date = new Date(date);
  }
  if (/(y+)/.test(fmt)) {
    fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
  }
  const o = {
    'M+': date.getMonth() + 1,
    'd+': date.getDate(),
    'h+': date.getHours(),
    'm+': date.getMinutes(),
    's+': date.getSeconds()
  };
  for (const k in o) {
    if (new RegExp(`(${k})`).test(fmt)) {
      const str = o[k] + '';
      fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? str : padLeftZero(str));
    }
  }
  return fmt;
}

复杂表头的导出:

excel中复杂表头的导出方法 主要是 multiHeadermerges 两个参数

参数说明类型可选值默认值
multiHeader复杂表头的部分Array/[[]]
merges需要合并的部分Array/[]]

multiHeader里面是一个二维数组,里面的一个元素是一行表头,假设你想得到一个如图的结构mutiHeader应该这样定义:

const multiHeader = [['姓名', '主要信息', '', '', '', '', '部门']]

multiHeader中的一行表头中的字段的个数需要和真正的列数相等,假设想要跨列,多余的空间需要定义成空串它主要对应的是标准的表头

const header = ['姓名', '手机号', '入职日期', '聘用形式', '转正日期', '工号', '部门']

如果,我们要实现其合并的效果, 需要设定merges选项

  const merges = ['A1:A2', 'B1:F1', 'G1:G2']

merges的顺序是没关系的,只要配置这两个属性,就可以导出复杂表头的excel了

最后配置项:

 excel.export_json_to_excel({
          header: Object.keys(headers),
          data,
          filename: '员工资料表',
          multiHeader, // 复杂表头
          merges // 合并选项
        })