天天播报:在HbuilderX中实现微信小程序下蓝牙连接打印机完整实战案例

2023-01-04 14:32:55 来源:51CTO博客

1.基础开发环境,所用到的 Api 以及实现的思路。

应用场景:

商家打印小票,小票包含顾客消费的商品明细信息以及末尾附上二维码,二维码供顾客扫码开票。

HbuilderX开发工具:

​​HBuilderX-高效极客技巧HBuilderX是当前最快的HTML开发工具,强大的代码助手帮你快速完成开发,最全的语法库和浏览器兼容性数据让浏览器碎片化不再头痛


(资料图片)

https://www.dcloud.io/hbuilderx.html​​注:项目是基于VUE框架开发的。

关于蓝牙设备的api地址:

​​uni-app官网

https://uniapp.dcloud.io/api/system/bluetooth.html​​

大致实现思路:

1.在页面加载时,先判断本地缓存是否有历史连接蓝牙设备。

2.页面点击【打印】按钮时,最好是提前把此次需要打印的数据项先组装好。

3.点击打印后,先判断当前页面是否有历史连接蓝牙设备的信息:

如果有:直接连接蓝牙设备,进行打印;

如果没有:则以弹框的形式,搜索附近可用的蓝牙设备;

4.页面卸载时或者页面隐藏时,需要取消蓝牙连接并关闭蓝牙模块。

注:我这里是在当前页面弹框形式展示的蓝牙列表,大家可以根据自己实际项目需求来做出一些调整。

2.接下来直接上代码,以及依赖的js和弹框组件

弹框用的uni-popup 基础组件,如有需要,可以私信找我要。

页面蓝牙列表的视图view代码如下:

注: 当页面没有可用的蓝牙设备时,有一个背景图片,当然这个只是一个样式,不是关键的,但我还是尽量做到完善和完整度。empty-icon.png图片如下:有水印,如有需要原图,可以私信我,我会及时回复的。

页面引入弹框组件和相关的蓝牙打印的js文件:

