前端开发与第三方库之间不得不说的故事—excel篇

前言

大家好,我是沐浴在曙光下的贰货道士 。今年计划出一个使用第三方库的专栏,介绍一些在项目中常用的第三方库。而今天迎来了我们的第一位主角 --- excel有喜欢本文的朋友,欢迎一键三连哦~

前端开发与excel之间不得不说的故事

a. excel解析数据

需求分析: 在某些场景下,前端需要将上传的excel文件解析为表格数据,并展示在页面上,这个时候就涉及到excel的解析(sheetjs官方github)。

首先,我们需要安装依赖:npm install xlsx。依赖安装完成后,就可以愉快地操作了:

html 复制代码
<template>
  <div class="app-container system-home">
    <el-upload 
      action="#" 
      :auto-upload="false" 
      accept=".xlsx,.xls" 
      :show-file-list="false" 
      :on-change="onChange"
    >
      <el-button size="small" type="primary">上传excel文件</el-button>
    </el-upload>
  </div>
</template>

<script>

import xlsx from 'xlsx'
// import * as xlsx from 'xlsx'(视版本而定)

export default {
  data() {
    return {
      excelData: []
    }
  },

  methods: {
    async onChange(file) {
      let dataBinary = await new Promise((resolve) => {
        `https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader`
        let reader = new FileReader()
        `将文件读取为二进制字符串`
        reader.readAsBinaryString(file.raw)
        reader.onload = (ev) => {
          `通过监听onload事件,在文件读取完成后,将结果解析为二进制字符串,并将其传递给resolve函数`
          resolve(ev.target.result)
        }
      })
      `将二进制字符串解析为工作簿,cellDates: true用于将日期单元格解析为日期对象`
      let workBook = xlsx.read(dataBinary, { type: 'binary', cellDates: true })
      `获取工作簿中第一个工作表Sheet的引用`
      let firstWorkSheet = workBook.Sheets[workBook.SheetNames[0]]
      `将工作表转换为JSON格式的数据,返回一个包含工作表数据的数组`
      const data = xlsx.utils.sheet_to_json(firstWorkSheet)
      `格式化数据,重置数组对象的key值`
      this.formatData(data)
    },

    formatData(data) {
      this.excelData = data.map((item) => ({
        singer: item['歌手'],
        song: item['歌名'],
        hot: item['热度(亿)']
      }))
      console.log('this.excelData', this.excelData)
    }
  }
}
</script>

假定我们上传的excel文件长这个样子:

点击页面上的上传excel文件按钮,并选择需要上传的excel文件,就会在控制台上显示我们需要获取的最终数据:

至于最后的表格数据展示,就靠广大掘友们自行发挥了~

b. excel导出

需求分析: 在某些场景下,需要前端将数据导出为excel文件(FileSaver.js官方github)。

除了需要安装xlsx依赖外,我们还需要安装依赖:npm install file-saver。依赖安装完成后,就可以愉快地操作了。

1. 前端导出表格上的数据

html 复制代码
<template>
  <div class="app-container system-home">
    <loading-btn 
      type="primary" 
      size="mini" 
      @click="exportHandler(formatData(excelData), 'songs.xlsx')"
    >
      导出为excel文件
    </loading-btn>
  </div>
</template>

<script>
import xlsx from 'xlsx'
// import * as xlsx from 'xlsx'(视版本而定)
import { saveAs } from 'file-saver'

export default {
  data() {
    return {
     `假定这些数据是需要我们前端导出的表格数据: `
     excelData: [
        {
          singer: '周杰伦',
          song: '搁浅',
          hot: '1300.46'
        },
        {
          singer: '周杰伦',
          song: '退后',
          hot: '999.99'
        },
        {
          singer: '周杰伦',
          song: '说了再见',
          hot: '1221.67'
        },
        {
          singer: '周杰伦',
          song: '最长的电影',
          hot: '2004.79'
        },
        {
          singer: '周杰伦',
          song: '我落泪情绪零碎',
          hot: '2241.63'
        },
        {
          singer: '林俊杰',
          song: '圆圆圈圈',
          hot: '1200.24'
        },
        {
          singer: '林俊杰',
          song: '浪漫广西',
          hot: '666.66'
        },
        {
          singer: '华晨宇',
          song: '做法',
          hot: '0.00 '
        },
        {
          singer: '蔡徐坤',
          song: '只因你太美',
          hot: '99.86'
        }
      ]
    }
  },

  methods: {
    formatData(data) {
      return data.map(({ singer, song, hot }) => ({
        '歌手': singer,
        '歌名': song,
        '热度(亿)': hot
      }))
    },
    
    `可抽取为公共方法,data是导出数据,name是导出的xlsx名称`
    exportHandler(data, name) {
      `将需要导出的数据转换为工作表worksheet`
      const worksheet = xlsx.utils.json_to_sheet(data)
      `创建一个新的工作簿workbook,用于存储工作表worksheet`
      const workbook = xlsx.utils.book_new()
      `将工作表worksheet添加到工作簿workbook中,并指定工作表的名称为Sheet1`
      xlsx.utils.book_append_sheet(workbook, worksheet, 'Sheet1')
      `将工作簿workbook写入二进制数据excelBuffer,并指定生成的Excel文件类型为xlsx`
      const excelBuffer = xlsx.write(workbook, { bookType: 'xlsx', type: 'array' })
      `创建一个blob对象,用于表示Excel文件的二进制数据,type为Excel文件的MIME类型`
      const blob = new Blob([excelBuffer], {
        type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
      })
      `使用saveAs函数将Blob对象保存为文件`
      saveAs(blob, name)
    }
  }
}
</script>

