收据是交易场景中最常见的凭证,无论是电商订单、线下门店还是服务收费,用户付款后都需要一张规范的收据。很多系统的收据生成依赖后端,模板修改麻烦,中文字体容易出问题,而且每次开具都要占用服务器资源。dompdf.js作为纯前端PDF生成方案,可以在浏览器里直接完成收据渲染:数据在本地组装成HTML,调用addPage渲染成矢量PDF,再通过save下载,全程无需后端参与。本文将介绍收据PDF生成的完整流程,包括收据模板设计、金额格式处理、多页收据和常见问题排查,帮助你在几分钟内搭建起在线开收据能力。除了常规的消费收据,退款收据、窄幅热敏纸版式、时区处理这些细节也会一并讲到。文中展示的模式既适合小店,也适合大型平台,因为渲染工作都发生在顾客自己的设备上。读完本文,你就能在几乎任何支付流程里,用不多的代码加上在线开收据的能力。
收据广泛应用于电商平台、零售门店、物业收费、培训机构和软件服务等场景,通常包含商户名称、交易日期、收据编号、商品明细、金额大小写和收款方式等要素。一张规范的收据既要满足财务入账要求,也要让顾客一眼看清消费内容。
电子收据相比纸质收据有天然优势:不丢失、易归档、可随时重新下载。很多平台在用户支付成功后自动生成收据PDF并发送到邮箱,用户无需等待纸质单据,商家也节省了打印和邮寄成本,整体体验显著提升。
收据生成有明确的时效要求,支付成功后用户往往立刻查看,因此生成速度至关重要。前端方案在用户浏览器本地完成渲染,没有网络请求和服务器排队,几乎零延迟;相比后端渲染动辄几百毫秒到数秒的等待,体验差距非常明显。
除了常见的消费收据,还有一类是退款收据。退款场景下,收据上必须清晰标注原交易编号、退款金额和退款原因,金额用负数或括号标注(打印时可用括号代替颜色),并注明仅退款不退货等条款,避免后续纠纷。这类模板可以在基础收据上扩展,复用一个模板函数传入不同场景配置即可。
收据内容相对固定、结构清晰,非常适合用HTML模板描述。dompdf.js直接解析DOM渲染PDF,模板即页面,开发人员无需学习新的绘图API,只要会写HTML和CSS就能快速产出符合要求的收据,维护成本极低。
收据中的中文排版是很多方案的痛点,dompdf.js内置思源黑体,中文标点、金额大写、特殊符号都能正确渲染,无需在服务器安装字体或配置字体映射,彻底告别乱码和方块字问题。
安全性方面,收据数据涉及交易信息,前端方案数据不出浏览器,不经过第三方服务,降低了泄露风险;同时矢量PDF体积小、清晰度高,支持搜索复制,方便用户和财务人员后续检索核对。
兼容性也是选择前端方案的重要理由。dompdf.js在主流现代浏览器中表现一致,渲染结果不依赖操作系统字体库,Windows、macOS、Linux上生成的收据版式完全相同;而服务端方案往往要针对不同Linux发行版单独配置字体包,维护成本明显更高。
收据模板一般分为三个区域:头部放置商户Logo、收据标题和编号;中部是交易明细表格,包含商品名称、单价、数量、金额;底部是合计金额、大写金额、支付方式和开票日期,形成完整的视觉闭环。
表格使用border-collapse: collapse保持边框统一,金额列右对齐并保留两位小数;合计行可以加粗或用浅色背景突出。收据宽度建议控制在页面可读范围内,避免过宽导致打印时被截断。
金额大写是收据的必备元素,建议在JavaScript中实现数字转中文大写的工具函数,生成PDF前转换好再填入模板;日期格式统一为YYYY-MM-DD,编号规则建议包含日期和流水号,便于对账查询。
模板的样式建议统一放在style标签里而不是散落的内联样式,方便后期统一调整品牌色和字体;收据宽度建议以75mm、80mm热敏纸为参照做一版窄版模板,同时保留A4版用于电子发送,一套数据两套版式,覆盖门店打印和线上发送两种场景。
下面演示如何用dompdf.js生成一张标准电子收据。代码先构造收据HTML,包含商户信息、交易明细和合计金额,然后创建DomPDF实例渲染并保存,核心逻辑非常简洁。
实际项目中,收据数据通常来自支付回调或订单接口,建议封装一个buildReceiptHtml(data)函数,接收订单对象返回HTML字符串,再调用生成函数完成渲染保存,这样业务代码与模板代码分离,便于维护和测试。
示例中的表格只有两列(项目、金额),这是最常见的小额收据结构。如果收据需要展示多行商品,把商品明细做成独立表格,列设为商品名称、数量、单价、金额,与示例中的合计区上下排列即可;列数增多时记得控制列宽总和为100%,避免内容溢出页面。
关于字体,示例中显式声明了Source Han Sans SC作为首选字体。虽然dompdf.js内置了这款中文字体,显式声明可以保证在自定义字体或英文环境下也能正确回退;英文和数字建议使用等宽或半角字体,让金额列在视觉上更整齐。
import { DomPDF } from 'dompdf.js';
const receiptHtml = `
<div style='font-family:Source Han Sans SC,sans-serif;width:100%'>
<div style='text-align:center;border-bottom:2px solid #333;padding-bottom:10px'>
<h1 style='margin:0;font-size:18pt'>电子收据</h1>
<p style='margin:4px 0;font-size:10pt'>某某科技有限公司</p>
<p style='margin:0;font-size:10pt'>收据编号:R20260818001</p>
</div>
<table style='width:100%;border-collapse:collapse;margin-top:12px'>
<tr>
<th style='border:1px solid #333;padding:8px;text-align:left'>项目</th>
<th style='border:1px solid #333;padding:8px;text-align:right'>金额</th>
</tr>
<tr>
<td style='border:1px solid #333;padding:8px'>软件订阅费(2026年8月)</td>
<td style='border:1px solid #333;padding:8px;text-align:right'>¥299.00</td>
</tr>
<tr>
<td style='border:1px solid #333;padding:8px;text-align:right;font-weight:bold'>合计</td>
<td style='border:1px solid #333;padding:8px;text-align:right;font-weight:bold'>¥299.00</td>
</tr>
</table>
<p style='font-size:10pt'>大写金额:人民币贰佰玖拾玖元整</p>
<p style='font-size:10pt'>支付方式:微信支付 支付时间:2026-08-18 10:23:45</p>
</div>
`;
const pdf = new DomPDF();
pdf.addPage(receiptHtml, { format: 'A4', margin: '20mm' });
pdf.save('收据-R20260818001.pdf');
当一次交易包含大量明细时,收据会超过一页。dompdf.js支持自动分页,多页内容会按页面高度自动拆分,同时支持@page媒体属性控制页面边距和尺寸,保证打印时页边距一致、内容不被裁切。
批量开具场景(如门店一天几十笔交易)可以在循环中逐张addPage,最终保存为一个包含多张收据的PDF文件,方便集中打印和归档;每张收据从新页开始,配合分页控制确保单据之间互不粘连。
需要注意收据编号的唯一性:前端生成时建议由后端接口分配编号后再渲染,避免并发开具时出现重复编号;如果不依赖后端,也可以使用时间戳加随机数的方式生成,并本地记录已用编号用于查重。
多页收据打印时,建议在每页页脚输出收据编号和页码,方便财务归档;如果收据需要存档,可以把所有页合成一个PDF保存,命名规则采用收据编号加日期,例如R20260818-001.pdf,检索时按编号即可快速定位。
常见问题一:收据中的Logo或印章图片不显示。建议将图片转为base64内嵌到HTML中,并显式设置宽度,避免使用过大的图片导致渲染变慢或文件体积膨胀。
常见问题二:金额出现浮点误差。金额计算应使用分作为整数单位运算,或使用toFixed(2)格式化后再展示,切勿直接用浮点数累加后渲染,避免出现299.00000000000006这类问题。
常见问题三:生成的PDF在部分浏览器下载失败。save方法触发的下载依赖浏览器行为,建议在调用前确认页面允许下载,并做好异常捕获,失败时提示用户重试或改用新窗口打开。
还要注意时区和时间格式:收据上的交易时间应以商户本地时间为准,而不是用户设备时间;跨时区的SaaS产品尤其要注意,最好由后端在支付回调时返回标准时间字符串,前端直接渲染,避免设备时区不一致导致收据时间对不上。
下面的按钮用 dompdf.js 在浏览器端实时生成 PDF,无需后端:
这是由 dompdf.js 渲染的示例 PDF 内容。