PHP打印票据怎么设计

wen PHP项目 2

本文目录导读:

PHP打印票据怎么设计

  1. 核心设计原则(避坑指南)
  2. 推荐技术方案:window.print()
  3. CSS 打印样式设计(关键)
  4. 如果打印排版乱了?试试“CSS 列计术”
  5. 进阶方案:使用 jsPDF + 前端生成图片
  6. 常见问题排查记录(实战经验)
  7. 完整的示例模板(可以直接复制使用)

在PHP中设计打印票据(小票),主要分为页面设计(HTML/CSS)打印控制(JavaScript) 两部分,由于打印环境(热敏打印机/激光打印机)差异很大,设计时需要特别关注兼容性。

以下是一套完整的从设计到打印的方案,涵盖核心代码和避坑指南。


核心设计原则(避坑指南)

  1. 宽度固定:热敏纸通常为 58mm(约 384px)或 80mm(约 576px)。必须设置纸张宽度,否则排版会乱。
  2. 禁止缩放:打印时关闭“缩放以适应页面”,否则会出现页边距和白边。
  3. 颜色限制:热敏打印机基本只能打印黑白,不要依赖彩色区分内容。
  4. 字体兼容:打印时浏览器默认字体优先级最高,不要使用 Arial 以外的特殊字体(除非嵌入字体文件),推荐使用 宋体(SimSun)微软雅黑(Microsoft YaHei)

推荐技术方案:window.print()

这是最通用的方案,不需要安装打印插件或驱动,直接调用浏览器打印功能。

前端 HTML 结构(关键:隐藏无关内容)

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">小票打印</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <!-- 页面普通显示内容(用户看到的) -->
    <div class="no-print">
        <button onclick="doPrint()">🖨️ 打印小票</button>
        <!-- 你的订单列表、页面导航等 -->
    </div>
    <!-- 打印区域(只有这个区域会被打印) -->
    <div id="printArea" class="print-content">
        <h2>XX超市</h2>
        <p>电话:010-88888888</p>
        <hr>
        <p>单号:202403001</p>
        <p>时间:2024-03-01 10:30:00</p>
        <hr>
        <!-- 商品列表 -->
        <table>
            <thead>
                <tr>
                    <th>商品</th>
                    <th>数量</th>
                    <th>小计</th>
                </tr>
            </thead>
            <tbody>
                <tr><td>农夫山泉</td><td>1</td><td>¥2.50</td></tr>
                <tr><td>康师傅泡面</td><td>2</td><td>¥8.00</td></tr>
            </tbody>
        </table>
        <hr>
        <p>合计:<strong>¥10.50</strong></p>
        <p>实收:¥100.00</p>
        <p>找零:¥89.50</p>
        <hr>
        <p class="footer">谢谢惠顾,欢迎再次光临!</p>
    </div>
    <script>
        function doPrint() {
            // 1. 备份当前页面body内容
            var bodyContent = document.body.innerHTML;
            // 2. 提取打印区域内容
            var printContent = document.getElementById('printArea').innerHTML;
            // 3. 替换body内容为打印区域(此时页面只显示小票)
            document.body.innerHTML = printContent;
            // 4. 调用系统打印
            window.print();
            // 5. 恢复页面内容
            document.body.innerHTML = bodyContent;
        }
    </script>
</body>
</html>

CSS 打印样式设计(关键)

这是决定票据是否美观的核心。请将以下代码写入 style.css

/* 默认隐藏打印区域(屏幕显示时) */
#printArea {
    display: none;
}
/* 针对打印机的样式 */
@media print {
    /* 隐藏不需要打印的元素 */
    .no-print, button {
        display: none !important;
    }
    /* 页面核心设置 */
    @page {
        /* 宽度:58mm = 1mm约3.8px,但建议直接设置百分比 */
        /* 关键:纸张大小设置为 58mm 宽,高度自动 */
        size: 80mm auto; /* 如果是58mm纸,改成 58mm auto */
        /* 关键:必须设置为0,否则打印机默认加白边 */
        margin: 0; 
    }
    body {
        /* 去除浏览器默认边距 */
        margin: 0;
        padding: 0;
        /* 宽度设为 80mm 对应像素,约 306px (80 * 3.8) */
        width: 306px; 
        font-family: '宋体', SimSun, monospace; /* 热敏打印专用 */
        font-size: 12px;
        color: #000;
        background: #fff;
    }
    /* 打印显示区域 */
    #printArea {
        display: block;
        width: 100%;
        padding: 5px; /* 给一点内边距,防止文字贴边 */
        box-sizing: border-box;
        word-break: break-all; /* 防止长字符串溢出 */
    }
    /* 标题样式 */
    #printArea h2 {
        text-align: center;
        font-size: 16px;
        margin: 0 0 5px 0;
        font-weight: bold;
    }
    #printArea p {
        margin: 2px 0;
        line-height: 1.5;
    }
    /* 分割线 */
    #printArea hr {
        border: none;
        border-top: 1px dashed #000; /* 虚线,省钱且美观 */
        margin: 5px 0;
    }
    /* 商品表格样式 */
    #printArea table {
        width: 100%;
        border-collapse: collapse;
        margin: 5px 0;
    }
    #printArea th {
        text-align: left;
        font-weight: bold;
        border-bottom: 1px solid #000;
        padding-bottom: 3px;
    }
    #printArea td {
        padding: 1px 0;
        vertical-align: top;
    }
    /* 对齐需求,左对齐商品,右对齐价格 */
    #printArea td:first-child {
        width: 50%;
    }
    #printArea td:last-child {
        text-align: right;
    }
    /* 合计样式 */
    #printArea .total {
        display: flex;
        justify-content: space-between;
        font-weight: bold;
        margin-top: 5px;
    }
    /* 底部公告 */
    #printArea .footer {
        text-align: center;
        margin-top: 10px;
        font-size: 10px;
    }
}

