From 057858a1321059d06cbf1f7f96253417bb5a6aba Mon Sep 17 00:00:00 2001 From: liub Date: Wed, 27 Aug 2025 10:32:13 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A0=E4=BA=86=E5=87=A0=E4=B8=AA=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- utils/BleHelper.js | 1274 +++++++++++++++++++++++++++++++++++++++++++ utils/BleReceive.js | 411 ++++++++++++++ utils/gbk.js | 122 +++++ utils/loading.js | 44 ++ 新建 文本文档.txt | 1 - 5 files changed, 1851 insertions(+), 1 deletion(-) create mode 100644 utils/BleHelper.js create mode 100644 utils/BleReceive.js create mode 100644 utils/gbk.js create mode 100644 utils/loading.js delete mode 100644 新建 文本文档.txt diff --git a/utils/BleHelper.js b/utils/BleHelper.js new file mode 100644 index 0000000..56c9523 --- /dev/null +++ b/utils/BleHelper.js @@ -0,0 +1,1274 @@ +import receivTool from "@/utils/BleReceive.js" +var recei = null; +class BleHelper { + constructor() { + this.StorageKey = "linkedDevices"; + recei = receivTool.getBleReceive(); + + this.init(); + } + init() { + let key = this.StorageKey; + var store = uni.getStorageInfoSync(); + var f = store.keys.find(function(v) { + return v == key; + }); + var linkedDevices = []; + if (f) { + linkedDevices = uni.getStorageSync(key); + } + if (linkedDevices && linkedDevices.length && linkedDevices.length > 0) { + console.log("111111", linkedDevices); + linkedDevices.filter((v) => { + v.Linked = false; + v.notifyState = false; + }); + } + + setTimeout(() => { + this.linkAllDevices(); + }, 10); + this.data = { + isOpenBlue: false, //蓝牙模块是否开启 + available: false, //蓝牙状态是否可用 + discovering: false, //蓝牙是否正在搜索 + searchList: [], //已搜索到的设备列表, + isSubscribe: false, //是否开启了订阅 + LinkedList: linkedDevices, //已连接的设备列表 + } + this.cfg = { + onDeviceFound: [], //发现新设备的事件 + receivDataCallback: [] //接收到数据的事件 + } + this.addReceiveCallback((a,b,c)=>{ + recei.ReceiveData(a,b,c); + }, "BleReceiveData"); + this.dic = { + errRemarks: [{ + key: '10000', + remark: '未初始化蓝牙适配器' + }, + { + key: '10001', + remark: '当前蓝牙适配器不可用' + }, + { + key: '10002', + remark: '没有找到指定设备' + }, + { + key: '10003', + remark: '蓝牙设备连接失败' + }, + { + key: '10004', + remark: '蓝牙设备没有找到指定服务' + }, + { + key: '10005', + remark: '蓝牙设备没有找到指定特征值' + }, + { + key: '10006', + remark: '蓝牙连接已断开' + }, + { + key: '10007', + remark: '蓝牙设备当前特征值不支持此操作' + }, + { + key: '10008', + remark: '其余所有系统上报的异常' + }, + { + key: '10009', + remark: '蓝牙设备不支持BLE低功耗蓝牙' + }, + { + key: '10010', + remark: '已连接' + }, + { + key: '10011', + remark: '蓝牙设备配对设备需要配对码' + }, + { + key: '10012', + remark: '蓝牙设备连接超时' + }, + { + key: '10013', + remark: '连接 deviceId 为空或者是格式不正确' + } + ] + } + } + + linkAllDevices() { + console.log("模块启动时,自动连接已连接过的设备", this.data.LinkedList); + + if (this.data.LinkedList && this.data.LinkedList.length > 0) { + for (var i = 0; i < this.data.LinkedList.length; i++) { + let device = this.data.LinkedList[i]; + console.log("自动连接:" + device.deviceId); + this.LinkBlue(device.deviceId, device.writeServiceId, device.wirteCharactId, device + .notifyCharactId); + } + } else { + console.log("无设备连接"); + } + } + + getCurrentPagePath() { + + const pages = getCurrentPages(); + + if (pages.length === 0) { + return ""; + } + + const currentPage = pages[pages.length - 1]; + console.log("currentPage=", currentPage.route); + return currentPage.route; + } + + //设置发现新设备的回调 + addDeviceFound(callback, currKey) { + let key = currKey ? currKey : this.getCurrentPagePath(); + if (!key) { + key = new Date().getTime(); + } + if (key) { + console.log("key=" + key); + let f = this.cfg.onDeviceFound.findIndex((v) => { + return v.key == key; + }); + if (f > -1) { + this.cfg.onDeviceFound[f].callback = callback; + return; + } + this.cfg.onDeviceFound.push({ + key: key, + callback: callback + }); + } + } + + //移除发现新设备的回调 + removeDeviceFound(currKey) { + let key = currKey ? currKey : this.getCurrentPagePath(); + + if (key) { + console.log("key=" + key); + let f = this.cfg.onDeviceFound.findIndex((v) => { + return v.key == key; + }); + if (f > -1) { + this.cfg.onDeviceFound.splice(f, 1); + } + } else { + if (this.cfg.onDeviceFound.length > 0) { + this.cfg.onDeviceFound.splice(this.cfg.onDeviceFound.length - 1, 1); + } + + } + } + + //添加接收到数据的回调 + addReceiveCallback(callback, currKey) { + let key = currKey ? currKey : this.getCurrentPagePath(); + if (!key) { + key = new Date().getTime(); + } + if (key) { + console.log("订阅消息回调,key=" + key); + let f = this.cfg.receivDataCallback.findIndex((v) => { + return v.key == key; + }); + if (f > -1) { + this.cfg.receivDataCallback.callback = callback; + return; + } + this.cfg.receivDataCallback.push({ + key: key, + callback: callback + }); + } + } + + + + //设置接收到数据的回调 + removeReceiveCallback(currKey) { + let key = currKey ? currKey : this.getCurrentPagePath(); + if (key) { + console.log("移除消息回调:" + key); + let f = this.cfg.receivDataCallback.findIndex((v) => { + return v.key == key; + }); + + if (f > -1) { + this.cfg.receivDataCallback.splice(f, 1); + } + + } else { + if (this.cfg.receivDataCallback.length > 0) { + this.cfg.receivDataCallback.splice(this.cfg.receivDataCallback.length - 1, 1); + } + } + } + + + + getError(ex) { + let code = ex.code; + ex.msg = "未知的异常" + let f = this.dic.errRemarks.find((v) => { + return v.key == code; + }); + if (f) { + ex.msg = f.remark; + } + return ex; + } + //引导到设置中打开蓝牙 + showBlueSetting(showTip) { + let platform = process.env.UNI_PLATFORM; + + + var openBlueSetting = function() { + // 判断平台类型 + if (platform === 'mp-weixin') { + uni.openSetting(); + } else if (platform === 'app-plus' || platform === 'app') { + //---------------------------------------------------------------- + const osName = plus.os.name; + + if (osName === 'iOS') { + // iOS 平台打开蓝牙设置 + plus.runtime.openURL('App-Prefs:root=Bluetooth', function() { + ////console.log('成功打开蓝牙设置'); + }, function(err) { + console.error('打开蓝牙设置失败:' + err.message); + uni.showModal({ + title: '提示', + content: '无法自动打开蓝牙设置,请手动前往设置 > 蓝牙 进行操作。', + showCancel: false + }); + }); + } else if (osName === 'Android') { + // Android 平台打开蓝牙设置 + try { + const Intent = plus.android.importClass('android.content.Intent'); + const Settings = plus.android.importClass('android.provider.Settings'); + const main = plus.android.runtimeMainActivity(); + const intent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS); + main.startActivity(intent); + } catch (e) { + console.error('打开蓝牙设置失败:' + e.message); + // 尝试使用通用设置页面作为备选方案 + plus.runtime.openURL('settings://', function() { + ////console.log('打开系统设置成功,请手动找到蓝牙选项'); + }, function() { + uni.showModal({ + title: '提示', + content: '无法自动打开蓝牙设置,请手动前往设置页面开启蓝牙。', + showCancel: false + }); + }); + } + } else { + uni.showModal({ + title: '提示', + content: '当前系统不支持自动打开蓝牙设置,请手动操作。', + showCancel: false + }); + } + + + //-------------------------------------------------------------------- + } else if (platform === 'mp-alipay') { + uni.openSetting(); + } + + } + + if (showTip !== undefined) { + openBlueSetting(); + return; + } + if (platform === 'mp-weixin' || platform === 'app-plus' || + platform === 'mp-alipay' || platform === 'app') { + uni.showModal({ + title: '蓝牙未开启', + content: '请在系统设置中打开蓝牙以使用此功能', + success: (res) => { + if (res.confirm) { + openBlueSetting(); + } + } + }); + } else { + ////console.log("当前平台不支持打开系统设置" + platform); + } + + + } + + //获取蓝牙适配器状态 + CheckBlue() { + + return new Promise((resolve, reject) => { + if (this.data.available) { + resolve({ + available: this.data.available, + discovering: this.data.discovering + }); + return; + } + if (!this.data.isOpenBlue) { + resolve({ + available: false, + discovering: false + }); + return; + } + uni.getBluetoothAdapterState({ + success: (info) => { + this.data.available=info.available; + this.data.discovering=info.discovering; + resolve(info); + }, + fail: (ex1) => { + ////console.log("ex1", ex1); + let res1 = { + available: false, + discovering: false + } + resolve(res1); + } + }); + }); + } + //打开蓝牙适配器 + OpenBlue() { + + var init = () => { + return new Promise((resolve, reject) => { + if (this.data.isOpenBlue) { + console.log("蓝牙状态已可用,提前返回"); + resolve(false); + return; + } + + uni.openBluetoothAdapter({ + success: (args) => { + console.log("蓝牙初始化成功:" + JSON.stringify(args)); + this.data.isOpenBlue = true; + this.data.available=true; + resolve(true); + + if (this.data.isSubscribe) { //整个App生命周期,只订阅一次 + return; + } + console.log("开始订阅各类变化消息"); + this.data.isSubscribe = true; + + + var bytesToHexString = (bytes) => { + return bytes.map(byte => byte.toString(16).padStart(2, + '0')); + } + + uni.onBluetoothAdapterStateChange((state) => { + console.log('蓝牙状态发生变化:' + JSON.stringify(state)); + this.data.available = state.available; + this.data.discovering = state.discovering; + if (this.data.available && this.data + .isOpenBlue) { //蓝牙状态再次可用,重连所有设备 + + + } + + if (!state.available) { //蓝牙状态不可用了,将所有设备标记为断开连接 + this.data.LinkedList.filter((v) => { + v.Linked = false; + v.notifyState = false; + return true; + }); + uni.setStorageSync(this.StorageKey, this.data + .LinkedList); + } + }); + + uni.onBLEConnectionStateChange((res) => { + + if (!res.connected) { + + console.log("蓝牙连接已断开", res); + this.data.LinkedList.find((v) => { + if (v.deviceId == res.deviceId) { + v.Linked = false; + v.notifyState = false; + return true; + } + return false; + }); + uni.setStorageSync(this.StorageKey, this.data + .LinkedList); + } else { + console.log("蓝牙连接已恢复,", res); + } + }); + + uni.onBluetoothDeviceFound((devices) => { + // ////console.log("发现新设备:" + JSON.stringify(devices)); + this.data.searchList = this.data.searchList.concat( + devices); + if (this.cfg.onDeviceFound) { + if (this.cfg.onDeviceFound.length > 0) { + this.cfg.onDeviceFound.forEach((found) => { + found.callback(devices); + }); + } + + } + }); + + uni.onBLECharacteristicValueChange((receive) => { + //订阅消息 + console.log("收到订阅消息",receive); + let f=this.data.LinkedList.find((v) => { + return v.deviceId == receive.deviceId; + }) + let dataView = new DataView(receive.value); + + // 转换为字节数组 + let bytes = []; + for (let i = 0; i < dataView.byteLength; i++) { + bytes.push(dataView.getUint8(i)); + } + + let hexs = bytesToHexString(bytes); + let str = ''; + try { + let uint8Array = new Uint8Array(receive.value); + + for (let i = 0; i < uint8Array.length; i++) { + // 将每个字节转换为对应的字符 + str += String.fromCharCode(uint8Array[i]); + } + let header = "mac address:"; + if (str.indexOf(header) == 0) { //650以文本传输mac + + this.data.LinkedList.find((v) => { + if (v.deviceId == receive + .deviceId) { + v.macAddress = str.replace( + header, ""); + // console.log("收到mac地址:", str) + } + }); + uni.setStorageSync(this.StorageKey, this.data + .LinkedList); + } + + if (bytes[0] == 0xFC) { //6155以0xFC开头代表mac地址 + // console.log("收到mac地址:", bytes) + if (bytes.length >= 7) { + let mac = hexs.slice(1, 7).join(":") + .toUpperCase(); + this.data.LinkedList.find((v) => { + if (v.deviceId == receive + .deviceId) { + v.macAddress = mac; + // console.log("收到mac地址:", str) + } + }); + uni.setStorageSync(this.StorageKey, this + .data.LinkedList); + } + } + } catch (ex) { + ////console.log("将数据转文本失败", ex); + } + let recData = { + deviceId: receive.deviceId, + serviceId: receive.serviceId, + characteristicId: receive.characteristicId, + bytes: bytes, + str: str, + hexs: hexs + }; + console.log("监听到特征值:" + JSON.stringify(recData)); + if (this.cfg.receivDataCallback) { + + if (this.cfg.receivDataCallback.length > 0) { + + let path = this.getCurrentPagePath(); + + this.cfg.receivDataCallback.forEach((rec) => { + + if (rec.callback) { + rec.callback(recData, f, path); + } + }) + } + } else { + console.log("无人订阅receivDataCallback,不处理数据"); + } + }); + + + }, + fail: (ex2) => { + console.log("蓝牙模块启动失败", ex2); + reject(this.getError(ex2)); + } + }); + }); + } + + return new Promise((resolve, reject) => { + if (this.data.isOpenBlue) { + resolve(); + return; + } + + this.CheckBlue().then((res) => { + console.log("res=", res) + return init(); + }).then(resolve).catch((ex) => { + console.log("22222"); + reject(ex); + }); + + + }); + } + //关闭蓝牙适配器 + CloseBlue() { + return new Promise((resolve, reject) => { + this.data.available = false; + this.data.discovering = false; + this.data.isOpenBlue = false; + this.data.searchList = []; + + this.StopSearch(); + + this.disconnectDevice(); + + uni.closeBluetoothAdapter({ + success: () => { + console.log("蓝牙模块已关闭"); + this.data.isOpenBlue = false; + }, + fail: (ex) => { + ex = this.getError(ex); + ////console.log(msg); + }, + complete: () => { + resolve(); + } + }); + + }); + + } + + //开始搜索新设备 + StartSearch() { + this.data.searchList = []; + var these = this; + //开始搜索 + var Search = () => { + console.log("Search........"); + return new Promise((resolve, reject) => { + uni.startBluetoothDevicesDiscovery({ + services: [], + allowDuplicatesKey: true, + success: (res) => { + //console.log('开始搜索蓝牙设备成功'); + resolve(res); + + }, + fail: (err) => { + console.log(`搜索蓝牙设备失败:`, err); + + reject(this.getError(err)); + } + }); + }); + + + } + + + + return this.OpenBlue().then((res) => { + console.log("蓝牙适配器状态", res) + + return Search(); + }); + + + } + //停止搜索 + StopSearch() { + return new Promise((resolve, reject) => { + uni.stopBluetoothDevicesDiscovery({ + success: (res) => { + //console.log("停止搜索蓝牙设备成功"); + resolve(); + }, + fail: (ex) => { + //console.log("无法停止蓝牙搜索"); + + reject(this.getError(ex)); + } + }); + }) + + } + //获取已连接的设备 + getLinkBlue(callback) { + return new Promise((resolve, reject) => { + uni.getConnectedBluetoothDevices({ + success: (res) => { + + resolve(res); + + + }, + fail: (ex) => { + ////console.log("获取已连接设备异常", ex); + + reject({ + devices: [] + }); + + } + }); + }); + + + } + + //订阅消息 + subScribe(deviceId, state) { + return new Promise((resolve, reject) => { + setTimeout(() => { + let c = this.data.LinkedList.find((v) => { + return v.deviceId == deviceId; + }); + if (state) { + if (c.notifyState) { + resolve(); + return; + } + } + console.log("c=", c); + let startSubScribe = (id, serviceId, characteristicId) => { + + return new Promise((succ, err) => { + uni.notifyBLECharacteristicValueChange({ + deviceId: id, + serviceId: serviceId, + characteristicId: characteristicId, + state: state, + success: (res) => { + if (state) { + console.log("订阅消息成功", res); + } else { + console.log("取消订阅成功", res); + } + + this.data.LinkedList.find((v) => { + if (v.deviceId == deviceId) { + v.notifyState = state; + } + }); + succ(); + }, + fail: (ex) => { + + err(this.getError(ex)); + } + }); + }); + } + let promies = new Array(); + for (var i = 0; i < c.Characteristics.length; i++) { + let item = c.Characteristics[i]; + let serviceId = item.serviceId; + let characteristicId = item.uuid; + + if (item.properties.notify) { + promies.push(startSubScribe(deviceId, serviceId, characteristicId)); + } + } + if (promies.length > 0) { + Promise.allSettled(promies).then((results) => { + + results.forEach((result, index) => { + if (result.status === "fulfilled") { + //console.log(`操作${index + 1}成功:`, result.value); + } else { + // console.log(`操作${index + 1}失败:`, result.reason + // .message); + } + }); + + resolve(); + }).catch((ex) => { + reject(ex); + }); + } else { + resolve(); + } + + + + }, 20); + + }); + } + + //获取服务 + getService(id, targetServiceId, writeCharId, notifyCharId) { + + if (!targetServiceId) { + targetServiceId = "xx"; // "FFE0"; + } + return new Promise((resolve, reject) => { + var repeatCnt = 0; + var startgetService = () => { + + uni.getBLEDeviceServices({ + deviceId: id, + success: (res) => { + if (res.services && res.services.length > 0) { + console.log("获取到服务:" + JSON.stringify(res)); + + this.data.LinkedList.find((v) => { + if (v.deviceId == id) { + v.services = res.services; + } + }); + uni.setStorageSync(this.StorageKey, + this.data.LinkedList); + + var promises = []; + let se = res.services.find((v) => { + return v.uuid.indexOf(targetServiceId) > -1; + }); + if (se) { + promises.push(this.getFeatrus(id, se.uuid, writeCharId, + notifyCharId)); + } else { + for (var i = 0; i < res.services.length; i++) { + let service = res.services[i]; + promises.push(this.getFeatrus(id, service.uuid, + writeCharId, notifyCharId)); + } + } + + + if (promises.length == 0) { + console.log("未找到主服务"); + reject({ + msg: "未找到主服务", + code: -1 + }); + } else { + + Promise.all(promises) + .then(results => { + + return this.subScribe(id, true); + }) + .then((res) => { + console.log('设备连接成功,初始化完成', res); + console.log("LinkedList=", this.data + .LinkedList); + resolve(); + }) + .catch(error => { + console.log("errrr", error); + reject(error); + }); + } + + + + } else { + repeatCnt++; + if (repeatCnt > 5) { + + reject({ + msg: "获取设备服务失败", + code: -1 + }); + + return; + } + setTimeout(function() { + startgetService(id); + }, 100); + } + }, + fail: (ex) => { + reject(this.getError(ex)); + } + }) + } + + setTimeout(function() { + startgetService(id); + }, 100); + }); + } + + //获取特征 + getFeatrus(id, serviceId, writeCharId, notifyCharId) { + var promise = new Promise((resolve, reject) => { + uni.getBLEDeviceCharacteristics({ + deviceId: id, + serviceId: serviceId, + success: (res) => { + console.log("获取到特征:" + JSON.stringify(res)); + res.characteristics.forEach((v) => { + v.serviceId = serviceId; + }); + //写特征 + let writeChar = res.characteristics.find(char => { + return char.uuid.indexOf(writeCharId) > -1 + }); + if (!writeChar) { + writeChar = res.characteristics.find(char => { + return char.properties.write; + }); + } + //通知特征 + let notiChar = res.characteristics.find(char => { + return char.uuid.indexOf(notifyCharId) > -1; + }); + if (!notiChar) { + notiChar = res.characteristics.find(char => { + return char.properties.notify; + }); + } + this.data.LinkedList.find(function(v) { + if (v.deviceId == id) { + if (!v.Characteristics) { + v.Characteristics = []; + } + + for (var i = 0; i < res.characteristics.length; i++) { + let item = res.characteristics[i]; + let flag = false; + for (var j = 0; j < v.Characteristics.length; j++) { + let charact = v.Characteristics[j]; + if (charact.uuid == item.uuid) { + flag = true; + break; + } + } + if (!flag) { + v.Characteristics.push(item); + } + } + + + if (writeChar) { + v.writeServiceId = serviceId; + v.wirteCharactId = writeChar.uuid; + } + + if (notiChar) { + v.notifyServiceid = serviceId; + v.notifyCharactId = notiChar.uuid; + } + return true; + } + return false; + }); + + + + uni.setStorageSync(this.StorageKey, this.data.LinkedList); + resolve(res); + }, + fail: (ex) => { + ////console.log("获取特征出现异常:" + JSON.stringify(ex)); + reject(ex); + } + }); + }); + return promise; + } + + //连接某个设备 + LinkBlue(deviceId, targetServiceId, writeCharId, notifyCharId) { + + + if (!writeCharId) { + writeCharId = "xxxx"; // "FFE1"; + } + if (!notifyCharId) { + notifyCharId = "xxxxx"; // "FFE2"; + } + + + + let fIndex = -1; + let f = this.data.LinkedList.find((v, index) => { + if (v.deviceId == deviceId) { + fIndex = index; + return true; + } + return false; + }); + ////console.log("findex=" + fIndex); + + var these = this; + + + //连接设备 + var linkDevice = () => { + return new Promise((resolve, reject) => { + + if (fIndex > -1 && f?.Linked) { + console.log("当前已连接,跳过其他步骤") + resolve(false); + return; + } + if(!this.data.available){ + reject(this.getError({code:10001})); + return; + } + console.log("正在连接" + deviceId); + uni.createBLEConnection({ + deviceId: deviceId, + timeout: 3000, + success: (info) => { + + console.log("新连接成功", this.data.LinkedList); + this.getLinkBlue().then((arr) => { + let cr = arr.devices.find(c => { + if (c.deviceId == deviceId) { + c.Linked = true; + return true; + } + return false; + + }); + if (fIndex > -1) { + this.data.LinkedList[fIndex].Linked = true; + } else { + this.data.LinkedList.push(cr); + } + + uni.setStorageSync(this.StorageKey, this.data + .LinkedList); + + let os = plus.os.name; + if (os == 'android') { + uni.setBLEMTU({ + deviceId: deviceId, + mtu: 512, + success: (mtu) => { + + ////console.log("mtu设置成功"); + }, + fail: () => { + ////console.log("mtu设置失败") + } + }); + } + + resolve(true); + + }).catch((ex) => { + + reject(this.getError(ex)); + }); + + + }, + fail: (ex) => { + ex = this.getError(ex) + console.log("蓝牙连接失败" + JSON.stringify(ex)); + reject(ex); + } + }); + }); + } + + + + + return new Promise((resolve, reject) => { + this.OpenBlue().then((res) => { + ////console.log("2222222"); + return linkDevice(deviceId); + }).then((res) => { + ////console.log("11111111"); + if (res) { //新连接 + if (fIndex == -1) { + console.log("开始获取服务", targetServiceId) + return this.getService(deviceId, targetServiceId, writeCharId, + notifyCharId); //获取服务 + } else { + if (!f.notifyState) { + this.subScribe(deviceId, true); + } + return Promise.resolve(true); + } + } else { //已连接过,直接订阅消息 + if (fIndex > -1 && f && !f.notifyState) { + + this.subScribe(deviceId, true); + } + return Promise.resolve(true); //已连接过的设备无需获取服务 + } + + }).then(() => { + + + setTimeout(() => { + resolve(); + }, 500); + }).catch((ex) => { + ////console.log("出现异常", ex); + reject(ex); + }); + }); + + + } + //断开连接 + disconnectDevice(deviceId) { + var disconnect = (id) => { + return new Promise((resolve, reject) => { + uni.closeBLEConnection({ + deviceId: id, + success: (res) => { + ////console.log("蓝牙连接已断开:" + id); + this.subScribe(id, false); + resolve(); + }, + fail: (ex) => { + + reject(this.getError(ex)); + } + }); + }); + + } + if (deviceId) { + disconnect(deviceId); + + this.data.LinkedList.find(v => { + if (v.deviceId === deviceId) { + return true; + } + return false; + }); + + return; + } + + if (this.data.LinkedList.length > 0) { + let arr = []; + for (var i = 0; i < this.data.LinkedList.length; i++) { + let item = this.data.LinkedList[i]; + if (item.Linked) { + arr.push(disconnect(item.deviceId)); + } + + } + if (arr.length == 0) { + return; + } + + Promise.allSettled(arr).then((results) => { + results.forEach((result, index) => { + if (result.status === 'fulfilled') { + ////console.log(`第${index + 1}个Promise成功:`, result.value); + } else { + ////console.log(`第${index + 1}个Promise失败:`, result.reason.message); + } + }); + }); + + } else { + ////console.log("无已连接设备"); + } + } + + //向蓝牙设备发送数据,如果没连接将自动连接后再发 + sendData(deviceid, buffer, writeServiceId, wirteCharactId, ms) { + + // console.log("deviceid=" + deviceid + ",writeServiceId=" + writeServiceId + ",wirteCharactId=" + + // wirteCharactId + ",timeout=" + ms) + if (ms === undefined) { + ms = 50; + } + let c = this.data.LinkedList.find((v) => { + return v.deviceId == deviceid; + }); + let device = { + deviceId: deviceid, + writeServiceId: null, + wirteCharactId: null + }; + if (writeServiceId) { + device.writeServiceId = writeServiceId; + } + if (wirteCharactId) { + device.wirteCharactId = wirteCharactId; + } + if (!device.writeServiceId || !device.wirteCharactId) { + //console.log("从已连接中找设备") + if (c) { + device = c; + } + } + + + + let sendBuffer = () => { + + return new Promise(async (resolve, reject) => { + + var promise = new Promise((succ, err) => { + if (!c) { + err(this.getError({ + code: 10002 + })); //没有找到指定设备 + return; + } + // console.log("device=", device); + uni.writeBLECharacteristicValue({ + deviceId: device.deviceId, + serviceId: device.writeServiceId, + characteristicId: device.wirteCharactId, + value: buffer, + success: () => { + console.log("发送数据成功"); + succ(); + }, + fail: (ex) => { + ex = this.getError(ex); + console.log("发送数据失败", ex); + + err(ex); + } + }); + }); + if (plus.os.name == 'iOS') { + + function timeOut(ms) { + return new Promise((_, err) => { + setTimeout(() => { + err({ + code: -1, + errMsg: '超时了' + }) + }, ms); + }); + } + + Promise.race([timeOut(ms), promise]).then(resolve).catch((ex) => { + console.log("ex=", ex); + if (ex.code == -1) { + resolve(ex); + } else { + reject(ex); + } + + }).finally(() => { + //console.log("完成了") + }); + } else { + promise.then(resolve).catch(reject); + } + }); + + } + if (c.Linked) { + // console.log("蓝牙已连接,直接发送"); + return sendBuffer(); + } else { + // console.log("先连接蓝牙再发送"); + return new Promise((resolve, reject) => { + let f = this.data.LinkedList.find((v) => { + return v.deviceId == deviceid; + }); + if (!f) { + reject({ + code: '-9', + msg: '蓝牙未连接过此设备,请重新使用蓝牙添加该设备' + }); + retrn; + } + this.LinkBlue(f.deviceId, f.writeServiceId, f.wirteCharactId, f.notifyCharactId).then(( + res) => { + console.log("连接成功"); + return sendBuffer(); + }).then(() => { + //console.log("发送成功") + resolve(); + }).catch((ex) => { + //console.log("出现异常", ex); + reject(this.getError(ex)); + }); + }); + } + + + } + + //将点阵数据转换成RGB565 + convertToRGB565(pixels, type) { + if (!type) { + type = 'rgb'; + } + const result = new Uint16Array(pixels.length / 4); + let index = 0; + for (let i = 0; i < pixels.length; i += 4) { + let r = pixels[i]; + let g = pixels[i + 1]; + let b = pixels[i + 2]; + let a = pixels[i + 3]; + + if (type == 'bgr') { + result[index++] = (b >> 3) | ((g & 0xFC) << 3) | ((r & 0xF8) << 8); + } else { + result[index++] = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3); + } + + } + + return result; + } + + setBleData() { + uni.setStorageSync(this.StorageKey, this.data.LinkedList); + } +} + +let instance = null; +export default { + getBleTool: function(found, receive) { + if (!instance) { + instance = new BleHelper(); + + } else { + ////console.log("调用现有实例"); + } + + return instance; + } +} \ No newline at end of file diff --git a/utils/BleReceive.js b/utils/BleReceive.js new file mode 100644 index 0000000..c3b11ae --- /dev/null +++ b/utils/BleReceive.js @@ -0,0 +1,411 @@ +class BleReceive { + constructor() { + this.StorageKey = "linkedDevices"; + + } + + getCurrentPagePath() { + + const pages = getCurrentPages(); + + if (pages.length === 0) { + console.log("pages.length=0"); + return ""; + } + + const currentPage = pages[pages.length - 1]; + console.log("currentPage=", currentPage.route); + return currentPage.route; + } + + + setBleFormData(data,f) { + if(data){ + let linkedList=uni.getStorageSync(this.StorageKey); + linkedList.find((v)=>{ + if(f.deviceId==v.deviceId){ + let keys=Object.keys(data); + keys.forEach((key)=>{ + if(!v.formData){ + v.formData={}; + } + if(!f.formData){ + f.formData={}; + } + v.formData[key]=data[key]; + f.formData[key]=data[key]; + }); + + uni.setStorageSync(this.StorageKey,linkedList); + } + }); + } + + } + + + ReceiveData(receive,f,path) { + if(f && f.macAddress && f.id){ + let data={}; + if(f.detailPageUrl=='/pages/6155/deviceDetail'){ + console.log("该设备是6155"); + data= this.Receive_6155(receive,f,path); + } + + if(f.detailPageUrl=='/pages/650/HBY650'){ + console.log("该设备是650"); + data= this.Receive_650(receive,f,path); + } + if(f.detailPageUrl=='/pages/670/HBY670'){ + console.log("该设备是670"); + data= this.Receive_670(receive,f,path); + } + console.log("收到数据并处理完毕,",data); + return data; + } + console.log("已收到该消息,但无法处理",f); + + } + + Receive_650(receive,f,path) { + console.log("通用程序正在处理650的数据",receive); + + var parseData = () => { + let bytes = receive.bytes; + if (bytes[0] == 0x55) { + + try { + + let staticLevelByte = bytes[1]; + let staticLevelText = '未知'; + let modeCurr = ""; + + switch (staticLevelByte) { + case 0x65: + staticLevelText = '高档'; + modeCurr = "hight"; + break; + case 0x66: + staticLevelText = '中档'; + modeCurr = "center"; + break; + case 0x67: + staticLevelText = '低档'; + modeCurr = "low"; + break; + case 0x68: + staticLevelText = '关闭'; + modeCurr = "close"; + break; + } + + // 解析照明档位 + let lightingLevelByte = bytes[2]; + let lightingLevelText = lightingLevelByte === 0x6e ? '开启' : '关闭'; + + + // 解析剩余照明时间(第三和第四字节,大端序) + let lightingTime = (bytes[3] << 8) | bytes[4]; + let hours = Math.floor(lightingTime / 60); + let remainingMinutes = lightingTime % 60; + let xuhang = '0分'; + // 处理不同情况的显示 + if (hours === 0) { + xuhang = `${remainingMinutes}分`; + } else if (remainingMinutes === 0) { + xuhang = `${hours}小时`; + } else { + xuhang = `${hours}小时${remainingMinutes}分`; + } + + // 解析剩余电量 + let batteryLevelByte = bytes[5]; + // 电量百分比范围检查 + let batteryLevel = Math.max(0, Math.min(100, batteryLevelByte)); + let iswarn = false; + let warn = bytes[6]; + if (warn == 0x00) { + warn = '无预警'; + } else if (warn == 0x01) { + warn = '弱预警'; + } else if (warn == 0x02) { + iswarn = true; + warn = '中预警'; + } else if (warn == 0x03) { + iswarn = true; + warn = '强预警'; + } else if (warn == 0x04) { + iswarn = true; + warn = '非常强预警'; + } + + let formData={}; + formData.battary = batteryLevel; + formData.xuhang = xuhang; + formData.cMode = lightingLevelByte === 0x6e; + formData.modeCurr = modeCurr; + formData.warnLevel = warn; + formData.iswarn = iswarn; + this.setBleFormData(formData,f); + + let route=this.getCurrentPagePath(); + console.log("f=",f); + console.log("route="+route); + if (iswarn && f.detailPageUrl.indexOf(route)==-1 ) { + uni.showModal({ + content:"环境存在漏电电源", + title:"警告", + success(res){ + if(res.confirm){ + + if(f){ + uni.navigateTo({ + url: f.detailPageUrl, + events: { + ack: function(data) {} + }, + success: (res) => { + res.eventChannel.emit('detailData', { + data: f, + deviceType: '', + apiType: 'listA' + }); + } + }); + } + } + } + }) + }else{ + console.log("当前全局不处理此消息"); + } + return formData; + } catch (error) { + return null; + } + } + + if (receive.str) { + try { + + let str = receive.str; + + if (str.indexOf('mac address:') == 0) { + let formData={}; + formData.macAddress = str.split(':')[1]; + this.setBleFormData(formData,f); + return formData; + } + else{ + let receiveData={a:1}; + try { + let json=JSON.parse(str); + + if("staBlue_picture" in json){ + //重发图片 + console.log("收到重新发送图片的命令"); + receiveData=json; + } + else if("staBlue_text" in json){ + //重发文本 + console.log("收到重新发送文本的命令"); + receiveData=json; + } + else if("staBlue_vidio" in json){ + //重发视频 + console.log("收到重新发送视频的命令"); + receiveData=json; + } + else if("staBlue" in json){ + if(json.staBlue=="finish"){ + console.log("收到设备回复,全部传输完成"); + receiveData=json; + } + + } + else{ + receiveData={}; + console.log("无法解析该文本"); + } + + + + } catch (error) { + receiveData={}; + console.log("文本解析失败") + } + return receiveData; + } + return null; + } catch (ex) { + return null; + } + } + + } + + let data=parseData(receive.bytes); + console.log("data=",data); + return data; + + } + + Receive_670(receive,f,path){ + + var todo = (bytes) =>{ + console.log("todo"); + let receiveData = {}; + if (bytes[0] == 0x55) { + try { + console.log("todo"); + // 跳过帧头(第一个字节),从第二个字节开始解析 + let staticLevelByte = bytes[1]; + let staticLevelText = ''; + switch (staticLevelByte) { + case 0x65: + staticLevelText = 'hight'; + break + case 0x66: + staticLevelText = 'center'; + break + case 0x67: + staticLevelText = 'low'; + break + case 0x68: + staticLevelText = 'close'; + break + } + console.log("todo"); + // 解析照明档位 + let lightingLevelByte = bytes[2]; + let lightingLevelText = lightingLevelByte === 0x6d ? 'hight': lightingLevelByte === 0x6e ? 'low': 'close'; + + // 解析剩余照明时间(第三和第四字节,小端序) + let lightingTime = (bytes[3] << 8) | bytes[4]; + + // 解析剩余电量 // 电量百分比范围检查 + let batteryLevelByte = bytes[5]; + + let batteryLevel = Math.max(0, Math.min(100, batteryLevelByte)); + console.log("todo"); + let warn = bytes[6]; + if (warn == 0x00) { + warn = 'none'; + } else if (warn == 0x01) { + warn = 'ruo'; + } else if (warn == 0x02) { + warn = 'center'; + } else if (warn == 0x03) { + warn = 'hight'; + } else if (warn == 0x04) { + warn = 'veryhight'; + } + + let staticWarn = bytes[7] == 0x01;//静止报警 + let fourGStrenth = bytes[8]; //4g信号强度 + let sosTxt = bytes[9] == 0x00 ? 'close' : bytes[9] == 0x01 ? 'sg' : 'rb'; + console.log("todo"); + receiveData.modeCurr = staticLevelText; + receiveData.lightCurr = lightingLevelText; + receiveData.xuhang = lightingTime ; + receiveData.battary = batteryLevel; + receiveData.warnLevel = warn; + receiveData.staticWarn = staticWarn; + receiveData.fourGStrenth = fourGStrenth; + receiveData.SOS=sosTxt; + } catch(error) { + console.log('数据解析错误:', error); + } + console.log("todo"); + } else { + try { + let uint8Array = new Uint8Array(receive.value); + let str = ''; + for (let i = 0; i < uint8Array.length; i++) { + // 将每个字节转换为对应的字符 + str += String.fromCharCode(uint8Array[i]); + } + if (str.indexOf('mac address:') == 0) { + receiveData.macAddress = str.split(':').slice(1).join(":"); + console.log('收到mac地址:', +this.receiveData.macAddress); + } else if (str.indexOf('imei:') == 0) { + receiveData.imei = str.split(':')[1]; + console.log('收到IEMI:', +this.receiveData.macAddress); + + } else if (str.indexOf('longitude:') == 0) { + receiveData.Lon = str.split(':')[1]; + console.log('收到经度:', +this.receiveData.macAddress); + } else if (str.indexOf('latitude:') == 0) { + receiveData.Lat = str.split(':')[1]; + console.log('收到纬度:', +this.receiveData.macAddress); + } else { + try { + let json=JSON.parse(str); + if("staBlue_picture" in json){ + //重发图片 + console.log("收到重新发送图片的命令"); + receiveData=json; + } + else if("staBlue_text" in json){ + //重发文本 + console.log("收到重新发送文本的命令"); + receiveData=json; + } + else if("staBlue_vidio" in json){ + //重发视频 + console.log("收到重新发送视频的命令"); + receiveData=json; + } + else if("staBlue" in json){ + if(json.staBlue=="finish"){ + console.log("收到设备回复,全部传输完成"); + receiveData=json; + } + + } + else{ + receiveData={}; + console.log("无法解析该文本"); + } + + + + } catch (error) { + receiveData={}; + console.log("文本解析失败") + } + } + } catch(ex) { + receiveData={}; + console.log('将数据转文本失败', ex); + } + } + console.log("todo",receiveData); + this.setBleFormData(receiveData,f); + return receiveData; + } + + + let data=todo(receive.bytes); + + return data; + } + Receive_6155() { + console.log("通用程序正在处理6155的数据"); + } + + +} + + + +let receiveInstance = null; +export default { + getBleReceive: function(found, receive) { + if (!receiveInstance) { + receiveInstance = new BleReceive(); + + } + return receiveInstance; + } +} \ No newline at end of file diff --git a/utils/gbk.js b/utils/gbk.js new file mode 100644 index 0000000..61ac280 --- /dev/null +++ b/utils/gbk.js @@ -0,0 +1,122 @@ +export default { + + arr2hex: function(arr) { + return arr.reduce(function(s, v) { + s += ''; + s += ('00' + v.toString(16)).match(/..$/)[0]; + return s; + }, ""); + }, + decode: function(arr) { + var arr_index = 0x8140; + var str = ""; + var gbk_us = this.gbkus(); + for (var n = 0, max = arr.length; n < max; n++) { + var code = arr[n] & 0xff; + if (code > 0x80 && n + 1 < max) { + var code1 = arr[n + 1] & 0xff; + if (code1 >= 0x40) { + code = gbk_us[(code << 8 | code1) - arr_index]; + n++; + } + } + str += String.fromCharCode(code); + } + return str; + }, + encode: function(str) { + + var arr_index = 0x8140; + str += ''; + var gbk_us = this.gbkus(); + var gbk = []; + var wh = '?'.charCodeAt(0); //gbk中没有的字符的替换符 + for (var i = 0; i < str.length; i++) { + var charcode = str.charCodeAt(i); + if (charcode < 0x80) gbk.push(charcode); + else { + var gcode = gbk_us.indexOf(charcode); + if (~gcode) { + gcode += arr_index; + gbk.push(0xFF & (gcode >> 8), 0xFF & gcode); + } else { + gbk.push(wh); + } + } + } + return gbk; + }, + + + gbkus: function() { + + var Fn_unzip=function() { + return arguments[0].replace(/\x23(\d+)\x24/g, function(a, b) { + return Array(+b + 4).join("#"); + }) + .replace(/[\x26-\x7e]\x25[\x26-\x7e]/g, function(a) { + var b = a.substr(0, 1).charCodeAt(0), + e = a.substr(2).charCodeAt(0), + str = String.fromCharCode(b); + while (b++ < e) { + str += String.fromCharCode(b); + } + return str; + }) + .replace(/\x23/g, "###") + .replace(/([\x26-\x7e]{2})\x21([\x26-\x7e\x23]+)(?:\x20|$)/g, function(all, hd, dt) { + return dt.replace(/./g, function(a) { + if (a != "#") { + return hd + a; + } else { + return a; + } + }); + }) + .match(/.../g); + } + + var gbkArray= function(gbkArr) { + + var Fn_Hex_decode = function() { + var n = 0, + str = arguments[0]; + for (var i = 0, w = str.length; i < w; i++) { + var code = str.charCodeAt(i); + if (code < 38 || code > 126) return NaN; + n += (code - 38) * Math.pow(89, w - i - 1); + } + return n; + }; + + var data = []; + for (var i = 0x81, k = 0; i <= 0xfe; i++) { + if (data.length > 0) { + data.length += 0x40 + 1; + } + for (var j = 0x40; j <= 0xfe; j++) { + if ( + (j == 0x7f) || + ((0xa1 <= i && i <= 0xa7) && j <= 0xa0) || + ((0xaa <= i && i <= 0xaf) && j >= 0xa1) || + (0xf8 <= i && j >= 0xa1) + ) { + data.push(undefined); + continue; + } + var hex = gbkArr[k++]; + var key = Fn_Hex_decode(hex); + data.push(key ? key : undefined); + } + } + return data; + } + + var arr = gbkArray(Fn_unzip( + "(T!HJ%LUX]e%gilotuwy{} (U!)-%/137>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: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" + )); + return arr; + } + + +} \ No newline at end of file diff --git a/utils/loading.js b/utils/loading.js new file mode 100644 index 0000000..01258bd --- /dev/null +++ b/utils/loading.js @@ -0,0 +1,44 @@ +// utils/loading.js + +// 显示loading +export const showLoading = (ev,options) => { + if(!ev){ + return; + } + + if(!options){ + options={a:1}; + } + if(!options.text && options.title){ + options.text=options.title; + } + if(!options.text){ + options.text="请稍候..."; + } + ev.$refs.loading.show(options); + + +} + +// 隐藏loading +export const hideLoading = (ev) => { + + if(!ev){ + return; + } + ev.$refs.loading.hide(); + + +} + +// 更新loading配置 +export const updateLoading = (ev,options) => { + if(!ev){ + return; + } + if(!options){ + options={a:1}; + } + ev.$refs.loading.update(options) + +} \ No newline at end of file diff --git a/新建 文本文档.txt b/新建 文本文档.txt deleted file mode 100644 index 72943a1..0000000 --- a/新建 文本文档.txt +++ /dev/null @@ -1 +0,0 @@ -aaa