草庐IT

记录--uni-app实现蓝牙打印小票

林恒 2023-03-28 原文

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

说明

基于uni-app开发,调用官方蓝牙相关api实现连接蓝牙与向蓝牙热敏打印机发送字节流,可打印文字,二维码,图片,调整字体大小等,本文提供大概思路

结构

  • bluetooth.js 蓝牙连接相关模块封装
  • commands.js 打印十六进制相关代码库
  • gbk.js 编码转换库地址
  • printerjobs.js 打印实现库

bluetooth.js

蓝牙连接相关封装代码

class Bluetooth {

    constructor() {
        this.isOpenBle = false;
        this.deviceId = "";
        this.serviceId = "";
        this.writeId = "";
        this.notifyId = "";
        this.BluetoothConnectStatus = false this.printStatus = false this.init()
    }
    init() {
        this.closeBluetoothAdapter().then(() = >{
            console.log("init初始关闭蓝牙模块") this.openBluetoothAdapter().then(() = >{
                console.log("init初始化蓝牙模块") this.reconnect() //自动连接蓝牙设备
            })

        })
    }

    showToast(title) {
        uni.showToast({
            title: title,
            icon: 'none',
            'duration': 2000
        });
    }

    openBluetoothAdapter() {
        return new Promise((resolve, reject) = >{
            uni.openBluetoothAdapter({
                success: res = >{
                    this.isOpenBle = true;
                    resolve(res);
                },
                fail: err = >{
                    this.showToast(`蓝牙未打开`);
                    reject(err);
                },
            });
        });

    }

    startBluetoothDevicesDiscovery() {
        if (!this.isOpenBle) {
            this.showToast(`初始化蓝牙模块失败`) return;
        }

        let self = this;
        uni.showLoading({
            title: '蓝牙搜索中'
        }) return new Promise((resolve, reject) = >{
            setTimeout(() = >{
                uni.startBluetoothDevicesDiscovery({
                    success: res = >{
                        resolve(res)
                    },
                    fail: res = >{
                        self.showToast(`搜索设备失败` + JSON.stringify(err));
                        reject(err);
                    }
                })
            },
            300);
        });
    }

    stopBluetoothDevicesDiscovery() {
        let self = this;
        return new Promise((resolve, reject) = >{
            uni.stopBluetoothDevicesDiscovery({
                success: e = >{
                    uni.hideLoading();
                },
                fail: e = >{
                    uni.hideLoading();
                    self.showToast(`停止搜索蓝牙设备失败` + JSON.stringify(err));
                }
            })
        });
    }

    createBLEConnection() {
        //设备deviceId
        let deviceId = this.deviceId;
        let self = this;

        // uni.showLoading({
        // 	mask: true,
        // 	title: '设别连接中,请稍候...'
        // })
        console.log(this.deviceId);
        return new Promise((resolve, reject) = >{
            uni.createBLEConnection({
                deviceId,
                success: (res) = >{
                    console.log("res:createBLEConnection " + JSON.stringify(res));
                    resolve(res)
                },
                fail: err = >{
                    uni.hideLoading();
                    self.showToast(`停止搜索蓝牙设备失败` + JSON.stringify(err));
                    reject(err);
                }
            })
        });
    }

    //获取蓝牙设备所有服务(service)
    getBLEDeviceServices() {
        let _serviceList = [];
        let deviceId = this.deviceId;
        let self = this;

        return new Promise((resolve, reject) = >{
            setTimeout(() = >{
                uni.getBLEDeviceServices({
                    deviceId,
                    success: res = >{
                        for (let service of res.services) {
                            if (service.isPrimary) {
                                _serviceList.push(service);
                            }
                        }
                        uni.hideLoading();
                        console.log("_serviceList: " + JSON.stringify(_serviceList));
                        resolve(_serviceList)
                    },
                    fail: err = >{
                        uni.hideLoading();
                        // self.showToast(`获取设备Services` + JSON.stringify(err));
                        reject(err);
                    },
                })
            },
            500);
        });
    }

