前端excel导出

0 阅读1分钟

前端 Excel 导出实战:基于 ExcelJS 实现复杂样式表格导出

在企业级后台系统开发中,Excel 文件导出是高频刚需功能,尤其需要支持自定义表头、单元格合并、样式美化、冻结窗格、数据格式化等复杂场景。

本文将基于ExcelJS(前端 Excel 处理神器),手把手带你实现一份带复杂样式、动态数据、格式化导出的资金使用计划 Excel 文件,代码可直接复用,适配 Vue/React 等所有前端框架

try {
        const headers = ['序号', '项目名称'];
        const workbook = new ExcelJS.Workbook();
        const worksheet = workbook.addWorksheet('sheet1');

        worksheet.views = [{ state: 'frozen', ySplit: 3 }];
        worksheet.columns = [
          { width: 10 },
          { width: 28 },
          { width: 16 },
          { width: 18 },
          { width: 18 },
          { width: 20 },
          { width: 18 },
          { width: 16 },
        ];

        worksheet.mergeCells('A1:H1');
        worksheet.getCell('A1').value = this.plan.name || '资金使用计划';
        worksheet.getCell('A1').font = { name: 'Arial', size: 16, bold: true };
        worksheet.getCell('A1').alignment = { vertical: 'middle', horizontal: 'center' };

        worksheet.mergeCells('A2:H2');
        worksheet.getCell('A2').value = `制表日期:${this.plan.createTime || ''}    单位:${this.form.unit || ''}`;
        worksheet.getCell('A2').alignment = { vertical: 'middle', horizontal: 'right' };
        worksheet.getCell('A2').font = { name: 'Arial', size: 12 };

        headers.forEach((h, idx) => {
          const cell = worksheet.getCell(3, idx + 1);
          cell.value = h;
          cell.font = { bold: true, color: { argb: 'FF303133' } };
          cell.alignment = { vertical: 'middle', horizontal: 'center' };
          cell.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF99CCFF' } };
        });

        const borderThin = {
          top: { style: 'thin' },
          left: { style: 'thin' },
          bottom: { style: 'thin' },
          right: { style: 'thin' },
        };

        const rows = this.tableData || [];
        let excelRow = 4;
        rows.forEach((r) => {
          const item = r.item || {};
          const rowValues = r.rowType === 'ITEM'
            ? [
              item.seq || '',
              item.itemName || '',
              toNumberOrNull(item.deptReportedAmount) !== null ? roundTo4(toNumberOrNull(item.deptReportedAmount)) : '',
              item.expensePeriod || '',
              item.budgetSubject || '',
              item.otherUnit || '',
              toNumberOrNull(item.accountBalanceAmount) !== null ? roundTo4(toNumberOrNull(item.accountBalanceAmount)) : '',
              item.reimburseDate || '',
            ]
            : [
              '',
              r.label || '',
              r.rowType === 'DEPARTMENTSUMMARY' ? this.officeTotals.deptReportedAmount : '',
              '',
              '',
              '',
              r.rowType === 'DEPARTMENTSUMMARY' ? this.officeTotals.accountBalanceAmount : '',
              '',
            ];

          const fillColor = r.rowType === 'CATEGORY' || r.rowType === 'DEPARTMENTSUMMARY' ? 'FFBFBFBF' : 'FFFFFFFF';
          for (let c = 0; c < headers.length; c++) {
            const cell = worksheet.getCell(excelRow, c + 1);
            cell.value = rowValues[c];
            cell.border = borderThin;
            cell.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: fillColor } };
            cell.alignment = {
              vertical: 'middle',
              horizontal: c === 1 ? 'left' : 'center',
            };
            if (r.rowType !== 'ITEM') {
              cell.font = { bold: true };
            }
          }
          excelRow++;
        });

        const buffer = await workbook.xlsx.writeBuffer();
        const blob = new Blob([buffer], {
          type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
        });
        const url = URL.createObjectURL(blob);
        const link = document.createElement('a');
        link.href = url;
        link.download = `${this.plan.name || '资金使用计划'}.xlsx`;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        URL.revokeObjectURL(url);