使用ExcelJS精准实现Ant-Table数据到Excel的模板化导出

作者:新兰2025.10.12 09:09浏览量:2

简介:本文详细介绍如何使用ExcelJS库将Ant Design的Table组件数据按照预设模板导出为Excel文件,涵盖模板设计、数据填充、样式优化等核心环节,提供完整的代码实现方案。

一、技术选型与场景分析

在Web开发中,将前端表格数据导出为Excel是常见需求。Ant Design的Table组件提供了丰富的数据展示功能,但原生不支持直接导出为带有复杂样式的Excel文件。ExcelJS作为一款功能强大的Node.js库,能够精确控制Excel文件的单元格样式、合并区域、公式计算等特性,非常适合实现模板化导出。

典型应用场景包括:

  1. 财务报表导出(需保持固定格式)
  2. 业务数据模板下载(如订单模板、考勤表)
  3. 多Sheet协同展示(主表+明细表结构)
  4. 条件格式应用(如数据异常高亮)

与xlsx等库相比,ExcelJS的优势在于:

  • 更精细的样式控制(字体、边框、填充等)
  • 支持模板文件加载与修改
  • 异步写入大文件能力
  • 跨平台兼容性(Node.js/浏览器)

二、核心实现步骤

1. 环境准备与依赖安装

  1. npm install exceljs antd @ant-design/icons
  2. # 或使用yarn
  3. yarn add exceljs antd @ant-design/icons

2. 模板设计原则

优秀模板应具备:

  • 固定表头区域(冻结首行)
  • 动态数据区域(预留扩展空间)
  • 样式隔离区(标题、页脚独立样式)
  • 公式计算区(如自动求和)

建议采用分层设计:

  1. const templateConfig = {
  2. sheetName: '业务数据',
  3. headerRange: 'A1:F1', // 表头区域
  4. dataRange: 'A2:F100', // 数据预留区域
  5. footerRange: 'A101:F101', // 页脚区域
  6. styles: {
  7. header: { /* 样式定义 */ },
  8. data: { /* 样式定义 */ },
  9. footer: { /* 样式定义 */ }
  10. }
  11. };

3. 数据填充实现

基础数据填充

  1. const ExcelJS = require('exceljs');
  2. const { Table } = require('antd');
  3. async function exportToExcel(tableData, templatePath) {
  4. const workbook = new ExcelJS.Workbook();
  5. // 加载模板或新建工作簿
  6. if (templatePath) {
  7. await workbook.xlsx.readFile(templatePath);
  8. } else {
  9. const sheet = workbook.addWorksheet('Sheet1');
  10. // 初始化模板结构...
  11. }
  12. const worksheet = workbook.getWorksheet('业务数据');
  13. // 填充表头(示例)
  14. const headerRow = worksheet.getRow(1);
  15. headerRow.values = ['序号', '名称', '数量', '单价', '金额', '备注'];
  16. // 填充数据
  17. tableData.forEach((item, index) => {
  18. const row = worksheet.getRow(index + 2);
  19. row.values = [
  20. index + 1,
  21. item.name,
  22. item.quantity,
  23. item.price,
  24. item.quantity * item.price,
  25. item.remark
  26. ];
  27. });
  28. // 自动调整列宽
  29. worksheet.columns.forEach(column => {
  30. let maxLength = 0;
  31. column.eachCell({ includeEmpty: true }, cell => {
  32. const columnLength = cell.value ? cell.value.toString().length : 0;
  33. if (columnLength > maxLength) {
  34. maxLength = columnLength;
  35. }
  36. });
  37. column.width = maxLength < 10 ? 10 : maxLength + 2;
  38. });
  39. // 生成Excel文件
  40. const buffer = await workbook.xlsx.writeBuffer();
  41. // 后续处理(如触发下载)...
  42. }

高级样式处理

  1. // 应用表头样式
  2. function applyHeaderStyle(row) {
  3. row.eachCell(cell => {
  4. cell.font = { bold: true, color: { argb: 'FFFFFF' } };
  5. cell.fill = {
  6. type: 'pattern',
  7. pattern: 'solid',
  8. fgColor: { argb: '4472C4' }
  9. };
  10. cell.border = {
  11. top: { style: 'thin' },
  12. left: { style: 'thin' },
  13. bottom: { style: 'thin' },
  14. right: { style: 'thin' }
  15. };
  16. cell.alignment = { vertical: 'middle', horizontal: 'center' };
  17. });
  18. }
  19. // 应用数据行样式
  20. function applyDataStyle(row, isOdd) {
  21. row.eachCell(cell => {
  22. cell.border = {
  23. top: { style: 'thin' },
  24. left: { style: 'thin' },
  25. bottom: { style: 'thin' },
  26. right: { style: 'thin' }
  27. };
  28. cell.alignment = { vertical: 'middle' };
  29. });
  30. if (isOdd) {
  31. row.eachCell(cell => {
  32. cell.fill = {
  33. type: 'pattern',
  34. pattern: 'solid',
  35. fgColor: { argb: 'EDEDED' }
  36. };
  37. });
  38. }
  39. }

4. 与Ant-Table深度集成