    //获取蓝牙设备某个服务中所有特征值(characteristic)
    getBLEDeviceCharacteristics() {
        // console.log("getBLEDeviceCharacteristics")
        let deviceId = this.deviceId;
        let serviceId = this.serviceId;

        let self = this;
        return new Promise((resolve, reject) = >{
            uni.getBLEDeviceCharacteristics({
                deviceId,
                serviceId,
                success: res = >{
                    for (let _obj of res.characteristics) {
                        //获取notify
                        if (_obj.properties.notify) {
                            self.notifyId = _obj.uuid;
                            uni.setStorageSync('notifyId', self.notifyId);
                        }
                        //获取writeId
                        if (_obj.properties.write) {
                            self.writeId = _obj.uuid;
                            uni.setStorageSync('writeId', self.writeId);
                        }
                    }

                    //console.log("res:getBLEDeviceCharacteristics " + JSON.stringify(res));
                    let result = {
                        'notifyId': self.notifyId,
                        'writeId': self.writeId
                    };
                    // self.showToast(`获取服务中所有特征值OK,${JSON.stringify(result)}`);
                    this.BluetoothStatus = true resolve(result)
                },
                fail: err = >{
                    self.showToast(`getBLEDeviceCharacteristics` + JSON.stringify(err));
                    reject(err);
                }
            })
        });
    }

    //断开联链接
    closeBLEConnection() {
        let deviceId = this.deviceId;
        uni.closeBLEConnection({
            deviceId,
            success(res) {
                console.log("closeBLEConnection" + res)
            }
        })
    }

    notifyBLECharacteristicValue() {
        let deviceId = this.deviceId;
        let serviceId = this.serviceId;
        let characteristicId = this.notifyId;

        uni.notifyBLECharacteristicValueChange({
            state: true,
            // 启用 notify 功能
            deviceId,
            serviceId,
            characteristicId,
            success(res) {
                uni.onBLECharacteristicValueChange(function(res) {
                    console.log('onBLECharacteristicValueChange', res);
                });
            },
            fail(res) {
                console.log('notifyBLECharacteristicValueChange failed:' + res.errMsg);

            }
        });
    }

    writeBLECharacteristicValue(buffer) {
        let deviceId = this.deviceId;
        let serviceId = this.serviceId;
        let characteristicId = this.writeId;

        // console.log(deviceId);
        // console.log(serviceId);
        // console.log(characteristicId);
        return new Promise((resolve, reject) = >{
            uni.writeBLECharacteristicValue({
                deviceId,
                serviceId,
                characteristicId,
                value: buffer,
                success(res) {
                    // console.log('message发送成功', JSON.stringify(res));
                    resolve(res);
                },
                fail(err) {
                    // console.log('message发送失败', JSON.stringify(err));
                    reject(err);
                }
            });
        });
    }

    closeBluetoothAdapter() {
        return new Promise((resolve, reject) = >{
            uni.closeBluetoothAdapter({
                success: res = >{
                    resolve()
                }
            });
        })

    }

    //若APP在之前已有搜索过某个蓝牙设备,并成功建立连接,可直接传入之前搜索获取的 deviceId 直接尝试连接该设备,无需进行搜索操作。
    reconnect() { (async() = >{
            try {
                this.deviceId = this.deviceId || uni.getStorageSync("deviceId");
                this.serviceId = this.serviceId || uni.getStorageSync("serviceId");
                console.log("this.deviceId", this.deviceId) console.log("this.serviceId", this.serviceId) let result1 = await this.createBLEConnection();
                console.log("createBLEConnection: " + JSON.stringify(result1));

                let result2 = await this.getBLEDeviceServices();
                console.log("getBLEDeviceServices: " + JSON.stringify(result2));

                let result3 = await this.getBLEDeviceCharacteristics();
                console.log("getBLEDeviceCharacteristics: " + JSON.stringify(result3));
                this.BluetoothConnectStatus = true this.showToast("蓝牙打印设备连接成功")
                // this.writeId = uni.getStorageSync("writeId");
                // this.notifyId = uni.getStorageSync("notifyId");
            } catch(err) {
                console.log("err: " + err);
                // this.showToast("蓝牙打印设备连接失败")
            }

        })();
    }
}

export
default Bluetooth;

commands.js

打印机ESC/POS十六进制编码库

