【纯前端excel导出】vue3纯前端导出excel,使用xlsx插件,修改样式、合并单元格

一.需求

在做一个报价小项目,需要excel合并相同的危害因素。如下图

二.实现

官网:

1、xlsx-js-style xlsx-js-style | xlsx-js-style homepage

2、xlsx SheetJS 中文网

一、使用第三方插件
1、安装

npm install xlsx-js-style

2、引入

import xlsx from 'xlsx-js-style'

xlsx插件是基础的导出,不可以修改样式,直接xlsx-style插件式修改样式的,所以这里直接用二者合体插件即可

二、页面使用
1、数据源

"data": [
        {
            "factorName": "X射线;电焊弧光",
            "postItem": "放射体检皮肤检查",
            "postPrice": 0,
            "onDutyItem": "放射体检皮肤检查",
            "onDutyPrice": 0,
            "departItem": "放射体检皮肤检查",
            "departPrice": 0
        },
        {
            "factorName": "X射线;电焊弧光",
            "postItem": "放射体检眼科检查",
            "postPrice": 0,
            "onDutyItem": "放射体检眼科检查",
            "onDutyPrice": 0,
            "departItem": "放射体检眼科检查",
            "departPrice": 0
        },
        {
            "factorName": "X射线;电焊弧光",
            "postItem": "肝脾B超(肝脾彩超)",
            "postPrice": 48,
            "onDutyItem": "甲状腺功能",
            "onDutyPrice": 90,
            "departItem": "肝脾B超(肝脾彩超)",
            "departPrice": 48
        }]


2、将数据源转成需要的二维数组
 

const data = res.map((ele, i) => ({
    factorName: ele.factorName,
    postItem: ele.postItem,
    postPrice: ele.postPrice,
    onDutyItem: ele.onDutyItem,
    onDutyPrice: ele.onDutyPrice,
    departItem: ele.departItem,
    departPrice: ele.departPrice,
  }))
  const body = data.map((x) => [
    x.factorName,
    x.postItem,
    x.postPrice,
    x.onDutyItem,
    x.onDutyPrice,
    x.departItem,
    x.departPrice,
  ])

3、定义表头

 // 定义Excel表头
  const header = [
    [
      '危害因素名称',
      '入职前必检项目',
      '价格',
      '在职时必检项目',
      '价格',
      '离职时必检项目',
      '价格',
    ],
  ]


4、将定义好的表头添加到 body 中
body.unshift(...header) 
5、创建虚拟的 workbook
const workbook = xlsx.utils.book_new()
6、aoa_to_sheet 将二维数组转成 sheet

const sheet = xlsx.utils.aoa_to_sheet(body)
// aoa_to_sheet      是将【一个二维数组】转化成 sheet
// json_to_sheet     是将【由对象组成的数组】转化成sheet
// table_to_sheet      是将【table的dom】直接转成sheet
7、!merges 单元格合并

// 合并单元格的函数
  const merges = [
  ]
  function addMerges(startRow, endRow, col) {
    if (startRow < endRow) {
      merges.push({
        s: { r: startRow, c: col },
        e: { r: endRow, c: col },
      })
    }
  }
  // 动态合并相同内容的单元格的函数
  function mergeDynamicCells(colIndex) {
    let startRow = 1 // 数据部分从第2行开始
    for (let i = 1; i < body.length; i++) {
      if (body[i][colIndex] !== body[startRow][colIndex]) {
        addMerges(startRow, i - 1, colIndex) // 合并相同内容的单元格
        startRow = i // 更新起始行
      }
    }
    addMerges(startRow, body.length - 1, colIndex) // 合并最后一段相同内容
  }
  // 只对第2列和第5列进行动态合并
  mergeDynamicCells(0) // 合并 1 列
  sheet['!merges'] = merges // 将合并信息添加到 sheet 中

 8、!cols 设置列宽

const cols = [
    { wch: 30 },
    { wch: 30 },
    { wch: 10 },
    { wch: 30 },
    { wch: 10 },
    { wch: 30 },
    { wch: 10 },
  ]
  sheet['!cols'] = cols // 设置列宽


9、!rows 设置行高
 const rows = [{ hpx: 30 }, { hpx: 30 }, { hpx: 30 }]
 sheet['!rows'] = rows // 行高添加到sheet中
