vue中将表格数据导出为excel文件

409 阅读3分钟

在日常项目开发中,我们得到的表格数据通常为数组格式

tableData: [{
        date: '2016-05-02',
        name: '王小虎',
        address: '上海市普陀区金沙江路 1518 弄'
      }, {
        date: '2016-05-04',
        name: '王小虎',
        address: '上海市普陀区金沙江路 1517 弄'
      }, {
        date: '2016-05-01',
        name: '王小虎',
        address: '上海市普陀区金沙江路 1519 弄'
      }, {
        date: '2016-05-03',
        name: '王小虎',
        address: '上海市普陀区金沙江路 1516 弄'
      }]

可直接丢给表格进行展示

<el-table
      :data="tableData"
      style="width: 100%"
    >
      <el-table-column
        prop="date"
        label="日期"
        width="180"
      />
      <el-table-column
        prop="name"
        label="姓名"
        width="180"
      />
      <el-table-column
        prop="address"
        label="地址"
      />
    </el-table>

1690271125824.jpg

这里我们借助一个XLSX插件来完成导出(还有其他的导出方式,这里不做叙述)

1.安装xlsx

npm install xlsx

2.封装公共的表格导出方法

/**
 * 封装公共的表格导出方法
 */

var XLSX = require('xlsx')
import { Loading } from 'element-ui'
import axios from './new_downloadAxios'
import Vue from 'vue'

var loadingInstance

export function s2ab(s) { // 字符串转字符流
  var buf = new ArrayBuffer(s.length)
  var view = new Uint8Array(buf)
  for (var i = 0; i !== s.length; ++i) {
    view[i] = s.charCodeAt(i) & 0xFF
  }
  return buf
}

export function getCharCol(n) { // 将指定的自然数转换为26进制表示。映射关系:[0-25] -> [A-Z]。
  let s = ''
  let m = 0
  while (n > 0) {
    m = n % 26 + 1
    s = String.fromCharCode(m + 64) + s
    n = (n - m) / 26
  }
  return s
}

export function changeData(val) {
  // 对每一项数据进行处理:
  // 这里重点是处理对象、数组类型的数据,
  // 对象转为JSON字符串,数组转为逗号拼接的字符串
  if (!val && val != 0) {
    return ''
  } else if (typeof val !== 'object') {
    return val
  } else if (Array.isArray(val)) {
    let str = ''
    val.forEach(item => {
      if (typeof item !== 'object') {
        str = str + item + ','
      } else {
        str = str + JSON.stringify(item) + ','
      }
    })
    return str
  } else {
    return JSON.stringify(val)
  }
}

export function downloadExl(json, downName, outFile, type) { // 导出到excel
  const keyMap = [] // 获取键
  for (const k in json[0]) {
    keyMap.push(k)
  }

  const tmpdata = [] // 用来保存转换好的json
  json.map((v, i) => keyMap.map((k, j) => Object.assign({}, {
    v: v[k],
    position: (j > 25 ? getCharCol(j) : String.fromCharCode(65 + j)) + (i + 1)
  }))).reduce((prev, next) => prev.concat(next)).forEach(function(v) {
    tmpdata[v.position] = {
      v: changeData(v.v)
    }
  })
  const outputPos = Object.keys(tmpdata) // 设置区域,比如表格从A1到D10
  const tmpWB = {
    SheetNames: ['mySheet'], // 保存的表标题
    Sheets: {
      'mySheet': Object.assign({},
        tmpdata, // 内容
        {
          '!ref': outputPos[0] + ':' + outputPos[outputPos.length - 1] // 设置填充区域
        })
    }
  }
  const tmpDown = new Blob([s2ab(XLSX.write(tmpWB,
    { bookType: (type === undefined ? 'xlsx' : type), bookSST: false, type: 'binary' } // 这里的数据是用来定义导出的格式类型
  ))], {
    type: ''
  }) // 创建二进制对象写入转换好的字节流
  var href = URL.createObjectURL(tmpDown) // 创建对象超链接
  outFile.download = downName + '.xlsx' // 下载名称
  outFile.href = href // 绑定a标签
  outFile.click() // 模拟点击实现下载
  setTimeout(function() { // 延时释放
    URL.revokeObjectURL(tmpDown) // 用URL.revokeObjectURL()来释放这个object URL
  }, 100)
}

export async function downloadFile(outFile, name, head, url, query) { // 点击导出按钮
  // outFile a标签
  // name 导出表格名称
  // head 表头
  // url 导出数据请求的接口地址或者是导出的数据数组
  // query 导出列表的过滤参数
  loadingInstance = Loading.service({ text: '加载中...' })
  let num = 1
  let rs = []
  // 判断如果url是数组就直接赋值,反之就是接口地址
  if (Array.isArray(url)) {
    rs = url
  } else {
    for (var i = 1; i <= num; i++) {
      const res = await axios({
        url,
        method: 'get',
        // query 其他过滤参数
        // page 请求的第几页
        // limit 一次请求数据的数量
        params: { ...query, page: i, limit: 1000 }
      })
      // 根据返回接口数据判断总页数,更新num值,
      num = Math.ceil(res.data.page.totalCount / res.data.page.perPage)
      rs = rs.concat(res.data.items)
    }
  }

  loadingInstance.close()

  if (rs.length == 0) {
    Vue.prototype.$message(
      '暂无数据',
      'error'
    )
    return
  }

  let data = [{}]

  for (const k in head) {
    data[0][k] = head[k]
  }
  data = data.concat(rs)
  downloadExl(data, name, outFile)
}

注意:

        在vue3中引入xlsx,const XLSX = require('./xlsx')  这一步会报错,可以使用这种方式解决:const XLSX = await import('xlsx')。

        这样加完后还可能报一个await错误,可以尝试安装“vite-plugin-top-level-await” 插件解决.

       其他错误就是vue3跟vue2语法不同的错,自行解决即可

 3.封装好之后在main.js中引入

import { downloadFile } from '@/utils/download'
Vue.prototype.$downloadFile = downloadFile

 4.在页面中调用

<el-button type="primary" size="mini" @click="handleExport">导出</el-button>
<a id="downlink" />




handleExport() {
    // 这里head对象中属性的排序就是导出excel表格表头的顺序
    const head = { date: '日期', name: '姓名', address:'地址' }
    this.$downloadFile(this.outFile, '导出的文件名', head, '/xxxx/xxxxx/xxxx', this.query)
},

 5.导出结果

1690275149554.jpg