ExcelJS +file-saver 实现excel文件导出(前端,纯JS方式)

1. 版本说明

//  "exceljs": "^4.2.1"      https://www.npmjs.com/package/exceljs
//  "file-saver": "^2.0.5",
import ExcelJS from "exceljs";
import { saveAs } from "file-saver";

2. 简单导出(无合并单元格)

// 前置数据
const resList = [
  {applyForName:"张三",title:"报销主题1",/*....其他数据*/},
  {applyForName:"李四",title:"报销主题2",/*....其他数据*/},
  /*....其他数据*/
];
const columns = [
   {header:"序号",key:"index",width:10},
   {header:"报销人",key:"applyForName",width:20},
   {header:"报销主题",key:"title",width:40},
];
// ----------------------------------------------
   handleExport() {
    const resList =   this.resList.map((i, idx) => ({
          index: idx + 1,
          applyForName:i.applyForName,//报销人
          title:i.expenseTitle,//报销主题
          // ...其他数据
        }));

      this.commonExport("expenseTableRef", resList  , "报销单明细数据");

      // this.commonExport(columns, resList  , "报销单明细数据");
    },
    /**
     * 简单数据(无合并单元格)excel导出
     * @param elTableRef elTable ref对象
     * @param tableData elTable 数据
     * @param fileName 文件名
     */
    commonExport(elTableRef, tableData, fileName) {
      // 返回表头数组
      const getTableHeader = () => {
        return this.$refs[elTableRef].columns.map(column => column.label);
      };
      // 返回表格行数据数组
      const getTableRow = row => {
        return this.$refs[elTableRef].columns.map(
          column => row[column.property]
        );
      };
   
      const workbook = new ExcelJS.Workbook();  // 创建工作簿
      const worksheet = workbook.addWorksheet("Sheet");  // 创建工作表
      worksheet.addRow(getTableHeader());  // 添加表头

      // 添加表格数据
      tableData.forEach(row => { worksheet.addRow(getTableRow(row));   });

      // 定义表格样式
      worksheet.eachRow({ includeEmpty: true }, function(row, rowNumber) {
        row.eachCell({ includeEmpty: true }, function(cell, colNumber) {
          cell.border = { style: "thin" };
        });
      });

      // 定义每列的宽度
      worksheet.columns.forEach((column, index) => {
        column.width = index < 1 ? 10 : 20; // 假设前三列宽度为20,其余为30
      });

      // 定义工作表视图
      workbook.xlsx.writeBuffer().then(data => {
        // 使用 FileSaver 保存文件
        const blob = new Blob([data], {
          type:  "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8"
        });
        saveAs(blob, (fileName || "数据导出") + ".xlsx");
      });
    }

/**
     * 简单数据(无合并单元格)excel导出
     * @param columns 表头数据
     * @param tableData elTable 数据
     * @param fileName 文件名
     */
    commonExport2(columns, tableData, fileName) {
      // 返回表格行数据数组
      const getTableRow = row => {
        return columns.map(c=> row[c.key]);
      };
   
      const workbook = new ExcelJS.Workbook();  // 创建工作簿
      const worksheet = workbook.addWorksheet("Sheet");  // 创建工作表
      //// 添加表头 方式1
      // worksheet.addRow(columns.map(c=>c.header));  
      // 添加表头 方式2 ,自动读取width,style等参数,具体参照官网 https://www.npmjs.com/package/exceljs#styles
      worksheet.column = column;
      // 添加表格数据
      tableData.forEach(row => { worksheet.addRow(getTableRow(row));   });

      // 定义表格样式 
      worksheet.eachRow({ includeEmpty: true }, function(row, rowNumber) {
        if(rowNumber <= 1) {
            row.eachCell({ includeEmpty: true }, function(cell, colNumber) {
                 cell.border = { style: "thin" };
                  cell.font = {
                       bold: true, // 字体加粗
                       size: 12, // 字体大小
                       color: { argb: "FF000000" } // 字体颜色
                 };
                 cell.fill = {
                       type: "pattern",
                       pattern: "solid",
                       fgColor: { argb: "FFF0F0F0" } // 背景颜色
                  };
                 cell.alignment = {
                       horizontal: "center", // 水平居中
                       vertical: "middle" // 垂直居中
                  };
            });
        }else{
            row.eachCell({ includeEmpty: true }, function(cell, colNumber) {
              cell.border = { style: "thin" };
            });
        }
      });

      // 定义工作表视图
      workbook.xlsx.writeBuffer().then(data => {
        // 使用 FileSaver 保存文件
        const blob = new Blob([data], {
          type:  "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8"
        });
        saveAs(blob, (fileName || "数据导出") + ".xlsx");
      });
    }