<script>import * as Common from "@/common/util.js"import {    wxAsyncPromise ,debounce,catchToast,      getDeviceCharacteristics,      sendDataToDevice,    printCommand,LAST_CONNECTED_DEVICE  } from "../bluetooth/bluetoolth.js";  import GBK from "../bluetooth/gbk.js";  import uniPopup from "@/components/uni-popup.vue"  export default {    components: {    uniPopup    },    data() {      return {    /* 蓝牙打印相关参数-start */    isShowSearchBle:false,//是否显示蓝牙设备列表    devices: [],//搜索到的附近蓝牙设备列表//{"name":"T5810000","devId":"UUUUUUUU","rssi":-20,"status":false}    deviceName: "",//已连接或已选择的蓝牙设备名称    deviceId: "",//已连接或已选择的蓝牙设备ID    services:[],//蓝牙设备所有服务    character:[],    blueToothConnected:false,//是否已经连接    blueToothPrintData:{},//传输给蓝牙设备的数据    /* 蓝牙打印相关参数-end */      }    },    computed: {      //计算蓝牙设备列表长度    deviceListLength() {      return this.devices.length;    }    },    onShow() {    console.log(" 页面onShow");    let lastDevice = wx.getStorageSync(LAST_CONNECTED_DEVICE);    console.log("历史连接蓝牙设备:"+lastDevice);    if(Common.isExist(lastDevice)){      this.deviceId = lastDevice.split("&")[0];    this.deviceName = lastDevice.split("&")[1];    }    },  onHide() {    console.log(" 页面onHide");      //取消蓝牙连接并关闭蓝牙模块      wxAsyncPromise("closeBLEConnection", {          deviceId: this.deviceId,      }).finally((res) => {          wxAsyncPromise("closeBluetoothAdapter");      });  },    onLoad(e) {    let lastDevice = wx.getStorageSync(LAST_CONNECTED_DEVICE);    console.log("历史连接蓝牙设备:"+lastDevice);    if(Common.isExist(lastDevice)){      this.deviceId = lastDevice.split("&")[0];      this.deviceName = lastDevice.split("&")[1];    }    },  onUnload() {      //取消蓝牙连接并关闭蓝牙模块      wxAsyncPromise("closeBLEConnection", {          deviceId: this.deviceId,      }).finally((res) => {          wxAsyncPromise("closeBluetoothAdapter");      });  },</script>

此处我只列出,当前与蓝牙打印相关的js文件:

1.

util.js 文件内容如下:根据自己实际需求决定是否引用

// alert提示function alert (msg) {  alert_duration(msg,2000);}function alert_duration (msg, duration) {  if(!isExist(duration)){    duration = 3000;  }  uni.showToast({    title: msg,    icon: "none",    position: "bottom",    duration: duration  });}//判断字符串是否存在function isExist (str) {  if (str !== null && str !== "undefined" && str !== "" && typeof str != "undefined" && !(typeof (str) == "string" &&      str      .split(" ").join("").length == 0)) {    return true;  } else {    return false;  }}//转换日期格式(时间戳转换为datetime格式)function changeDateFormat(cellval, hms) {  var dateVal = cellval + "";  if (cellval != null) {    var date = new Date(parseInt(dateVal.replace("/Date(", "").replace(")/", ""), 10));    var month = date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1;    var currentDate = date.getDate() < 10 ? "0" + date.getDate() : date.getDate();    var hours = date.getHours() < 10 ? "0" + date.getHours() : date.getHours();    var minutes = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes();    var seconds = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();    if(hms){      return date.getFullYear() + "-" + month + "-" + currentDate + " " + hours + ":" + minutes + ":" + seconds;    }else{      return date.getFullYear() + "-" + month + "-" + currentDate;    }  }}//计算字符长度(一个汉字2个字符)function calculateStrLengh(str){  var len=0;  if(!isExist(str)){    return 0;  }  for (var i=0; i= 0x0001 && c <= 0x007e) || (0xff60<=c && c<=0xff9f)) {      len++;    }    else {      len+=2;    }  }  return len;}// 声明module.exports = {  isExist,  alert,  alert_duration,  changeDateFormat,  calculateStrLengh}

2.

分别是蓝牙设备相关的方法和中文编码js文件,截图中是我的文件在项目中的目录位置

bluetoolth.js

/* * @Author: zyf * @Date: 2022年2月18日12:30:21 * @LastEditors: zyf * @LastEditTime: 2022年2月18日12:30:21 * @Description: file content *//** * * * @param {string} name 微信api的名称 ,如 wxAsyncPromise("getSystemInfo",options) * @param {object} options 除了success 和 fail 的其他参数 * @returns */export function wxAsyncPromise(name, options) {    return new Promise((resolve, reject) => {        wx[name]({            ...(options || {}),            success: function(res) {                resolve(res);            },            fail: function(res) {                reject(res);            },        });    });}export function debounce(func, wait) {    let timer = null;    return function(opt) {        clearTimeout(timer);        timer = setTimeout(() => {            func && func.call(this, opt);        }, wait || 60);    };}//微信小程序向蓝牙打印机发送数据进行打印的坑://小程序api向蓝牙打印机发送数据打印,发送的任何内容都应该要转成二进制数据,而且蓝牙打印的文本编码是GBK的,发送中文需转成GBK编码再转成二进制数据发送//发送打印机指令也要转成二进制数据发送//蓝牙打印机一次接收的二级制数据有限制,不同的系统不同的蓝牙设备限制可能不同,微信建议一次20个字节,需做递归分包发送//发送完要打印的内容后,一定要发送一个打印的指令才能顺利打印 (有些指令就不需要)//一、初始化蓝牙、开始检索蓝牙设备export function openBlue() {    return wxAsyncPromise("openBluetoothAdapter").then((res) => {        console.log("初始化蓝牙成功", res);        return wxAsyncPromise("startBluetoothDevicesDiscovery").then((res) => {            console.log("正在搜寻蓝牙设备", res);        });    });}//二、/** * * * @export * @param {function} getDevices wx.getBluetoothDevices的监听回调函数 */export function onfindBlueDevices(getDevices) {    //监听寻找到新设备的事件    wx.onBluetoothDeviceFound(function(devices) {        //获取在蓝牙模块生效期间所有已发现的蓝牙设备        wxAsyncPromise("getBluetoothDevices").then((res) => {            getDevices && getDevices(res.devices);        });    });}//三、连接蓝牙设备// wxAsyncPromise("createBLEConnection",{deviceId:""}).then(res=>{//连接成功可选择停止搜索蓝牙// wxAsyncPromise("stopBluetoothDevicesDiscovery").then()// }).catch(res=>{}).finally(()=>{})//四、连接成功后, 获取蓝牙设备的service服务// wxAsyncPromise("getBLEDeviceServices",{deviceId:""}).then(res=>{})//五、获取的service服务可能有多个,递归获取特征值(最后要用的是能读,能写,能监听的那个值的uuid作为特征值id)/** * * * @export * @param {number} deviceId 蓝牙设备id * @param {array} services wxAsyncPromise("getBLEDeviceServices",{deviceId:""}).then(res=>{})获取的res.services * @param {function} success 成功取得有用特征值uuid的回调函数 */export function getDeviceCharacteristics(deviceId, services = [], success, fail) {    services = services.slice(0);    if (services.length) {        const serviceId = services.shift().uuid;        wxAsyncPromise("getBLEDeviceCharacteristics", {            deviceId,            serviceId,        })            .then((res) => {                console.log("getBLEDeviceCharacteristics", deviceId, serviceId, res);                let finished = false;                let write = false;                let notify = false;                let indicate = false;                                for (var i = 0; i < res.characteristics.length; i++) {                    if (!notify) {                        notify = res.characteristics[i].properties.notify;                    }                    // if (!indicate) {                    //     indicate = res.characteristics[i].properties.indicate;                    // }                    if (!write) {                        write = res.characteristics[i].properties.write;                    }                    if ( write) {                        /* 获取蓝牙特征值uuid */                        success &&                            success({                                serviceId,                                characteristicId: res.characteristics[i].uuid,                            });                                              finished = true;                        break;                    }                }                if (!finished) {                    getDeviceCharacteristics(deviceId, services, success, fail);                }            })            .catch((res) => {                getDeviceCharacteristics(deviceId, services, success, fail);            });    } else {        fail && fail();    }}//六、启动notify 蓝牙监听功能 然后使用 wx.onBLECharacteristicValueChange用来监听蓝牙设备传递数据/** * * * @export * @param {object} options * {            deviceId,//蓝牙设备id            serviceId,//服务id            characteristicId,//可用特征值uuid    } * @param {function} onChange 监听蓝牙设备传递数据回调函数 */export function onBLECharacteristicValueChange(options, onChange = function() {}) {    wxAsyncPromise("notifyBLECharacteristicValueChange", {        state: true,        ...options,    }).then((res) => {        wx.onBLECharacteristicValueChange(onChange);    });}//七、发送数据(递归分包发送)/** * * * @export * @param {object} options * {            deviceId,            serviceId,            characteristicId,            value [ArrayBuffer],            lasterSuccess,    } */export function sendDataToDevice(options) {    let byteLength = options.value.byteLength;    //这里默认一次20个字节发送    const speed = options.onceByleLength || 20;    if (byteLength > 0) {        wxAsyncPromise("writeBLECharacteristicValue", {            ...options,            value: options.value.slice(0, byteLength > speed ? speed : byteLength),        })        .then((res) => {            if (byteLength > speed) {                sendDataToDevice({                    ...options,                    value: options.value.slice(speed, byteLength),                });            } else {                options.lasterSuccess && options.lasterSuccess();            }        })        .catch((res) => {            console.log("发送数据失败"+res);            wx.hideLoading();            catchToast(res);        });    }}export function charToArrayBuffer(str) {    var out = new ArrayBuffer(str.length);    var uint8 = new Uint8Array(out);    var strs = str.split("");    for (var i = 0; i < strs.length; i++) {        uint8[i] = strs[i].charCodeAt();    }    return uint8;}export function charToArray(str) {    var arr = [];    var strs = str.split("");    for (var i = 0; i < strs.length; i++) {        arr[i] = strs[i].charCodeAt();    }    return arr;}//打印二维码/** * * * @export * @param {object} options * {            deviceId,            serviceId,            characteristicId,            value,//ArrayBuffer:二维码的数据    } */export function printQR(options) {    //打印二维码的十进制指令data:    let data = [29, 107, 97, 0, 1, options.value.byteLength, 0];    sendDataToDevice({        ...options,        value: new Uint8Array(data).buffer,        lasterSuccess: () => {            //指令发送成功后,发送二维码的数据            sendDataToDevice(options);        },    });}function grayPixle(pix) {    return pix[0] * 0.299 + pix[1] * 0.587 + pix[2] * 0.114;}export function overwriteImageData(data) {    let sendWidth = data.width,        sendHeight = data.height;    const threshold = data.threshold || 180;    let sendImageData = new ArrayBuffer((sendWidth * sendHeight) / 8);    sendImageData = new Uint8Array(sendImageData);    let pix = data.imageData;    const part = [];    let index = 0;    for (let i = 0; i < pix.length; i += 32) {        //横向每8个像素点组成一个字节(8位二进制数)。        for (let k = 0; k < 8; k++) {            const grayPixle1 = grayPixle(pix.slice(i + k * 4, i + k * 4 + (4 - 1)));            //阈值调整            if (grayPixle1 > threshold) {                //灰度值大于threshold位   白色 为第k位0不打印                part[k] = 0;            } else {                part[k] = 1;            }        }        let temp = 0;        for (let a = 0; a < part.length; a++) {            temp += part[a] * Math.pow(2, part.length - 1 - a);        }        //开始不明白以下算法什么意思,了解了字节才知道,一个字节是8位的二进制数,part这个数组存的0和1就是二进制的0和1,传输到打印的位图数据的一个字节是0-255之间的十进制数,以下是用权相加法转十进制数,理解了这个就用上面的for循环替代了        // const temp =        //     part[0] * 128 +        //     part[1] * 64 +        //     part[2] * 32 +        //     part[3] * 16 +        //     part[4] * 8 +        //     part[5] * 4 +        //     part[6] * 2 +        //     part[7] * 1;        sendImageData[index++] = temp;    }    return {        array: Array.from(sendImageData),        width: sendWidth / 8,        height: sendHeight,    };}/** * printImage * @param {object} opt * {            deviceId,//蓝牙设备id            serviceId,//服务id            characteristicId,//可用特征值uuid            lasterSuccess , //最后完成的回调    } */// export function printImage(opt = {}, imageInfo = {}) {//     let arr = imageInfo.array,//         width = imageInfo.width,//         height = imageInfo.height;//     const writeArray = [];//     const xl = width % 256;//     const xh = width / 256;//     const hl = height % 256;//     const hh = height / 256;//     //分行发送图片数据,用的十进制指令//     const command = [29, 118, 48, 0, xl, xh, hl, hh];//     // for (let i = 0; i < arr.length / width; i++) {//     //     const subArr = arr.slice(i * width, i * width + width);//     //     const tempArr = command.concat(subArr);//     //     writeArray.push(new Uint8Array(tempArr));//     // }//   const tempArr = command.concat(arr);//   writeArray.push(new Uint8Array(tempArr));//     const print = (options, writeArray) => {//         if (writeArray.length) {//             sendDataToDevice({//                 ...options,//                 value: writeArray.shift().buffer,//                 lasterSuccess: () => {//                     if (writeArray.length) {//                         print(options, writeArray);//                     } else {//                         options.lasterSuccess && options.lasterSuccess();//                     }//                 },//             });//         }//     };//     print(opt, writeArray);// }export const LAST_CONNECTED_DEVICE = "last_connected_device";export const printCommand = {    left: [27, 97, 0], //居左    center: [27, 97, 1], //居中    right: [27, 97, 2], //居右    clear: [27, 64], //初始化    enter: [10],};export function printImage(opt = {}, imageInfo = {}) {       const { printAlign = "left" } = opt;       let arr = imageInfo.array,       width = imageInfo.width;       const writeArray = [];       const xl = width % 256;       const xh = width / 256;       //分行发送图片数据       const command = []           .concat(printCommand.clear)           .concat(printCommand[printAlign])           .concat([29, 118, 48, 0, xl, xh, 1, 0]);       for (let i = 0; i < arr.length / width; i++) {           const subArr = arr.slice(i * width, i * width + width);           const tempArr = command.concat(subArr);           writeArray.push(new Uint8Array(tempArr));       }       const len = writeArray.length;       const print = (options, writeArray) => {           if (writeArray.length) {               sendDataToDevice({                   ...options,                   value: writeArray.shift().buffer,                   lasterSuccess: () => {                       options.onProgress && options.onProgress(Math.floor(((len - writeArray.length) / len) * 100));                       if (writeArray.length) {                           print(options, writeArray);                       } else {                           options.lasterSuccess && options.lasterSuccess();                       }                   },               });           }       };       print(opt, writeArray);}/** * 蓝牙设备异常信息 */export const errMsg = {    10000: "未初始化蓝牙模块",    10001: "蓝牙未打开",    10002: "没有找到指定设备",    10003: "连接失败",    10004: "没有找到指定服务",    10005: "没有找到指定特征值",    10006: "当前连接已断开",    10007: "当前特征值不支持此操作",    10008: "系统上报异常",    10009: "系统版本低于 4.3 不支持BLE",    10012: "连接超时",    10013: "连接 deviceId 为空或者是格式不正确"};/** * toast显示捕获的蓝牙异常 */export function catchToast(err) {    let coode = err.errCode ? err.errCode.toString() : "";    let msg = errMsg[coode];    if(msg==undefined||msg==null || msg==""){        msg=err.errMsg;    }    wx.showToast({        title: msg || coode,        icon: "none",        position: "bottom",        duration: 3000    });}/*****CPCL指令接口****/

gbk.js文件内容如下:

/*! * gbk.js v0.2.4 * Homepage https://github.com/cnwhy/GBK.js * License MIT */!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.GBK=t()}(this,function(){"use strict";var e,n=function(c){return{decode:function(e){for(var t="",n=0,r=e.length;n>8,255&u)):t.push(n)}}return t}}},t=function(e){var t=n(e);return t.URI=function(X){var e="!"()*-._~";function t(t){t.split("").sort();var U=function(e){return~t.indexOf(e)||/[0-9a-zA-Z]/.test(e)};return{encode:function(e){return(e+"").replace(/./g,function(e){if(U(e))return e;for(var t=X.encode(e),n=0;nBDGHO%RTUW%\\_a%jl%rtw} (V!*+-0%27>C%EHJ%MP%R\\`cdfn%ptvz{} (W!()*,/3%579;=%CFGM%QWX\\^cdg%ilnprtvy%} (X!&")%.468CDHJLMOPSTWY%\\_b%dg%ilnprtuwxz%|~ (Y!"(*+-469%=?%GI%KO%RT%V[%_bdikmnptuy{}~ (Z!&")+%-/%;>@ACE%GKMNPR%TW[_%ikmo%rt%vy%{} ([!"(%+-%024%;=%BD%LO%QSTX%[]^`%ce%y{} (\\!()+%/1%7:%LN%SU%WY%cf%im%prt%xz%~ (]!&"%*-%/1%68%EG%cgloqs%uwx|%~ (^!")%-/02356;>FJKOPRSVWZ%]_`dfi%kmor%vyz (_!"+%-124%68;=@ACE%MOQRUVX%]_adegjqwx|~ (`!&)*-%/689;%=?%ADFIKLNOVX^%cehilmoq%uwyz|%~ (a!"),%/124%=?AD%HJ%PRSU%[]e%ho%qu%~ (b!()*,%.024%79;%=?A%FH%KM%WY%`c%ei%loq%tvy%| (c!"*+-.1346%8:%<>%GKLOQSUZ%\\_cghjltwy{| (d!(,/1%4679=>@D%JLMOQRTVWZ]`%ce%km%pr%tvy%} (e!+,-/0279%;>?DQW[%]_bdhqu%wy (f!&().47:;>ACEFHIKMP%SU^a%egikm%tx}~ (g!)*,.02%58<>BCGI%MPY[]`%bdeginpuv (h!(*-2%6=>A%CF%KMPRT%WZ\\%`deg%ln%qswxz{} (i!&+-%/1%469;>@AD%HJ%MP%TV%Y[\\abdh%mrsvx~ (j!&,%.0235%7:;>@%FH%PRTVXZ\\_%cf%hjkn%puw%{~ (k!").04578;=?%CFI%NQRTW%^`acdg%ilmo%ru%wz|~ (l!&(*%,.%9=%ACDHIK%OQS%U[%^`%ce%hk%ru%{~ (m!&")%79%CE%KM%PR%^`%hjkmnqxz%~ (n!&(*+./2%478<%>ACG%WYZ\\%^`%cgmnp%txy{} (o!&"%)+,.5%9;%BE%UWZ\\^%`b%il%ps%ux%~ (s!&)%:<%?A%CE%OQ%SU%bd%ilnpqstvwy%~ (t!&")+,.%246%9=>ACDF%ILNRVXY[\\ac%fiklprsvxy (u!&(%,.013%?BDG%IK%MRVXY[%^abeg%jl%ostyz}~ (v!"(%,.013%9;%=@%CIJMOR%VXZ[]%_a%lnp%rtv~ (w!&(%+-/%24%689<=?A%CE%KNPR%VX%Z\\%`bcf%oq%tv%|~ (x!&"(*+-%5:;=%@B%SU%[^%km%svxy{%~ (y!&(),%1346%:<>@B%DF%HKMNPQSU%Y[%qs%~ (z!()%ACEFH%OQ%_bfnpqwx{}~ ({!&)+-%023569=ADEG%IKMV^%`c%fhinq%swxz (|!&"%)+%-/2%:>@ADEG%KMO%U[\\^`acefi%lnpquwy|%~ (}!&(+-%02%578:%?AI%MP%TVZ%\\^`be%hjlnoq%vx%} )&!&"%+-%356:<>?ABD%MO%TWX[%`b%fhj%mopr%vx{}~ )"!&)%-/%69%@BCG%QSTVX%bdghj%mo%{}~ )(!&"%9;%=?%WY%eg%mo%{} ))!"(+,2458=>@%DGHLOQ%SUVZ[_f%mp%twxz%~ )*!()-%025%:<%BE%IKLNOR%`dfhmp%rtwx{%} )+!(.137%:>%BD%HJP%SU%^a%fjkm%pr%} ),!&(%02568%:B%DFI%KMOQSVWY%[^%aehikmo%uxz%|~ )-!&"%358=%@B%DGIKLORSVX%Z\\^a%cgjq%suwxz~ ).!&(%+-%2467:%?AC%FI%MRSUVY^%`e%gijmnpqstwz{}~ )/!&()+,.9%;=>BCEIJLPQT%V^%`b%fh%loprv%|~ )0!"(*,-/%1457%:>?GJKMNPWY%[^%acdg%jlnp%ruwz{}~ )1!")*.035%79:=%DG%IL%PR%TVWY[^a%ejqruwx{%~ )2!&)%,.1%37%;=%@B%EHILO%QS%eg%nprtvwy%{}~ )3!&"%*,%/1%47%=?%BDF%XZ[]%ac%jl%txy{%~ )4!&"%+-%24%68%<>A%EHJ%Z\\%ik%su%z|~ )5!/058%:@D%FH%KN%SW\\_%afijlmp%su%wy%{} )7!(*+-59;?GHJKNTUZ\\_b%ejm%pt%wyz|%~ )8!&(%*-78:=%?ABD%ILMO%RUXY[b%eg%nqu%wy{%} )9!"(*%-014%79;%>CEIK%MOQ%WY\\^cgijmnqsuw%{} ):!&")%+02%46%<>AC%GJKM%PRTVX%[]bce%ilmpqt%y|~ );!()%+-%/14%9;%?ABDFHIOQ%WY[%]_%ce%lnp%rt%} )!"(+%.2578;%?ABEFHIKM%ORSUWZ%\\_`b%eh%jlnpqs%~ )?!&"(*%,/%146%8:ABD%HJMNP%RTVY%[_%ac%egj%mor%uwy{| )A!"(%+-/024%:=>@AHIK%NPRSUWY%]a%cehik%mo%qsuvx%{}~ )B!&")%,/%35%=@%DF%JL%OQ%TV%oqu%|~ )C!&(%-/34689;?%EJLMO%QTV%XZ\\^%ceglnpqt%wyz|} )D!&)*,/0279:<%@BG%IKLPQSVY[]%`beghjkmoq%tv%y{~ )E!"+%-0258:=>@E%LNQ%Za%cgkmopr%tvwyz~ )F!()%/14%79@CEGHK%OQ%SU%[^%dg%imnrz~ )G!"),0%9DF%MOR%Z\\^a%hj%ln%pr%{}~ )H!&*%-/2357%@BD%IKMO%RTUWXZ\\%`bce%nps%uw%y|~ )I!&"(*%,.%2479;%FI%KM%TVWY%[]%`b%dfhik%{}~ )J!"(*+.01346%9;<>?A%EIJL%NPRTY%[]%befhil%uxy{}~ )K!&()+,.%02%68%?ACEG%IK%PR%TW%bd%gi%rtv%~ )L!&(%36%;=?%DFH%KMO%QS%Y\\%`bce%ln%twy%} )M!&"(*%46%<>?A%CE%GI%QSV%Z\\%ce%lnq%~ )N!&"%~ )O!&"%178:%CE%HJM%OQ%TVWZ\\]_%jl%sz{}~ )P!&"%136%9>AEG%JMNP%RU%Y[%bd%koq%wy|} )Q!&"%*,.018:<%@B%IL%NP%RTV%XZ%\\^%dh%lnrw|%~ )R!"*,2%48:=>@%CEFJ%LOR%VX%Z]_%aijl%nr%vxz|~ )S!&(%*-.2589;ABGINTUWXZ[]%_bd%fh%kmqrt%y{}~ )U!()+,02%46;<>@EHLQ%TWY[]^`acdg%il%oqru%wy%} )V!&")*-.02359%=?EHOPSTVWYZ\\%ad%fk%mp%su%xz}~ )W!&),-/1%479:<>%@BDG%NP%SUY\\]_bcefhilp%rtvxz{} )X!&"(*,%36%=@%CFHJKM%OQ%[]^`%nq%suxy{%~ )Y!&)%/1%35689;<>@AC%FHKMPQTV%X[%^`%bd%fhjnpqs%u{ )Z!&)%24%79%@B%DFGI%MO%QU%^`%bd%gkmoqstv%|~ )[!&"%+.%024%=?%ACE%GI%KM%RU%WY[%]_ac%ik%mpqu%~ )\\!&"(*%-/%35%?ABDEG%LNP%UW%]`%jlo%z}~ )]!&"%DF%MP%VX%hj%ln%~ )^!&()+%8:%EIL%ORT%VX\\%_a%cf%hj%lnrsuvy%~ )_!&"%,246%8<@AF%IKM%Y\\^%`b%eglpr%xz|%~ )`!"(%14%8:;=@D%NP%W[%^`%mo%rtvx%~ )a!&"%+-/%359%=?%AD%GIKLN%SU%Y[%^`%ce%gj%nq%wy%{}~ )b!&"%)+-%/1%9;%DF%JM%VX%[]_%df%oq%|~ )c!&"%:<%EGIK%MP%RXZ\\^%dg%il%oq%suvxz|~ )d!()*-/%25689;%=@%BGHJ%NQSUVX%ce%psv%xz )e!&"%,1%35%8;=?%BDFG "W!,-. &(+&."&-~&"u"W!/1 ")>.= ".u"/!K. "0`"/!94 "1t"0T"/!?Bu`\\Q1t "0!)* "/!xy2IH ";!*( &"}")!\\] "+{.;U&"q.>!&" ")Z&"t",5":!GF "9!eiha`;:ML ")e"-!XVWY "W?"-!67%?#3$ "6!-.%@ "5!rs%~ "6!&"%, "5!^_%g## "]!67%?## "-!&"%1## .;!RST .>+.;!VW%~ .)"W!mn%~ "X!&"%f#8$t%~ "Y!&"%p#5$ &0!=>%MO%U#5$]%mo%u#4$ .9!89<=BC@AD%G##>?:;4#67#6$ &1!cd%hTi%~ &2!&"%)#12$*%/K0%I#10$ &.!()7 ")!=?O_ "+}",("-!\\]%_ "/!)37fz{ "0z"8!CD%ft%~ "9!&"%)-%/VW|%~ ":!&J "0P"W!>IJ#8$ &(!uU &+7&(T&).&(]&)6&(\\&)F&(a&+9&(`&)h&(g&+;&(f&*-&(n&+=&(m&+!?ACE &(!p^ &,a-Qc&)!_c -Qd&,q#1$"Z!&"%J#18$ "W!MN%U "^`"a!@AN%PSv "b!"*+. .93.>!(*# ",@"]G#"):#0$"Yv"X!no "Y!wx "W2"X!pq .9!LM%UW%Z\\%ik%n -R!*+%6 "W3#10$"7!LM%~ "8!&"%>#12$ )e!HIKN%SVWabei%lnp%uw%y{%~ )f!"+%-23679%;@BCEFHIM%PS%_abdf%ik%rt%~ )g!(*%79%=?@BDFGIJL%OQ%TV%XZ[]%bdfgkn%prsv%y{}~ )h!&"(,-/3%9;%>@B%EGIK%MO%RT%cehil%or%z}~ )i!&"%)+,/13579:?%CE%HJ%\\^`eh%tvwy%} )j!"(%,.13%57%9;<>@%JLN%UY%hj%~ )k!&")%1357;=%CF%IKN%TV%Y[%bdfhj%mqstv%z|} )l!"+1369:<>%ACDFGJM%PR%UZ%ad%fh%npr%tw%{}~ )m!&()+%.0%2479@%CEFHIMOQ%TVX%Z\\%_aeg%ilnrswyz|} )o!()+%-/346%=@%EGI%MOQ%TV%\\^%`b%iklnq%suw{|~ )p!&()+-.013%<>%FHILN%WYZ\\ (iC*r5(pM)89(gy(h[(gk)p*)o>*A;)s|*9E)ui)cO*s5*ux)R/({@(Z*)7s)B.(~d*4~)F{*42)@K)pg(_l)>Q)a|*2"*Jb(\\0(u2)4?)\\@*9t)8])5n(eJ(f+)|s(^7)mH))<)7>*Yr*ua)6M*2O(o@*t|*0J)cV)oo)E[)op);L(XR*W~)7F)z6)?3)hN);2)66*8L*xa)Dd)cf)61)76(Wo)k9(cY(a_*.d*b,))v)G`)Jk*6R*.k)HS)vH*E"*oR([d*U/*:L*4b(bm*L>(a&)p!]`bdelnrsu%wy%} )q!&"%-035%7:;=?@BCEFJLN%X[%^ac%egj%lnp%ty{}~ )r!&)+%-/%69%@C *B**t=(Yf(qR*{F({T)6!th )BK*V+++A)b})DT)um)12(c!`& *^r*4P*Wv*mT(Z=)e4(t-)1k)`B*K0(tz*:])Cj)}<)&|*/8)l*)TJ*[[*`!0t +3")Q4*cF)}()-`)v**@.*A<)Q!596 ))I)*v)nD*q<)>X),G).P*_0(s@*7;*a^*rQ*v?*_J*/W*X,)5](YH(e5(cm*_!9:< *a,)F:)-N*6j*JF+,1)3Y(`E)nu)-P)?.)\\_)Z"({u);N(^!A| )EP(T_)yA*{Q)_5)r!DE%GKNPQT%_a%ch%jm%rvwz%~ )s!&"%,/12469;%=?@BD%HJLN%Z\\%df%hk%or )mi)*e)gu*=C)(dx*E0)PD)1!mh )^Z*:;*8Q(Vg)SU*Bu)Y)lB(i*(ZQ*Y,)6G*mQ)C[(ky)[T))*(f9)^m*^P)62)*Y(*JJ)<>)yh(pX)Lv)5,(fL(UE)z*)1i)[j*T>)6B*`V*~U)y\\(e`)n?)7k(c()Rg*_p),X*~:*2q+3k(Xj(}?*Xd*1T)?G)_?(]j(^~*D_)&Z({W)7"*d@)lq*ZZ)z?)2()~4(V[*/9)rl(TW*7f(`7(_m)M5(d^*[|*n^*sl)YY*rZ)J))u!`abdfgklnp%ruyz|~ )v!")+%28%;=%@BCE%GI%KO%RT%VY%[]%jl%np%tvxy{|~ )w!&"%*,%.0%6 )tu(\\&)se):o*N`(t*):B)(~){E)Ie(W[*8Y(j8(Tx)mR){])*!Qy *q>(`5(f=)^e)9.*n~(oe)@n)Ig*d[(hY)W=*.I*IY)5O*/1)mY*;=)vD*si*_/)2o)kM*T1)Ov(`T*XP)O3*3G*>{(n-(bn(Vb(Ta(_D*(G*d_*&i(YL*[t*&C){b);m)&g(\\**51)nL*(i)W6*1o)D6(zh(|V)vN)<[):r)9b)8<*ns);3*_O)}h)nt)5o(tM(fJ)P2([z)5P))n)P?(Vw*X7*Ji)-i*`f)w!78%<>%GI%MO%]_%cgil%oqt%|~ )x!&)*2457:%=?A%GJL%PSTW%Z\\%_a%c )/R*2s)7/(U&(cd*b~)9p*4J)@/)R5(X()1n)W+*TB),v*Ef)-7)82(^&*;v)G=(_s)8t*[=(ZB(~G)xH(|Z(`J)zZ)1<*a2)pp).B)-{)ov*[a)^J)om)}])s8(_f*ar(qU(X0)Z3*_{)>G)}/)e0)VG*1n(yJ)6x)++(nl*?3)}@))e),\\*`J*/U*y")9:)Y_)ut)_;(^D*uF(p5)l2(W~)l5)+-)1f(u-)Vc)Px)ue(eY*sr(_!>t )9A(eg*mF*Tg*Ys)cW)u{*G_*_~*Tq(e=)x!de%jl%wy%~ )y!&"%*,%047:<=?@BDF%HKLNOQ%VX%Z]`bdfi%oqrtvwy|} )z!&"%)+.%24578:; (TG)q/(eK*m<*xV+2S*o.({Q*S_(T!hb (^x*>m)47(ai)F>(Xy)0D(_.)Ts(^()6Y)?9*rW*UQ*`O)m|*c*)rJ)Q2)dO)eX*T_(qf)r`*XL)DA*oA*3w)+<)Wk(u_)|\\)s{*o<)Pn)?O*/O(q7(]v*qn(|W(s((f,*[g)>a*x_(my*mP)q>*`y)9?(gq(t!@` (o~*\\N)Cs*ZH*8U(`[)1p(qF*F@)&;+0<(YM*x}*Sv(w@)0O(d:)6?*a.*c/*{T)0B*2B(]d*2i(|r*{J)U-(Uy)z!<>@AC%QS%UWY]_%df%oq%uw%~ ){!&"%57%9;A%DHKMO%RT%VYZ_%adgh *X0(e.*0B)}c(WK(U<*qO)T*)h1*C6))N)lg*21)L")t3*mE*-4(_T)_h(e**_e*:q*X))dt*{B)T0(o-*9z)?[*4.)5[*r((uu(W:*S|*.T)>9*=U*uI(iZ*ye*4)(c9*Ta(e}*4>)+5)Sf*X9*9s*d.(f-)Q{(_y*.Q(oB)`C)S,*(9(tq(W8)/1)2K*(Z(Tv(|_)E7*FD)&C*ne*yU)mS)`&*`Z(^{*/^*Sz(to(_W(X=(f*(tQ)>r*4(({,)69)7,*^z)*4)R&)}:(WJ(Ya)CK){!ijmo%qt%xz%~ )|!&"%+-%79%BDEG%JNOQT%Y[]%ehikm%ortvy%{} )}!&"*%,.0135%7;=A )Uj)VM)x`*K6),T)l()6]*^o(Yx*eW)?I*5!Z| )+2*5{*Xt(a0*MY*XK(t3([\\(Vl*qk)cT*6K*Wx(|**S`*r:(uT*/[(g;(ld(kU*TI)>4)JQ*mL)po)Xz)*a)kn)D+)E])|l*3z*Xv)2F)y>)>]*Xc(^T(`4*mU*/y*3x*.L(~C)Wy)DE*&;)o}+&I*6a*0|*),):}*oQ)z^(fN(h7)O^):`)4}+04*4w)m=(a3*uT*>e)Fo*F&*qP*s1*nF(Tp(ea*.s)Fl*Z-*2K)C2)+0*1H)}!CDFH%KMOQS%XZ\\^abgikmprsuw%~ )~!&"(*+./13578:<%?A%GIJL%NQS%VY%[^%`i%kmnp%rt%{}~ *&&(pL*2u)Gq))-)>6(a`)0F+4-(X}*\\H(^8({b),P))1)Re)7[*Wz(^=*m\\(bf)SM*:M)eC(p,)Di*X-(tE*_-*=*(g@)~H(Wk)Sk(zt(vE+2X(eA*Ee*~r*UB*3~)>@*x^(n6*sd(`H)k2(`j(|?)7l*L.(UC)7:)/\\)H{(^?({O(^l*N<)~\\*{[*08)1o)^"*X/(]n)*n*`S)ix*N>(ni)tz)-6+42*qI*^R+"T*TE)oj)Fu)Eh*Z8*X5(`W*^t)Yr)HN*n_*bs(n9)E(*K~*_X(gs*&!"*%-/%246%:=>@%BD%FI%KM%OQRT%XZ%]_a%hj%lopr%wy%~ *"!&")%.124%@B%GK +&)(zy)Us)R-(V9({j*~Q*d7)3v*b5*v{(f/)VX(|0(_p(j**0=*2&)u*4e*G])e<*(z)XG*"!LM%UW%{~ *(!&"%*.%8:%@CDFI%KPQSV%Y[%]_`d%hjk ),d*DB(h~)6g(V&)SX)5S*9x(h)(c])fQ)Yi)l8(`,(b~*TA)KF)-n)/2)W8(o2)O4)gi)G-*_i*/T)8s)0|(hv)n5*Um)`>)VF({])*j*;g)2s+1b)v}*G@*"0)oy(_c)1v)`u)A3)*;)0&*Tr)^K)86*^s).H)0;*Eo(ms)Pz)0m)35(cX)1`)AV*X?)yu(WU)_k)RN*Sp*TV*.r*;y)@X(wu+"Z)UM)WA*UL)U7(WT)^F*@%EG%NPQS%`b%vxz%} **!&"(*%,.%4 *3i*{:*`1)\\M*Sb+/q(v`*/!*J (ef)Df)HY*^{*"V*sc(e")/W)mb)Ry(d)(y2*.A)85(_S*55))9*@7)6C(^L(zs(WI)x>(`\\)18)UJ({!{y *S!^m )@+).W*r;(u`*/(*.D(kP)EC(t_(XU({m(aa*;o*xj*X>)l/*mq(Zw)z[(W2)EB*~H(y*)P5)pj(o=(|t)}N*qC)`w(^H*4-*97(uE*/E*;<)HA)Ex)v4)uS)7M)8r)~;(Yv(a+(_B*;e)KQ*g=*ZC*X1*N;*o/)~h(W1**!56%km%tv%~ *+!&"%= ([<*8P(`k*{D)WZ)Xv)VJ),7(\\s(vP(|d)UB)Rf)m**?<)GB(t|*So(c/*dE*rC*AH)I:)w+)`O*4z(V8*bP)UC*~N(v{(mQ):d*nJ)sy(Y.*5E)eM*NL*{O*/u*.x(a@)>T*dI*^!im (eH){\\):L)9])ox)yp*J5*r,)5F(al*9I)G.)DU)9/)rR)|Z)TV*.m*N](vD)5.*Bo*9l)lI(ZO(V_)mI*TO(}O))F)}_)?F*eL(V^)Tz*M2*)~*o"(VY*U3*_l*u^)A;)xR*_b)_n)Ut*+!>?%JL%RT%~ *,!&"%F (}B(as*;[(^.*:|)rB)Af++l(V@)1J*(!nc *.i(V)*R|)A_*xh*uD(r[)>g*o])-h)mm*uA)|!LK (_3)_1){^):I){<*.:)gP*w&(U2)^S(UJ*d&(d_)>L)@0*7!u~ (g9(}6)m>*v2)7B)eE)ma(}J*~C*=-(}E(g+)sw(U+)S\\*37)7<)9&))0(^C*Z!+l *o0(Yz(eB)1g)_.(a()8a+0:(w:(ZV)qw(d-*.|)<2)>&)6L)9P(ZD)cS*NC(_&*S}*.w(o**=/*mZ(^g(ex)&N*,!GH%~ *-!&"%-/%35%9;%P (YS)|P)UV*bg*~Y(iy(gA(cp(gZ),=):H)JF(_b)36(_}(q[(b@(o1)tB)qK)+M)3E*)7)5w)6Z(V6)^p*29)7a)_f*uK(oZ*:I)E{)Hv)vX*xw)yI(sr(g^(eL)W~(]p(`U({Y*Tb*43(i<(p0)0L(o4(f1){@)0)(zd)9_)6c(e@)6&({~)E;(h?){e*:T*dK)+)*Ki(t^(p8)7x*Z6*4s(o:)~2(Y2).v({t)OP(c0)}d)e.)Fy)t4)qv)@@(_((U|)pm(~k){k*?&(tt+"Y([W*-!QR%tv%~ *.!&"%46%8HNUVp~ */!-:KQ_xz|%~ *0!&"%)+.%7:; *{])2N)Xt(oG)@O)8W(n,)7V)6,)+i*qc(of)73(j"*Fc*5u*_4(i_*)_[)V@(b:(U=*t;*on*A})vM**-(]F(ou)AC%FHK%OS%UWX[]%`b%eghj%qsuw%{}~ *1!&"(*+/12457%=@B%FIJMOQ%SU%XZ\\^%befh%mqr )n-)|8*(B)SS*sS(a\\)j2(h;(Yr*31*V*5c*nX)/})_>*.]*(^(_<)G[*(a*96(Tj*^p*eJ)An(VB*Ti)(er*n7(Y,)?g(}!jf */V*DE)R6)-o)gA)Y|*/@)&9)8,(zz)Y~(d+(U^)9X)lb)6d*^X(Wa(Ve)ST*.P*Sw*>"*HJ*0?*`s*n})~d*~k*K}*>G)Tp*~P*.v(uU+1Y)gU)t6*Dv*~!6L (kb(et(pU(U]+1D(ce*_Y*54)ry*{g*F0)Yy*@k)C:)30)Zj*3![^_begj%oqrtv{ *4!"*%,0479:<=?IK%OS%Y\\cdfgjry{} *5!&)%,.%02367@BDF%HJ%VXY[^%`bd%hj *U4*9Y*;@)q2)Qs).d**:s*nt)Q;)si*my(c~*:p(^^)*,*V>*X8(U5)ge)E&)G_*dJ*tM)a4)SO)qI(xT*oB)Q/*0v)@p(|.(v-(x\\*q!7F (W"*=o*?J)Mp)px*o1)f.)H"(zo)qo*dn*uL*)3*`/*U"),b*"|)yP*N?)Sz*F^({})*1)HJ)q`*/6(^a(]r(b1(wD*/F*uM*:E):.)rk*Bv)yJ)X+)+/(uf*@i)^H)Qq)7))pX)>J)1/*qL)@W*H/+/s*nG(ej(g(*U7);~*5!kl%prtx%z} *6!()*-%13%68:<%>ADEG%JSU%Y[]%`bd%iklp%tvwy{}~ *7!&"%+-%25%8=>@%BDF%L )<&*C^)L>*6n)vk);o+"l)6|)Ci*)YR*A*)72)*c)vA))UO(^h),A(uA(ak)/D(u@)B-*DD)mC)8;)4[*)F*T^(h8)O9+/u)Pm*B{(fY([M)SR(ic*Y[)cJ))])/G)i>)77)9J)cj)-|)X4)U:)=T)[>)pq)vS)q<)lQ(`>(e8(U@)+4)?c*2`*4Z)6^(g1(`B)_0*Jk*Yx)T6(f|*4^(q**Ky*XT*r`*cz(XK)SI(jW)p_)te)6k)_{)[t)-Q*@D)H[)uX*&Y*6z)CI+0F)8x)v3(oR)tG*t})6!DGHKPU%XZ%\\^`acdfhj%nxy{~ *;!"(+-.2%;>?CEGLPR (i]*5i)mV)pG(cH)n)(\\y(}F*S)*&)([&)ce*~a*y_)ZA)k8)x9)<4)s3)7f*xq*X+(gl*35(U?(oM*:g*=p)*b(_`)kL*T((}T(oD)TQ*xg(wa)ti+"\\*_|*(E(`p)5m*nT(VZ*k*=L*?c(p^)Cr(n?)A^)Ub*U<)Bt)]E)C])OL)FI)56(v/(TI(_7)ZE)E^)S=(^B)HL(tm)Oy)lc)&a*5;+3<)?y)w!rk *~[*Xw(~.(UK(W<(f3(tw)tA(lV)Qt*_U),@)S+)T8*Sh)9H*;!STVWYZ]_%chj%nrw *AE%GI%KN%P )0I)U9)d:)6)*m!Ax (cR*_R)~9(Vs*U1(Uk+1r*m!df (`g)[B*aB)UA*U?(qB)"F)O!2[ )E6(f_*6&*0t(rq*A6),w)6e)Ld*o:)>m)0])G?)s5(UA(Y1))/*v*)(VF)y^)i<(bL*:")=&)y5(|;)S/*u=*^e)R)(Zn(d5)WO)<{)Ad)R7)1E*xW*T[)*u)iD){=)yC(T^*b7)hd)RI(mo*cx)Ss*Y2(cI*Ea)C0*\\K(}M*~;)Wj*=!QRV%Z]%_a%cehj%msu%z|%~ *>!()%-3%:<%AC%EH%OQ%Z\\%`bg%jln%rtx%z|~ *?!(*,- (e3(wQ+3w*YJ(dq(nD*y.)D(*do)Sw(^4)mn)7])dq(`R(a^*/{)^o)o*(d*+0A)K-)uJ*s]*K^*13)`_)b:(^U)E_)k:(sc)=l(mL*Sf*{K)63*.o)1y)_o(sk(V!UN ).X*vy(W6*Sa(_v(uW*.E)5p(Va)@.)5**;M*?z*;x*Gv)ad)YN({p*.l(^c):()E`*3f*;N*IL(]7({1(uO*_B(U`)Rd(^1*LQ*^Y*q}*AI)if*nj*q?)VI*:S),n)2*6@++w(r6*X[(TO(dw*?!./%24%7;>@%GIKLN%QS%_abdf%hlmoqstv%y|} *@!&"*,-/%469:^)G+(js(Tm):s*~d)6.(y+)ig*99)VL)Ho(\\e*1)5g*{k({?(|])9@(i"(e4)nq(^I*A+)E.)H4)PC)6U*^}(e1*82)Vt)m3(US*XN({")ao*BU)B!(p *0Z*FB+1k+2e(}c)Cm*`()FD)DD)5))|C(r])+L)>k)Zp*B!(),.%46%@C%IK%MOQ%SW%\\^`%fh%np%twxz|%~ *C!&"(*%02%57%E )0.(`Q*2C);Z*JN)l&))M)FF)7^*t`(e))c[*Te)R;)rH)z-(f2)s:*xZ*8<*TX)u1)CR)_C)?Q))MD)n;)[S*U8*Tl(oI)QO*Jp)v6)9Z(eV)_/)Rk(r0);0(q3)Ha*6m(hb*)-(ZH*\\V)Vo*YF)_m)9G)V>)Yl*/"*=g)Fv*`;)V{*rl*Se(t})d>*C1*6P)m`*PR)H0(`n*Zp*nk):/*xx*[9(iu(X5)C=(l})1U)V8(`](}N(tO)WE(rC)l-)kr)0E*84*5q)7{)DF*_s)qz*ZA*X4*mt))^(d;(f0)_E*D!acdf%lw%} *E!)*%-/1%69%=@AFHIMNR%TV%Z\\%^`cghmnp%ruwx{%~ *F!"(%*,%.17%9<%?ACF%HMNP%SVX )RG),4([3(VA)+C)8K*8g*`R)G**dB)sA(ei*n6)-H)|R)GP)8V)}f*.y)n&([N)6X(tZ*`G(_k)y;){G(n:)-v*`L)U~(}e(n[(em(]y)X>(mw)9`*xs*6Q*FW*LV*`8(e{(}t*79)7@)5t*5\\+0,*r6*~!8F )IU(ur)7=(i7)8"(gr*Et)U8(vw)k<*Jz)O|(t<*UW),l)Xo)gc*x](T~)_y({C)us)8N)AJ)p/)1K({R(wd(TM*6")Rq)gE(V5)Dn*09)-d)@C)CY).c(uQ(vu*F![]_abfh%jl%oqrtuwxz%|~ *G!&)%/3468%;=%?AC%HJKO%VXZ%\\^`%ch%jmoprsuw%{}~ *H!&(%.0%4 )(n).9(d"*vx*_n)Fx(n0(i()1_(U")[3*/5*:5*=q(vH(W!Ze (}C*.F)Ch*@r(fw)lL*4G(i{+"g(X/)Ww*Tw(|g)3>(cz)nj)Ws*D~)?)*qJ*.S(f?)zR*)a*q=(k(*1P)kD)8.*FY)VD){c)5-)?C(cv):@)S1))3(f`*m[(U9)Rh*TP(}P)@)*9c)H((f!B8 )Q+)Ox(V()8S(cW*8>(WH(Y7(oA)5&*tB)AT)d.(bg(ab)0x*TS)@])ai)Du*MR)u.)t2)A`(l)(f<*n{)d?*H!5679:>@%BDF%IKLNOQ%UW%Y\\%`d%jl%oq%tv%y{%} *I!&"%/1%46%9;%@B%IKN%QS%WZ )yx)0H))T)0b*6o(t;)1\\)aT)_q)YB)RW)dE)MH*K2))u(UM(xu(j<*n=)Tl*)R*s6)5+*.`)ru*Dr(c2*tb*_")Aw)&4(Y))eY(TQ(d\\(t5(ep)98*o)(]m(Vm++P*.>(gc*W}*J2*8W*qA)V1),c(y5)A.*r_)gj)fA(du*6L*{Z*10({a*r!mF *.Y)q8(dX*ow*{n)~l*Lp)XI(UN*1c)x6*TJ)1s(gx(V?*xY))?)L~(\\M(en*Ul*>1)u9*tH(`d(h@(m8*nZ)V+({l)Vi)AC*I![\\%hj%oqrt%vx%z}~ *J!&"%),%13468%>DEHKMOQ%TV%Z^adeghjl%oq%su%y{%~ *K!&(*%/ (|m(zm)FT*3s(iq)|~)>C)I|(V/)OY*G7*uv(c^)80);@*b0)?>*4E*Tt(^e)H1(d.*I0)4=)Sr)TP*U-(}V*BP*9v*nf)O5*_,)?R)/-).N(Yj*I|)@h*67)f<)1z(^@(`+(on)-F*b(*J**df)*g)D4)/Y(vG(]i(_i*4|(bp)S"*sa)aZ(WL(Yw*1-)C{(je*nb)y8*Jt*.}(rw)5(*tD*DN(k_*x[)-t*Su*?u*d+(e6)@6)*~)lY)0=**l(xA*/!+. *^S(jG({Z)1,)?m)g>*K!13%57%;=%@B%EHILMO%RT%VX[\\_%cefkln%twz%| *L!&")+%-/%57%;=@%DH%LN%PR%UX%[]%_ac *`A)oU)qi*.t*u_*BA(zv){:*t.*Iw(b3)a_).5)@5*TF(|Y*~9))))PB)Rb*S!jl *`B*_V(dA)7&+/!v} (fD(Tq*:+)cw*E7)&i*J7)VR*sb(g|(V.*Ss)`2(gh*qX)_a)WC)pi(Tk)tI*FT(~U),3*Nz*x|*q])^i)Sm(tj)9a)q|*J+)ZS*9:(qa)bL(dB)tO*s^(hS)-J)`Y({k*T~*~V)P<(tT+0&)y_)DM)6/(XQ)mE*0r)T`*r))R.(W+)mv(^G)Fj)Z}*a_*6B(uc)i~*L!de%lnoq%~ *M!&"%03%8:%MO%QSUWXZ%t );M(},*aD){W*U.(vN(gW*7y*<+*MT*26)IX(l<*2l)cU*eI(_)(UL*xU)9N(T`(eS)C1*5v(tB)VU(TF(sx(cJ):5*nK(Y&(WE*JA*xp(t]*a6)ys(V~*4t)m5)S0({S(v\\)-A)I-*Du(ZJ)):(UF)sq(Vy*9A)6T)@\\*ap(}~(VS)-]*7?*`p*3a)mO(V:*KY)26)P~).Q(U6)pc)Y:*TG*Sk*T!n5 ),?*22*1[*.h*;J*<"(mu)Pp*w,*r*(vo(e|*nP)W5({|*{C(}>),R*s-*M!uv%~ *N!&"%:=GM%OQ%SUWY%\\^a%df%hj%ln%y{%~ *O!&")%> (d&*:_)LN(x6+0g)+**/i*!./ *;|*G0*__*Xb)pa)-U*xv)uH)@i(h1)7O(XE*7<)ml)+N*sj*ZX*F;(g:)TH)V/)R?)0X(b/)k~)*l(WS)0y).r(}))l.*b4*ms)d~)Sg)X)*cv)CG(e!(P (X3*eD*TU(d~)*k*_S)W.(U{)p[)JU*DR)2A*8&(X;(Yg*`v++0)20)W!g^ *r{(Uu(wL*t,(TT(~+)y1({>*TQ*1L)gC(pi*9R*bG(e^*a&(j[)&=).,)2/)OD)e]*46*PQ*O!?@%^`%hj%~ *P!&"%@B%H )Up(|1*KZ*xd*Uv*~7+0*)WT(^p(]h(p-*J_(dU):S*_&(n)(f\\(nB)fR)Y(*/Y*_m*:?)2f)/Z*rX)C.*.@*Y!1j *mX(~_*(T)CF)1&*qT*2N(U~*bm(bh*r^(]f*_a*As*d1)AE*w.(ze(c,)tm)D")Fp(gt)^Y({g*^g(^Y(nd(g\\)=f)Ar*X!^C *5:*>v*aL*)6(_r*CH)E/)@2)\\.*E?*[v(lX*`X)ot)^Q)b\\*W|)7S(`3)TR*X<)/@(rv)3\\)C<*X`)5j(jS)Wm)Ck*^Q*P!IJ%PS%Z\\%~ *Q!&"%R *n;*qG)qh)F8)Jw(g6(U:)_:*T,);^)?-({<){J(Zj(|F)sI)QA*PA*(H)Sl)ph)?K*_()""(}Z*D,)F;(co)54)|F)XL*/>)L<)|M)-T)Zr),])9D)VK)D;(TN))X*X&(Vk*4D)o&*s9)7E)@v)6A)Sj*Xu)a7*0Q(]k*3d(eI*aM)6[(hD*F2*_**2;*nq*@o*_j)UU)h.)?A)q_)kZ)t\\*7t*Ww*m;(Y/)Dz)m]*T.*tK)8@*oH*r+*?i)84)r()-e)e\\(Uv)/F)>o)7Y)Ou)>:*Q!ST%uw%~ *R!&"%;=%[ ))o)mk*bW*S~*9d)E))>*(f5*FZ)ss*4&*5-)RH(U8*.a*3))q4)I5)PO)6*(Zl(Xf(n|)7q)Ot*Ye(eG)@1*.R).b)8+)9o*7!lm *0V))d)&,(`P)Aj)ya)z9*X()^t)mx)Yv)Si){X(Ts)p^).k*mR*JU*.b)yE*4C*eH(W!-R *8?(fl))&)U.*T0*(U*^^*3u))b(g=)pJ({P)A,*~n)hA)F]*Ev)Co)f=*T!\\? *`H)|f)_])a>)7.)s7(kx(U(*>a)E}(aj*E[*X.*nR)}e*R!\\]%{}~ *S!&"(*%\\inx *T!+4Nm *U)(X1)T=)x.*~E)6V)cH)7g(TY)tK*^V)<]*XZ*&()GC)*s*KJ(uS)*0R(|h*aT)@q)).(X2)Ej([1(X>(o3)+=#2$*U!CD%KM%PSUVX%_a%df%kn%tw%} *V!&"%)-%3568%:=?%EG%IK%_ (Uz(TR(]e(TV),<(cT(T[(V,(Td+"J(z|(lW(Tn)y2(U,(b}(U4(tS)cY(c})Qp(mt*4h*{l)Q3)re+2\\(T|(V3+2U(U!IV (V"*9O(zk(ie(kV(VX(d!CSY[d *uP*X](c)(eR(c!5=M *X\\(c!ur (_!u{ (`!(GMYZfx{v *?`(a!>CBQT (^N*0<(V!OW]V|u (W&(Vr(W!V.] (XF(W!0`bDf (X!@I )>3(X!@A (cx(d0*q!lqp *r"*qy*r!328?JU]bhj~ *s!*4M *a!vz|x *b!2":9<@ *Y|*Z!&"*.%03%57;<>%@BDJL%RT%VY[\\^%ac%jnor%vy{~ *[!"()+%-01348:;A%EH%MO%Z\\]_`cf *b!>6UKDVJMYlhb]aqu *c!+1OKP^\\fps (`"(th(a!cmn (b&(hQ(b!8>X (e<)be)s[),1(_!ho +"G(dl),>)lW))7(o/(p!3+ (rV(s!;DP (n!ehfoujk (o?(n!w~ (o!LhNi_^KPYV (p!7. (oo(p! (ov(p!*_bdc\\B~y (q)(p!ng *[!hjl%prsuw%y{}~ *\\!&"%*-%/1%79;%FIJLMO%UW%ik%~ *]!&" (q!<>. (pw(q!1OJuQ (r!=DYarX *{}+2^)4t*9!@DFVQoKUX^`r *:!&, *9w*:!68Q *9!_~u *:!9:/ *9!p{hqf *:!2< *9k*:!OweR}uJb@FziA *;!&/ *:t*;!)* *:!NC *;!1fpq *:v*!&B *=!ti7Brf[\\M( *<|*>0*=!SD *>!f[dc} *?!89+:) *>!FPs *?!"=? *>!;w *?!r~ *@!(> *?n*@+*?!{pk *@8*?!RjeH *@!=;d )kc*A7*@H*A8*@w*A9*^!/0%OTjv| *_!.1%35%8;=%@C%FHIK%MPQTWZ%^`cghkqrtx} *`!")*,%.2%467?@DFKMP *A!U: *@!WT *A!XGgc]y )s0*A!Bqz *B!JN-5 *Ax*B!TB+_]Vyg *C!)LtRvc *D0),!;E (t!bg *2m(t{(u"(t~(bw(}!"*19 )5h)6!-b~ )7!4DAX )83)7`)8!J\\T )9e):U)9!)2Fl )8~)9!dB )8z)9!ft ):!QW?^ )9|):!=- );X)9!~v );!KG ):!jz );&):k);!,"d )<1);s))),H).])ko),N(cn(e!NZFEXosc (f!OV%XZG (e~(f![]"6 (g!-? (f!{uh (g!EF/ (f!zj (g!H{Xm_}RQ~ (h&(fv(gN(h"(g!fjw (h!+,. (g!TUO (h/(gz(h!0m9rOfEtuacy| (i`(j!/U (i!Bf:N8I5 *b!HILNOQ%SXZ%\\^%`d%fi%koprtv%y{%} *c!&"%),%.02%EG%JL%NQ%[]_%eg%oq (i!0On%p,)U (j!+r (i|(j!QY( (iz(j?(iw(j!v1 (it(k&(j!]^49 (k!1> (ji(kf(jq(k!23 (jt(k-(j!}d (k!6,9: (j!lm (k!/<+ *^~(k!eOSHkjEGnt (l!F"E (ks(l!;PGJ:BtR_Yijs (m!(Dilrv (n!1"5;@FEX ))!Wa\\Yy )*!*+ *c!rtu~ *d!"(%*,-/034:% *U~)(!Xf )-!W[fkmpy} ).!"38@G *J@)-E)d!ruy|} )e!/-:9>T[U^`cm_of *e!ab%~ *f!&"%g )eg)f!*&4 )ev)f!10(5L8?KDc`js )g!&)8 *8;(tK*yn(t!J( *{!8<%AILRUY\\_befh%j )*o)+")*z)+!,;6OKTI *XS)+!hgq~ ),"*73).!OTaZ )/5).y)/6).!lo )/!078 ).!ux )/!*KgXMA?[]%VX%p )0!CVos )1-)0!v\\ket )1!l;1]XFZ4 )2!GMB: )R!op{} )S!4W~ )T))S[)T!LMF, )S`)T!5+O@C )S_)T!(DS *g!qr%vx%~ *h!&"%79%y )U5)T!|a )U!?D )T!\\Ync )U!"I*1 )To)U!KNP/ )V4)Uk)V!AC )UZ)V!67 )U!X\\x_ )Wd)V!jh )W!(X )Vb)W!*[0" )V|)W`)VN)WV)VQ)Wu)X!_?\\ )YJ)X!PwDE )W!|o )X5)Y!wI0x )Zl)Y!z=?USOoLg"Z7 )[!H,- )Z!cT8nu )[!1Z )Z!iHh )[!osr^Xb` )\\^*h!z{%~ *i!&"%TV%~ *j!&") )\\!V4FCOn{km| )]!NWOmi )^!*9 ({!7;LJ\\o )l,*~h);:(|!BL *L\\(|N*[<*Qv*_!Gdfv *`&*_!uzw *`+*_y*`!=(u!xkq (v!F& (u|(v!K2yzQ|WLm (w"(v}(w!MO,>.[ (x)(w!epW} (x7*4p(x!l]9< (y!"T (xt(y!;= (xz(y!ILZ?OAEr (z!"&BGP (|!sz (z!ju ({*(z!acr *~!?ABDIGJKORTZ\\]`be%gijlmoq *.!9;=?BC *k!34%:<%~ *l!&"%: *.!GJXZ%\\cefjnquz{ */!),/023N7;] )FP)G!/@ )Ft)GA)F!sq}|\\ )H6)G!iNQmE )HC)J=)H})I!GH )Hz*o!(4>DILV[aouvx%~ *p!&(%s )IL)Hq)I!3) )J&)Hd)I8)J@)Ia)J/)Ij)J!,S:OgG )I\\)KB)J!VW )KD)J!X52Hjv )K1)J!z| )K!J"7@U )Jd)KV)J\\)Ku)LL)Kc)L!45 )M))Kh)L!a[ )Md)L!mxR )M!R= )L!ZEu )M!@UTm[o )f!Je )P!:;@=KLFSTZc *^!UW[%]_`bacdfhklnqu *p!tu%~ *q!&"%6;NVbgjmor%xz%|~ *r!&-%0479@DEG%IL%PR%TVY[\\ac%gin%z} *s!&" *^!wxy *_!)+ *\\j)5!"37;>=A@H *7`)k!EJUegi )>/)@!789Sb )DW)@!L^I )^`)C5)@!f}z )A&)@~*2<)A!OD1?FQGgt| )B!>4Urs} )C"*X!236;=@EFHIGMUY *N!AD%FH%K )d!3d )c{)d!+4CDIPRTW )7r)8C)9h*s!)+.%02378:%CE%LNOQRTUWX\\_`eghmnpqs%|~ *t!&"%+/%24%689<>%ACFGIN%PT%VXY[%_acd );E)=,*2:)Q!JS]UYegfmux%z )R!(0+19 )>!0Pf )c!kptN *DV(`0*3!|yp *4@)CH*4!A5/18;k%n_FH]Q[ )CN*4!aouxq *5!89 *4v*5")CS*5=*Ue*5!aIWC]sw~ *6!F+,C2;NO?9 (q~(xw*6!ZTc )Ym*6!u| *7E)Cd*7!C4 *t!ef%moprsuw%{~ *u!&"(*%@BCEG%wz *w!"(%+-0%G )/n)0!63@ )/s)0!2< )1+)2!-4 )3!+wk )4!@F{ *3!]\\ )SA)TK)WF)t!+-5>LMF )?M)t!bQRn%pTqZaX^ )u!*I23 )t[)u,)t|)u!(Fwovcx} )v!&5<(W\\Lou )w/)vw)w!=NH^ +4.+2!>@A )q!1DM9GHZbYmfux )r!78.*AO *w!HI%~ *x!&"%N )r!ILftdgsx )s!-.>CK )l!7=KHEXov )m"*0!@IG *Oi*0!P\\Yaif )p!f~ *Ii*m!89:>=?@BCGJHM%OVW^]`_bcegi%moprw{%~ *n!&"(*%,/%13258%:<>?ABD *x!OP%Tknruy *y!*/%TVW[]^b%dg%mp%~ *z!&"%5 *n!CEHIM%OQSVWY]\\`acdg%il%pruw%y| *o!+,*-2357%9=?@C6E%GJMOPS%UW%Z\\_`^bce%mpr%t; )s!uz *sf)y!Me[c~ )"A)y!{z )z!=3 *z!67%:<=?%[]%~ *{!&"(+%79;HMNPSWX^ )z!XVp +1m*{z){6)oz)p!",2= )k!46 )l)+/!prx%| +0!)(-/.01589;=%@BCEHLNQ%SUZ]%chdj )m!68:DAPGWX\\hUTgfzq )n!.31/62N?FLN[lfrnsy *J!BGP[]` *K!"KE.DQ_PiKULJdjO *F!+34/5 *Ek*FU*Ey*F6*E!zsl *F!EI:JLOKe}`gp\\ *G!"1 *F!vsky *G!(k *Fd*G!fgdIY5Wt%ACE%HJ%OQS%VX%[]_a%ikm%su%w *HV*G!qn *H!=" *Gl*H!;u-BE498GR2lnY )~0)}!`qt )~!)6 )}v)~!-,KabsgXc@eW]fo *&3)~|*&!.??A *(,)>D*(!AMLNRblmqw|}xu *)!.? *95)B?*1!N]dtpux} *2)*)O**u*+!KS *-:*.5+1!VR *Xs*Y!+;8X *X!gm *U!RT *c!w}{| *d8+(!;<%~ +)!&"%A *d!65D=CQRFHM`Ydaevw~ *e!()*0/5 *U`+0u*Y_*Z!9S *[!N^ *Y!\\fcak}~ *Z!(, *Y!uv *Z)*Ym*Z!:EF1GI2WKbkmz|q} *[!/5.*2&>67FG?ekqdiz *\\!+0,8: *V!*4<7JF )?=*N!PXTV +)!BC%~ +*!&"%H *N!_ei *O_*R<*u>*t!-73JELSRnqt *u)+3!loq%vx +2!CPR *s!VYZko )sM*s}*g!Ww *h8*iU*j(*k/*jM*l!Jw ++!369%< )zB++!?BD%FHINOR%WY%]_abd%kn%qx%{~ +,&+*!IJ%~ ++!&"%/12478=>@CGJ%MQ^`cmr%v|} +,!,-56;<"%+.%02%478:= *u!gp|} *v!&A=5DF *~!{z +&(+0O+&!"+-2163<=B +"!][_a`ei *yo*z!;\\ *{!*) +&!PRW`\\l^jy +"!"+7; +1![\\ *-.+0!y~ +1!&)0 *k;+1!;=y%{~} +2!".,*/4gqt~| +3!)*0 +,!>?%~ +-!&"%~ +.!&"%~ +/!&"%ntw~ +0!"+237DGI%KMPTV%Y\\efik%twxz%} +1!"(*%/2%:<>%CE%QS%UWXZ]%ac%fh%jnpqs%uwx| +2!&()+-0%35%=?BD%OQTVWY%[]_%dfh%prsu%{} +3!&(+%/1%;>%jmny%~ +4!&"%+/%14%8 .*f.+!Zv .,!oy .-!;<%>@BCGN%PRSV%X -R!XY%~ -S!&"%N")),t(e)});

