// pages/user/printer-commands.js export class PrinterCommands { constructor(model = 'generic') { this.model = model; // 打印机型号(zebra/epson/citizen/generic) // 1. ESC/POS 协议对齐指令(爱普生等非斑马打印机用) this.escPosAlign = { left: [0x1B, 0x61, 0x00], center: [0x1B, 0x61, 0x01], right: [0x1B, 0x61, 0x02] }; // 2. ZPL 协议基础指令(斑马打印机专用) this.zplBase = { start: '^XA', // 标签开始 end: '^XZ', // 标签结束 utf8: '^CI28', // 支持UTF-8编码(解决中文乱码) font: '^CFSIMSUN,', // 中文字体(需提前上传SIMSUN.TTF到斑马打印机) qrCode: '^BQN,2,10' // 二维码配置(QR Code,纠错等级2,大小10) }; } // ========================== 通用工具方法 ========================== // 拼接多个Uint8Array(兼容两种协议) concatArrays(...arrays) { const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0); const result = new Uint8Array(totalLength); let offset = 0; arrays.forEach(arr => { result.set(arr, offset); offset += arr.length; }); return result; } // 文本转Uint8Array(兼容UTF-8) textToUint8(text) { return new TextEncoder('utf-8').encode(text); } // ZPL指令字符串转Uint8Array(斑马专用) zplToUint8(zplStr) { // 去除多余空格,避免ZPL解析错误 const cleanZpl = zplStr.replace(/\s+/g, ''); return this.textToUint8(cleanZpl); } // ========================== ESC/POS 协议指令(非斑马用) ========================== // 初始化打印机(ESC/POS) escPosInit() { return new Uint8Array([0x1B, 0x40]); } // 换行(ESC/POS) escPosNewLine(lines = 1) { const cmd = []; for (let i = 0; i < lines; i++) cmd.push(0x0A); return new Uint8Array(cmd); } // 文本加粗(ESC/POS) escPosBold(enable = true) { return new Uint8Array([0x1B, 0x45, enable ? 0x01 : 0x00]); } // 设置字号(ESC/POS) escPosSetFontSize(size = 0) { // 0:正常, 1:双倍高, 2:双倍宽, 3:双倍高低 return new Uint8Array([0x1D, 0x21, size]); } // 切纸(ESC/POS) escPosCutPaper(partial = true) { return new Uint8Array([0x1D, 0x56, partial ? 0x00 : 0x01]); } // 打印图片(ESC/POS,非斑马用) escPosPrintImage(buffer) { const header = this.model === 'epson' ? [0x1B, 0x2A, 0x00] // 爱普生位图指令 : [0x1D, 0x76, 0x30, 0x00]; // 通用位图指令 // 图片宽度/高度(示例128x128,实际需根据图片计算) header.push(0x00, 0x80, 0x00, 0x80); return this.concatArrays(new Uint8Array(header), new Uint8Array(buffer)); } // ========================== ZPL 协议指令(斑马专用) ========================== // 构建ZPL文本指令(带坐标定位) zplText(x, y, text, fontSize = 24) { // ^FOx,y: 定位坐标(x横坐标,y纵坐标) // ^CF字体: 字号(默认24) return `${this.zplBase.font}${fontSize}^FO${x},${y}^FD${text}^FS`; } // 构建ZPL二维码指令(带坐标定位) zplQrCode(x, y, content) { // ^FOx,y: 二维码坐标 // ^BQN,2,10: 二维码类型(QR Code) // ^FDQA,content: QA前缀=QR Code,content=二维码内容 return `${this.zplBase.qrCode}^FO${x},${y}^FDQA,${content}^FS`; } // 斑马切纸指令(ZPL) zplCutPaper() { // 斑马打印机通过^PQ1(打印1份)+ ^XZ(结束)自动切纸 return '^PQ1'; } // ========================== 对外统一接口(自动适配协议) ========================== // 1. 初始化打印机(统一调用) init() { return this.model === 'zebra' ? this.textToUint8(this.zplBase.start + this.zplBase.utf8) // 斑马:ZPL开始+UTF-8编码 : this.escPosInit(); // 其他:ESC/POS初始化 } // 2. 切纸(统一调用) cutPaper(partial = true) { if (this.model === 'zebra') { // 斑马:返回ZPL切纸指令转Uint8Array return this.textToUint8(this.zplCutPaper()); } else { // 其他:ESC/POS切纸 return this.escPosCutPaper(partial); } } // 3. 构建测试页(统一调用) buildTestPage(data) { if (this.model === 'zebra') { // 斑马:ZPL测试页(含中文+文本) const zpl = [ this.zplBase.start, this.zplBase.utf8, this.zplText(50, 50, '斑马打印机测试页(中文)', 30), this.zplText(50, 100, `测试内容:${data.text}`, 24), this.zplText(50, 150, `打印时间:${new Date().toLocaleString()}`, 24), this.zplCutPaper(), this.zplBase.end ].join(''); return this.zplToUint8(zpl); } else { // 其他:ESC/POS测试页(原有逻辑) return this.concatArrays( this.escPosInit(), this.escPosSetFontSize(1), this.escPosAlign.center, this.textToUint8('测试打印页\n'), this.escPosSetFontSize(0), this.escPosAlign.left, this.textToUint8(data.text + '\n'), this.escPosNewLine(3), this.escPosCutPaper() ); } } // 4. 构建成品入库标签(统一调用) buildStockLabel(data) { if (this.model === 'zebra') { // 斑马:ZPL入库标签(含中文+二维码) const qrContent = `物料号:${data.materialNo},批次:${data.batchNo},数量:${data.quantity}`; const zpl = [ this.zplBase.start, this.zplBase.utf8, // 标题(居中) this.zplText(100, 30, '成品入库标签', 30), // 标签内容(左对齐,坐标依次向下) this.zplText(30, 80, `销售订单号: ${data.orderNo || '-'}`), this.zplText(30, 120, `客户名称: ${data.customer || '-'}`), this.zplText(30, 160, `客户型号: ${data.model || '-'}`), this.zplText(30, 200, `生产分厂: 五分厂调度员`), this.zplText(30, 240, `检验状态: 合格`), // 重点字段(加粗用大号字体) this.zplText(30, 280, `物料号: ${data.materialNo}`, 28), this.zplText(30, 320, `生产批号: ${data.batchNo}`, 28), // 补充字段 this.zplText(30, 360, `规格型号: ${data.spec || '-'}`), this.zplText(30, 400, `数量: ${data.quantity || 0} 件`), this.zplText(30, 440, `日期: ${data.date || new Date().toLocaleDateString()} 表面: 合格`), // 二维码(右侧对齐) this.zplQrCode(250, 80, qrContent), // 切纸+结束 this.zplCutPaper(), this.zplBase.end ].join(''); return this.zplToUint8(zpl); } else { // 其他:ESC/POS入库标签(原有逻辑) return this.concatArrays( this.escPosInit(), this.escPosSetFontSize(2), this.escPosAlign.center, this.textToUint8('成品入库标签\n'), this.escPosSetFontSize(0), this.escPosNewLine(1), this.escPosAlign.left, this.textToUint8(`销售订单号: \n`), this.textToUint8(`生产订单号: ${data.orderNo}\n`), this.textToUint8(`客户名称: ${data.customer}\n`), this.textToUint8(`客户型号: ${data.model}\n`), this.textToUint8(`生产分厂: 五分厂调度员\n`), this.textToUint8(`检验状态: \n`), this.escPosBold(true), this.textToUint8(`物料号: ${data.materialNo}\n`), this.textToUint8(`生产批号: ${data.batchNo}\n`), this.escPosBold(false), this.textToUint8(`规格型号: ${data.spec}\n`), this.textToUint8(`数量: ${data.quantity}\n`), this.textToUint8(`日期: ${data.date} 表面: 合格\n`), this.escPosNewLine(1), this.escPosAlign.center, this.escPosPrintImage(data.qrBuffer), this.escPosNewLine(3), this.escPosCutPaper() ); } } // 5. 构建移库单(统一调用) buildTransferOrder(data) { if (this.model === 'zebra') { // 斑马:ZPL移库单 const qrContent = `移库单:${data.orderNo},物料:${data.materialNo},数量:${data.quantity}`; const zpl = [ this.zplBase.start, this.zplBase.utf8, this.zplText(120, 30, '移库单', 30), this.zplText(30, 80, `生产订单号: ${data.orderNo || '-'}`), this.zplText(30, 120, `产品型号: ${data.productModel || '-'}`), this.zplText(30, 160, `客户名称: ${data.customer || '-'}`), this.zplText(30, 200, `物料号: ${data.materialNo || '-'}`), this.zplText(30, 240, `客户型号: ${data.customerModel || '-'}`), this.zplText(30, 280, `生产批号: ${data.batchNo || '-'}`), this.zplText(30, 320, `生产分厂: ${data.factory || '-'}`), this.zplText(30, 360, `数量: ${data.quantity || 0} 件`, 28), this.zplQrCode(250, 80, qrContent), this.zplCutPaper(), this.zplBase.end ].join(''); return this.zplToUint8(zpl); } else { // 其他:ESC/POS移库单(原有逻辑) return this.concatArrays( this.escPosInit(), this.escPosSetFontSize(2), this.escPosAlign.center, this.textToUint8('移库单\n'), this.escPosSetFontSize(0), this.escPosNewLine(1), this.escPosAlign.left, this.textToUint8(`生产订单号: ${data.orderNo}\n`), this.textToUint8(`产品型号: ${data.productModel}\n`), this.textToUint8(`客户名称: ${data.customer}\n`), this.textToUint8(`物料号: ${data.materialNo}\n`), this.textToUint8(`客户型号: ${data.customerModel}\n`), this.textToUint8(`生产批号: ${data.batchNo}\n`), this.textToUint8(`生产分厂: ${data.factory}\n`), this.textToUint8(`数量: ${data.quantity}\n`), this.escPosNewLine(1), this.escPosAlign.center, this.escPosPrintImage(data.qrBuffer), this.escPosNewLine(3), this.escPosCutPaper() ); } } // 6. 构建检验单(统一调用) buildInspectionForm(data, type) { if (this.model === 'zebra') { // 斑马:ZPL检验单 const formTitle = type === 'inspectionA' ? '检验单A' : '检验单B'; const qrContent = `检验单:${data.formNo},物料:${data.materialNo},批次:${data.batchNo}`; // 动态生成检验项(根据data.items) let inspectionItems = ''; data.items.forEach((item, idx) => { const yPos = 120 + idx * 40; // 每行检验项向下偏移40 inspectionItems += this.zplText(30, yPos, `${item.name}: ${item.result}`); }); const zpl = [ this.zplBase.start, this.zplBase.utf8, this.zplText(120, 30, formTitle, 30), this.zplText(30, 80, `表单编号: ${data.formNo || '-'}`), inspectionItems, // 动态检验项 this.zplText(30, 120 + data.items.length * 40 + 20, `检验员: ${data.inspector || '-'}`), this.zplText(30, 120 + data.items.length * 40 + 60, `检验日期: ${data.date || new Date().toLocaleDateString()}`), this.zplQrCode(250, 80, qrContent), this.zplCutPaper(), this.zplBase.end ].join(''); return this.zplToUint8(zpl); } else { // 其他:ESC/POS检验单(原有逻辑) let formContent = type === 'inspectionA' ? '检验单A\n' : '检验单B\n'; data.items.forEach(item => { formContent += `${item.name}: ${item.result}\n`; }); return this.concatArrays( this.escPosInit(), this.escPosSetFontSize(2), this.escPosAlign.center, this.textToUint8(formContent), this.escPosSetFontSize(0), this.escPosNewLine(1), this.escPosAlign.left, this.textToUint8(`表单编号: ${data.formNo}\n`), this.textToUint8(`物料号: ${data.materialNo}\n`), this.textToUint8(`生产批号: ${data.batchNo}\n`), this.textToUint8(`检验员: ${data.inspector}\n`), this.textToUint8(`检验日期: ${data.date}\n`), this.escPosNewLine(1), this.escPosAlign.center, this.escPosPrintImage(data.qrBuffer), this.escPosNewLine(3), this.escPosCutPaper() ); } } }