如果打印排版乱了?试试“CSS 列计术”

如果用户使用一台没有安装驱动的打印机,或者显示器分辨率太高导致像素计算错误,我们可以使用 <div> 配合 table,或者使用 width:100%; max-width: 300px; 来强制适应纸张。

改进方案(懒人兼容法): 直接在 @media print 中,不用固定 306px,而是固定毫米单位:

@media print {
    body {
        width: 80mm; /* 直接使用毫米单位,浏览器会按打印机DPI换算 */
        margin: 0;
    }
    /* 其他不变 */
}

这种方式是最稳妥的,浏览器会按照打印机的物理尺寸进行渲染,不会因屏幕分辨率产生误差。


进阶方案:使用 jsPDF + 前端生成图片

如果客户要求不弹打印框,直接后台静默打印,或者要求把票据转成 PDF 保存,可以使用以下方案:

安装引入(CDN方式)

<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
<script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script>

生成PDF并打印

// 假设你的票据DOM在 #printArea 内
async function printAsPdf() {
    // 1. 将DOM转成图片
    const canvas = await html2canvas(document.getElementById('printArea'), {
        scale: 2, // 高清缩放
        useCORS: true
    });
    const imgData = canvas.toDataURL('image/png');
    // 2. 创建PDF(A5尺寸或自定义)
    const doc = new jsPDF({
        orientation: 'portrait',
        unit: 'mm',
        format: [80, 200] // 宽度80mm,高度200mm
    });
    // 3. 将图片放入PDF
    doc.addImage(imgData, 'PNG', 0, 0, 80, 0); // 按比例缩放
    // 4. 保存或打印
    doc.autoPrint(); // 调用打印对话框
    doc.save('小票.pdf'); // 或直接下载
}

注意:此方案生成速度较慢,且 html2canvas 对复杂 CSS 兼容性差,如果简单的小票可以,复杂的表格建议用第一种方案。


常见问题排查记录(实战经验)

  1. 打印出来太宽,被截断?

    • 原因:设置了 width: 306px,但打印机默认有边距。
    • 解决:检查 @page margin: 0;,并在 CSS 中对 #printArea 添加 padding 替代边距。
  2. 打印出来有背景色(灰色)?

    • 解决:在 @media print 中添加:
      * {
          -webkit-print-color-adjust: exact; /* Chrome/Safari */
          print-color-adjust: exact;         /* 标准属性 */
      }
  3. 打印出来字体大小不一致?

    • 解决:CSS 中设置 font-size: 12px !important;,并且确保没有使用 remvw 等响应式单位,直接使用 px
  4. 如何去掉打印页眉页脚(网址、日期)?

    • 这是浏览器行为,只能用 @page margin: 0 隐藏,但无法彻底去除页眉(Chrome 打印对话框的“更多设置”中有选项),如果想彻底去除,需要转向 Electron 或 vbscript 方案。
  5. 如何打印连续空白?

    • 热敏打印纸有定位孔,如果用户选择“匹配纸张尺寸”导致空白,建议在 @page 中设置 size: 80mm auto; 让高度自适应内容,或者建议用户打印设置中勾选“无边距”。

完整的示例模板(可以直接复制使用)

如果你想要一个开箱即用的模板,可以参考如下结构(包含样式):

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">收银小票</title>
    <style>
        .print-area { display: none; }
        .btn { margin: 20px; padding: 10px; }
        @media print {
            .btn { display: none; }
            body { margin: 0; width: 80mm; font-family: '宋体', SimSun; }
            .print-area { display: block; width: 100%; }
            h1 { text-align: center; font-size: 16px; }
            p { margin: 2px 0; }
            table { width: 100%; border-collapse: collapse; }
            th { text-align: left; }
            td { padding: 2px 0; }
            td:first-child { width: 60%; }
            td:last-child { text-align: right; }
            hr { border-top: 1px dashed #000; }
            .total { font-weight: bold; font-size: 14px; }
        }
    </style>
</head>
<body>
    <button class="btn" onclick="window.print()">打印小票</button>
    <div class="print-area">
        <h1>🌈 小卖部</h1>
        <p>单号:#8888</p>
        <p>时间:2024-03-01 12:00</p>
        <hr>
        <table>
            <tr><th>商品</th><th>数量</th><th>金额</th></tr>
            <tr><td>可乐</td><td>1</td><td>3.00</td></tr>
            <tr><td>薯片</td><td>2</td><td>12.00</td></tr>
        </table>
        <hr>
        <p class="total">合计:¥15.00</p>
        <p style="text-align:center;">谢谢惠顾,欢迎再次光临!</p>
    </div>
</body>
</html>

最后提醒:在 PHP 后端生成 HTML 时,务必对输出内容使用 htmlspecialchars() 转义(尤其是商品名称、备注),防止 XSS 注入破坏打印排版,祝开发顺利!

抱歉,评论功能暂时关闭!