商家打印小票,小票包含顾客消费的商品明细信息以及末尾附上二维码,二维码供顾客扫码开票。
HBuilderX-高效极客技巧HBuilderX是当前最快的HTML开发工具,强大的代码助手帮你快速完成开发,最全的语法库和浏览器兼容性数据让浏览器碎片化不再头痛
(资料图片)
https://www.dcloud.io/hbuilderx.html注:项目是基于VUE框架开发的。uni-app官网
https://uniapp.dcloud.io/api/system/bluetooth.html如果有:直接连接蓝牙设备,进行打印;
如果没有:则以弹框的形式,搜索附近可用的蓝牙设备;
注:我这里是在当前页面弹框形式展示的蓝牙列表,大家可以根据自己实际项目需求来做出一些调整。
{{item.name?item.name:"未命名"}} ({{Math.max(0, item.rssi + 100)}}%) {{item.status?"已连接":"未连接"}} ~ 无可搜索到的设备 ~
注: 当页面没有可用的蓝牙设备时,有一个背景图片,当然这个只是一个样式,不是关键的,但我还是尽量做到完善和完整度。empty-icon.png图片如下:有水印,如有需要原图,可以私信我,我会及时回复的。
<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>
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}
分别是蓝牙设备相关的方法和中文编码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;n BDGHO%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:@BDEHJLNPS%Z\\]`%bdi%lorsu%wz%~ )@!&"(*,-34;%>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;@C%FHJ%LNPVZ]^b%ehn%qt%vy{%} )T!&"-%/1249;<>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@BFJ%NQZ[^_c%ejopr%uw{}~ )n!"(*%,047%:=>@%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))Tg*>2*6@++w(r6*X[(TO(dw*?!./%24%7;>@%GIKLN%QS%_abdf%hlmoqstv%y|} *@!&"*,-/%469:%CE%GI%SUVX%Z (WY)>^)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! *V!`a%~ *W!&"%f (X!A9 (W!mx (X!XVskeq`] (Y3(X^(Y5(Xv(Y!oqsNcleZh (Z!(Y| (Y|(Z!\\?^].L @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!GM B: )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!&"(*%@BCEGHJNOQ%SU%]`b%fh%oq%uwy%{~ *v!"()+%/ *7:*6x)O!6IKUXk *y!XYZ\\`a )Pl),})P{*Nm)?2+3=)?!5^hfenptx )^![wqx )_!=B9-3jL )`!9@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!"K E.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.
打印相关的方法: