最近开发项目要求导出excel表格,之前用Jq做过导出。这次用vue,记录一下经过

装依赖并且引入

安装依赖(如果用vue-admin-template基础版本,需要自己安装), 并且把 集成版 vendor以及下面的Export2Excel.js复制过来

npm install -S file-saver
npm install -S xlsx
npm install -D script-loader

//如果想自定义样式安装xlsx-style
npm install --save xlsx-style

注意安装xlsx-style启动可能会报错(按照下面方式修改即可):
import XLSX from "xlsx-style"报错:This relative module was not found: ./cptable in ./node_modules/xlsx-style@0.8.13@xlsx-style/dist/cpexcel.js,需要修改源码:
在 \node_modules\xlsx-style\dist\cpexcel.js找到 var cpt = require(’./cpt’ + ‘able’); 并改成 var cpt = cptable;

引入依赖

import { saveAs } from 'file-saver'
import XLSX from "xlsx-style";

修改Export2Excel文件

Export2Excel源文件 export_json_to_excel 方法

/* eslint-disable */
import { saveAs } from 'file-saver'
import XLSX from 'xlsx'
…………
export function export_json_to_excel({
  multiHeader = [],
  header,
  data,
  filename,
  merges = [],
  autoWidth = true,
  bookType = 'xlsx'
} = {}) {
  /* original data */
  filename = filename || 'excel-list'
  data = [...data]
  data.unshift(header);

  for (let i = multiHeader.length - 1; i > -1; i--) {
    data.unshift(multiHeader[i])
  }

  var ws_name = "SheetJS";
  var wb = new Workbook(),
    ws = sheet_from_array_of_arrays(data);

  if (merges.length > 0) {
    if (!ws['!merges']) ws['!merges'] = [];
    merges.forEach(item => {
      ws['!merges'].push(XLSX.utils.decode_range(item))
    })
  }

  if (autoWidth) {
    /*设置worksheet每列的最大宽度*/
    const colWidth = data.map(row => row.map(val => {
      /*先判断是否为null/undefined*/
      if (val == null) {
        return {
          'wch': 10
        };
      }
      /*再判断是否为中文*/
      else if (val.toString().charCodeAt(0) > 255) {
        return {
          'wch': val.toString().length * 2
        };
      } else {
        return {
          'wch': val.toString().length
        };
      }
    }))
    /*以第一行为初始值*/
    let result = colWidth[0];
    for (let i = 1; i < colWidth.length; i++) {
      for (let j = 0; j < colWidth[i].length; j++) {
        if (result[j]['wch'] < colWidth[i][j]['wch']) {
          result[j]['wch'] = colWidth[i][j]['wch'];
        }
      }
    }
    ws['!cols'] = result;
  }

  /* add worksheet to workbook */
  wb.SheetNames.push(ws_name);
  wb.Sheets[ws_name] = ws;

  var wbout = XLSX.write(wb, {
    bookType: bookType,
    bookSST: false,
    type: 'binary'
  });
  saveAs(new Blob([s2ab(wbout)], {
    type: "application/octet-stream"
  }), `${filename}.${bookType}`);
}

修改为:

/* eslint-disable */
import { saveAs } from 'file-saver'
import XLSX from "xlsx-style";  //修改引入样式
…………
export function export_json_to_excel({
  title = [], // 新增的参数,表格标题
  multiHeader = [],
  SheetNames = [] ,// 新增的参数,sheet名称
  header,
  data,
  filename,
  merges = [],
  autoWidth = true,
  bookType = 'xlsx'
} = {}) {
  /* original data */
  filename = filename || 'excel-list'
  data = [...data]
  var wb = new Workbook();
  data.forEach((item,index) => {
     let sheet = oneSheet(item,title[index],header[index],merges[index],autoWidth)
     wb.SheetNames.push(SheetNames[index])
     wb.Sheets[SheetNames[index]] = sheet
     setExcelStyle(sheet,data.length)
  })
  var wbout = XLSX.write(wb, {
    bookType: bookType,
    bookSST: false,
    type: 'binary'
  });
  saveAs(new Blob([s2ab(wbout)], {
    type: "application/octet-stream"
  }), `${filename}.${bookType}`);
}

