在Vue中使用xlsx获取表格数据

3,138 阅读1分钟

前言

基于xlsx插件,可以控制读取Excel表中的特定Sheet中的数据和从第几个单元格开始获取数据。

因为xlsx插件默认是从A1单元格开始读取的,这样就不适合某些业务场景,如在表格中包含了一些表格说明等,官方好像也没有API可以进行控制(有可能是我没发现,有发现的朋友可以告知一下),于是自己通过返回的数据进行改造,从而达到目的。

获取读取的数据

readerData(rawFile) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader()
    reader.onload = e => {
      const data = e.target.result
      const workbook = XLSX.read(data, { type: 'array' })

      // 这里是控制获取第几个Sheet表的数据
      const firstSheetName = workbook.SheetNames[this.sheet]
      // 获取还未处理的表格的数据
      let worksheet = workbook.Sheets[firstSheetName]
      // 这里对返回的表格数据进行处理,设置开始读取的单元格
      worksheet = this.handleStartTd(worksheet, this.cell)
      // 获取数据的表头
      const header = this.getHeaderRow(worksheet)
      // 获取最终数据
      const results = XLSX.utils.sheet_to_json(worksheet)
      
      resolve()
    }
    reader.readAsArrayBuffer(rawFile)
  })
}

更改开始读取的单元格位置

handleStartTd(worksheet, start) {
  let old = worksheet['!ref']
  old = old.split(':')
  worksheet['!ref'] = start + ':' + old[1]
  return worksheet
}

完整组件

<template>
  <div style="display: inline;">
    <input ref="excel-upload-input" class="excel-upload-input" type="file" accept=".xlsx, .xls" @change="handleClick">
    <button class="d_btn font14 text_white bg_blue" @click="handleUpload">
      {{text}}
    </button>
  </div>
</template>

<script>
import XLSX from 'xlsx'

export default {
  props: {
    text: {
      type: String,
      default: '导入'
    },
    sheet: { // Excel中的第几个表
      type: Number,
      default: 0
    },
    cell: { // 从哪个单元格开始获取数据
      type: String,
      default: 'A1'
    },
    beforeUpload: Function, // eslint-disable-line
    onSuccess: Function// eslint-disable-line
  },
  data() {
    return {
      loading: false,
      excelData: {
        header: null,
        results: null
      }
    }
  },
  methods: {
    generateData(header, results) {
      this.excelData.header = header
      this.excelData.results = results
      this.onSuccess && this.onSuccess(this.excelData)
    },
    handleUpload() {
      this.$refs['excel-upload-input'].click()
    },
    handleClick(e) {
      const files = e.target.files
      const rawFile = files[0]
      if (!rawFile) return
      this.upload(rawFile)
    },
    upload(rawFile) {
      this.$refs['excel-upload-input'].value = null

      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[this.sheet]
          let worksheet = workbook.Sheets[firstSheetName]
          worksheet = this.handleStartTd(worksheet, this.cell)
          const header = this.getHeaderRow(worksheet)
          const results = XLSX.utils.sheet_to_json(worksheet)
          this.generateData(header, results)
          this.loading = false
          resolve()
        }
        reader.readAsArrayBuffer(rawFile)
      })
    },
    handleStartTd(worksheet, start) {
      let old = worksheet['!ref']
      old = old.split(':')
      worksheet['!ref'] = start + ':' + old[1]
      return worksheet
    },
    getHeaderRow(sheet) {
      const headers = []
      const range = XLSX.utils.decode_range(sheet['!ref'])
      let C
      const R = range.s.r
      /* start in the first row */
      for (C = range.s.c; C <= range.e.c; ++C) { /* walk every column in the range */
        const cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })]
        /* find the cell in the first row */
        let hdr = 'UNKNOWN ' + C // <-- replace with your desired default
        if (cell && cell.t) hdr = XLSX.utils.format_cell(cell)
        headers.push(hdr)
      }
      return headers
    },
  }
}
</script>

<style scoped>
.excel-upload-input{
  display: none;
  z-index: -9999;
}
.d_btn {
  height: 40px;
  padding: 0 21px;
  border: 0;
  outline: 0;
  cursor: pointer;
  border-radius: 4px;
}
</style>

使用

<upload-excel :cell="'A8'" :on-success="execlSuccess"></upload-excel>