使用Vue+xlsx+xlsx-style实现导出自定义样式的Excel文件

简介: 本文介绍了在Vue项目中使用`xlsx`和`xlsx-style`(或`xlsx-style-vite`)库实现导出具有自定义样式的Excel文件的方法,并提供了详细的示例代码和操作效果截图。

本文就是上一篇《使用Python+openpyxl实现导出自定义样式的Excel文件》文章中提到的“之前项目的导出Excel文件操作都是在前端完成的...”这段话中基于前端实现的导出Excel文件方法。

文档地址:https://docs.sheetjs.com/

一、导入依赖
1.若项目是由Vue2 + Webpack构建的,那所需依赖包,如下所示

npm i xlsx
npm i xlsx-style

可能还要解决 Can't resolve './cptable' in '...' 的问题,在 vue.config.js 文件中加入该配置

module.exports = {
   
   
    externals: {
   
   
        './cptable': 'var cptable'
     }
}

2.若项目是由Vue3 + Vite构建的,那所需依赖包,如下所示

npm i xlsx
npm i xlsx-style-vite

二、引用依赖

import * as XLSX from 'xlsx'
import * as XLSX_STYLE from 'xlsx-style' // Vue2 + Webpack
import * as XLSX_STYLE from 'xlsx-style-vite' // Vue3 + Vite

三、示例代码

/src/Example/ExportExcel/index.vue

<template>
  <div style="display: flex; width: 100%; height: 100%; align-item: center;">
    <a
      style="margin: auto; padding: 5px 11px; background-color: #5e7ce0; border-radius: 3px; color: #fff; cursor: pointer;"
      @click="handelExportExcelClick"
    >
      <small style="display: inline-flex; align-items: center; line-height: 1px;">导出数据</small>
    </a>
  </div>
</template>

<script>
import * as XLSX from 'xlsx'
// import * as XLSX_STYLE from 'xlsx-style' // Vue2 + Webpack
import * as XLSX_STYLE from 'xlsx-style-vite' // Vue3 + Vite

