用纯HTML写一个凭证并打印
用纯HTML写一个凭证并打印
一、背景与问题
在传统业务场景中,凭证(如发票、收据、证明文件等)的生成和打印一直是关键环节。传统方式需要通过后端生成PDF或Word文档,再通过打印机输出。但随着Web技术的发展,我们可以通过纯HTML+CSS实现凭证的动态生成与打印,这在以下场景中尤为实用:
- 快速生成单页凭证(如电子发票)
- 无需后端处理的轻量级业务场景
- 需要快速预览和打印的临时性凭证
- 降低服务器资源消耗
但这种方案也存在局限性,比如无法处理复杂格式、数据动态计算、安全校验等。本文将深入探讨这种方案的实现原理、技术细节和实际应用边界。
二、基本原理
1. HTML与CSS的打印机制
浏览器在打印时会使用@media print媒体查询来调整页面样式。关键特性包括:
- 隐藏非打印元素(如导航栏、页脚)
- 控制页面布局(如设置
@page规则) - 调整字体大小和边距
- 使用
print样式表覆盖默认样式
2. 动态内容生成
通过JavaScript动态生成HTML内容,结合模板字符串或模板引擎(如Handlebars),可以实现凭证内容的动态填充。
3. 打印预览与触发
使用window.print()方法触发打印对话框,或通过window.print()结合print()方法实现自动打印。
三、环境准备
# 前提条件
- 浏览器支持CSS打印样式
- 支持JavaScript动态内容生成
- 现代浏览器(Chrome/Firefox/Edge等)四、核心实现
1. 基础凭证模板
<!-- 基础模板 -->
<!DOCTYPE html>
<html>
<head>
<title>凭证打印</title>
<style>
@media print {
body {
font-size: 12pt;
margin: 1cm;
background: white;
}
.no-print {
display: none;
}
}
</style>
</head>
<body>
<div class="voucher">
<h1>凭证编号: <span id="voucherId"></span></h1>
<p>日期: <span id="date"></span></p>
<p>金额: <span id="amount"></span></p>
</div>
<button class="no-print" onclick="printVoucher()">打印凭证</button>
<script>
function printVoucher() {
const voucherId = 'V20231001';
const date = '2023-10-01';
const amount = '¥120.00';
document.getElementById('voucherId').textContent = voucherId;
document.getElementById('date').textContent = date;
document.getElementById('amount').textContent = amount;
window.print();
}
</script>
</body>
</html>关键点解释:
- 使用
@media print定义打印样式,隐藏非打印元素 - 通过JavaScript动态填充内容
- 使用
window.print()触发打印
2. 动态模板引擎
<!-- 动态模板示例 -->
<script>
const template = `
<div class="voucher">
<h1>凭证编号: ${voucherId}</h1>
<p>日期: ${date}</p>
<p>金额: ${amount}</p>
</div>
`;
const voucherId = 'V20231001';
const date = '2023-10-01';
const amount = '¥120.00';
document.body.innerHTML = template;
</script>注意:直接使用模板字符串时需注意XSS风险,应配合数据校验。
3. 打印样式优化
@media print {
body {
color: black;
background: white;
font-family: 'Courier New', monospace;
}
.voucher {
border: 1px solid #ccc;
padding: 20px;
width: 600px;
margin: auto;
}
.voucher h1 {
text-align: center;
font-size: 16pt;
}
.voucher p {
font-size: 12pt;
line-height: 1.5;
}
@page {
size: A4;
margin: 1cm;
}
}关键点:
- 使用
@page控制打印纸张尺寸 - 设置字体样式确保打印清晰
- 调整边距防止内容被裁剪
五、完整案例
1. 电子发票生成器
完整代码:
<!DOCTYPE html>
<html>
<head>
<title>电子发票打印</title>
<style>
@media print {
body {
font-size: 12pt;
margin: 1cm;
background: white;
}
.no-print {
display: none;
}
.invoice {
border: 1px solid #ccc;
padding: 20px;
width: 600px;
margin: auto;
background: #f9f9f9;
}
.invoice h1 {
text-align: center;
font-size: 16pt;
margin-bottom: 10px;
}
.invoice .header {
font-weight: bold;
margin-bottom: 10px;
}
.invoice .footer {
margin-top: 20px;
font-size: 10pt;
color: #555;
}
}
</style>
</head>
<body>
<div class="invoice" id="invoice">
<h1>电子发票</h1>
<div class="header">客户信息</div>
<p>姓名: <span id="name"></span></p>
<p>地址: <span id="address"></span></p>
<div class="header">订单信息</div>
<p>订单号: <span id="orderId"></span></p>
<p>金额: <span id="amount"></span></p>
<div class="footer">本发票仅限一次使用</div>
</div>
<button class="no-print" onclick="printInvoice()">打印发票</button>
<script>
function printInvoice() {
const data = {
name: '张三',
address: '北京市朝阳区',
orderId: 'INV20231001',
amount: '¥120.00'
};
Object.keys(data).forEach(key => {
document.getElementById(key).textContent = data[key];
});
window.print();
}
</script>
</body>
</html>运行效果:
- 页面加载后显示空白内容
- 点击"打印发票"按钮填充内容
- 调用
window.print()弹出打印窗口
关键改进:
- 使用对象统一管理数据
- 分离样式和内容
- 增加页脚说明
六、源码解析
1. 打印样式的作用域
@media print {
body {
font-family: 'Courier New', monospace;
}
}@media print确保仅在打印时应用- 设置字体保证打印清晰度
- 避免使用
@media screen影响打印效果
2. 动态内容填充
Object.keys(data).forEach(key => {
document.getElementById(key).textContent = data[key];
});- 使用
Object.keys遍历对象键 - 保证元素ID与数据键一致
- 支持动态添加字段
3. 打印预览优化
<button class="no-print" onclick="printInvoice()">打印发票</button>- 使用
class="no-print"隐藏按钮 - 使用
onclick触发打印 - 可通过
window.print()自动打印
七、进阶使用
1. 动态计算金额
const data = {
name: '李四',
address: '上海市浦东新区',
orderId: 'INV20231002',
quantity: 2,
price: 60.00
};
data.amount = (data.quantity * data.price).toFixed(2);注意:需要在模板中使用{{amount}},需要配合模板引擎(如Handlebars)。
2. 多页凭证生成
@media print {
.page {
page-break-after: always;
}
.page:last-child {
page-break-after: avoid;
}
}应用场景:长凭证(如多页合同)的分页打印
3. 二维码嵌入
<img src="https://api.qrserver.com/v1/create-qr-code/?data=INV20231001&size=150x150" alt="二维码">注意:需确保二维码内容在打印时清晰可读
八、性能与工程实践
1. 性能优化建议
| 场景 | 优化策略 |
|---|---|
| 大量数据 | 使用documentFragment批量插入 |
| 频繁打印 | 使用<template>标签缓存模板 |
| 复杂样式 | 使用@media print避免样式冲突 |
| 多页打印 | 使用page-break控制分页 |
2. 异常处理
try {
window.print();
} catch (e) {
console.error('打印失败:', e);
alert('打印失败,请检查浏览器设置');
}3. 安全注意事项
- 避免直接使用用户输入生成HTML
- 对金额、订单号等敏感字段进行校验
- 禁止直接在客户端生成敏感凭证
- 建议配合后端校验凭证有效性
九、常见问题与踩坑
1. 打印样式不生效
错误示例:
@media print {
body {
font-size: 12pt;
}
}问题:未设置margin和padding导致内容被裁剪
解决办法:
@media print {
body {
font-size: 12pt;
margin: 1cm;
padding: 0;
}
}2. 内容被隐藏
错误示例:
<div class="no-print">隐藏内容</div>问题:未在@media print中定义display: none
解决办法:
@media print {
.no-print {
display: none;
}
}3. 页面布局错乱
错误示例:
.invoice {
width: 800px;
}问题:打印时页面尺寸不同导致布局错乱
解决办法:
@media print {
.invoice {
width: 600px;
margin: auto;
}
}4. 打印预览显示异常
错误示例:
<img src="image.png" alt="图片">问题:图片未设置width和height导致缩放
解决办法:
<img src="image.png" alt="图片" style="width: 100%; height: auto;">十、最佳实践
1. 推荐方案
| 场景 | 推荐方案 |
|---|---|
| 单页凭证 | 纯HTML+CSS方案 |
| 多页凭证 | 分页打印+CSS控制 |
| 动态计算 | JavaScript+模板引擎 |
| 安全要求 | 后端生成+客户端打印 |
2. 实施建议
- 使用
@media print控制打印样式 - 通过JavaScript动态填充内容
- 使用
window.print()触发打印 - 对关键字段进行校验
- 避免使用复杂布局和动画
- 提供打印预览功能
3. 工程实践
- 使用
<template>标签存储模板内容 - 使用
documentFragment优化DOM操作 - 使用
Promise处理异步数据加载 - 使用
localStorage缓存凭证数据
十一、总结
纯HTML+CSS实现凭证打印方案具有以下特点:
优点:
- 实现简单,无需后端处理
- 适合快速生成单页凭证
- 无需依赖第三方库
- 可实现动态内容填充
局限性:
- 不适合复杂格式要求
- 缺乏数据校验和安全机制
- 无法处理动态计算
- 无法生成PDF等格式文件
适用场景:
- 快速生成的临时凭证
- 简单的电子发票
- 需要快速预览的场景
不适用场景:
- 需要加密或签名的凭证
- 复杂格式的文档
- 涉及财务数据的凭证
- 需要打印后存档的场景
通过合理设计和结合前端技术,这种方案可以有效降低开发成本,提高业务响应速度。但在涉及敏感数据时,仍需配合后端校验和安全机制。
评论已关闭