用纯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>

运行效果:

  1. 页面加载后显示空白内容
  2. 点击"打印发票"按钮填充内容
  3. 调用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. 实施建议

  1. 使用@media print控制打印样式
  2. 通过JavaScript动态填充内容
  3. 使用window.print()触发打印
  4. 对关键字段进行校验
  5. 避免使用复杂布局和动画
  6. 提供打印预览功能

3. 工程实践

  • 使用<template>标签存储模板内容
  • 使用documentFragment优化DOM操作
  • 使用Promise处理异步数据加载
  • 使用localStorage缓存凭证数据

十一、总结

纯HTML+CSS实现凭证打印方案具有以下特点:

优点:

  • 实现简单,无需后端处理
  • 适合快速生成单页凭证
  • 无需依赖第三方库
  • 可实现动态内容填充

局限性:

  • 不适合复杂格式要求
  • 缺乏数据校验和安全机制
  • 无法处理动态计算
  • 无法生成PDF等格式文件

适用场景:

  • 快速生成的临时凭证
  • 简单的电子发票
  • 需要快速预览的场景

不适用场景:

  • 需要加密或签名的凭证
  • 复杂格式的文档
  • 涉及财务数据的凭证
  • 需要打印后存档的场景

通过合理设计和结合前端技术,这种方案可以有效降低开发成本,提高业务响应速度。但在涉及敏感数据时,仍需配合后端校验和安全机制。

none
最后修改于:2026年09月19日 01:36

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日