export default {
    
    
  data: () => ({
    
    
    targetList: []
  }),
  methods: {
    
    
    /**
     * 导出数据
     */
    handelExportExcelClick() {
    
    
      // 一、准备数据
      const list = this.getExportDataList()
      console.log('list =>', list)

      // 二、新建一个工作簿
      const workBook = XLSX.utils.book_new()

      // 三、使用二维数组生成一个工作表
      const workSheet = this.sheet_from_array_of_arrays(list)

      // 四、将 "A1" 到 "M1" 的单元格合并为 "A1"
      workSheet['!merges'] = [
        {
    
    
          s: {
    
     // s ("start"): c = 0 r = 0 -> "A1"
            r: 0,
            c: 0
          },
          e: {
    
     // e ("end"):   c = 0 r = 9 -> "J1"
            r: 0,
            c: 9
          }
        }
      ]

      // 五、设置每列的宽度(单位:px)
      const wsCols = [
        {
    
     wch: 10 },
        {
    
     wch: 30 },
        {
    
     wch: 40 },
        {
    
     wch: 15 },
        {
    
     wch: 15 },
        {
    
     wch: 15 },
        {
    
     wch: 15 },
        {
    
     wch: 15 },
        {
    
     wch: 20 },
        {
    
     wch: 20 },
      ]
      workSheet['!cols'] = wsCols

      // 六、设置每行的高度(单位:px)
      let wsRows = []
      for (let i in list) {
    
    
        if (i == 0) {
    
    
          wsRows.push({
    
     hpx: 100 }) // 首行高度为 100px
        } else {
    
    
          // wsRows.push({ hpx: 30 }) // 其他行高度为 30px
        }
      }
      workSheet['!rows'] = wsRows

      // 七、设置单元格样式
      for (let key in workSheet) {
    
    
        if (key == '!ref' || key == '!merges' || key == '!cols' || key == '!rows') {
    
    
          continue
        } else {
    
    
          // 匹配表格第一行(注意 A1 单元已合并为一个单元),设置其样式
          if (key == 'A1') {
    
    
            workSheet[key] = {
    
    
              t: 's', // 设置单元格类型(type: b Boolean, e Error, n Number, d Date, s Text, z Stub)
              v: '爆款商品列表', // 设置单元格内容(raw value (number, string, Date object, boolean))
              l: {
    
     Target: "https://sheetjs.com", Tooltip: "Find us @ SheetJS.com!" }, // 单元格超链接 cell hyperlink / tooltip (More Info)
              s: {
    
     // 设置单元格样式
                fill: {
    
     // 设置背景色
                  fgColor: {
    
     rgb: 'ffffff' },
                },
                font: {
    
     // 设置字体
                  name: '等线', // 字体名称
                  sz: 35, // 字体大小
                  bold: true, // 字体是否加粗
                  color: {
    
     rgb: '5e7ce0' }, // 文字颜色
                },
                alignment: {
    
     // 设置居中
                  horizontal: 'center', // 水平(向左、向右、居中)
                  vertical: 'center', // 上下(向上、向下、居中)
                  wrapText: false, // 设置单元格自动换行,目前仅对非合并单元格生效
                  indent: 0 // 设置单元格缩进
                },
              },
            }
          }
          // 匹配表格第二行(A2 B2 C2 D2...),设置其样式
          else if (key.match(/\d+/g).join('') == '2') {
    
    
            workSheet[key].s = {
    
    
              border: {
    
    
                top: {
    
    
                  style: 'thin',
                },
                bottom: {
    
    
                  style: 'thin',
                },
                left: {
    
    
                  style: 'thin',
                },
                right: {
    
    
                  style: 'thin',
                },
              },
              fill: {
    
     // 设置背景色
                fgColor: {
    
     rgb: 'eeeeee' },
              },
              font: {
    
     // 设置字体
                name: '微软雅黑', // 字体名称
                sz: 10, // 字体大小
              },
              alignment: {
    
    
                horizontal: 'center', // 水平(向左、向右、居中)
                vertical: 'center', // 上下(向上、向下、居中)
                wrapText: false, // 设置单元格自动换行,目前仅对非合并单元格生效
                indent: 0 // 设置单元格缩进
              }
            }
          }
          // 匹配表格中除了 B1 B2 的 B 列单元格,以及除了 C1 C2 的 C 列单元格,设置其样式
          else if ((key != 'B1' && key != 'B2' && key.indexOf('B') > -1) || (key != 'C1' && key != 'C2' && key.indexOf('C') > -1)) {
    
    
            workSheet[key].s = {
    
    
              border: {
    
    
                top: {
    
    
                  style: 'thin',
                },
                bottom: {
    
    
                  style: 'thin',
                },
                left: {
    
    
                  style: 'thin',
                },
                right: {
    
    
                  style: 'thin',
                },
              },
              fill: {
    
     // 设置背景色
                fgColor: {
    
     rgb: 'ffffff' },
              },
              font: {
    
     // 设置字体
                name: '微软雅黑', // 字体名称
                sz: 10, // 字体大小
              },
              alignment: {
    
    
                horizontal: 'left', // 水平(向左、向右、居中)
                vertical: 'center', // 上下(向上、向下、居中)
                wrapText: true, // 设置单元格自动换行,目前仅对非合并单元格生效
                indent: 1 // 设置单元格缩进
              }
            }
          }
          // 匹配表格中除了 G1 G2 的 G 列单元格,设置其样式
          else if (key != 'G1' && key != 'G2' && key.indexOf('G') > -1) {
    
    
            workSheet[key].s = {
    
    
              border: {
    
    
                top: {
    
    
                  style: 'thin',
                },
                bottom: {
    
    
                  style: 'thin',
                },
                left: {
    
    
                  style: 'thin',
                },
                right: {
    
    
                  style: 'thin',
                },
              },
              fill: {
    
     // 设置背景色
                fgColor: {
    
     rgb: 'ffffff' },
              },
              font: {
    
     // 设置字体
                name: '微软雅黑', // 字体名称
                sz: 10, // 字体大小
              },
              alignment: {
    
    
                horizontal: 'center', // 水平(向左、向右、居中)
                vertical: 'center', // 上下(向上、向下、居中)
                wrapText: false, // 设置单元格自动换行,目前仅对非合并单元格生效
                indent: 0 // 设置单元格缩进
              }
            }

            if (workSheet[key].v == '待审核') {
    
    
              workSheet[key].s.font.color = {
    
     rgb: '1a44d7' }
            } else if (workSheet[key].v == '审核已通过') {
    
    
              workSheet[key].s.font.color = {
    
     rgb: '4ab913' }
            } else if (workSheet[key].v == '审核未通过') {
    
    
              workSheet[key].s.font.color = {
    
     rgb: 'ed143d' }
            } else {
    
    
              workSheet[key].s.font.color = {
    
     rgb: '000000' }
            }
          }
          // 匹配表格中除了 H1 H2 的 H 列单元格,设置其样式
          else if (key != 'H1' && key != 'H2' && key.indexOf('H') > -1) {
    
    
            workSheet[key].s = {
    
    
              border: {
    
    
                top: {
    
    
                  style: 'thin',
                },
                bottom: {
    
    
                  style: 'thin',
                },
                left: {
    
    
                  style: 'thin',
                },
                right: {
    
    
                  style: 'thin',
                },
              },
              fill: {
    
     // 设置背景色
                fgColor: {
    
     rgb: 'ffffff' },
              },
              font: {
    
     // 设置字体
                name: '微软雅黑', // 字体名称
                sz: 10, // 字体大小
              },
              alignment: {
    
    
                horizontal: 'center', // 水平(向左、向右、居中)
                vertical: 'center', // 上下(向上、向下、居中)
                wrapText: false, // 设置单元格自动换行,目前仅对非合并单元格生效
                indent: 0 // 设置单元格缩进
              }
            }

            if (workSheet[key].v == '待上架') {
    
    
              workSheet[key].s.font.color = {
    
     rgb: '1a44d7' }
              workSheet[key].s.fill.fgColor = {
    
     rgb: 'eff2fc' }
            } else if (workSheet[key].v == '正常') {
    
    
              workSheet[key].s.font.color = {
    
     rgb: '4ab913' }
              workSheet[key].s.fill.fgColor = {
    
     rgb: 'f0f9eb' }
            } else if (workSheet[key].v == '已下架') {
    
    
              workSheet[key].s.font.color = {
    
     rgb: 'ed143d' }
              workSheet[key].s.fill.fgColor = {
    
     rgb: 'fef0f0' }
            } else {
    
    
              workSheet[key].s.font.color = {
    
     rgb: '000000' }
              workSheet[key].s.fill.fgColor = {
    
     rgb: 'ffffff' }
            }
          }
          // 其它单元格,设置其样式
          else {
    
    
            workSheet[key].s = {
    
    
              border: {
    
    
                top: {
    
    
                  style: 'thin',
                },
                bottom: {
    
    
                  style: 'thin',
                },
                left: {
    
    
                  style: 'thin',
                },
                right: {
    
    
                  style: 'thin',
                },
              },
              fill: {
    
     // 设置背景色
                fgColor: {
    
     rgb: 'ffffff' },
              },
              font: {
    
     // 设置字体
                name: '微软雅黑', // 字体名称
                sz: 10, // 字体大小
              },
              alignment: {
    
    
                horizontal: 'center', // 水平(向左、向右、居中)
                vertical: 'center', // 上下(向上、向下、居中)
                wrapText: false, // 设置单元格自动换行,目前仅对非合并单元格生效
                indent: 0 // 设置单元格缩进
              }
            }
          }
        }
      }
      console.log('workSheet =>', workSheet)

      // 八、在工作簿中添加工作表
      XLSX.utils.book_append_sheet(workBook, workSheet, '第一页')

      // 九、使用 xlsx-style 写入文件方式,使得自定义样式生效
      const tmpDown = new Blob([
        this.s2ab(
          XLSX_STYLE.write(workBook, {
    
    
            bookType: 'xlsx',
            bookSST: true,
            type: 'binary',
            cellStyles: true,
          })
        ),
      ])

      // 十、导出 Excel 文件
      const date = new Date()
      const formattedDate =
        `` +
        `${
      
      date.getFullYear()}` + // 年
        `${
      
      (date.getMonth() + 1).toString().padStart(2, '0')}` + // 月
        `${
      
      date.getDate().toString().padStart(2, '0')}` + // 日
        `${
      
      date.getHours().toString().padStart(2, '0')}` + // 时
        `${
      
      date.getMinutes().toString().padStart(2, '0')}` + // 分
        `${
      
      date.getSeconds().toString().padStart(2, '0')}` + // 秒
        ``.trim()
      this.downloadExcelFile(tmpDown, '年度最新爆款商品列表 - ' + formattedDate + '.xlsx')
    },

    /**
     * 准备数据
     */
    getExportDataList() {
    
    
      const thList = [
        '商品ID',
        '商品名称',
        '商品描述',
        '规格',
        '库存',
        '责任人',
        '是否审核',
        '商品状态',
        '创建时间',
        '修改时间',
      ]

      const keyList = [
        'id',
        'name', 
        'desc',
        'spec', 
        'num', 
        'principal', 
        'isApproved', 
        'status', 
        'createTime',
        'updateTime',
      ]

      const targetList = [
        {
    
    
          id: 1,
          name: '乐事真脆薯条', 
          desc: '百事旗下产品,好吃又好玩!',
          spec: '单位/包', 
          num: 666, 
          principal: '全哥',
          isApproved: '审核已通过', 
          status: 0, 
          createTime: '2023-06-08 22:13:46',
          updateTime: '2023-06-08 22:15:18',
        },
        {
    
    
          id: 2,
          name: '干脆面', 
          desc: '味道好极了 ~',
          spec: '单位/箱', 
          num: 123, 
          principal: '全哥',
          isApproved: '审核已通过', 
          status: 1, 
          createTime: '2023-06-08 22:13:46',
          updateTime: '2023-06-08 22:15:18',
        },
        {
    
    
          id: 3,
          name: '双汇火腿肠', 
          desc: 'Good!!!',
          spec: '单位/包', 
          num: 666, 
          principal: '全哥',
          isApproved: '审核已通过', 
          status: 2, 
          createTime: '2023-06-08 22:13:46',
          updateTime: '2023-06-08 22:15:18',
        },
        {
    
    
          id: 4,
          name: '曲奇饼', 
          desc: '美味。',
          spec: '单位/盒', 
          num: 666, 
          principal: '全哥',
          isApproved: '待审核', 
          status: 1, 
          createTime: '2023-06-08 22:13:46',
          updateTime: '2023-06-08 22:15:18',
        },
        {
    
    
          id: 5,
          name: '香飘飘奶茶', 
          desc: 'Yeah',
          spec: '单位/包', 
          num: 666, 
          principal: '全哥',
          isApproved: '审核未通过', 
          status: 2, 
          createTime: '2023-06-08 22:13:46',
          updateTime: new Date(),
        }
      ]

      const tdList = this.formatJson(keyList, targetList) // 过滤字段以及转换数据格式,即:表格数据
      tdList.unshift(thList) // 将 thList 数组添加到 tdList 数组开头,即:表格头部
      tdList.unshift(['']) // 将空字符串数组添加到 tdList 数组开头,即:表格首行
      const list = tdList
      return list
    },

    /**
     * 过滤字段以及转换数据格式
     */
    formatJson(filterVal, jsonData) {
    
    
      return jsonData.map(v => filterVal.map(item => {
    
    
        if (item === 'name') {
    
    
          if (v['name'] != null && v['name'] != '') {
    
    
            return v[item].split(';').join('\n')
          } else {
    
    
            return '-'
          }
        }
        else if (item === 'status') {
    
    
          if (v['status'] != null && v['status'] == 0) {
    
    
            return '正常'
          } else if (v['status'] != null && v['status'] == 1) {
    
    
            return '待上架'
          } else if (v['status'] != null && v['status'] == 2) {
    
    
            return '已下架'
          } else {
    
    
            return '-'
          }
        }
        else {
    
    
          return v[item]
        }
      }))
    },

    /**
     * 使用二维数组生成一个工作表
     */
    sheet_from_array_of_arrays(data, opts) {
    
    
      var ws = {
    
    };
      var range = {
    
    
        s: {
    
    
          c: 10000000,
          r: 10000000
        },
        e: {
    
    
          c: 0,
          r: 0
        }
      }

      for (var R = 0; R != data.length; ++R) {
    
    
        for (var C = 0; C != data[R].length; ++C) {
    
    
          if (range.s.r > R) range.s.r = R;
          if (range.s.c > C) range.s.c = C;
          if (range.e.r < R) range.e.r = R;
          if (range.e.c < C) range.e.c = C;
          var cell = {
    
    
            v: data[R][C]
          };
          if (cell.v == null) continue;
          var cell_ref = XLSX.utils.encode_cell({
    
    
            c: C,
            r: R
          })

          if (typeof cell.v === 'number') cell.t = 'n';
          else if (typeof cell.v === 'boolean') cell.t = 'b';
          else if (cell.v instanceof Date) {
    
    
            cell.t = 'n';
            cell.z = XLSX.SSF._table[14];
            cell.v = this.date_num(cell.v);
            // cell.z = 'YYYY-MM-DD'
            cell.z = 'YYYY-MM-DD HH:mm:ss'
          } else cell.t = 's'

          ws[cell_ref] = cell
        }
      }

      if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range)
      return ws
    },

    /**
     * 日期转换
     */
    date_num(v, date1904) {
    
    
      if (date1904) {
    
    
        v += 1462;
      }
      var epoch = Date.parse(v);
      return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
    },

    /**
     * 文件流转换
     */
    s2ab(s) {
    
    
      if (typeof ArrayBuffer !== 'undefined') {
    
    
        const buf = new ArrayBuffer(s.length)
        const view = new Uint8Array(buf);
        for (let i = 0; i != s.length; ++i) {
    
    
          view[i] = s.charCodeAt(i) & 0xff
        }
        return buf
      } else {
    
    
        const buf = new Array(s.length)
        for (let i = 0; i != s.length; ++i) {
    
    
          buf[i] = s.charCodeAt(i) & 0xff
        }
        return buf
      }
    },

    /**
     * 使用 a 标签下载文件
     */
    downloadExcelFile(obj, fileName) {
    
    
      const a_node = document.createElement('a')
      a_node.download = fileName
      if ('msSaveOrOpenBlob' in navigator) {
    
    
        window.navigator.msSaveOrOpenBlob(obj, fileName)
      } else {
    
    
        a_node.href = URL.createObjectURL(obj)
      }
      a_node.click()
      setTimeout(() => {
    
    
        URL.revokeObjectURL(obj)
      }, 2000)
    },
  }
}
</script>

