添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

1 背景

架构设计:VueJS + Spring Cloud微服务架构

功能要求:

  • 调用小票打印机打印小票,功能和超市收银结算功能相同
  • 使用NWJS包装VueJS前端代码实现exe安装包和可执行文件

2 调查

经过调查,主要有如下几种思路。

2.1 思路1:使用IP+Port方式调用网络打印机

代码如下,只需要调用node的net模块即可。详情请看参考1。

var net = require('net');
var client = new net.Socket();
var buffer; // Buffer类型,放你的打印指令,具体的小票打印指令可以搜索ESC/POS指令
client.connect(port, ip, function () {
    client.write(buffer, function(){});

这种方式没有去尝试,因为不清楚IP地址和端口。这边的需求是小票机直连PC的,不是网络方式。

2.2 思路2:NodeJS IPP协议

Github上有一个project名为ipp,Internet Printing Protocol即网络打印协议。IPP协议始于90年代,一直沿用至今。目前有数以百万计的打印机都支持该协议。

可以使用如下方式验证你的打印机是否支持IPP协议:

ipp检测本机打印机方法如下:

var mdns = require('mdns'),
	browser  = mdns.createBrowser(mdns.tcp('ipp'));
mdns.Browser.defaultResolverSequence[1] = 'DNSServiceGetAddrInfo' in mdns.dns_sd ? mdns.rst.DNSServiceGetAddrInfo() : mdns.rst.getaddrinfo({families:[4]}); 
browser.on('serviceUp', function (rec) {
	console.log(rec.name, 'http://'+rec.host+':'+rec.port+'/'+rec.txtRecord.rp);
browser.start();
//example output...
//HP LaserJet 400 M401dn (972E51) http://CP01.local:631/ipp/printer
//HP LaserJet Professional P1102w http://P1102W.local:631/printers/Laserjet
//Officejet Pro 8500 A910 [611B21] http://HPPRINTER.local:631/ipp/printer

官方示例:

var ipp = require('ipp');
var PDFDocument = require('pdfkit');
//make a PDF document
var doc = new PDFDocument({margin:0});
doc.text(".", 0, 780);
doc.output(function(pdf){
    // 这里需要打印机的url,可以使用上面的方法得到
	var printer = ipp.Printer("http://NPI977E4E.local.:631/ipp/printer");
	var msg = {
		"operation-attributes-tag": {
			"requesting-user-name": "William",
			"job-name": "My Test Job",
			"document-format": "application/pdf"
		data: pdf
    // 执行打印任务,这里打印的是PDF
	printer.execute("Print-Job", msg, function(err, res){
		console.log(res);

这种方式没有尝试,在说明书里面没找到这个IPP协议。也没有尝试使用上面的JavaScript代码进行检测。

具体请看参考2和参考3。

2.3 Lodop打印控件

官方示例如下:

看样子还是比较简单的,但是需要exe文件安装浏览器控件或者Web服务。这个会加大实施运维的难度,所以放弃了。

具体请看参考4、参考5和参考6。

2.4 使用Node Printer模块

在参考7中使用了NWJS,且打印的效果和我们的项目预期非常相似。

其封装了nodejs的printer模块,并且是在NWJS中调用的小票机进行打印。

下面的解决方案将以此调查结果进行开展。

3 解决

下面将一步步介绍如何在NWJS中调用小票打印机。为了尽量描述项目实际情况且不包含任何公司机密信息,下面的例子都是网上可见的。

注意:项目中NWJS和VueJS是分成两个项目,二者通过NWJS中index.html中的iframe建立联系。当然也可以把二者合一,这个可以参照nw-seed-vue项目( https://github.com/anchengjian/vue-nw-seed )。

3.1 准备VueJS项目

为了简化VueJS项目,这里直接使用vue-cli创建一个hello world项目。

创建后的项目如下:

3.2 准备NWJS项目

NWJS原本的打包工作不是自动化的,不过NWJS官方推荐了自动化打包工具: https://github.com/evshiron/nwjs-builder-phoenix

我们直接使用官方的示例作为初始NWJS项目: https://github.com/evshiron/nwjs-builder-phoenix/tree/master/assets/project

注意:这里用的NWJS是0.14.7版本,不要问我为什么,因为它支持XP系统!

附XP下Chrome浏览器下载地址: https://dl.google.com/release2/h8vnfiy7pvn3lxy9ehfsaxlrnnukgff8jnodrp0y21vrlem4x71lor5zzkliyh8fv3sryayu5uk5zi20ep7dwfnwr143dzxqijv/49.0.2623.112_chrome_installer.exe

3.2.1 检出并安装依赖

项目检出后,执行npm install命令安装依赖,具体项目结构如下:

3.2.2 调整package.json中的配置

这个Sample写的无力吐槽,我们按照 https://github.com/evshiron/nwjs-builder-phoenix 官方给出的Getting Started改造一下吧。

官方是在package.json中添加如下两段代码:

{
    // 1. 指定NWJS版本为0.14.7
    "build": {
        "nwVersion": "0.14.7"
    // 2.指定npm脚本
    // 移除不需要的构建任务:linux-x86,linux-x64,mac-x64
    // 替换掉NWJS镜像(国内还是淘宝镜像好使):https://dl.nwjs.io/改成https://npm.taobao.org/mirrors/nwjs/
    // 本地运行x64版本
    "scripts": {
        "dist": "build --tasks win-x86,win-x64 --mirror https://npm.taobao.org/mirrors/nwjs/ .",
        "start": "run --x64 --mirror https://npm.taobao.org/mirrors/nwjs/ ."

修改上述配置后,需要重新npm install一下。

3.2.3 改造index.html页面

原本的index.html如下:启动完NWJS后就关闭了。

<html>
<head></head>
<script>
	process.exit(parseInt(nw.App.argv[0]));
</script>
</body>
</html>

index.html页面需要嵌入iframe,地址就是VueJS项目运行地址:

<html>
<head></head>
<style>
    /* 有一丢丢滚动条,隐藏掉 */
    body {
        overflow: hidden;
    /* 调整iframe为100%大小,并去掉默认的边框 */
    #iframe {
        width: 100%;
        height: 100%;
        border: none;
</style>
<iframe id="iframe" src="http://localhost:8080/"></iframe>
</body>
</html>

上面设置的仅仅是iframe的大小,但是本身NWJS运行的窗口却很小,需要调整下。

修改package.json配置文件,调整NWJS窗口大小:详情参照 https://nwjs.org.cn/doc/api/Manifest-Format.html#%E7%AA%97%E5%8F%A3%E5%AD%90%E5%AD%97%E6%AE%B5

运行如下命令,查看NWJS程序:

npm run start

打开的窗口如下:

3.3 安装打印相关依赖

在参考7( https://juejin.im/post/5c6a77816fb9a049f23d50d4 )中是这样做的:使用node printer模块,然后自己封装esc/pos指令。

这里也是使用node printer,但是使用的是chn-escpos来处理ESC/POS指令。

3.3.1 添加chn-escpos依赖

在package.json中的"dependencies"中添加如下依赖:

"chn-escpos": "1.1.4"

先不急执行npm install。

3.3.2 编译node printer

安装nw-gyp参照: https://www.npmjs.com/package/nw-gyp

安装node printer参照: https://github.com/tojocky/node-printer

汇总下,就变成下面这样:

# !!!不确定是否这个需要安装
npm install -g node-gyp
# 安装时间有点长,还会安装Python。nw-gyp需要它!
# 参考:https://www.npmjs.com/package/windows-build-tools
# 可以指定python镜像地址:--python_mirror=https://npm.taobao.org/mirrors/python/
npm --python_mirror=https://npm.taobao.org/mirrors/python/ --vs2015 install --global windows-build-tools 
# 全局安装nw-gyp
npm install -g nw-gyp
# 安装依赖:chn-escpos、printer...
npm install
# 切换到刚刚npm install后的printer目录下
cd node_modules/printer
# 使用nw-gyp rebuild:Runs clean, configure and build all in a row
# 这样就得到了针对NWJS0.14.7版本的64位的printer模块
nw-gyp rebuild --target=0.14.7 --arch=x64 --dist-url=https://npm.taobao.org/mirrors/nwjs --python=C:\Users\63450\.windows-build-tools\python27\python.exe --msvs_version=2015

关于32位printer模块的编译:

# 一定要用32位node!
# arch改成ia32
nw-gyp rebuild --target=0.14.7 --arch=ia32 --dist-url=https://npm.taobao.org/mirrors/nwjs --python=C:\Users\63450\.windows-build-tools\python27\python.exe --msvs_version=2015

3.4 调用打印相关API

打印函数提供方:NWJS项目调用chn-escpos实现

打印函数调用方:vue-hello-world项目

二者通讯方式:window.postMessage( https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage

3.4.1 NWJS项目提供函数用于打印

在index.html页面添加如下代码:

<script>
    // 初始化iframe的src
    var iframe = document.getElementById("iframe");
    var domain = "http://localhost:8080/";
    iframe.src = domain;
    // 检查下node printer装好了没有,如果弹不出来,那么你的node printer没有编译好!
    var printer = require('chn-escpos');
    alert(printer);
    // 监听message事件,这个就是window.postMessage触发的事件
    window.addEventListener('message', function(e) {
        var data = e.data;
        // 别的地方可能也会触发message事件,所以需要过滤
        if (data.type === 'print') {
            print(data);
    function print(data) {
        new printer(data.printerName, function(err, msg) {
            // 处理回调
            if (err || msg) {
                iframe.contentWindow.postMessage({err: err, msg: msg, type: 'printCallback'}, domain);
                if (err) {
                    return;
            // 这里写实际的打印逻辑
            this.line(3);
            this.text('测试打印');
            this.line(3);
            // 执行打印并处理回调
            this.print(function(err, msg) {
                iframe.contentWindow.postMessage({err: err, msg: msg, type: 'printCallback'}, domain);
</script>

3.4.2 vue-hello-world调用NWJS提供的打印函数

在HelloWorld.vue中添加如下代码:原谅我还不会写ES6语法

<script>
  // 监听message事件,处理回调
  window.addEventListener('message', function(e) {
    var data = e.data;
    // 别的地方可能也会触发message事件,所以需要过滤
    if (data.type === 'printCallback') {
      console.log(data);
  // 调用打印机进行打印
  window.parent.postMessage({printerName: 'OneNote', type: 'print'}, '*');
</script>

3.4.3 验证打印

启动vue-hello-world和NWJS项目,先看到弹窗,说明node printer编译好了:

然后可以看到OneNote被调用起来,并打印出了回调函数值:

OK!!!

3.5 XP下存在的问题

XP32下,chn-escpos调用打印机打印功能会导致NWJS直接闪退:

[0422/180048:WARNING:pe_image_reader.cc(144)] CodeView debug entry of unexpected size in C:\WINDOWS\system32\OLEACC.dll

后来调试发现,只要在打印前不获取打印机信息就不会闪退,出问题的代码如下:

这个问题解决不掉,只能不去获取打印机信息了。

我们可以把chn-escpos相关代码全部提取出来,放到chn-escpose-printer.js中:

var iconv = require('iconv-lite'),
    cmds = {
        INITIAL_PRINTER: '\x1B\x40', //Initial paper
        NEW_LINE: '\x0A', //Add new line
        PAPER_CUTTING: '\x1d\x56\x41', //Cut paper
        LINE_HEIGHT: '\x1b\x32', //Normal line height
        LINE_HEIGHT_B: '\x1b\x33\x6e', //Normal line height large
        CHN_TEXT: '\x1b\x52\x0f', //CHN text
        // text style
        TXT_NORMAL: '\x1d\x21\x00', // Normal text
        TXT_SIZE: '\x1d\x21', // Double height text
        TXT_UNDERL_OFF: '\x1b\x2d\x00', // Underline font OFF
        TXT_UNDERL_ON: '\x1b\x2d\x01', // Underline font 1-dot ON
        TXT_UNDERL2_ON: '\x1b\x2d\x02', // Underline font 2-dot ON
        TXT_BOLD_OFF: '\x1b\x45\x00', // Bold font OFF
        TXT_BOLD_ON: '\x1b\x45\x01', // Bold font ON
        TXT_ALIGN_L: '\x1b\x61\x00', // Left justification
        TXT_ALIGN_C: '\x1b\x61\x01', // Centering
        TXT_ALIGN_R: '\x1b\x61\x02', // Right justification
        TXT_FONT_A: '\x1b\x4d\x00', // Font type A
        TXT_FONT_B: '\x1b\x4d\x01', // Font type B
        TXT_FONT_C: '\x1b\x4d\x02', // Font type C
        TXT_FONT_D: '\x1b\x4d\x48', // Font type D
        TXT_FONT_E: '\x1b\x4d\x31', // Font type E
        //barcode
        BARCODE_TXT_OFF: '\x1d\x48\x00', // HRI barcode chars OFF
        BARCODE_TXT_ABV: '\x1d\x48\x01', // HRI barcode chars above
        BARCODE_TXT_BLW: '\x1d\x48\x02', // HRI barcode chars below
        BARCODE_TXT_BTH: '\x1d\x48\x03', // HRI barcode chars both above and below
        BARCODE_FONT_A: '\x1d\x66\x00', // Font type A for HRI barcode chars
        BARCODE_FONT_B: '\x1d\x66\x01', // Font type B for HRI barcode chars
        BARCODE_HEIGHT: '\x1d\x68\x64', // Barcode Height [1-255]
        BARCODE_WIDTH: '\x1d\x77\x03', // Barcode Width  [2-6]
        //一维码
        BARCODE_UPC_A: '\x1d\x6b\x00', // Barcode type UPC-A
        BARCODE_UPC_E: '\x1d\x6b\x01', // Barcode type UPC-E
        BARCODE_EAN13: '\x1d\x6b\x02', // Barcode type EAN13
        BARCODE_EAN8: '\x1d\x6b\x03', // Barcode type EAN8
        BARCODE_CODE39: '\x1d\x6b\x04', // Barcode type CODE39
        BARCODE_ITF: '\x1d\x6b\x05', // Barcode type ITF
        BARCODE_NW7: '\x1d\x6b\x06', // Barcode type NW7
        //二维码,from http://stackoverflow.com/questions/23577702/printing-qr-codes-through-an-esc-pos-thermal-printer
        QRCODE_SIZE_MODAL: '\x1D\x28\x6B\x03\x00\x31\x41\x32\x00', //Select the model,[49 x31, model 1] [50 x32, model 2] [51 x33, micro qr code]
        QRCODE_SIZE: '\x1D\x28\x6B\x03\x00\x31\x43',  //Set the size of module
        QRCODE_ERROR: '\x1D\x28\x6B\x03\x00\x31\x45\x31', //Set n for error correction [48 x30 -> 7%] [49 x31-> 15%] [50 x32 -> 25%] [51 x33 -> 30%]
        QRCODE_AREA_LSB: '\x1D\x28\x6B', //Store the data in the symbol storage area LSB
        QRCODE_AREA_MSB: '\x31\x50\x30', //Store the data in the symbol storage area MSB
        QRCODE_PRINT:'\x1D\x28\x6B\x03\x00\x31\x51\x30', //Print the symbol data in the symbol storage area
        CASHBOX_OPEN: '\x1B\x70\x00\xFF\xFF', //Open casebox
        BEEP:'\x1b\x42' //beep
    node_printer = require("printer"),
    BufferHelper = require('bufferhelper');
 * 打印任务
 * @param  {string}   printer_name 打印机名
 * @param  {function} callback     function(err,msg),当获取打印机后执行,如果不存在指定打印机,返回err信息
var printer = function(printer_name, callback) {
    if (!printer_name) {
        printer_name = node_printer.getDefaultPrinterName();
    this.printer = printer_name;
    /* 解决在XP系统下获取打印机信息崩溃的问题
    try {
        node_printer.getPrinter(this.printer);
    } catch (err) {
        if (callback) callback.call(this, err, 'Can\'t find the printer');
        return false;
    this._queue = new BufferHelper();
    this._writeCmd('INITIAL_PRINTER');
    this._writeCmd('CHN_TEXT');
    if (callback) callback.call(this, null, 'Get printer success');
printer.prototype = {
     * 打印文字
     * @param  {string} text    文字内容
     * @param  {boolen} inline  是否换行
     * @return {object}         当前对象
    text: function(text, inline) {
        if (text) {
            this._queue.concat(iconv.encode(text, 'GBK'));
            if (!inline) this._writeCmd('NEW_LINE');
        return this;
     * 打印空行
     * @param  {number} number 行数
     * @return {object}        当前对象
    line: function(number) {
        number = number || 1;
        for (var i = 0; i < number; i++) {
            this._writeCmd('NEW_LINE');
        return this;
     * 设置对其
     * @param  {string} align 居中类型,L/C/R
     * @return {object}       当前对象
    setAlign: function(align) {
        this._writeCmd('TXT_ALIGN_' + align.toUpperCase());
        return this;
     * 设置字体
     * @param  {string} family A/B/C/D/E
     * @return {object}        当前对象
    setFont: function(family) {
        this._writeCmd('TXT_FONT_' + family.toUpperCase());
        return this;
     * 设置行距
     * @param {number} hex 16进制数据,如'\x05'
    setLineheight: function(hex) {
        this._writeCmd('LINE_HEIGHT');
        if (hex) {
            //console.log('\x1b\x33'+hex);
            this._queue.concat(new Buffer('\x1b\x33' + hex));
            //设置默认行间距
        return this;
     * 设置格式(加粗,下拉)
     * @param  {string} type B/U/U2/BU/BU2
     * @return {object}      当前对象
    setStyle: function(type) {
        switch (type.toUpperCase()) {
            case 'B':
                this._writeCmd('TXT_UNDERL_OFF');
                this._writeCmd('TXT_BOLD_ON');
                break;
            case 'U':
                this._writeCmd('TXT_BOLD_OFF');
                this._writeCmd('TXT_UNDERL_ON');
                break;
            case 'U2':
                this._writeCmd('TXT_BOLD_OFF');
                this._writeCmd('TXT_UNDERL2_ON');
                break;
            case 'BU':
                this._writeCmd('TXT_BOLD_ON');
                this._writeCmd('TXT_UNDERL_ON');
                break;
            case 'BU2':
                this._writeCmd('TXT_BOLD_ON');
                this._writeCmd('TXT_UNDERL2_ON');
                break;
            case 'NORMAL':
            default:
                this._writeCmd('TXT_BOLD_OFF');
                this._writeCmd('TXT_UNDERL_OFF');
                break;
        return this;
     * 设定字体尺寸
     * @param  {string} size  2/null
     * @return {object}       当前对象
    setSize: function(size) {
        this._writeCmd('TXT_NORMAL');
        this._writeCmd('LINE_HEIGHT');
        switch(parseInt(size)){
            case 2:
                this._queue.concat(new Buffer(cmds['TXT_SIZE']+'\x10'));
                this._queue.concat(new Buffer(cmds['TXT_SIZE']+'\x01'));
                break;
            case 3:
                this._queue.concat(new Buffer(cmds['TXT_SIZE']+'\x32'));
                this._queue.concat(new Buffer(cmds['TXT_SIZE']+'\x02'));
                break;
            case 4:
                this._queue.concat(new Buffer(cmds['TXT_SIZE']+'\x48'));
                this._queue.concat(new Buffer(cmds['TXT_SIZE']+'\x03'));
                break;
        return this;
     * 二维码
     * @param  {string} code     打印内容
     * @param  {string} type     打印类型,UPC-A(11-12)/UPC-E(11-12,不可用)/EAN13(默认,12-13)/EAN8(7-8)/CODE39(1-255,不可用)/ITF(1-255偶数,不可用)/NW7(1-255,不可用)
     * @param  {number} width    宽度
     * @param  {number} height   高度
     * @param  {string} position OFF/ABV/BLW/BTH
     * @param  {string} font     字体A/B
     * @return {object}          当前对象
    barcode: function(code, type, width, height, position, font) {
        if (width >= 1 || width <= 255) {
            this._writeCmd('BARCODE_WIDTH');
        if (height >= 2 || height <= 6) {
            this._writeCmd('BARCODE_HEIGHT');
        this._writeCmd('BARCODE_FONT_' + (font || 'A').toUpperCase());
        this._writeCmd('BARCODE_TXT_' + (position || 'BLW').toUpperCase());
        this._writeCmd('BARCODE_' + ((type || 'EAN13').replace('-', '_').toUpperCase()));
        this._queue.concat(new Buffer(code));
        return this;
     * 打印二维码,需要打印机支持
     * @param  {string} text    打印文字内容
     * @param  {string} size   二维码大小,16进制字符串,如'\x01'.默认为'\x06'
     * @param  {string} lsb    (text长度+3)%256转16进制后的字符,如'\x01';
     * @param  {[type]} msb  (text长度+3)/256取整转16进制后的字符,如'\x00';
     * @return {object}          当前对象
    qrcode:function(text,size,lsb,msb){
        size=size?size:'\x06';
        if(!/^[\w\:\/\.\?\&\=]+$/.test(text)){
            this.text('二维码请使用英文和数字打印');
            return this;
        this._writeCmd('QRCODE_SIZE_MODAL');
        this._queue.concat(new Buffer(cmds['QRCODE_SIZE']+size));
        this._writeCmd('QRCODE_ERROR');
        this._queue.concat(new Buffer(cmds['QRCODE_AREA_LSB']+lsb+msb+cmds['QRCODE_AREA_MSB']));
        this._queue.concat(new Buffer(text));
        this._writeCmd('QRCODE_PRINT');
        return this;
     * 蜂鸣警报
     * @param  {string} times    蜂鸣次数,16进制,1-9.默认'\x09'
     * @param  {string} interval 蜂鸣间隔,16进制,实际间隔时间为interval*50ms,默认'\x01'
     * @return {object}          当前对象
    beep:function(times,interval){
        times=times?times:'\x09';
        interval=interval?interval:'\x01';
        this._queue.concat(new Buffer(cmds['BEEP']+times+interval));
        return this;
     * 打开钱箱
     * @return {object} 当前对象
    openCashbox: function() {
        this._writeCmd('CASHBOX_OPEN');
        return this;
     * 编译指定语法字符串为print方法
     * @param  {string} string 语法字符串
     * @return {object}        当前对象
    compile: function(string) {
        if (typeof string != 'string') {
            console.log('必须为字符串');
            return this;
        var _this = this;
        //替换换行
        var tpl = string.replace(/[\n\r]+/g, '/n')
            //替换函数
            .replace(/<%([\s\S]+?)%>/g, function(match, code) {
                return '",true);\n' + _this._renderFunc(code) + '\nobj.text("';
            //替换换行
            .replace(/\/n/g, '");\nobj.text("');
        tpl = 'obj.text("' + tpl + '")';
        new Function('obj', tpl).call(this, this);
        return this;
     * 执行命令
     * @param  {string} cmd 命令名
    _writeCmd: function(cmd) {
        if (cmds[cmd]) {
            this._queue.concat(new Buffer(cmds[cmd]));
    _renderFunc: function(string) {
        var _this = this,
            status = true;
        string = string.trim();
        //函数名生命
        var func = string.replace(/^([\S]+?):/, function(match, code) {
            var func_name = _this._escape(code);
            if (!_this[func_name]) {
                //无效函数
                status = false;
                console.log('解析模板出错没有名为' + func_name + '的方法');
            return 'obj.' + func_name + ':';
            //函数变量
        }).replace(/:([\S]+?)$/, function(match, code) {
            var func_var = _this._escape(code).split(','),
                tpl_var = '';
            var length = func_var.length;
            for (var i = 0; i < length; i++) {
                //%u hack
                var cur_func_var = func_var[i];
                if(/%u/.test(func_var[i])){
                    cur_func_var=cur_func_var.replace(/%u/g,'u');
                tpl_var += '"' + cur_func_var + '",';
            tpl_var = tpl_var.replace(/\,$/, '');
            return '(' + tpl_var + ');';
        if (status) return func
        else return '';
     * 预留跨域攻击防御
     * @param  {string} string 目标内容
     * @return {string}        转换后
    _escape: function(string) {
        string = unescape(string.replace(/\u/g, "%u")); //转换unicode为正常符号
        string = string.replace(/[\<\>\"\'\{\}]/g, '');
        return string;
     * 执行打印
     * @param  {Function} callback function(err,msg),当执行打印后,回调该函数,打印错误返回err信息
    print: function(callback) {
        this._writeCmd('PAPER_CUTTING');
        this._writeCmd('INITIAL_PRINTER');
        this.sendCmd(callback);
     * 发送命令
     * @param  {Function} callback function(err,msg),当执行打印后,回调该函数,打印错误返回err信息
    sendCmd:function(callback){
        var _this = this;
        node_printer.printDirect({
            data: _this._queue.toBuffer(),
            printer: _this.printer,
            type: "RAW",
            success: function() {
                callback.call(_this, null, 'Print successed');
                _this._queue.empty();
            error: function(err) {
                callback.call(_this, null, 'Print failed');
     * 清空打印内容
     * @return {object} 当前对象
    empty: function() {
        this._queue.empty();
        return this;
module.exports = printer;

然后调整package.json中的依赖:之前的chn-escpose删掉

"dependencies": {
	"printer": "^0.4.0",
	"bufferhelper": "^0.2.1"

再调整index页面对原有chn-escpose的引入就可以了:

var printer = require('./chn-escpos-printer');
alert(printer);
print({printerNamer:"One"});
alert(11);

详情参见nwjs-node-printer-xp.rar。

1. NWJS项目:nwjs-project.zip

2. vue-hello-world项目:vue-hello-world.zip

3. 编译好的printer(64位、32位)模块:node printer.zip.001、node printer.zip.002、node printer.zip.003,三个一起解压,然后把64位或者32位放到你的node_modules下面,这样就不需要install这个printer模块了

demo下载链接: https://download.csdn.net/download/u012383839/16031727

参考

1. [NodeJs 如何驱动小票打印机打印小票?]( https://segmentfault.com/q/1010000008790881/a-1020000008802345 )

2. [NodeJS使用ipp协议打印]( https://www.iteye.com/blog/liyunpeng-2098669 )

3. [NodeJS ipp]( https://github.com/williamkapke/ipp )

4. [nwjs打印小票案例,包含二维码]( https://www.jianshu.com/p/003aad570c08 )

5. [lodop官方打印示例]( http://www.lodop.net/demolist/PrintSample1.html )

6. [VueJS中使用lodop]( http://www.c-lodop.com/faq/pp35.html )

7. [如何通过Nw.js纯前端实现调用热敏打印机打印小票?]( https://juejin.im/post/5c6a77816fb9a049f23d50d4 )

8. [NWJS自动化打包工具:nwjs-builder-phoenix]( https://nwjs.org.cn/doc/user/Package-and-Distribute.html )

9. [github: nwjs-builder-phoenix]( https://github.com/evshiron/nwjs-builder-phoenix )

10. [ESC/POS指令]( https://wenku.baidu.com/view/9a165cb8647d27284a735128.html )

11. [npm chn-escpos]( https://www.npmjs.com/package/chn-escpos )

12. [关于在nw里使用require('printer')和nw.require('printer')报错的问题]( https://blog.csdn.net/qq_39702364/article/details/82800935 )

13. [在nw.js要如何优雅的使用node-printer]( https://www.jianshu.com/p/b3c558ddb914 )

14. [npm nw-gyp]( https://www.npmjs.com/package/nw-gyp )

15. [window.postMessage]( https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage )

16. [NW.js安装原生node模块node-printer控制打印机]( https://www.cnblogs.com/pannysp/p/9687743.html )

17. [在NW.js中安装Node原生模块]( https://nwjs.org.cn/doc/user/Advanced/Use-Native-Node-Modules.html )

18. [A seed project with vue and nwjs ]( https://github.com/anchengjian/vue-nw-seed )

版权声明:本文为CSDN博主「u012383839」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接: https://blog.csdn.net/u012383839/article/details/106988705