//渲染一个sheet
function oneSheet(data,title,header,merges,autoWidth){
  data.unshift(header);
  //写死的二级标题以及倒数两行(空行和合计)
  data.unshift(['资料区间:2021-08-04~2021-08-05']); // 表格标题
  data.unshift([title]); // 表格标题
  data.push(['']); // 接一行
  data.push(['份数:4 金额:96911.76元 税额:12557.32元']); // 接一行
  //最后两行合并单元格
  let merges_prev_last = 'A' + String(data.length-1)+':M'+ String(data.length-1);
  let merges_last = 'A' + String(data.length)+':M'+ String(data.length);
  merges.push(merges_prev_last)
  merges.push(merges_last)
  //调用上面的合并单元格函数
  var ws = sheet_from_array_of_arrays(data);
  if (merges.length > 0) {
    if (!ws['!merges']) ws['!merges'] = [];
    merges.forEach(item => {
      ws['!merges'].push(XLSX.utils.decode_range(item))
    })
  }
  if (autoWidth) {
    /*设置worksheet每列的最大宽度*/
    const colWidth = data.map(row => row.map(val => {
      /*先判断是否为null/undefined*/
      if (val == null) {
        return {
          'wch': 10
        };
      }
      /*再判断是否为中文*/
      else if (val.toString().charCodeAt(0) > 255) {
        return {
          'wch': val.toString().length * 2
        };
      } else {
        return {
          'wch': val.toString().length
        };
      }
    }))
    /*以主表第三行为初始值,因为我的第一行是表格标题,第二行是副标题时间,所以以主表第三行表头为初始值*/
    let result = colWidth[3];
    for (let i = 2; i < colWidth.length-1; i++) {  //从第二行(i=2)开始到倒数第二行(i<colWidth.length-1)进行比较,调整列宽
      for (let j = 0; j < colWidth[i].length; j++) {
        if (result[j]['wch'] < colWidth[i][j]['wch']) {
          result[j]['wch'] = colWidth[i][j]['wch'];
        }
      }
    }
    ws['!cols'] = result;
  }
  return ws;
}

//设置表格样式
function setExcelStyle(dataInfo,length){
    // 设置单元格框线
  const borderAll = {
    top: {
      style: "thin"
    },
    bottom: {
      style: "thin"
    },
    left: {
      style: "thin"
    },
    right: {
      style: "thin"
    }
  };
  // 给所有单元格加上边框,内容居中,字体,字号,标题表头特殊格式部分后面替换
  for (var i in dataInfo) {
    if (i == "!ref" || i == "!merges" || i == "!cols" || i == "!rows" || i == "A1"|| i == "A2"|| i == "A"+String(length+1) || i == "A"+String(length+2)) {  //第一行A1,第二行A2以及倒数第二行,倒数第一行不加border
      //不做处理
     } else {
      dataInfo[i + ""].s = {
        border: borderAll,
        alignment: {
          horizontal: "center",
          vertical: "center"
        },
        font: {
          name: "微软雅黑",
          sz: 10
        }
      };
    }
  }

  // 设置表格样式,数字不够可以再往后面加AA,AB……
  const arrabc = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"]

  // 给标题、表格描述信息、表头等部分加上特殊格式
  arrabc.some(function (v) {
    for (let j = 1; j < 4; j++) {
      const _v = v + j
      if (dataInfo[_v]) {
        dataInfo[_v].s = {};
        // 标题部分A1-Z1
        if (j == 1) { //A1第一行居中加粗
          dataInfo[v + j].s = {
            font: {
              name: "微软雅黑",
              sz: 11,
              color: {
                rgb: "000000"
              },
              bold: true,
              italic: false,
              underline: false
            },
            alignment: {
              horizontal: "center",
              vertical: "center"
            }
          };
        }else if (j == 2) { //A1第一行居左
          dataInfo[v + j].s = {
            font: {
              name: "微软雅黑",
              sz: 10,
              bold: false,
              italic: false,
              underline: false
            },
            alignment: {
              horizontal: "left",
              vertical: "center"
            }
          };
        } else { //表头  加背景加粗
          const fv = v + 3
          dataInfo[fv].s = {
            border: borderAll,
            font: {
              name: "微软雅黑",
              sz: 10,
              bold: true
            },
            alignment: {
              horizontal: "center",
              vertical: "center"
            },
            fill: {
              fgColor: {
                rgb: "f0f0f0"
              },
            },
          }
        }
      }
    }
  });
}

使用

列表数据

