几天前,我一直在努力使用ESCPOS Java API打印收据,但失败了,因为我找不到足够简单的USB API来允许程序使用USB接口打印到我的热敏打印机。我后来决定在 Node 和 ESCPOS 模块中实现这一点。
我正在将此应用程序连接到MySQL数据库,以便能够打印记录交易的收据。我正在使用表格来创建产品列表及其各自的价格,但只能通过硬编码。我现在的问题是如何动态创建这些表。根据交易中涉及的产品,我希望脚本仅查询和打印这些产品。
这是收据代码!
const db = require('./database.js');
const escpos = require('escpos');
const uniqueRandom = require('unique-random');
const random = uniqueRandom(1000, 9999);
const device = new escpos.USB();
const options = { encoding: "GB18030" }
exports.printGeneralReceipt = function(status){
db.getTransactionDetails(function(res){
data = res;
});
const printer = new escpos.Printer(device, options);
console.log("Printer found!");
device.open(function(){
console.log("Receipt generating...");
printer
.font('b')
.align('ct')
.style('bu')
.size(1, 1)
.encode('utf8')
.text('n*****START OF LEGAL RECEIPT*****'+
'nnCOMPUTICKET MALAWIn'+
'SHOP 31A, GAME COMPLEXn'+
'LILONGWE MALLnnwww.computicket.mwn+265 (0) 99 974 7576n')
.table(["BUYER NAME :", "CLIFFORD MWALE", ""])
.table(["RECEIPT # :", random(), ""])
.table(["DATE: ", "12/AUG/2019", ""])
.text("----------ITEM LIST----------n")
// ITEM LIST STARTS HERE
.table(["MILK","$2"])
.table(["PEANUT BUTTER", "$6"])
// ITEM LIST ENDS HERE
.text("--------------------------------")
.table(["TOTAL PRICE", "$8.00", ""])
.text("Operator: Jon Doen-------------------------------n")
.barcode('123456789012')
.text("nnTHANK YOUnn*****END OF LEGAL RECEIPT*****")
.beep(1,100)
.cut().close();
console.log("Receipt printed!");
});
}
这是从数据库中提取交易详细信息的函数。我将为您省去创建连接的开销。
exports.getTransactionDetails = function(trans_id){
var res = "";
conn.query("SELECT * FROM transactions JOIN products_in_transaction WHERE transactions.trans_id = products_in_transaction.trans_id "+
" transactions.trans_id = '"+trans_id+"'",
function (error, results, fields) {
for(var i = 0; i < results.length; i++){
// SOME OPERATION HERE
}
});
}
我将发布一些我玩过的代码,它有效;您需要安装 HTML 到文本
const escpos = require('escpos');
// Select the adapter based on your printer type
const device = new escpos.USB();
const printer = new escpos.Printer(device);
const cartItems = [
{category: 'test', price: 80, quantityToSell: 2, title: 'Hosting'},
{category: 'test1', price: 820, quantityToSell: 63, title: 'Mouse'},
{category: 'test00', price: 60, quantityToSell: 20, title: 'Sale'},
{category: 'dvhfgnfgjfjg', price: 20, quantityToSell: 8, title: 'Keyboards'},
{category: 'dvhfgnfgjfjg', price: 10, quantityToSell: 4, title: 'Keyss'},
{category: 'dvhfgnfgjfjg', price: 70, quantityToSell: 1, title: 'Test'},
{category: 'dvhfgnfgjfjg', price: 500, quantityToSell: 12, title: 'Whale oil'},
{category: 'dvhfgnfgjfjg', price: 560, quantityToSell: 22, title: 'Papers'},
]
// get total per line items
const totalPerItemList = (item) => {
let totalPerItem = 0
totalPerItem = item.quantityToSell * item.price
return totalPerItem
}
// get the total price
let total = 0;
for (let cartItem of cartItems) {
var unitSum = cartItem.quantityToSell * cartItem.price
total += unitSum
}
// Create our html template, could be an html file on it's own
const TestTable = `
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Testing Title for table</title>
</head>
<body>
<div class="invoice-box">
<table class="receipt-table" cellpadding="0" cellspacing="0" border="0">
<thead>
<tr class="heading">
<th>Item</th>
<th>Quantity</th>
<th>Unit Price</th>
<th>Total</th>
</tr>
</thead>
<tbody>
${cartItems.map(item =>
`
<tr>
<td>${item.title}</td>
<td>${item.quantityToSell}</td>
<td>${item.price}</td>
<td>${totalPerItemList(item)}</td>
</tr>
`
)}
</tbody>
<tfoot>
<tr>
<td>
TOTAL:${total}
</td>
</tr>
</tfoot>
</table>
</div>
</body>
</html>
`
const htmlToText = require('html-to-text');
const text = htmlToText.fromString(TestTable, {
wordwrap: false,
tables: ['.receipt-box', '.receipt-table']
});
device.open(function(err){
printer
.font('a')
.align('ct')
.style('bu')
.size(1, 1)
.text('Printing Tables Dynamically with epos')
.text('||||||||||||||||||||||||||')
.text(text)
.text('||||||||||||||||||||||||')
.text('========================')
.cut()
.close()
});
我发现的最快的方法是做我在自己的项目中所做的,如
'use strict'
import { app, protocol, BrowserWindow,ipcMain } from 'electron'
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'
const escpos = require('escpos');
escpos.USB = require('escpos-usb');
const isDevelopment = process.env.NODE_ENV !== 'production'
// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([
{ scheme: 'app', privileges: { secure: true, standard: true } }
])
async function createWindow() {
// Create the browser window.
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
// Use pluginOptions.nodeIntegration, leave this alone
// See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION
}
})
win.menuBarVisible = false;
if (process.env.WEBPACK_DEV_SERVER_URL) {
// Load the url of the dev server if in development mode
await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
if (!process.env.IS_TEST) win.webContents.openDevTools()
} else {
createProtocol('app')
// Load the index.html when not in development
win.loadURL('app://./index.html')
}
}
// Quit when all windows are closed.
app.on('window-all-closed', () => {
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', async () => {
if (isDevelopment && !process.env.IS_TEST) {
// Install Vue Devtools
try {
await installExtension(VUEJS_DEVTOOLS)
} catch (e) {
console.error('Vue Devtools failed to install:', e.toString())
}
}
createWindow()
})
// Exit cleanly on request from parent process in development mode.
if (isDevelopment) {
if (process.platform === 'win32') {
process.on('message', (data) => {
if (data === 'graceful-exit') {
app.quit()
}
})
} else {
process.on('SIGTERM', () => {
app.quit()
})
}
}
function print(load){
var _message = '';
device.open(function(){
_message = 'done'
printer
.font('a')
.align('ct')
.style('bu')
.size(0.05, 0.05)
.text('Road House Magodo')
.text('Magodo Shopping Arcade Ayodele rn Fanoki Magodo Phase I')
.table(['item','qty','total'])
load.load.forEach((element)=>{
return printer.table(element)
})
printer.text(`Total: ${load.total}`)
if(load.change!='card'){
printer.text(`Change: ${load.change}`)
}else{
printer.text('Method: Card')
}
printer.newLine()
printer.newLine()
printer.cut()
printer
.font('a')
.align('ct')
.style('bu')
.size(0.05, 0.05)
.text('Road House Magodo')
.text('Magodo Shopping Arcade Ayodele rn Fanoki Magodo Phase I')
.table(['item','qty','total'])
load.load.forEach((element)=>{
return printer.table(element)
})
printer.text(`Total: ${load.total}`)
if(load.change!='card'){
printer.text(`Change: ${load.change}`)
}else{
printer.text('Method: Card')
}
printer.newLine()
printer.newLine()
printer.cut()
printer
.font('a')
.align('ct')
.style('bu')
.size(0.05, 0.05)
.text('Road House Magodo')
.text('Magodo Shopping Arcade Ayodele rn Fanoki Magodo Phase I')
.table(['item','qty','total'])
load.load.forEach((element)=>{
return printer.table(element)
})
printer.text(`Total: ${load.total}`)
if(load.change!='card'){
printer.text(`Change: ${load.change}`)
}else{
printer.text('Method: Card')
}
printer.newLine()
printer.cut()
printer.close()
},error=>{
if(error){
console.log(error)
_message = error+'error'
return
}
})
return _message
}
const device = new escpos.USB();
const printer = new escpos.Printer(device);
ipcMain.on('print', (_,load) => {
let _fish =()=>{
return print(JSON.parse(load))
}
_.reply(_fish());
})
基本上传递数组或对象并循环遍历它。