/**
 * 修改自https://github.com/song940/node-escpos/blob/master/commands.js
 * ESC/POS _ (Constants)
 */
var _ = {
  LF: [0x0a],
  FS: [0x1c],
  FF: [0x0c],
  GS: [0x1d],
  DLE: [0x10],
  EOT: [0x04],
  NUL: [0x00],
  ESC: [0x1b],
  EOL: '\n',
};

/**
 * [FEED_CONTROL_SEQUENCES Feed control sequences]
 * @type {Object}
 */
_.FEED_CONTROL_SEQUENCES = {
  CTL_LF: [0x0a],   // Print and line feed
  CTL_GLF: [0x4a, 0x00],   // Print and feed paper (without spaces between lines)
  CTL_FF: [0x0c],   // Form feed
  CTL_CR: [0x0d],   // Carriage return
  CTL_HT: [0x09],   // Horizontal tab
  CTL_VT: [0x0b],   // Vertical tab
};

_.CHARACTER_SPACING = {
  CS_DEFAULT: [0x1b, 0x20, 0x00],
  CS_SET: [0x1b, 0x20]
};

_.LINE_SPACING = {
  LS_DEFAULT: [0x1b, 0x32],
  LS_SET: [0x1b, 0x33]
};

/**
 * [HARDWARE Printer hardware]
 * @type {Object}
 */
_.HARDWARE = {
  HW_INIT: [0x1b, 0x40], // Clear data in buffer and reset modes
  HW_SELECT: [0x1b, 0x3d, 0x01], // Printer select
  HW_RESET: [0x1b, 0x3f, 0x0a, 0x00], // Reset printer hardware
  Print:[0x1b, 0x64,0x01] //Print and feed paper
};

/**
 * [CASH_DRAWER Cash Drawer]
 * @type {Object}
 */
_.CASH_DRAWER = {
  CD_KICK_2: [0x1b, 0x70, 0x00], // Sends a pulse to pin 2 []
  CD_KICK_5: [0x1b, 0x70, 0x01], // Sends a pulse to pin 5 []
};

/**
 * [MARGINS Margins sizes]
 * @type {Object}
 */
_.MARGINS = {
  BOTTOM: [0x1b, 0x4f], // Fix bottom size
  LEFT: [0x1b, 0x6c], // Fix left size
  RIGHT: [0x1b, 0x51], // Fix right size
};

/**
 * [PAPER Paper]
 * @type {Object}
 */
_.PAPER = {
  PAPER_FULL_CUT: [0x1d, 0x56, 0x00], // Full cut paper
  PAPER_PART_CUT: [0x1d, 0x56, 0x01], // Partial cut paper
  PAPER_CUT_A: [0x1d, 0x56, 0x41], // Partial cut paper
  PAPER_CUT_B: [0x1d, 0x56, 0x42], // Partial cut paper
};

/**
 * [TEXT_FORMAT Text format]
 * @type {Object}
 */
_.TEXT_FORMAT = {
  TXT_NORMAL: [0x1b, 0x21, 0x00], // Normal text
  TXT_2HEIGHT: [0x1b, 0x21, 0x10], // Double height text
  TXT_2WIDTH: [0x1b, 0x21, 0x20], // Double width text
  TXT_4SQUARE: [0x1b, 0x21, 0x30], // Double width & height text

  TXT_UNDERL_OFF: [0x1b, 0x2d, 0x00], // Underline font OFF
  TXT_UNDERL_ON: [0x1b, 0x2d, 0x01], // Underline font 1-dot ON
  TXT_UNDERL2_ON: [0x1b, 0x2d, 0x02], // Underline font 2-dot ON
  TXT_BOLD_OFF: [0x1b, 0x45, 0x00], // Bold font OFF
  TXT_BOLD_ON: [0x1b, 0x45, 0x01], // Bold font ON
  TXT_ITALIC_OFF: [0x1b, 0x35], // Italic font ON
  TXT_ITALIC_ON: [0x1b, 0x34], // Italic font ON

  TXT_FONT_A: [0x1b, 0x4d, 0x00], // Font type A
  TXT_FONT_B: [0x1b, 0x4d, 0x01], // Font type B
  TXT_FONT_C: [0x1b, 0x4d, 0x02], // Font type C

  TXT_ALIGN_LT: [0x1b, 0x61, 0x00], // Left justification
  TXT_ALIGN_CT: [0x1b, 0x61, 0x01], // Centering
  TXT_ALIGN_RT: [0x1b, 0x61, 0x02], // Right justification
};

/**
 * [BARCODE_FORMAT Barcode format]
 * @type {Object}
 */
_.BARCODE_FORMAT = {
  BARCODE_TXT_OFF: [0x1d, 0x48, 0x00], // HRI barcode chars OFF
  BARCODE_TXT_ABV: [0x1d, 0x48, 0x01], // HRI barcode chars above
  BARCODE_TXT_BLW: [0x1d, 0x48, 0x02], // HRI barcode chars below
  BARCODE_TXT_BTH: [0x1d, 0x48, 0x03], // HRI barcode chars both above and below

  BARCODE_FONT_A: [0x1d, 0x66, 0x00], // Font type A for HRI barcode chars
  BARCODE_FONT_B: [0x1d, 0x66, 0x01], // Font type B for HRI barcode chars

  BARCODE_HEIGHT: function (height) { // Barcode Height [1-255]
    return [0x1d, 0x68, height];
  },
  BARCODE_WIDTH: function (width) {   // Barcode Width  [2-6]
    return [0x1d, 0x77, width];
  },
  BARCODE_HEIGHT_DEFAULT: [0x1d, 0x68, 0x64], // Barcode height default:100
  BARCODE_WIDTH_DEFAULT: [0x1d, 0x77, 0x01], // Barcode width default:1

  BARCODE_UPC_A: [0x1d, 0x6b, 0x00], // Barcode type UPC-A
  BARCODE_UPC_E: [0x1d, 0x6b, 0x01], // Barcode type UPC-E
  BARCODE_EAN13: [0x1d, 0x6b, 0x02], // Barcode type EAN13
  BARCODE_EAN8: [0x1d, 0x6b, 0x03], // Barcode type EAN8
  BARCODE_CODE39: [0x1d, 0x6b, 0x04], // Barcode type CODE39
  BARCODE_ITF: [0x1d, 0x6b, 0x05], // Barcode type ITF
  BARCODE_NW7: [0x1d, 0x6b, 0x06], // Barcode type NW7
  BARCODE_CODE93: [0x1d, 0x6b, 0x48], // Barcode type CODE93
  BARCODE_CODE128: [0x1d, 0x6b, 0x49], // Barcode type CODE128
};

/**
 * [IMAGE_FORMAT Image format]
 * @type {Object}
 */
_.IMAGE_FORMAT = {
  S_RASTER_N: [0x1d, 0x76, 0x30, 0x00], // Set raster image normal size
  S_RASTER_2W: [0x1d, 0x76, 0x30, 0x01], // Set raster image double width
  S_RASTER_2H: [0x1d, 0x76, 0x30, 0x02], // Set raster image double height
  S_RASTER_Q: [0x1d, 0x76, 0x30, 0x03], // Set raster image quadruple
};

/**
 * [BITMAP_FORMAT description]
 * @type {Object}
 */
_.BITMAP_FORMAT = {
  BITMAP_S8: [0x1b, 0x2a, 0x00],
  BITMAP_D8: [0x1b, 0x2a, 0x01],
  BITMAP_S24: [0x1b, 0x2a, 0x20],
  BITMAP_D24: [0x1b, 0x2a, 0x21]
};

/**
 * [GSV0_FORMAT description]
 * @type {Object}
 */
_.GSV0_FORMAT = {
  GSV0_NORMAL: [0x1d, 0x76, 0x30, 0x00],
  GSV0_DW: [0x1d, 0x76, 0x30, 0x01],
  GSV0_DH: [0x1d, 0x76, 0x30, 0x02],
  GSV0_DWDH: [0x1d, 0x76, 0x30, 0x03]
};

/**
 * [BEEP description]
 * @type {string}
 */
_.BEEP = [0x1b, 0x42]; // Printer Buzzer pre hex

/**
 * [COLOR description]
 * @type {Object}
 */

