JS实现ZPL标签直接发送至IP10.100.2.200网络热敏打印机方案咨询
库存标签ZPL打印优化实现方案
实现选项说明
下面分别对应你提的两个需求的可落地实现:
- 方案一:点击按钮直接唤起系统打印窗口,无需下载txt文件,手动选择对应热敏打印机即可完成打印
- 方案二:搭配极简本地代理服务,实现点击按钮直接发送打印任务到IP为10.100.2.200的网络打印机,完全无中间操作
方案一:一键唤起打印窗口的完整页面代码
<!DOCTYPE html> <html> <head> <title>Print Inventory Labels at Papa's</title> <style> * { box-sizing: border-box; } div { padding: 10px; background-color: #f6f6f6; overflow: hidden; } input[type=text], textarea, select { font: 17px Calibri; width: 100%; padding: 12px; border: 1px solid #ccc; border-radius: 4px; } input[type=button]{ font: 17px Calibri; width: auto; float: right; cursor: pointer; padding: 7px; } .container{ width: 100%; margin: 3.2rem auto 0 auto; } @media(min-width: 576px){ .container{ max-width: 540px; } } @media(min-width: 768px){ .container{ max-width: 720px } </style> </head> <body> <div> <div class="container"> <div align="center"> <img src="https://www.papasjeepram.com/wp-content/uploads/2019/09/logo2.jpg" width="500" height="189" alt="Papa's Logo"> </div> <h2>Enter Values to Generate Stock Label</h2> <div> <input type="text" onkeyup="this.value = this.value.toUpperCase();"id="txtStock" placeholder="Stock Number" /> </div> <div> <input type="text" onkeyup="this.value = this.value.toUpperCase();"id="txtVin" maxlength="17" placeholder="Vin" /> </div> <div> <input type="text" onkeyup="this.value = this.value.toUpperCase();"id="txtYear" maxlength="4" placeholder="Year" /> </div> <div> <input type="text" onkeyup="this.value = this.value.toUpperCase();" id="txtMake" placeholder="Make" /> </div> <div> <input type="text" onkeyup="this.value = this.value.toUpperCase();" id="txtModel" placeholder="Model" /> </div> <div> <input type="text" onkeyup="this.value = this.value.toUpperCase();" id="txtColor" placeholder="Color" /> </div> <div> <div align="left"> <input type="button" id="bt" value="Generate Label" onclick="printLabel()" /> </div> </div> </div> </body> <script> let printLabel = () => { // 获取表单数据 const Stock = document.getElementById('txtStock'); const Vin = document.getElementById('txtVin'); const Year = document.getElementById('txtYear'); const Make = document.getElementById('txtMake'); const Model = document.getElementById('txtModel'); const Color = document.getElementById('txtColor'); // 生成ZPL内容 let data = '\r^XA\n' + '^A0,213\n' + '^FO25,35^FD' + Stock.value + '^FS \r\n '+ '^BY3,2,225\n' + '^FO45,220^BC^FD' + Vin.value + '^FS \r\n '+ '^A0,50\n' + '^FO45,500^FD' + Year.value + ' ' + Make.value + ' ' + Model.value + ' ' + Color.value + ' ^FS \r\n '+ '^A0,55\n' + '^FO435,630^FD' + Stock.value + '^FS \r\n '+ '^A0,50\n' + '^FO275,630^FD' + Year.value + '^FS \r\n '+ '^A0,55\n' + '^FO45,630^FD' + Stock.value + '^FS \r\n '+ '^A0,50\n' + '^FO695,630^FD' + Year.value + '^FS \r\n '+ '^A0,55\n' + '^FO435,835^FD' + Stock.value + '^FS \r\n '+ '^A0,50\n' + '^FO275,835^FD' + Year.value + '^FS \r\n '+ '^A0,55\n' + '^FO45,835^FD' + Stock.value + '^FS \r\n '+ '^A0,50\n' + '^FO695,835^FD' + Year.value + '^FS \r\n '+ '^A0,30\n' + '^FO45,705^FD' + Make.value + ' ' + Model.value + '^FS \r\n '+ '^A0,30\n' + '^FO45,745^FD' + Vin.value + '^FS \r\n '+ '^A0,30\n' + '^FO45,780^FD' + Color.value + '^FS \r\n '+ '^A0,30\n' + '^FO435,705^FD' + Make.value + ' ' + Model.value + '^FS \r\n '+ '^A0,30\n' + '^FO435,745^FD' + Vin.value + '^FS \r\n '+ '^A0,30\n' + '^FO435,780^FD' + Color.value + '^FS \r\n '+ '^A0,30\n' + '^FO40,900^FD' + Make.value + ' ' + Model.value + '^FS \r\n '+ '^A0,30\n' + '^FO40,940^FD' + Vin.value + '^FS \r\n '+ '^A0,30\n' + '^FO40,975^FD' + Color.value + '^FS \r\n '+ '^A0,30\n' + '^FO435,900^FD' + Make.value + ' ' + Model.value + '^FS \r\n '+ '^A0,30\n' + '^FO435,940^FD' + Vin.value + '^FS \r\n '+ '^A0,30\n' + '^FO435,975^FD' + Color.value + '^FS \r\n '+ '^XZ'; // 创建隐藏的打印容器 const printFrame = document.createElement('iframe'); printFrame.style.position = 'absolute'; printFrame.style.width = '0'; printFrame.style.height = '0'; printFrame.style.border = 'none'; document.body.appendChild(printFrame); // 写入ZPL内容到iframe const frameDoc = printFrame.contentWindow || printFrame.contentDocument; frameDoc.document.open(); frameDoc.document.write(`<pre>${data}</pre>`); frameDoc.document.close(); // 等待加载完成后唤起打印 setTimeout(() => { printFrame.contentWindow.focus(); printFrame.contentWindow.print(); document.body.removeChild(printFrame); }, 100); } </script> </html>
方案二:直接发送到指定网络打印机实现
步骤1:启动本地代理服务
浏览器无法直接发送TCP请求到打印机的9100原始打印端口,需要先启动一个本地代理服务,你可以用Node.js快速实现,代码如下:
const net = require('net'); const http = require('http'); // 打印机配置 const PRINTER_IP = '10.100.2.200'; const PRINTER_PORT = 9100; const PROXY_PORT = 8080; const server = http.createServer((req, res) => { // 允许跨域请求 res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', '*'); if (req.method === 'OPTIONS') { res.writeHead(200); res.end(); return; } if (req.method === 'POST') { let zplContent = ''; req.on('data', chunk => zplContent += chunk.toString()); req.on('end', () => { // 连接打印机发送ZPL数据 const printerSocket = net.connect(PRINTER_PORT, PRINTER_IP, () => { printerSocket.write(zplContent, () => { printerSocket.end(); res.writeHead(200); res.end('打印任务已发送'); }); }); printerSocket.on('error', err => { console.error('连接打印机失败', err); res.writeHead(500); res.end('打印机连接失败'); }); }); } else { res.writeHead(405); res.end('仅支持POST请求'); } }); server.listen(PROXY_PORT, () => { console.log(`打印代理服务已启动,监听端口${PROXY_PORT}`); });
只要本地安装Node.js,把上面代码保存为print-proxy.js,执行node print-proxy.js即可启动代理。
步骤2:修改前端打印逻辑
把方案一中的printLabel函数替换为以下代码即可:
let sendToPrinter = async () => { // 获取表单数据 const Stock = document.getElementById('txtStock'); const Vin = document.getElementById('txtVin'); const Year = document.getElementById('txtYear'); const Make = document.getElementById('txtMake'); const Model = document.getElementById('txtModel'); const Color = document.getElementById('txtColor'); // 生成ZPL内容 let data = '\r^XA\n' + '^A0,213\n' + '^FO25,35^FD' + Stock.value + '^FS \r\n '+ '^BY3,2,225\n' + '^FO45,220^BC^FD' + Vin.value + '^FS \r\n '+ '^A0,50\n' + '^FO45,500^FD' + Year.value + ' ' + Make.value + ' ' + Model.value + ' ' + Color.value + ' ^FS \r\n '+ '^A0,55\n' + '^FO435,630^FD' + Stock.value + '^FS \r\n '+ '^A0,50\n' + '^FO275,630^FD' + Year.value + '^FS \r\n '+ '^A0,55\n' + '^FO45,630^FD' + Stock.value + '^FS \r\n '+ '^A0,50\n' + '^FO695,630^FD' + Year.value + '^FS \r\n '+ '^A0,55\n' + '^FO435,835^FD' + Stock.value + '^FS \r\n '+ '^A0,50\n' + '^FO275,835^FD' + Year.value + '^FS \r\n '+ '^A0,55\n' + '^FO45,835^FD' + Stock.value + '^FS \r\n '+ '^A0,50\n' + '^FO695,835^FD' + Year.value + '^FS \r\n '+ '^A0,30\n' + '^FO45,705^FD' + Make.value + ' ' + Model.value + '^FS \r\n '+ '^A0,30\n' + '^FO45,745^FD' + Vin.value + '^FS \r\n '+ '^A0,30\n' + '^FO45,780^FD' + Color.value + '^FS \r\n '+ '^A0,30\n' + '^FO435,705^FD' + Make.value + ' ' + Model.value + '^FS \r\n '+ '^A0,30\n' + '^FO435,745^FD' + Vin.value + '^FS \r\n '+ '^A0,30\n' + '^FO435,780^FD' + Color.value + '^FS \r\n '+ '^A0,30\n' + '^FO40,900^FD' + Make.value + ' ' + Model.value + '^FS \r\n '+ '^A0,30\n' + '^FO40,940^FD' + Vin.value + '^FS \r\n '+ '^A0,30\n' + '^FO40,975^FD' + Color.value + '^FS \r\n '+ '^A0,30\n' + '^FO435,900^FD' + Make.value + ' ' + Model.value + '^FS \r\n '+ '^A0,30\n' + '^FO435,940^FD' + Vin.value + '^FS \r\n '+ '^A0,30\n' + '^FO435,975^FD' + Color.value + '^FS \r\n '+ '^XZ'; try { const res = await fetch('http://localhost:8080', { method: 'POST', body: data }); if (res.ok) { alert('打印任务已发送成功'); } else { alert('打印失败,请检查代理服务和打印机是否正常'); } } catch (e) { alert('打印失败,请检查代理服务是否启动'); } }
同时把按钮的点击事件改为onclick="sendToPrinter()"即可。
内容的提问来源于stack exchange,提问作者Justin
相关产品推荐
相关产品推荐