1) 获取表格数据

  1. // 在Ant-Table组件中
  2. const [dataSource, setDataSource] = useState([]);
  3. const [selectedRowKeys, setSelectedRowKeys] = useState([]);
  4. const columns = [
  5. { title: '名称', dataIndex: 'name', key: 'name' },
  6. { title: '数量', dataIndex: 'quantity', key: 'quantity' },
  7. // 其他列定义...
  8. ];
  9. // 导出按钮处理
  10. const handleExport = () => {
  11. // 获取当前显示数据(可添加筛选条件)
  12. const exportData = dataSource.filter(item =>
  13. selectedRowKeys.includes(item.key) || selectedRowKeys.length === 0
  14. );
  15. exportToExcel(exportData);
  16. };

2) 动态列映射

  1. function mapAntColumnsToExcel(antColumns) {
  2. return antColumns.map(col => ({
  3. header: col.title,
  4. key: col.dataIndex,
  5. width: col.width ? col.width / 7.5 : 15, // 近似转换
  6. style: col.className ? getStyleByClassName(col.className) : null
  7. }));
  8. }
  9. // 在导出函数中使用
  10. const excelColumns = mapAntColumnsToExcel(columns);

三、性能优化策略

1. 大数据量处理

  • 分块写入:对于超过10万行数据,采用流式写入

    1. async function exportLargeData(data, chunkSize = 5000) {
    2. const workbook = new ExcelJS.Workbook();
    3. const sheet = workbook.addWorksheet('大数据');
    4. // 写入表头...
    5. for (let i = 0; i < data.length; i += chunkSize) {
    6. const chunk = data.slice(i, i + chunkSize);
    7. chunk.forEach((item, index) => {
    8. const rowIdx = i + index + 2; // +2因为表头占1行,索引从0开始
    9. const row = sheet.getRow(rowIdx);
    10. // 填充数据...
    11. });
    12. // 定期保存(浏览器环境需特殊处理)
    13. if (i % (chunkSize * 5) === 0) {
    14. console.log(`已处理 ${i} 条数据`);
    15. }
    16. }
    17. // 最终写入...
    18. }

2. 样式复用

  • 创建样式模板库
    ```javascript
    const styleTemplates = {
    currency: {
    numFmt: ‘“¥”#,##0.00’,
    alignment: { horizontal: ‘right’ }
    },
    percentage: {
    numFmt: ‘0.00%’,
    alignment: { horizontal: ‘right’ }
    },
    date: {
    numFmt: ‘yyyy-mm-dd’,
    alignment: { horizontal: ‘center’ }
    }
    };

// 应用样式
sheet.getCell(‘D2’).numFmt = styleTemplates.currency.numFmt;

  1. # 四、常见问题解决方案
  2. ## 1. 中文乱码问题
  3. - 解决方案:确保文件以UTF-8编码保存,并设置正确的字体
  4. ```javascript
  5. workbook.creator = 'My App';
  6. workbook.created = new Date();
  7. // 设置中文字体
  8. workbook.eachSheet((sheet, id) => {
  9. sheet.columns.forEach(column => {
  10. column.style = { font: { name: '微软雅黑', family: 2 } };
  11. });
  12. });

2. 浏览器端导出

  1. // 前端导出实现
  2. function downloadExcel(buffer, fileName = '导出数据.xlsx') {
  3. const blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
  4. const url = URL.createObjectURL(blob);
  5. const a = document.createElement('a');
  6. a.href = url;
  7. a.download = fileName;
  8. a.click();
  9. URL.revokeObjectURL(url);
  10. }
  11. // 在async函数中调用
  12. const buffer = await workbook.xlsx.writeBuffer();
  13. downloadExcel(buffer);

3. 合并单元格处理

  1. // 合并表头示例
  2. worksheet.mergeCells('A1:B1');
  3. const mergedCell = worksheet.getCell('A1');
  4. mergedCell.value = '基本信息';
  5. mergedCell.alignment = { horizontal: 'center', vertical: 'middle' };
  6. // 动态合并相同内容单元格
  7. function mergeSameCells(sheet, columnKey, startRow = 2) {
  8. let mergeStart = null;
  9. let prevValue = null;
  10. for (let i = startRow; i <= sheet.rowCount; i++) {
  11. const cell = sheet.getCell(`${columnKey}${i}`);
  12. if (cell.value === prevValue) {
  13. if (!mergeStart) mergeStart = i - 1;
  14. } else {
  15. if (mergeStart !== null && i - 1 > mergeStart) {
  16. sheet.mergeCells(`${columnKey}${mergeStart + 1}:${columnKey}${i - 1}`);
  17. }
  18. mergeStart = null;
  19. prevValue = cell.value;
  20. }
  21. }
  22. }

五、最佳实践建议

  1. 模板分离原则:将模板设计与数据填充分离,便于维护
  2. 样式命名规范:建立统一的样式命名体系(如header-primary)
  3. 错误处理机制
    1. try {
    2. await exportToExcel(data);
    3. } catch (error) {
    4. console.error('导出失败:', error);
    5. message.error('导出失败,请重试');
    6. }
  4. 进度反馈:对于大数据量导出,显示进度条
  5. 多环境适配:区分开发/生产环境的模板路径配置

通过以上方法,开发者可以高效实现Ant-Table数据到Excel的模板化导出,既保持前端展示的一致性,又满足业务对Excel文件格式的严格要求。实际项目中,建议结合具体业务场景进行定制化开发,建立可复用的导出组件库。