_.COLOR = {
  0: [0x1b, 0x72, 0x00], // black
  1: [0x1b, 0x72, 0x01] // red
};

/**
 * [exports description]
 * @type {[type]}
 */
module.exports = _;

printerjobs.js

const commands = require('./commands');
const gbk = require('./gbk');

const printerJobs = function() {
	this._queue = Array.from(commands.HARDWARE.HW_INIT);
	this._enqueue = function(cmd) {
		this._queue.push.apply(this._queue, cmd);
	}
};

/**
 * 增加打印内容
 * @param  {string} content  文字内容
 */
printerJobs.prototype.text = function(content) {
	if (content) {
		let uint8Array = gbk.encode(content);
		let encoded = Array.from(uint8Array);
		this._enqueue(encoded);
	}
	return this;
};

/**
 * 打印文字
 * @param  {string} content  文字内容
 */
printerJobs.prototype.print = function(content) {
	this.text(content);
	// this._enqueue(commands.LF);
	return this;
};
printerJobs.prototype.printL = function(content) {
	this.text(content);
	this._enqueue(commands.LF);
	return this;
};

printerJobs.prototype.printImage = function(content) {

	if (content) {
		const cmds = [].concat([29, 118, 48, 0], content);
		// console.log("cmds",cmds)
		this._enqueue(cmds);
		this._enqueue(commands.LF);
	}

	return this;
};


/**
 * 打印文字并换行
 * @param  {string}  content  文字内容
 */
printerJobs.prototype.println = function(content = '') {
	return this.print(content + commands.EOL);
};

/**
 * 设置对齐方式
 * @param {string} align 对齐方式 LT/CT/RT
 */
printerJobs.prototype.setAlign = function(align) {
	this._enqueue(commands.TEXT_FORMAT['TXT_ALIGN_' + align.toUpperCase()]);
	return this;
};

/**
 * 设置字体
 * @param  {string} family A/B/C
 */
printerJobs.prototype.setFont = function(family) {
	this._enqueue(commands.TEXT_FORMAT['TXT_FONT_' + family.toUpperCase()]);
	return this;
};

/**
 * 设定字体尺寸
 * @param  {number} width 字体宽度 1~2
 * @param  {number} height 字体高度 1~2
 */
printerJobs.prototype.setSize = function(width, height) {
	if (2 >= width && 2 >= height) {
		this._enqueue(commands.TEXT_FORMAT.TXT_NORMAL);
		if (2 === width && 2 === height) {
			this._enqueue(commands.TEXT_FORMAT.TXT_4SQUARE);
		} else if (1 === width && 2 === height) {
			this._enqueue(commands.TEXT_FORMAT.TXT_2HEIGHT);
		} else if (2 === width && 1 === height) {
			this._enqueue(commands.TEXT_FORMAT.TXT_2WIDTH);
		}
	}
	return this;
};

/**
 * 设定字体是否加粗
 * @param  {boolean} bold
 */
printerJobs.prototype.setBold = function(bold) {
	if (typeof bold !== 'boolean') {
		bold = true;
	}
	this._enqueue(bold ? commands.TEXT_FORMAT.TXT_BOLD_ON : commands.TEXT_FORMAT.TXT_BOLD_OFF);
	return this;
};

/**
 * 设定是否开启下划线
 * @param  {boolean} underline
 */
printerJobs.prototype.setUnderline = function(underline) {
	if (typeof underline !== 'boolean') {
		underline = true;
	}
	this._enqueue(underline ? commands.TEXT_FORMAT.TXT_UNDERL_ON : commands.TEXT_FORMAT.TXT_UNDERL_OFF);
	return this;
};

/**
 * 设置行间距为 n 点行,默认值行间距是 30 点
 * @param {number} n 0≤n≤255
 */
printerJobs.prototype.setLineSpacing = function(n) {
	if (n === undefined || n === null) {
		this._enqueue(commands.LINE_SPACING.LS_DEFAULT);
	} else {
		this._enqueue(commands.LINE_SPACING.LS_SET);
		this._enqueue([n]);
	}
	return this;
};

/**
 * 打印空行
 * @param {number} n
 */
