封装excleJS下载功能

57 阅读2分钟

一、介绍

exceljs 是一款可导出,可读取的 Excel 操作工具,可以实现样式的修改以及 Excel 的高级功能。 本代码基于antDesign.table实现。

解决:

1.加了表头的问题(177)

{
    title: 'parent',
    children: [
      {
          title: 'a',
          dataIndex: 'a',
          key: 'a',
          ...
      }
    ]
}

2.列数多的问题(55) excel A-Z后面的列为AA-AZ再到BA-BZ,CA-CZ...需要再次遍历。

二、代码

<!--单个表格下载功能-->

<template>
  <a-button :loading="loading"
            @click="downLoad"
            style="font-size: 12px"
            :style="{height : `${data[3]?data[3]:24}px`}"
  >
    {{ data[1] }}
  </a-button>
</template>

<script>
import ExcelJS from 'exceljs'
import FileSaver from 'file-saver'

export default {
  name: 'down-load',
  props: {
    //data:[table数据,标题,excel标题,height]
    //excel格式:[{ rowNumber: 1,rowFmt: '0.00%'}]  rowNumFmtX:行数 rowNumFmtY:列数
    data: {
      type: Array,
      default: () => {
        return [
          {
            columns: [],
            dataSource: [],
          },
          '下载',
          '明细',
          24,
        ]
      }
    },
    rowNumFmtX: {
      type: Array,
      default: () => {
        return []
      }
    },
    rowNumFmtY: {
      type: Array,
      default: () => {
        return []
      }
    },
  },
  data () {
    return {
      loading: false,
    }
  },
  methods: {
    changeStr (str) {
      // 数字转字母、字母转数字  A-Z后处理成 AA-AZ BA-BZ。。。
      if (isNaN(str * 1)) {
        if (str.length === 1) return str.charCodeAt(0) - 64
        else if (/^[A-Z]+$/.test(str)) {
          let result = 0
          for (let char of str) {
            result = result * 26 + (char.charCodeAt(0) - 64)
          }
          return result
        }
      } else {
        let num = parseInt(str * 1)
        let result = ''
        while (num > 0) {
          let remainder = (num - 1) % 26 // 计算余数并调整以使1对应'A'
          result = String.fromCharCode(65 + remainder) + result // 将余数转换为对应的大写字母,并添加到结果字符串的前面
          num = Math.floor((num - remainder) / 26) // 更新num以处理下一轮的计算
        }
        return result || 'A' // 如果num为0,则默认返回'A'
      }
    },
    //判断是否有children
    hasChildren (obj) {
      return Object.prototype.hasOwnProperty.call(obj, 'children')
    },
    //获取层数
    depthFun (col) {
      if (this.hasChildren(col)) {
        return 1 + Math.max(...col.children.map(cIt => this.depthFun(cIt)))
      }
      return 1
    },
    //设置层数
    setRowIndex(columns,depth){
      // 设置rowSpan
      const setRowSpan = (obj, i = 1) => {
        if (this.hasChildren(obj)) {
          obj.children.forEach(item => {
            setRowSpan(item, i + 1)
          })
          obj.rowSpan = 1
        } else {
          obj.rowSpan = depth - i + 1
        }
        obj.rowIndex = i
      }
      // 设置RowSpan
      const setColSpan = (obj) => {
        let fun = (o) => {
          if (this.hasChildren(o)) {
            return o.children.map(item => fun(item)).reduce((a, b) => a + b)
          } else return 1
        }
        if (this.hasChildren(obj)) {
          obj.colSpan = fun(obj)
          obj.children.forEach(item => setColSpan(item))
        } else {
          obj.colSpan = 1
        }
      }

      columns.forEach(it => setRowSpan(it))
      columns.forEach(it => setColSpan(it))

      const setStart = (it, i, arr, parentStart = 0) => {
        const { changeStr } = this
        const start = changeStr(arr.slice(0, i).map(_ => _.colSpan).reduce((a, b) => a + b, 1 + parentStart))
        const end = changeStr(changeStr(start) + it.colSpan - 1)
        it.start = start
        it.end = end
        it.startIndex = start + it.rowIndex
        it.endIndex = end + (it.rowIndex + it.rowSpan - 1)
        if (this.hasChildren(it)) {
          it.children.forEach((cit, ci) => setStart(cit, ci, it.children, changeStr(start) - 1))
        }
      }
      columns.forEach((it, i) => setStart(it, i, columns))
      return columns
    },
    downLoad () {
      this.loading = true
      let tables = [].concat(this.data[0])
      const _workbook = new ExcelJS.Workbook()

      // 添加工作表
      const _sheet1 = _workbook.addWorksheet('sheet1')

      //展开column children 获取所有column
      let allColumns = []
      function expandColumns (columnsToExpand) {
        for (let column of columnsToExpand) {
          if (Array.isArray(column.children)) {
            expandColumns(column.children)
          } else {
            allColumns.push(column)
          }
        }
      }
      expandColumns(tables[0].columns)

      // columns
      let _sheet1Columns = []
      allColumns.forEach((item, index) => {
        _sheet1Columns.push({
          header: item.title,
          key: 'a' + index,
          width: 15,
          style: { alignment: { vertical: 'middle', horizontal: item?.align || 'left' } }
        })
      })
      _sheet1.columns = _sheet1Columns

      // 删除第一行
      _sheet1.spliceRows(0, 1)

      // 获取层数
      const depth = Math.max(...tables[0].columns.map(item => this.depthFun(item)))

      // 设置层数 行数从1开始 列数从A开始
      const newColumns = this.setRowIndex(tables[0].columns,depth)

      // 设置空白行 根据newColumns填入表头
      _sheet1.addRows(Array(depth).fill({}).map(() => {}))
      const setHeader = (obj) => {
        if (this.hasChildren(obj)) {
          obj.children.forEach(item => setHeader(item))
        }
        const row = _sheet1.getRow(obj.rowIndex)
        const cell = row.getCell(obj.start)
        cell.value = obj.title
        cell.style = {alignment: {vertical: 'middle', horizontal: 'center'}}
        if (obj.startIndex !== obj.endIndex) {
          _sheet1.mergeCells(`${obj.startIndex}:${obj.endIndex}`);
        }
      }
      newColumns.forEach(item => setHeader(item))

      // 数据
      tables.forEach(item => {
        //prepend为合计行
        [...(item.prepend || []), ...item.dataSource].forEach(data => {
          let row = {}
          allColumns.forEach((column, di) => {
            row['a' + (di)] = data[column.dataIndex]
          })
          _sheet1.addRow(row)
        })
      })

      // 设置表格单位样式
      if (this.rowNumFmtX.length >= 1) {
        this.rowNumFmtX.forEach(item => {
          _sheet1.getRow(item?.rowNumber).numFmt = item?.rowFmt
        })
      }
      if (this.rowNumFmtY.length >= 1) {
        this.rowNumFmtY.forEach(item => {
          _sheet1.getColumn(item?.rowNumber).numFmt = item?.rowFmt
        })
      }
      // 导出表格
      _workbook.xlsx.writeBuffer().then((buffer) => {
        let _file = new Blob([buffer], {
          type: 'application/octet-stream',
        })
        FileSaver.saveAs(_file, `${this.data[2]}.xlsx`)
      })
      this.loading = false
    }
  }
}
</script>

<style scoped>

</style>