以下为excel导出结果(确认过眼神,是我们需要导出的excel文件):

2. 前端将后端返回的二进制文件导出为excel

js 复制代码
1.我们需要在接口请求中使用`responseType: 'blob'`, 来获取后端返回的二进制文件

2.封装公共方法`downloadBlob`:

downloadBlob(blob, fileName) {
  `根据所提供的url地址,创建对象URL, 存储在内存中`
  const blobUrl = URL.createObjectURL(blob)
  `创建超链接标签link`
  const link = document.createElement('a')
  `将超链接标签link的跳转指向blobUrl`
  link.href = blobUrl
  `为超链接标签link下载的文件重命名`
  link.download = fileName || '下载文件'
  `触发超链接标签的点击事件,开始下载`
  link.click()
  `因为是用浏览器内存开辟的地址,所以需要及时释放对象URL,否则会一直占用系统内存,导致死机等严重影响性能的事发生`
  URL.revokeObjectURL(blobUrl)
}

3. 使用封装好的公共方法:

async exportHandler(row) {
  const res = await declareApi.matchExport({
    batchIds: [row.id]
  })
  this.downloadBlob(res, `${row.batchCode}_${row.expressCompanyName}`)
}

c. excel转换为html(主要利用xlsx.utils.sheet_to_html这个api)

js 复制代码
<template>
  <div class="app-container system-home">
    <el-upload 
      action="#" 
      :auto-upload="false" 
      accept=".xlsx,.xls" 
      :show-file-list="false" 
      :on-change="onChange"
    >
      <el-button size="small" type="primary">上传excel文件</el-button>
    </el-upload>
    <h4 class="mb10">excel展示:</h4>
    <div ref="excelRef"></div>
  </div>
</template>

<script>
import * as xlsx from 'xlsx'

export default {
  data() {
    return {
      excelData: []
    }
  },

  methods: {
    async onChange(file) {
      `两种方案皆可`
      
     `1. 非promise版`
      const reader = new FileReader()
      reader.onload = (event) => {
        let workbook = xlsx.read(event.target.result, { type: 'array', cellDates: true })
        let worksheet = workbook.Sheets[workbook.SheetNames[0]]
        this.$refs.excelRef.innerHTML = xlsx.utils.sheet_to_html(worksheet)
      }
      reader.readAsArrayBuffer(file.raw)
      
      `2. promise版(同a方向的代码)`
      let dataBinary = await new Promise((resolve) => {
        let reader = new FileReader()
        reader.readAsBinaryString(file.raw)
        reader.onload = (ev) => {
          resolve(ev.target.result)
        }
      })
      let workBook = xlsx.read(dataBinary, { type: 'binary', cellDates: true })
      let firstWorkSheet = workBook.Sheets[workBook.SheetNames[0]]
      this.$refs.excelRef.innerHTML = xlsx.utils.sheet_to_html(firstWorkSheet)
    }
  }
}
</script>

Tips: xlsx.read的文件格式,需要与FileReader读取的文件格式保持一致。

结语

往期精彩推荐(强势引流):

面试不面试,你都必须得掌握的vue知识

无论如何,你都必须得掌握的JS知识

无论如何,你都必须得掌握的JS知识(续)

我的css世界

什么?都2022年了,你还在一遍又一遍重复写form表单?

大概就这样吧。具有复杂表头的excel文件解析,会在后续抽空研究~

更多精彩文章正在快马加鞭创作中,敬请期待哦~

相关推荐
码力码力我爱你4 分钟前
QT + WebAssembly + Vue环境搭建
vue.js·vue·wasm·webassembly·emscripten
qbbmnnnnnn6 分钟前
【CSS Tricks】如何做一个粒子效果的logo
前端·css
唐家小妹8 分钟前
【flex-grow】计算 flex弹性盒子的子元素的宽度大小
前端·javascript·css·html
涔溪10 分钟前
uni-app环境搭建
前端·uni-app
安冬的码畜日常13 分钟前
【CSS in Depth 2 精译_032】5.4 Grid 网格布局的显示网格与隐式网格(上)
前端·css·css3·html5·网格布局·grid布局·css网格布局
洛千陨14 分钟前
element-plus弹窗内分页表格保留勾选项
前端·javascript·vue.js
小小199215 分钟前
elementui 单元格添加样式的两种方法
前端·javascript·elementui
完球了34 分钟前
【Day02-JS+Vue+Ajax】
javascript·vue.js·笔记·学习·ajax
前端没钱35 分钟前
若依Nodejs后台、实现90%以上接口,附体验地址、源码、拓展特色功能
前端·javascript·vue.js·node.js
爱喝水的小鼠41 分钟前
AJAX(一)HTTP协议(请求响应报文),AJAX发送请求,请求问题处理
前端·http·ajax