printerJobs.prototype.lineFeed = function(n = 1) {
	return this.print(new Array(n).fill(commands.EOL).join(''));
};

/**
 *  设置字体颜色,需要打印机支持
 *  @param  {number} color - 0 默认颜色黑色 1 红色
 */
printerJobs.prototype.setColor = function(color) {
	this._enqueue(commands.COLOR[color === 1 ? 1 : 0]);
	return this;
};

/**
 * https://support.loyverse.com/hardware/printers/use-the-beeper-in-a-escpos-printers
 * 蜂鸣警报,需要打印机支持
 * @param  {number} n    蜂鸣次数,1-9
 * @param  {number} t 蜂鸣长短,1-9
 */
printerJobs.prototype.beep = function(n, t) {
	this._enqueue(commands.BEEP);
	this._enqueue([n, t]);
	return this;
};

/**
 * 清空任务
 */
printerJobs.prototype.clear = function() {
	this._queue = Array.from(commands.HARDWARE.HW_RESET);
	// this._enqueue(commands.HARDWARE.Print);
	return this;
};

/**
 * 返回ArrayBuffer
 */
printerJobs.prototype.buffer = function() {
	return new Uint8Array(this._queue).buffer;
};

module.exports = printerJobs;

代码实现

封装蓝牙连接,搜索,断开相关操作模块

蓝牙搜索

startBluetoothDeviceDiscovery() {
    let self = this;
    self.tempDeviceList = [];

    uni.startBluetoothDevicesDiscovery({
        success: res = >{
            uni.onBluetoothDeviceFound(devices = >{
                // console.log("发现设备: " + JSON.stringify(devices));
                if (!self.tempDeviceList.some(item = >{
                    return item.deviceId === devices.devices[0].deviceId || item.name === devices.devices[0].name
                })) {
                    // console.log("new", devices.devices)
                    self.tempDeviceList.push(devices.devices[0])
                }
            });

            this.connect = false this.$refs.popup.open()
        },
        fail: err = >{
            uni.showToast({
                title: '搜索设备失败' + JSON.stringify(err),
                icon: 'none'
            })
        }
    })
},

搜索完成选择设备

async select_deviceId(item) {
    this.deviceId = item.deviceId;
    this.$bluetooth.deviceId = item.deviceId;
    uni.setStorageSync('deviceId', this.$bluetooth.deviceId);
    this.serviceList = [];

    try {
        //1.链接设备
        let result = await this.$bluetooth.createBLEConnection();
        //2.寻找服务
        let result2 = null setTimeout(async() = >{
            result2 = await this.$bluetooth.getBLEDeviceServices();
            console.log("获取服务: " + JSON.stringify(result2));
            this.serviceList = result2;

            console.log("serviceList", this.serviceList.length)

            if (this.serviceList[2].uuid) {
                this.select_service(this.serviceList[2].uuid)
            } else {
                uni.showToast({
                    title: '不是打印设备',
                    icon: 'none'
                })
            }

        },
        1000)

    } catch(e) {
        //TODO handle the exception
        console.log("e: " + JSON.stringify(e));
    }
},

选中服务

async select_service(res) {
    console.log("select_service", res)

    this.$bluetooth.serviceId = res;
    console.log("this.$bluetooth.serviceId", this.$bluetooth.serviceId) uni.setStorageSync('serviceId', res);

    try {
        let result = await this.$bluetooth.getBLEDeviceCharacteristics();
        console.log("resultresult", result) this.$refs.popup.close()

        uni.showToast({
            title: "连接成功"
        })
        // this.pickUpOnce()
    } catch(e) {
        //TODO handle the exception
        console.log("e: " + JSON.stringify(e));
    }
},

打印内容组合

async writeBLECharacteristicValueTongzhishu() {
	let Qrcode_res = await this.get_Qrcode()
	let sign = await this.getSign()
	console.log("sign")
	let printerJobs = new PrinterJobs()
	let p = printerJobs.setAlign('ct')
		.setBold(true)
		.printL("打印测试")

	let buffer = printerJobs.buffer();
	this.printbuffs(buffer);
},

打印内容组合