打印相关的方法:

只要连接成功时,就直接传输数据给打印机。

methods: {    //打印    f_print(){        this.sfShowTc=false;        if (!Common.isExist(this.deviceId)) {          this.openBlueTooth();          this.isShowSearchBle=true;          return;        }else{            console.log("有历史设备信息直接连接");            this.createBLEConnectionWithDeviceId();        }      },    // 小程序在之前已有搜索过某个蓝牙设备,并成功建立连接,可直接传入之前搜索获取的 deviceId 直接尝试连接该设备      createBLEConnectionWithDeviceId(){          if (!Common.isExist(this.deviceId)) {            return          }          uni.openBluetoothAdapter({                success:(res) => {                console.log("初始化蓝牙设备模块:"+JSON.stringify(res));                if(res.errno==0){                    this.connectBlue(this.deviceId,this.deviceName,null);                }else{                    Common.alert_duration(res.errMsg);                }                },                fail: (res) => {                    console.log("初始化蓝牙设备模块失败:"+JSON.stringify(res));                    catchToast(res);                    wx.onBluetoothAdapterStateChange((res) => {                      console.log("onBluetoothAdapterStateChange", res)                      if (res.available) {                        // 取消监听                        wx.onBluetoothAdapterStateChange(() => {                        });                        this.connectBlue(this.deviceId,this.deviceName,null);                      }                    })                }          });      },      //初始化蓝牙      openBlueTooth(){          if (!wx.openBluetoothAdapter) {            wx.showModal({              title: "提示",              content: "当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。"            })            return          }          wx.openBluetoothAdapter({                success:(res) => {                console.log("初始化蓝牙设备模块:"+JSON.stringify(res));                if(res.errno==0){                    this.searchBlueToothDiscovery();                }else{                    Common.alert_duration(res.errMsg);                }                },                fail: (res) => {                    console.log("初始化蓝牙设备模块失败:"+JSON.stringify(res));                    catchToast(res);                    wx.onBluetoothAdapterStateChange((res) => {                      console.log("onBluetoothAdapterStateChange", res)                      if (res.available) {                        // 取消监听,否则stopBluetoothDevicesDiscovery后仍会继续触发onBluetoothAdapterStateChange,                        // 导致再次调用startBluetoothDevicesDiscovery                        wx.onBluetoothAdapterStateChange(() => {                        });                        this.searchBlueToothDiscovery();                      }                    })                }          });          wx.onBLEConnectionStateChange((res) => {            // 该方法回调中可以用于处理连接意外断开等异常情况            console.log("onBLEConnectionStateChange", `device ${res.deviceId} state has changed, connected: ${res.connected}`)            this.blueToothConnected = res.connected;            if (!res.connected) {              wx.showModal({                title: "提示",                content: "蓝牙连接已断开",                showCancel: false              });            }          });      },      //搜寻附近的蓝牙外围设备      searchBlueToothDiscovery(){          uni.startBluetoothDevicesDiscovery({            success:(res) => {                console.log("搜寻附近的蓝牙外围设备成功:"+JSON.stringify(res));                if(res.errno==0){                    wx.onBluetoothDeviceFound(this.onFoundDevice);                }else{                    catchToast(res);                }            },            fail: (res) => {                console.log("搜寻附近的蓝牙外围设备失败:"+JSON.stringify(res));                catchToast(res);            }          });      },      //监听寻找到新设备的事件      onBlueToothDeviceFound(){          uni.onBluetoothDeviceFound(function (devices) {            this.getBluetoothDevices();          })      },      //获取在蓝牙模块生效期间所有已发现的蓝牙设备。包括已经和本机处于连接状态的设备      getBluetoothDevices(){          uni.getBluetoothDevices({            success:(res) => {                console.log("获取附近的蓝牙外围设备成功:"+JSON.stringify(res));                if(res.errno==0){                    this.devices=this.getNewDevicesList(res.devices);                }else{                    catchToast(res);                }            },            fail: (res) => {                console.log("获取附近的蓝牙外围设备失败:"+JSON.stringify(res));                catchToast(res);            }          });      },      //获取设备列表      getNewDevicesList(devices) {          console.log("获取新的设备列表");          const ids = [];          //过滤无用蓝牙设备          const devs = devices              .filter((item) => !(item.RSSI > 0 || item.name.indexOf("T581")<0))              .map((item) => {                  ids.push(item.deviceId);                  return {                      rssi: item.RSSI,                      name: item.name,                      devId: item.deviceId,                      status:false                  };              });          //过滤重复          const filterId = [...new Set(ids)];          const newDevices = [];          while (filterId.length) {              const id = filterId.shift();              for (let index = 0; index < devs.length; index++) {                  const item = devs[index];                  if (item.devId === id) {                      newDevices.push(item);                      break;                  }              }          }          return newDevices;      },      onFoundDevice: debounce(function(devices) {          //获取搜寻的所有设备          console.log("获取搜寻的所有设备");          wxAsyncPromise("getBluetoothDevices").then((res) => {            this.devices=this.getNewDevicesList(res.devices);          });      }, 60),      onSearchDeviceFound(pro) {          pro.then((res) => {              console.log("初始化蓝牙成功");              return wxAsyncPromise("startBluetoothDevicesDiscovery").then((res) => {                  console.log("正在搜寻设备");                  wx.showLoading({                      title: "正在搜寻设备",                      mask: false,                  });              });          })        .then((res) => {            //监听寻找新设备            console.log("监听寻找新设备")            wx.onBluetoothDeviceFound(this.onFoundDevice);        })        .catch((res) => {            console.log("监听寻找新设备异常:"+JSON.stringify(res));            catchToast(res);            wx.setStorageSync(LAST_CONNECTED_DEVICE,"");            this.deviceId="";            this.deviceName = "";        })        .finally(() => {            setTimeout(() => {                wx.hideToast();            }, 300);            // this.stopSearchBlue();        });      },      //重新搜索蓝牙设备      searchBlue() {          wxAsyncPromise("closeBLEConnection", {              deviceId: this.deviceId,          }).finally((res) => {            this.devices=[];            this.deviceId="";            this.deviceName="";            this.blueToothConnected=false;              wx.offBluetoothDeviceFound(this.onFoundDevice);              //停止搜寻蓝牙              wxAsyncPromise("stopBluetoothDevicesDiscovery").then(() => {                  wx.getBluetoothAdapterState({                      success: (res) => {                          if (!res.available) {                              //未打开蓝牙                          } else if (!res.discovering) {                              //没有在搜寻蓝牙情况                              this.onSearchDeviceFound(Promise.resolve());                          }                      },                      fail: (res) => {                          if (res.errCode === 10000) {                              this.openBlueTooth();                          }                      },                  });              });          });      },      //停止搜索蓝牙设备      stopSearchBlue() {          console.log("停止搜索");          wxAsyncPromise("stopBluetoothDevicesDiscovery");      },      onBlueChange(e,index) {        const deviceId = e.devId;        // if (this.deviceId === deviceId) return;        this.deviceId="";        this.deviceName="";        this.blueToothConnected=false;        this.connectBlue(deviceId,e.name,index);      },      //连接蓝牙      connectBlue(deviceId,deviceName,index) {          wx.showLoading({              title: "正在连接"+deviceName,              mask: true,          });          wxAsyncPromise("createBLEConnection", { deviceId })              .then((res) => {                  return wxAsyncPromise("getBLEDeviceServices", { deviceId });              })              .then((res) => {                  console.log("获取蓝牙设备所有服务:"+JSON.stringify(res.services));                  this.services = res.services;                  getDeviceCharacteristics(                      deviceId,                      [...this.services],                      (character) => {                          console.log("成功获取到特征值:"+JSON.stringify(character));                          this.character = character;                          this.deviceName= deviceName;                          this.deviceId=deviceId;                          this.blueToothConnected=true;                          if(index!=null){                              this.devices[index].status=true;                          }                          wx.setStorageSync(LAST_CONNECTED_DEVICE, this.deviceId+"&"+this.deviceName);                          wx.hideLoading();                          wx.offBluetoothDeviceFound(this.onFoundDevice);                          wxAsyncPromise("stopBluetoothDevicesDiscovery");                          // 延时调用                          this.print();                      },                      (res) => {                          console.log("获取设备特征值失败");                          this.blueToothConnected=false;                          Common.alert_duration("获取设备特征值失败");                          wx.hideLoading();                          this.isShowSearchBle=true;                      },                  );              })              .catch((res) => {                  console.log("获取服务失败");                  this.blueToothConnected=false;                  Common.alert_duration("获取服务失败");                  wx.hideLoading();                  this.isShowSearchBle=true;              });      },      print: debounce(function() {          if (!Common.isExist(this.deviceId)) {                wx.showToast({                    title: "未连接任何蓝牙设备",                    icon: "none",                });                return;            }          wx.showLoading({              title: "正在打印中......",              mask: true,          });          const opt = { deviceId: this.deviceId, ...this.character };          let _queue=[printCommand.clear];          let line = GBK.encode("-------------------------------\r\n");          let spaceStr = " ";          let shName= this.blueToothPrintData.shName;          let addressPhone=this.blueToothPrintData.xfSellerAddressPhone;          _queue.push.apply(_queue, printCommand.center);          console.log("开始组装打印数据")          _queue.push.apply(_queue, printCommand.enter);          _queue.push.apply(_queue, GBK.encode(shName+"\n"));          _queue.push.apply(_queue, printCommand.enter);          _queue.push.apply(_queue, line);          _queue.push.apply(_queue, printCommand.left);          _queue.push.apply(_queue, GBK.encode("消费单号:"+this.blueToothPrintData.id+"\n"));          let currentTime1=Common.changeDateFormat(new Date().getTime(),true);          _queue.push.apply(_queue, GBK.encode("消费时间:"+currentTime1+"\r\n"));          _queue.push.apply(_queue, line);          _queue.push.apply(_queue, GBK.encode("商品名称        数量       金额"));          _queue.push.apply(_queue, printCommand.enter);          let spList = this.selectSpArr;          let sumSpNum=0;          for (var i = 0; i < spList.length; i++) {            let spmcSpaceNum = 16-Common.calculateStrLengh(spList[i].spmc);            let spItemStr=spList[i].spmc;            for (var j = 0; j < spmcSpaceNum; j++) {                spItemStr += spaceStr;            }            spItemStr += parseFloat(spList[i].spnum).toFixed(2);            let spnumSpaceNum = 6-Common.calculateStrLengh(parseFloat(spList[i].spnum).toFixed(2));            for (var k = 0; k < spnumSpaceNum; k++) {                spItemStr += spaceStr;            }            let spjeSpaceNum = 9-Common.calculateStrLengh(parseFloat(spList[i].spSumMoney).toFixed(2));            for (var l = 0; l < spjeSpaceNum; l++) {                spItemStr += spaceStr;            }            spItemStr += parseFloat(spList[i].spSumMoney).toFixed(2);            _queue.push.apply(_queue, GBK.encode(spItemStr));            _queue.push.apply(_queue, printCommand.enter);            sumSpNum = sumSpNum + spList[i].spnum;          }          _queue.push.apply(_queue, line);          _queue.push.apply(_queue, GBK.encode("消费总计:"+parseFloat(sumSpNum).toFixed(2)+"\r\n"));          _queue.push.apply(_queue, GBK.encode("消费金额:"+parseFloat(this.blueToothPrintData.spjsHj).toFixed(2)+"\r\n"));          _queue.push.apply(_queue, GBK.encode("实付金额:"+parseFloat(this.blueToothPrintData.actualAmount).toFixed(2)+"\r\n"));          _queue.push.apply(_queue, line);          _queue.push.apply(_queue, printCommand.enter);          _queue.push.apply(_queue, GBK.encode("地址:"+this.blueToothPrintData.shdz+"\n"));          _queue.push.apply(_queue, printCommand.enter);          _queue.push.apply(_queue, GBK.encode("联系电话:"+this.userInfo.username+"\n"));          _queue.push.apply(_queue, printCommand.enter);          _queue.push.apply(_queue, printCommand.center);          _queue.push.apply(_queue, GBK.encode("扫码开票\n"));          _queue.push.apply(_queue, [29, 119, 6]);//放大6倍二维码          let qrContent="https://ksp.tbzhchina.com/invoiceadd?orderId=" + this.blueToothPrintData.id + "&sfJyToken=false";          let qrCode1 = [29, 107, 97, 5, 1, new Uint8Array(GBK.encode(qrContent)).buffer.byteLength, 0];          _queue.push.apply(_queue, qrCode1);          _queue.push.apply(_queue, GBK.encode(qrContent));          _queue.push.apply(_queue, GBK.encode("感谢您的惠顾"));          _queue.push.apply(_queue, printCommand.enter);          _queue.push.apply(_queue, line);          _queue.push.apply(_queue, GBK.encode("快闪票\r\n"));          _queue.push.apply(_queue, printCommand.enter);          _queue.push.apply(_queue, GBK.encode("请在三日内开具发票,谢谢!\r\n"));          _queue.push.apply(_queue, printCommand.enter);          _queue.push.apply(_queue, printCommand.enter);          _queue.push.apply(_queue, GBK.encode("                               \r\n"));          _queue.push.apply(_queue, line);          console.log("开始发送打印数据")          sendDataToDevice({               ...opt,               value: new Uint8Array(_queue).buffer,               lasterSuccess: () => {                 wx.hideLoading();                 console.log("打印完成!")                 this.orderId="";                 this.spjeHj=0;                 this.spseHj=0;                 this.sumSpMoney="0";                 this.selectSpArr = [];                 this.isShowSearchBle=false;                 Common.alert("打印成功!");             },          });      }, 60),}

注意点:getNewDevicesList 方法中,此处我们用的设备名称是 T581 开头的,所以我用名称过滤无用的设备信息,第二步根据设备ID过滤重复设备。

最后页面样式部分如下:

附带页面弹框效果图如下,第一个为没有可用设备时,第二个是有可用设备时:

如有问题,还请各位大佬多多指教,我是一名做后端 Java开发的程序猿,前端难免会有疏漏的部分。另外,有吉他或者民谣爱好者,可以关注我的微信公众号@民谣嗑学家,可以微信搜索:民谣嗑学家,帮我点个免费的关注,我是一名爱生活,爱代码,爱交朋友的热血青年,希望我们能够共同进步,共同成长。

目录

​​1.基础开发环境,所用到的 Api 以及实现的思路。​​

​​应用场景:​​

​​HbuilderX开发工具:​​

​​关于蓝牙设备的api地址:​​

​​大致实现思路:​​

​​1.在页面加载时,先判断本地缓存是否有历史连接蓝牙设备。​​

​​2.页面点击【打印】按钮时,最好是提前把此次需要打印的数据项先组装好。​​

​​3.点击打印后,先判断当前页面是否有历史连接蓝牙设备的信息:​​

​​4.页面卸载时或者页面隐藏时,需要取消蓝牙连接并关闭蓝牙模块。​​

​​2.接下来直接上代码,以及依赖的js和弹框组件​​

​​弹框用的uni-popup 基础组件,如有需要,可以私信找我要。​​

​​页面蓝牙列表的视图view代码如下:​​

​​页面引入弹框组件和相关的蓝牙打印的js文件:​​

​​此处我只列出,当前与蓝牙打印相关的js文件:​​

​​1.​​

​​2.​​

​​打印相关的方法:​​


标签: 二进制数 外围设备

上一篇:kubernetes简单使用教程(一)
下一篇:【环球播资讯】Redis-6.2.6 Linux 离线安装教程,让你一路畅通无阻,5分钟轻松完成安装。