You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在jsPDF+jsPDF-autotable中渲染嵌套数组数据到PDF行?

解决方案:用jsPDF-autotable渲染嵌套数组的分组表格

我之前也碰到过一模一样的需求,jsPDF-autotable默认只支持扁平数据,要实现你期望的那种父级信息合并单元格、明细行展开的效果,得先把嵌套数据处理成适合渲染的结构,再配合autotable的回调函数实现单元格合并。下面是完整的实现步骤和代码:

核心思路

  1. 展开嵌套数据:把每个凭证的detailsList明细项展开,每个明细项和父级凭证信息绑定,只在第一个明细行保留完整的凭证信息,其余行对应位置留空。
  2. 单元格合并:利用autotable的didParseCell回调,给第一个明细行的凭证信息单元格设置rowspan(等于该凭证的明细行数),同时隐藏后续行的凭证信息单元格,实现跨行合并效果。

完整代码示例

首先确保你引入了jsPDF和jsPDF-autotable的依赖:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.5.29/jspdf.plugin.autotable.min.js"></script>

然后是处理数据和生成PDF的代码:

// 你的原始凭证数据
const voucherData = [{ 
  "voucherId":"18010110004", 
  "date":"2018-01-01", 
  "refNo":"0", 
  "narration":null, 
  "inWordTk":"Three Thousand Taka Only", 
  "detailsList":[{ 
    "ledgerName":"Cash", 
    "drAmount":3000, 
    "crAmount":0 
  }, { 
    "ledgerName":"New Admission Fee Collection", 
    "drAmount":0, 
    "crAmount":3000 
  }] 
}];

// 第一步:处理嵌套数据,转换成适合autotable的扁平结构
const tableRows = [];
voucherData.forEach(voucher => {
  const detailCount = voucher.detailsList.length;
  voucher.detailsList.forEach((detail, idx) => {
    tableRows.push({
      // 仅第一行保留凭证基础信息,其余行留空
      voucherId: idx === 0 ? voucher.voucherId : '',
      date: idx === 0 ? voucher.date : '',
      refNo: idx === 0 ? voucher.refNo : '',
      narration: idx === 0 ? voucher.narration : '',
      inWordTk: idx === 0 ? voucher.inWordTk : '',
      // 明细数据
      ledgerName: detail.ledgerName,
      drAmount: detail.drAmount,
      crAmount: detail.crAmount,
      // 标记是否为凭证的第一行,以及需要合并的行数
      isFirstRow: idx === 0,
      mergeRowCount: detailCount
    });
  });
});

// 第二步:初始化jsPDF并生成表格
const doc = new jspdf.jsPDF();

doc.autoTable({
  // 表头配置
  head: [['Voucher ID', 'Date', 'Ref No', 'Narration', 'In Word', 'Ledger Name', 'Dr Amount', 'Cr Amount']],
  // 表格数据行
  body: tableRows.map(row => [
    row.voucherId, row.date, row.refNo, row.narration, row.inWordTk,
    row.ledgerName, row.drAmount, row.crAmount
  ]),
  // 关键:通过回调实现单元格合并
  didParseCell: function(data) {
    const cell = data.cell;
    const rowIdx = data.row.index;
    const currentRow = tableRows[rowIdx];
    const colIdx = data.column.index;

    // 对前5列(凭证基础信息列)进行合并处理
    if (colIdx < 5) {
      if (currentRow.isFirstRow) {
        // 第一行设置rowspan为明细总数
        cell.rowSpan = currentRow.mergeRowCount;
      } else {
        // 非第一行隐藏该单元格
        cell.hidden = true;
      }
    }
  },
  // 可选:自定义表格样式
  styles: {
    cellPadding: 4,
    fontSize: 10,
    valign: 'middle'
  },
  headStyles: {
    fillColor: '#2c3e50',
    textColor: '#ffffff',
    fontStyle: 'bold'
  }
});

// 保存生成的PDF
doc.save('voucher-details.pdf');

关键细节说明

  • 数据处理部分:我们把每个凭证的明细项拆成独立行,只有第一行携带完整的凭证信息,这样后续可以通过合并单元格让这些信息跨所有明细行显示。
  • didParseCell回调:这是实现合并的核心,它会在每个单元格解析时触发。我们判断如果是凭证信息列(前5列),第一行就设置rowspan等于明细行数,其他行直接隐藏单元格,完美实现你要的分组合并效果。
  • 扩展性:如果有多个凭证,这段代码也能自动处理每个凭证的明细合并,不需要额外修改逻辑。

内容的提问来源于stack exchange,提问作者todd

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:07:18