基于hiprint的票据定位打印系统开发实践
在日常的Web开发中,我们经常需要实现打印功能,特别是对于票据、标签等需要精确排版的打印需求。今天我将分享一个基于hiprint插件实现的票据定位打印系统,重点介绍如何实现单行打印、批量打印以及金额数据动态绑定等功能。
项目背景
传统的网页打印往往难以控制精确的布局和格式,特别是对于发票、收据等需要固定格式的票据打印。hiprint 是一个专门用于解决这类问题的JavaScript打印插件,它提供了可视化设计工具和丰富的API,可以轻松实现精确定位打印。
核心功能实现
1. 页面结构设计
首先,我们构建了一个包含票据列表和操作按钮的页面结构:
<table class="table table-bordered" style="margin-top: 20px;"><thead><tr><th>发票编号</th><th>开票日期</th><th>客户名称</th><th>金额</th><th>操作</th></tr></thead><tbody><tr><td>INV-001</td><td>2023-10-01</td><td>某某公司</td><td>¥1085.00</td><td><a class="btn hiprint-toolbar-item bill-action"data-action="preview" data-index="1">快速预览</a><a class="btn hiprint-toolbar-item bill-action"data-action="print" data-index="1">打印</a></td></tr><tr><td>INV-001</td><td>2023-10-01</td><td>某某公司</td><td>¥985.00</td><td><a class="btn hiprint-toolbar-item bill-action"data-action="preview" data-index="2">快速预览</a><a class="btn hiprint-toolbar-item bill-action"data-action="print" data-index="2">打印</a></td></tr></tbody>
</table>
2. 模板外部化管理
为了提高代码的可维护性,我们将打印模板存储在独立的JSON文件中:
下例为简单表格的模板(发票的模板此处不展示,请见hiprint资源)
{"panels": [{"index": 0,"height": 210,"width": 297,"paperHeader": 10,"paperFooter": 380,"printElements": [{"options": {"left": 20,"top": 20,"width": 250,"height": 120,"content": "<table border='1' style='width:100%;border-collapse:collapse;text-align:center;'><tr><th>序号</th><th>姓名</th><th>成绩</th></tr><tr><td>1</td><td>张三</td><td>90</td></tr><tr><td>2</td><td>李四</td><td>85</td></tr><tr><td>3</td><td>王五</td><td>88</td></tr></table>"},"printElementType": {"title": "表格","type": "table"}}],"paperNumberDisabled": true}]
}
// 从外部JSON文件加载模板
$.getJSON('custom_test/bill-template.json', function (templateData) {hiprintTemplate_bill = new hiprint.PrintTemplate({template: templateData,settingContainer: '#PrintElementOptionSetting'});
});
这样做的好处是:
- 模板与代码分离,便于维护
- 可以在多个页面中复用同一模板
- 支持非技术人员通过可视化工具调整模板
3. 单行打印功能
实现单行打印的关键在于从当前行提取金额数据并传递给打印函数:
// 动态绑定事件
$(document).on('click', '.bill-action', function () {const action = $(this).data('action'); // 获取操作类型const index = $(this).data('index'); // 获取索引// 获取当前行的金额const amountText = $(this).closest('tr').find('td:eq(3)').text();const amount = parseFloat(amountText.replace(/[¥,]/g, '')); // 去除¥符号和逗号,转换为数字handleBillAction(action, index, amount);
});// 处理预览和打印逻辑
function handleBillAction(action, index, amount) {// 创建当前行的数据对象const rowData = {index: index,totalAmount: amount};if (action === 'preview') {$('#myModal .modal-body .prevViewDiv').html(hiprintTemplate_bill.getHtml(rowData));$('#myModal').modal('show');} else if (action === 'print') {if (hiprintTemplate_bill) {hiprintTemplate_bill.print([rowData]);}}
}
4. 批量打印功能
批量打印功能会遍历表格所有行,提取每行的金额数据:
$('#batchPrint').click(function () {const printData = [];$('.table tbody tr').each(function(index) {const amountText = $(this).find('td:eq(3)').text(); // 获取金额列文本const amount = parseFloat(amountText.replace(/[¥,]/g, '')); // 去除¥符号和逗号,转换为数字printData.push({ totalAmount: amount,index: index + 1});});if (hiprintTemplate_bill) {hiprintTemplate_bill.print(printData);}
});
多个发票批量打印:
单个发票打印:
技术要点解析
数据提取与处理
在实现过程中,我们需要注意金额数据的提取和格式化:
const amountText = $(this).closest('tr').find('td:eq(3)').text();
const amount = parseFloat(amountText.replace(/[¥,]/g, ''));
这行代码完成了以下工作:
- 定位到当前行的金额列(第4列,索引为3)
- 提取文本内容
- 使用正则表达式去除货币符号和千位分隔符
- 转换为浮点数便于后续处理
总结
通过这个项目,我们实现了以下功能:
- 可视化模板设计:使用外部JSON文件管理打印模板
- 单行精确打印:每个打印按钮绑定对应行的数据
- 批量处理能力:支持一次性打印所有票据
- 数据动态绑定:自动从表格中提取金额等关键信息
这个系统可以广泛应用于各种票据打印场景,如发票、收据、标签等,为Web应用提供了专业级的打印解决方案。通过合理的设计和实现,我们不仅提高了开发效率,也增强了系统的可维护性和扩展性。
— by agi
附录:
(1)temp.json
{"panels": [{"index": 0,"height": 148,"width": 210,"paperHeader": -1.5,"paperFooter": 380,"printElements": [{"options": {"left": 540,"top": 10.5,"height": 35,"width": 33,"borderColor": "#f20000"},"printElementType": {"title": "椭圆","type": "oval"}},{"options": {"left": 454.5,"top": 15,"height": 18,"width": 74,"title": "8888888","fontSize": 18,"fontWeight": "600","color": "#2935e3","textAlign": "center","lineHeight": 16},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 424.5,"top": 15,"height": 19,"width": 24,"title": "NO","fontSize": 18,"color": "#2935e3","textAlign": "center","lineHeight": 15},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 190.5,"top": 15,"height": 21,"width": 226,"title": "上海增值税普通发票","fontSize": 18,"fontWeight": "600","letterSpacing": 2.5,"color": "#cc5a5a","textAlign": "center","lineHeight": 18},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 244.5,"top": 19.5,"height": 51,"width": 112,"borderColor": "#eb1111","borderWidth": "2"},"printElementType": {"title": "椭圆","type": "oval"}},{"options": {"left": 90,"top": 19.5,"height": 21,"width": 96,"title": "8888888","fontSize": 19,"letterSpacing": 1,"color": "#2935e3","textAlign": "center","lineHeight": 18},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 19.5,"top": 19.5,"height": 61,"width": 65,"title": "031001800204","textType": "qrcode"},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 250.5,"top": 25.5,"height": 42,"width": 104,"borderColor": "#f00505"},"printElementType": {"title": "椭圆","type": "oval"}},{"options": {"left": 190.5,"top": 45,"height": 10,"width": 228,"borderColor": "#b5a8a8"},"printElementType": {"title": "横线","type": "hline"}},{"options": {"left": 190.5,"top": 49.5,"height": 10,"width": 228,"borderColor": "#baafaf"},"printElementType": {"title": "横线","type": "hline"}},{"options": {"left": 244.5,"top": 55.5,"height": 22,"width": 120,"title": "发票联","fontSize": 18,"fontWeight": "600","letterSpacing": 8,"color": "#cc5a5a","textAlign": "center","lineHeight": 18},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 510,"top": 55.5,"height": 13,"width": 69,"title": "2019年05月09日","color": "#2935e3"},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 445.5,"top": 55.5,"height": 15,"width": 57,"title": "开票日期:","color": "#cc5a5a","lineHeight": 13},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 90,"top": 64.5,"height": 15,"width": 141,"title": "校验码:123456 788942 52344","color": "#2935e3","textAlign": "center","lineHeight": 13},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 400,"top": 90,"height": 60,"width": 10,"borderColor": "#cc5a5a"},"printElementType": {"title": "竖线","type": "vline"}},{"options": {"left": 35,"top": 90,"height": 60,"width": 10,"borderColor": "#cc5a5a"},"printElementType": {"title": "竖线","type": "vline"}},{"options": {"left": 420,"top": 90,"height": 61,"width": 10,"borderColor": "#cc5a5a"},"printElementType": {"title": "竖线","type": "vline"}},{"options": {"left": 10.5,"top": 90,"height": 282,"width": 572,"borderColor": "#cc5a5a"},"printElementType": {"title": "矩形","type": "rect"}},{"options": {"left": 405,"top": 94.5,"height": 55,"width": 13,"title": "密码区","fontSize": 13,"color": "#cc5a5a","lineHeight": 18},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 424.5,"top": 94.5,"height": 50,"width": 152,"title": "","color": "#2935e3"},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 15,"top": 94.5,"height": 53,"width": 15,"title": "购买方","fontSize": 13,"color": "#cc5a5a","lineHeight": 18},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 45,"top": 100.5,"height": 10,"width": 348,"title": "名称:北京地铁税务局有限公司","color": "#2935e3"},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 45,"top": 115.5,"height": 10,"width": 347,"title": "纳税人识别号:999999999999999999","color": "#2935e3"},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 45,"top": 130.5,"height": 10,"width": 347,"title": "地址、电话:18888888888","color": "#2935e3"},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 345,"top": 150,"height": 190,"width": 10,"borderColor": "#cc5a5a"},"printElementType": {"title": "竖线","type": "vline"}},{"options": {"left": 409.5,"top": 150,"height": 190,"width": 10,"borderColor": "#cc5a5a"},"printElementType": {"title": "竖线","type": "vline"}},{"options": {"left": 295.5,"top": 150,"height": 190,"width": 10,"borderColor": "#cc5a5a"},"printElementType": {"title": "竖线","type": "vline"}},{"options": {"left": 480,"top": 150,"height": 190,"width": 10,"borderColor": "#cc5a5a"},"printElementType": {"title": "竖线","type": "vline"}},{"options": {"left": 215,"top": 150,"height": 224,"width": 10,"borderColor": "#cc5a5a"},"printElementType": {"title": "竖线","type": "vline"}},{"options": {"left": 520.5,"top": 150,"height": 190,"width": 10,"borderColor": "#cc5a5a"},"printElementType": {"title": "竖线","type": "vline"}},{"options": {"left": 10,"top": 150,"height": 10,"width": 574,"borderColor": "#cc5a5a"},"printElementType": {"title": "横线","type": "hline"}},{"options": {"left": 300,"top": 160.5,"height": 10,"width": 36,"title": "单位","fontSize": 13,"color": "#cc5a5a","textAlign": "center"},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 349.5,"top": 160.5,"height": 11,"width": 51,"title": "数量","fontSize": 13,"color": "#cc5a5a","textAlign": "center"},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 225,"top": 160.5,"height": 10,"width": 62,"title": "规格名称","fontSize": 13,"color": "#cc5a5a","textAlign": "center"},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 420,"top": 160.5,"height": 10,"width": 53,"title": "单价","fontSize": 13,"color": "#cc5a5a","textAlign": "center"},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 484.5,"top": 160.5,"height": 10,"width": 32,"title": "税率","fontSize": 13,"color": "#cc5a5a","textAlign": "center"},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 525,"top": 160.5,"height": 10,"width": 52,"title": "税额","fontSize": 13,"color": "#cc5a5a","textAlign": "center"},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 19.5,"top": 160.5,"height": 10,"width": 184,"title": "货物或应税劳务、服务名称","fontSize": 13,"color": "#cc5a5a","textAlign": "center"},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 40.5,"top": 175.5,"height": 12,"width": 120,"title": "*餐饮服务*餐费","color": "#2935e3"},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 10.5,"top": 340.5,"height": 10,"width": 574,"borderColor": "#cc5a5a"},"printElementType": {"title": "横线","type": "hline"}},{"options": {"left": 225,"top": 349.5,"height": 14,"width": 229,"title": "壹佰贰拾元整","color": "#2935e3"},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 460.5,"top": 349.5,"height": 13,"width": 58,"title": "(小写)","fontSize": 13,"color": "#cc5a5a"},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 520.5,"top": 349.5,"height": 13,"width": 48,"field": "totalAmount","color": "#2935e3"},"printElementType": {"title": "¥","type": "text"}},{"options": {"left": 15,"top": 349.5,"height": 14,"width": 193,"title": "价税合计(大写)","fontSize": 13,"color": "#cc5a5a","textAlign": "center"},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 300,"top": 385.5,"height": 10,"width": 39,"title": "开票人:","color": "#cc5a5a"},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 190.5,"top": 385.5,"height": 10,"width": 103,"title": "轩大可","color": "#2935e3"},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 150,"top": 385.5,"height": 10,"width": 33,"title": "复核:","color": "#cc5a5a"},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 345,"top": 385.5,"height": 10,"width": 86,"title": "张天天","color": "#2935e3"},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 64.5,"top": 385.5,"height": 10,"width": 78,"title": "轩天天","color": "#2935e3"},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 439.5,"top": 385.5,"height": 10,"width": 40,"title": "销售方:","color": "#cc5a5a"},"printElementType": {"title": "文本","type": "text"}},{"options": {"left": 15,"top": 385.5,"height": 10,"width": 44,"title": "收款人:","color": "#cc5a5a"},"printElementType": {"title": "文本","type": "text"}}],"paperNumberLeft": 565.5,"paperNumberTop": 394.5}]
}
(2)index.html
<!DOCTYPE html>
<html><head><meta charset="utf-8" /><title></title><link href="css/hiprint.css" rel="stylesheet" /><link href="css/print-lock.css" rel="stylesheet" /><link href="content/bootstrap.min.css" rel="stylesheet"><script src="https://cdn.staticfile.net/jquery/1.10.2/jquery.min.js"></script><script src="content/bootstrap.min.js"></script><style>.hinnn-layout,.hinnn-layout * {box-sizing: border-box;}.hinnn-layout {display: flex;flex: auto;flex-direction: column;}.hinnn-layout.hinnn-layout-has-sider {flex-direction: row;}.hinnn-layout-sider {display: flex;flex-direction: row;position: relative;}.hinnn-layout-content {flex: auto;}.hinnn-header {position: relative;z-index: 1030;display: block;}.wrapper {min-height: 100%;}.height-100-per {height: 100%;}</style>
</head><body><layout class="layout hinnn-layout hinnn-layout-has-sider height-100-per" style="background:#fff;"><content class="hinnn-layout-content" style="border-left:1px solid #e8e8e8;"><div class="container-fluid height-100-per print-content"><div class="row"><div class="col-sm-12"><div class="row"><div class="col-sm-9 col-md-10"><div class="row"><div class="col-md-12"><div class="hinnn-docs-section"><h1 class="page-header">票据定位打印</h1><button type="button" class="btn btn-danger" id="batchPrint">批量打印</button><table class="table table-bordered" style="margin-top: 20px;"><thead><tr><th>发票编号</th><th>开票日期</th><th>客户名称</th><th>金额</th><th>操作</th></tr></thead><tbody><tr><td>INV-001</td><td>2023-10-01</td><td>某某公司</td><td>¥1085.00</td><td><a class="btn hiprint-toolbar-item bill-action"data-action="preview" data-index="1">快速预览</a><a class="btn hiprint-toolbar-item bill-action"data-action="print" data-index="1">打印</a></td></tr><tr><td>INV-001</td><td>2023-10-01</td><td>某某公司</td><td>¥985.00</td><td><a class="btn hiprint-toolbar-item bill-action"data-action="preview" data-index="2">快速预览</a><a class="btn hiprint-toolbar-item bill-action"data-action="print" data-index="2">打印</a></td></tr></tbody></table></div></div></div></div></div></div></div></content><sider class="hinnn-layout-sider" style=""><div class="container height-100-per" style="width:250px;"><div class="row"><div class="col-sm-12"><div id="PrintElementOptionSetting" style="margin-top:10px;"></div></div></div></div></sider></layout><div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"><div class="modal-dialog modal-lg" role="document" style="width: 825px;"><div class="modal-content"><div class="modal-header"><button type="button" class="close" data-dismiss="modal" aria-label="Close"><spanaria-hidden="true">×</span></button><h4 class="modal-title" id="">打印预览</h4></div><div class="modal-body"><!-- 新增打印按钮 --><button type="button" class="btn btn-danger" id="printInModal">打印</button><div class="prevViewDiv"></div></div><div class="modal-footer"><button type="button" class="btn btn-default" data-dismiss="modal">Close</button></divmyModalLabel></div></div></div></div><script src="custom_test/custom-etype-provider.js"></script><script src="custom_test/custom-print-json.js"></script><script src="custom_test/print-data.js"></script><script src="polyfill.min.js"></script><script src="plugins/jquery.minicolors.min.js"></script><script src="plugins/JsBarcode.all.min.js"></script><script src="plugins/qrcode.js"></script><script src="hiprint.bundle.js"></script><script src="plugins/jquery.hiwprint.js"></script><script>$(document).ready(function () {var hiprintTemplate_bill;// 修改: 使用 $.getJSON 方法加载模板文件$.getJSON('custom_test/bill-template.json', function (templateData) {hiprintTemplate_bill = new hiprint.PrintTemplate({template: templateData,settingContainer: '#PrintElementOptionSetting'});});// 绑定模态框中的打印按钮事件$('#printInModal').click(function () {if (hiprintTemplate_bill) {hiprintTemplate_bill.print(printData);}});// 绑定批量打印按钮事件 - 参考提供的代码进行修改$('#batchPrint').click(function () {// 修改: 从表格中获取金额数据const printData = [];$('.table tbody tr').each(function(index) {const amountText = $(this).find('td:eq(3)').text(); // 获取金额列文本const amount = parseFloat(amountText.replace(/[¥,]/g, '')); // 去除¥符号和逗号,转换为数字printData.push({ totalAmount: amount,index: index + 1});});if (hiprintTemplate_bill) {hiprintTemplate_bill.print(printData);}});// 修改: 统一处理预览和打印逻辑,支持单行金额数据function handleBillAction(action, index, amount) {// 创建当前行的数据对象const rowData = {index: index,totalAmount: amount};if (action === 'preview') {$('#myModal .modal-body .prevViewDiv').html(hiprintTemplate_bill.getHtml(rowData));$('#myModal').modal('show');} else if (action === 'print') {if (hiprintTemplate_bill) {hiprintTemplate_bill.print([rowData]);}}}// 动态绑定事件$(document).on('click', '.bill-action', function () {const action = $(this).data('action'); // 获取操作类型const index = $(this).data('index'); // 获取索引// 获取当前行的金额const amountText = $(this).closest('tr').find('td:eq(3)').text();const amount = parseFloat(amountText.replace(/[¥,]/g, '')); // 去除¥符号和逗号,转换为数字handleBillAction(action, index, amount);});});</script></body></html>