tableHeaderArray:[
        {label:"发票类型",prop:'fplx'},
        {label:"发票代码",prop:'fpdm'},
        {label:"发票号码",prop:'fphm'},
        {label:"购方名称",prop:'gfmc'},
        {label:"主要商品名称",prop:'zyspmc'},
        {label:"开票日期",prop:'kprq'},
        {label:"发票状态",prop:'fpzt'},
        {label:"金额(不含税)",prop:'jebhs'},
        {label:"税率",prop:'sl'},
        {label:"税额",prop:'se'},
        {label:"价税合计",prop:'sjhj'},
        {label:"明细状态",prop:'mxzt'},
       /* {label:"作废状态",prop:'zfzt'}, */
        {label:"凭证号",prop:'pzh'}
      ],
      tableData2: [{'fplx':'机动车销售统一发票','fpdm':'91130100061','fphm':'91130100061','gfmc':'XXXXX有限公司','zyspmc':'XX盘','kprq':'2021-06-23',
      'fpzt':'','jebhs':'136.223.332.00','sl':'13%','se':'332.00','sjhj':'332.00','mxzt':'有','zsxm':'货物','jsfs':'一般计税','jzjt':'一般项目','zfzt':'是','pzh':'--'},
      {'fplx':'机动车销售统一发票','fpdm':'91130100061','fphm':'91130100061','gfmc':'XXXXX有限公司','zyspmc':'XX盘','kprq':'2021-06-23',
      'fpzt':'','jebhs':'136.223.332.00','sl':'13%','se':'332.00','sjhj':'332.00','mxzt':'有','zsxm':'货物','jsfs':'一般计税','jzjt':'一般项目','zfzt':'是','pzh':'--'},
      {'fplx':'机动车销售统一发票','fpdm':'91130100061','fphm':'91130100061','gfmc':'XXXXX有限公司','zyspmc':'XX盘','kprq':'2021-06-23',
      'fpzt':'','jebhs':'136.223.332.00','sl':'13%','se':'332.00','sjhj':'332.00','mxzt':'有','zsxm':'货物','jsfs':'一般计税','jzjt':'一般项目','zfzt':'是','pzh':'--'},
      {'fplx':'机动车销售统一发票','fpdm':'91130100061','fphm':'91130100061','gfmc':'XXXXX有限公司','zyspmc':'XX盘','kprq':'2021-06-23',
      'fpzt':'','jebhs':'136.223.332.00','sl':'13%','se':'332.00','sjhj':'332.00','mxzt':'有','zsxm':'货物','jsfs':'一般计税','jzjt':'一般项目','zfzt':'是','pzh':'--'},
      {'fplx':'机动车销售统一发票','fpdm':'91130100061','fphm':'91130100061','gfmc':'XXXXX有限公司','zyspmc':'XX盘','kprq':'2021-06-23',
      'fpzt':'','jebhs':'136.223.332.00','sl':'13%','se':'332.00','sjhj':'332.00','mxzt':'有','zsxm':'货物','jsfs':'一般计税','jzjt':'一般项目','zfzt':'是','pzh':'--'}]
    }

点击导出按钮,执行以下方法

handleDownload() {
       this.downloadLoading = true
       import('@/vendor/Export2ExcelMoreSheet').then(excel => {
        const tHeader= this.tableHeaderArray.map((item,index) => {return item.label})
        const filterVal= this.tableHeaderArray.map((item,index) => {return item.prop})
        const data = [this.formatJson(filterVal, this.tableData2),this.formatJson(filterVal, this.tableData2),this.formatJson(filterVal, this.tableData2),this.formatJson(filterVal, this.tableData2),this.formatJson(filterVal, this.tableData2),this.formatJson(filterVal, this.tableData2),this.formatJson(filterVal, this.tableData2),this.formatJson(filterVal, this.tableData2)]
        const merges = [["A1:M1","A2:M2"],["A1:M1","A2:M2"],["A1:M1","A2:M2"],["A1:M1","A2:M2"],["A1:M1","A2:M2"],["A1:M1","A2:M2"],["A1:M1","A2:M2"],["A1:M1","A2:M2"]]; // 根据Excel确定要合并的单元格
        excel.export_json_to_excel({
          title:['销项发票数据','销项发票数据','销项发票数据','销项发票数据','销项发票数据','销项发票数据','销项发票数据','销项发票数据'],
          SheetNames:["普通发票","电子普通发票","普通发票卷票","专用发票","电子专用发票","收购发票","机动车销售统一发票","二手车销售统一发票"],
          header: [tHeader,tHeader,tHeader,tHeader,tHeader,tHeader,tHeader,tHeader],
          data,
          filename: '发表列表'+(this.formInline.timeValue?('-'+this.formInline.timeValue[0]+'~'+this.formInline.timeValue[1]):''),
          merges,
          autoWidth: true,
          bookType: 'xlsx'
        })
        this.downloadLoading = false
      })
    },

最后实现效果如下图所示:

tempLate里面的方法直接让表格加一行 template sheets_前端


以上,如有小伙伴问题和意见可以随时提出。

感谢以下博主提供思路:
在VUE中使用Export2Excel导出表格,导出多个sheetvue导出excel,Export2Excel样式修改,复杂表头,多级表头