四、效果如下 ~

目录
相关文章
|
9天前
|
数据处理 Python
Python实用记录(十):获取excel数据并通过列表的形式保存为txt文档、xlsx文档、csv文档
这篇文章介绍了如何使用Python读取Excel文件中的数据,处理后将其保存为txt、xlsx和csv格式的文件。
27 3
Python实用记录(十):获取excel数据并通过列表的形式保存为txt文档、xlsx文档、csv文档
|
12天前
|
easyexcel Java UED
SpringBoot中大量数据导出方案:使用EasyExcel并行导出多个excel文件并压缩zip后下载
在SpringBoot环境中,为了优化大量数据的Excel导出体验,可采用异步方式处理。具体做法是将数据拆分后利用`CompletableFuture`与`ThreadPoolTaskExecutor`并行导出,并使用EasyExcel生成多个Excel文件,最终将其压缩成ZIP文件供下载。此方案提升了导出效率,改善了用户体验。代码示例展示了如何实现这一过程,包括多线程处理、模板导出及资源清理等关键步骤。
|
13天前
|
前端开发 JavaScript
💥【exceljs】纯前端如何实现Excel导出下载和上传解析?
本文介绍了用于处理Excel文件的库——ExcelJS,相较于SheetJS,ExcelJS支持更高级的样式自定义且易于使用。表格对比显示,ExcelJS在样式设置、内存效率及流式操作方面更具优势。主要适用于Node.js环境,也支持浏览器端使用。文中详细展示了如何利用ExcelJS实现前端的Excel导出下载和上传解析功能,并提供了示例代码。此外,还提供了在线调试的仓库链接和运行命令,方便读者实践。
118 5
|
5天前
|
前端开发 JavaScript API
前端基于XLSX实现数据导出到Excel表格,以及提示“文件已经被损坏,无法打开”的解决方法
前端基于XLSX实现数据导出到Excel表格,以及提示“文件已经被损坏,无法打开”的解决方法
33 0
|
9天前
|
前端开发 JavaScript Java
导出excel的两个方式:前端vue+XLSX 导出excel,vue+后端POI 导出excel,并进行分析、比较
这篇文章介绍了使用前端Vue框架结合XLSX库和后端结合Apache POI库导出Excel文件的两种方法,并对比分析了它们的优缺点。
145 0
|
12天前
|
数据采集 存储 JavaScript
自动化数据处理:使用Selenium与Excel打造的数据爬取管道
本文介绍了一种使用Selenium和Excel结合代理IP技术从WIPO品牌数据库(branddb.wipo.int)自动化爬取专利信息的方法。通过Selenium模拟用户操作,处理JavaScript动态加载页面,利用代理IP避免IP封禁,确保数据爬取稳定性和隐私性。爬取的数据将存储在Excel中,便于后续分析。此外,文章还详细介绍了Selenium的基本设置、代理IP配置及使用技巧,并探讨了未来可能采用的更多防反爬策略,以提升爬虫效率和稳定性。
|
2月前
|
关系型数据库 MySQL Shell
不通过navicat工具怎么把查询数据导出到excel表中
不通过navicat工具怎么把查询数据导出到excel表中
39 0
|
1月前
|
数据采集 存储 数据挖掘
使用Python读取Excel数据
本文介绍了如何使用Python的`pandas`库读取和操作Excel文件。首先,需要安装`pandas`和`openpyxl`库。接着,通过`read_excel`函数读取Excel数据,并展示了读取特定工作表、查看数据以及计算平均值等操作。此外,还介绍了选择特定列、筛选数据和数据清洗等常用操作。`pandas`是一个强大且易用的工具,适用于日常数据处理工作。
|
2月前
|
SQL JSON 关系型数据库
n种方式教你用python读写excel等数据文件
n种方式教你用python读写excel等数据文件
|
2月前
|
存储 Java Apache