如何在jsPDF+jsPDF-autotable中渲染嵌套数组数据到PDF行?
解决方案:用jsPDF-autotable渲染嵌套数组的分组表格
我之前也碰到过一模一样的需求,jsPDF-autotable默认只支持扁平数据,要实现你期望的那种父级信息合并单元格、明细行展开的效果,得先把嵌套数据处理成适合渲染的结构,再配合autotable的回调函数实现单元格合并。下面是完整的实现步骤和代码:
核心思路
- 展开嵌套数据:把每个凭证的
detailsList明细项展开,每个明细项和父级凭证信息绑定,只在第一个明细行保留完整的凭证信息,其余行对应位置留空。 - 单元格合并:利用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
相关产品推荐
相关产品推荐