10、样式添加-内容垂直居中
   let range = xlsx.utils.decode_range(sheet['!ref']);  
   let headerStyle = {  
        font: { sz: 14, name: '宋体', bold: true },  
        alignment: { vertical: 'center', horizontal: 'center' }  
    };  
   let defaultStyle = {  
        font: { sz: 11, name: '宋体' },  
        alignment: { wrapText: 1, vertical: 'center', horizontal: 'center' }  
        // wrapText: 1-------自动换行
    };  
    for (var row = range.s.r; row <= range.e.r; ++row) {  
        for (let col = range.s.c; col <= range.e.c; ++col) {  
            let cellAddress = xlsx.utils.encode_cell({ r: row, c: col });  
            let cell = sheet[cellAddress];  
            if (cell) {  
                if (!cell.s) cell.s = {};  
                // 处理表头  
                if (cell.v === '隐患排查治理台账') {  
                    cell.s = headerStyle;  
                } else {  
                    cell.s = defaultStyle;  
                }  
            }  
        }  
    }  

11、向workbook中添加sheet
 xlsx.utils.book_append_sheet(workbook, sheet, this.compname || 'Sheet')
12、导出
 xlsx.writeFile(workbook, (this.compname || 'Data') + '.xlsx', {type: 'binary',}) 
三、完整代码

/**
 * Excel导出
 * **/
const downLoad = () => {
  //  列表数据
  const res = quotationsData.value;

  const data = res.map((ele, i) => ({
    factorName: ele.factorName,
    postItem: ele.postItem,
    postPrice: ele.postPrice,
    onDutyItem: ele.onDutyItem,
    onDutyPrice: ele.onDutyPrice,
    departItem: ele.departItem,
    departPrice: ele.departPrice,
  }))
  const body = data.map((x) => [
    x.factorName,
    x.postItem,
    x.postPrice,
    x.onDutyItem,
    x.onDutyPrice,
    x.departItem,
    x.departPrice,
  ])

  // 定义Excel表头
  const header = [
    [
      '危害因素名称',
      '入职前必检项目',
      '价格',
      '在职时必检项目',
      '价格',
      '离职时必检项目',
      '价格',
    ],
  ]


  body.unshift(...header) // 将定义好的表头添加到 body 中
  const sheet = xlsx.utils.aoa_to_sheet(body) // aoa_to_sheet 将二维数组转成 sheet
  // 合并单元格的函数
  const merges = [
  ]
  function addMerges(startRow, endRow, col) {
    if (startRow < endRow) {
      merges.push({
        s: { r: startRow, c: col },
        e: { r: endRow, c: col },
      })
    }
  }
  // 动态合并相同内容的单元格的函数
  function mergeDynamicCells(colIndex) {
    let startRow = 1 // 数据部分从第2行开始
    for (let i = 1; i < body.length; i++) {
      if (body[i][colIndex] !== body[startRow][colIndex]) {
        addMerges(startRow, i - 1, colIndex) // 合并相同内容的单元格
        startRow = i // 更新起始行
      }
    }
    addMerges(startRow, body.length - 1, colIndex) // 合并最后一段相同内容
  }
  // 只对第2列和第5列进行动态合并
  mergeDynamicCells(0) // 合并 1 列
  sheet['!merges'] = merges // 将合并信息添加到 sheet 中
  const cols = [
    { wch: 30 },
    { wch: 30 },
    { wch: 10 },
    { wch: 30 },
    { wch: 10 },
    { wch: 30 },
    { wch: 10 },
  ]
  sheet['!cols'] = cols // 设置列宽
  // const rows = [{ hpx: 30 }, { hpx: 30 }, { hpx: 30 }]
  // sheet['!rows'] = rows // 设置行高
  // 合并居中表格内容
  var range = xlsx.utils.decode_range(sheet['!ref'])
  for (var R = range.s.r; R <= range.e.r; ++R) {
    for (var C = range.s.c; C <= range.e.c; ++C) {
      var cell_ref = xlsx.utils.encode_cell({ r: R, c: C })
      var cell = sheet[cell_ref]
      if (cell) {
        if (!cell.s) cell.s = {}
        if (cell.v == '隐患排查治理台账') {
          cell.s = {
            font: {
              sz: 14,
              name: '宋体',
              bold: true,
            },
            alignment: {
              vertical: 'center', // 垂直居中
              horizontal: 'left', // 水平居中
            },
          }
        } else {
          cell.s = {
            font: {
              sz: 11,
              name: '宋体',
            },
            alignment: {
              wrapText: 1, //自动换行
              vertical: 'center', // 垂直居中
              horizontal: 'left', // 水平居中
            },
          }
        }
      }
    }
  }
  const workbook = xlsx.utils.book_new()
  xlsx.utils.book_append_sheet(workbook, sheet, 'Sheet') // 向workbook中添加sheet
  xlsx.writeFile(workbook, 'quotations.xlsx', { type: 'binary', }) // 导出 workbook})},

}

参考链接:

【纯前端excel导出】vue2纯前端导出excel,使用xlsx插件,修改样式、合并单元格_前端xlsx插件-优快云博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值