主要是实现打印编码推送循环,手机蓝牙可能出现编码发送失败情况,这个时候就是要循环保证每个字节准确推送

printbuffs(buffer, fun) {
    console.log("printbuffs", buffer.byteLength) const maxChunk = 8;
    let p = Promise.resolve();
    for (let i = 0, j = 0, length = buffer.byteLength; i < length; i += maxChunk, j++) {
        let subPackage = buffer.slice(i, i + maxChunk <= length ? (i + maxChunk) : length);
        p = p.then(() = >{
            if (i == 0) {
                this.$bluetooth.printStatus = true this.$refs.loading.open();

            }
            if ((i + maxChunk) >= length) {
                console.log("printEnd")

                setTimeout(() = >{
                    this.$bluetooth.printStatus = false this.$refs.loading.close();
                },
                1000)
            }
            return this.printbuff(subPackage)
        })
    }
    p = p.then(() = >{
        console.log("printEve") fun()
    })

},
async printbuff(buffer) {
    while (true) {
        try {
            await this.$bluetooth.writeBLECharacteristicValue(buffer);
            break;
        } catch(e) {}
    }
},

本文转载于:

https://juejin.cn/post/7001752261722537991

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 

有关记录--uni-app实现蓝牙打印小票的更多相关文章

  1. ruby - Sinatra:运行 rspec 测试时记录噪音 - 2

    Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/

  2. ruby-on-rails - 如何在我的 Rails 应用程序 View 中打印 ruby​​ 变量的内容? - 2

    我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby​​中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R

  3. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  4. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  5. ruby-on-rails - 每次我尝试部署时,我都会得到 - (gcloud.preview.app.deploy) 错误响应 : [4] DEADLINE_EXCEEDED - 2

    我是Google云的新手,我正在尝试对其进行首次部署。我的第一个部署是RubyonRails项目。我基本上是在关注thisguideinthegoogleclouddocumentation.唯一的区别是我使用的是我自己的项目,而不是他们提供的“helloworld”项目。这是我的app.yaml文件runtime:customvm:trueentrypoint:bundleexecrackup-p8080-Eproductionconfig.ruresources:cpu:0.5memory_gb:1.3disk_size_gb:10当我转到我的项目目录并运行gcloudprevie

  6. ruby-on-rails - Rails 5 Active Record 记录无效错误 - 2

    我有两个Rails模型,即Invoice和Invoice_details。一个Invoice_details属于Invoice,一个Invoice有多个Invoice_details。我无法使用accepts_nested_attributes_forinInvoice通过Invoice模型保存Invoice_details。我收到以下错误:(0.2ms)BEGIN(0.2ms)ROLLBACKCompleted422UnprocessableEntityin25ms(ActiveRecord:4.0ms)ActiveRecord::RecordInvalid(Validationfa

  7. ruby-on-rails - 如何重命名或移动 Rails 的 README_FOR_APP - 2

    当我在我的Rails应用程序根目录中运行rakedoc:app时,API文档是使用/doc/README_FOR_APP作为主页生成的。我想向该文件添加.rdoc扩展名,以便它在GitHub上正确呈现。更好的是,我想将它移动到应用程序根目录(/README.rdoc)。有没有办法通过修改包含的rake/rdoctask任务在我的Rakefile中执行此操作?是否有某个地方可以查找可以修改的主页文件的名称?还是我必须编写一个新的Rake任务?额外的问题:Rails应用程序的两个单独文件/README和/doc/README_FOR_APP背后的逻辑是什么?为什么不只有一个?

  8. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  9. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  10. ruby - 使用 postgres.app 在 rvm 下要求 pg 时出错 - 2

    我正在使用Postgres.app在OSX(10.8.3)上。我已经修改了我的PATH,以便应用程序的bin文件夹位于所有其他文件夹之前。Rammy:~phrogz$whichpg_config/Applications/Postgres.app/Contents/MacOS/bin/pg_config我已经安装了rvm并且可以毫无错误地安装pggem,但是当我需要它时我得到一个错误:Rammy:~phrogz$gem-v1.8.25Rammy:~phrogz$geminstallpgFetching:pg-0.15.1.gem(100%)Buildingnativeextension

随机推荐