2. 合并行导出(合并行)

// 前置数据
const resList = [
  {applyForName:"张三",title:"报销主题1",/*....其他数据*/,itemList:[
    {ticketNum:"发票号1",money:"20元",time:"2025-01-17"},
    {ticketNum:"发票号2",money:"25.5元",time:"2025-01-27"},
    {ticketNum:"发票号3",money:"55元",time:"2025-01-27"},
]},
  {applyForName:"李四",title:"报销主题2",/*....其他数据*/,itemList:[]},
  /*....其他数据*/
];
const columns = [
   {header:"序号",key:"index",width:10,fixed:true},// fixed表示是否合并行
   {header:"报销人",key:"applyForName",width:20,fixed:true},
   {header:"报销主题",key:"title",width:40,fixed:true},
   {header:"发票号",key:"ticketNum",width:20,},
   {header:"发票金额",key:"money",width:20,},
   {header:"开票时间",key:"time",width:20,},
];
// ----------------------------------------------
handleExport() {
   this.commonExport(columns, resList, "报销单明细数据");
},
commonExport(columns, tableData, fileName) {
  // 生成行数据
  const getRowData = row => {
    return columns.map(i => row[i.key]);
  };
  // 创建工作簿
  const workbook = new ExcelJS.Workbook(); 
 // 创建工作表
  const worksheet = workbook.addWorksheet("Sheet");  
 // 添加表头 此种方式自动添加表头(header:表头,key:对应值)
  worksheet.columns = columns.map(i => ({ ...i /* style: headerStyle */ })); 

  // 添加表格数据
  let currentIndex = 2, xh = 1; //从此行添加数据 这个是下表(from 0 to ~)
  tableData.forEach(rowData => {
          //rowData 第一层,itemList.item 第二层
      let itemList = rowData.itemList|| [];

      if (itemList.length == 0) { // 无子集数据
        worksheet.insertRow( currentIndex, getRowData({  xh, ...rowData }) );
        currentIndex++;
        xh++;
      } else { // 存在子集数据
        itemList.forEach((itemData, idx2) => {
                   //在currentIndex插入行数据
          worksheet.insertRow( currentIndex,
                         getRowData({ xh, ...rowData, ...itemData }) ); //如果存在相同key,需自行调整
                  // 子集数据全部写入完成后,合并行数据
          if (idx2 == itemList.length - 1) {
            // 写入完成后行合并,merge by start row, start column, end row, end column (equivalent to K10:M12)
            // 参考 https://www.npmjs.com/package/exceljs#merged-cells
            columns.forEach((c, cidx) => {
              if (c.fixed) {
               // merge by start row, start column, end row, end column (equivalent to K10:M12)
                worksheet.mergeCells(
                   currentIndex - itemList.length + 1, cidx + 1,
                  currentIndex,cidx + 1
                );
              }
            });
          }
          // ++
          currentIndex++;
          xh++;
        });
      }
    });

  // 定义表格样式
  worksheet.eachRow({ includeEmpty: true }, function(row, rowNumber) {
    if (rowNumber <= 1) { //表头加粗、背景色、居中
      row.eachCell({ includeEmpty: true }, function(cell, colNumber) {
        cell.border = { style: "thin" };
        cell.font = {
          bold: true, // 字体加粗
          size: 12, // 字体大小
          color: { argb: "FF000000" } // 字体颜色
        };
        cell.fill = {
          type: "pattern",
          pattern: "solid",
          fgColor: { argb: "FFF0F0F0" } // 背景颜色
        };
        cell.alignment = {
          horizontal: "center", // 水平居中
          vertical: "middle" // 垂直居中
        };
      });
    } else { //表数据居中
      row.eachCell({ includeEmpty: true }, function(cell, colNumber) {
        cell.border = { style: "thin" };
        cell.alignment = { vertical: "middle", horizontal: "center" };
      });
    }
  });

  // 定义工作表视图
  workbook.xlsx.writeBuffer().then(data => {
    // 使用 FileSaver 保存文件
    const blob = new Blob([data], {
      type:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8"
    });
    saveAs(blob, (fileName || "数据导出") + ".xlsx");
  });
},
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容