uni-app 蓝牙扫码适配
1.前言
- 蓝牙设备扫码的效率要高于手机摄像头
- App需要进行对蓝牙扫码枪进行适配才能正常使用蓝牙设备枪,并兼容之前的摄像头扫码
- 适配的关键在于:扫码枪进行扫码时,需要对其进行事件监听,并拿到条码的值
2.注意事项
- 蓝牙模块是可选项,默认应该是关闭状态,需要时再手动开启
- 蓝牙模块的运行状态需要存储至全局变量中(vuex),以便所有的页面都能获知
- 蓝牙扫描附近的设备时会增加耗电,应在适当时机予以关闭
- 蓝牙设备连接后,需要保持屏幕常亮,蓝牙断开后,关闭屏幕常亮
3.逻辑梳理
- 初始化蓝牙相关的全局数据(vuex)
- 在设置页面中展示蓝牙连接的状态并监听点击
- 获取开启此功能所依赖的权限,并展示到页面中(仅小程序)
- 获取已保存过的设备列表,用于自动连接已匹配过的设备
- 尝试执行初始化,如果已经执行过,则不再执行
- 初始化成功后,进行事件监听(监听蓝牙适配器状态变化事件,监听低功耗蓝牙连接状态的改变事件)
- 初始化成功后,开启搜寻附近的蓝牙外围设备(一段时间后进行关闭,节省电量)
- 则监听设备发现事件,一旦监听到新设备,则将其保存并渲染到页面(进行去重处理),对比之前已经保存过的设备列表,符合条件自动进行连接
- 将扫码到的附近的蓝牙设备渲染到页面中,监听点击事件进行手动连接
- 设备连接成功后,更新获取已保存过的设备列表,停止搜索设备,并根据deviceId获取设备服务(延时获取)
- 选中目标设备服务,获取其特征码,并选中目标特征码,开启notify 功能
- 监听特征值变化事件(扫码会触发此事件)
- 扫码数据触发时,需要对数据进行转换,特征值的数据格式是arrayBuffer,需要将其转换成字符串
- 处理二维码对应的字符串
4.关于设备服务和特征码
- !!!获取设备服务时,需要延时获取,不然App端获取为空
- 监听扫码事件的关键在于获取正确的服务id和特征码id
- 如果不知道需要的是哪个服务和特征码,可以咨询设备厂家的技术客服
- 服务id和特征码id我是自己盲测出来的
5.数据格式转换 arrayBufferUTF8ToStr
arrayBufferUTF8ToStr(array) {
var out, i, len, c;
var char2, char3;
if (array instanceof ArrayBuffer) {
array = new Uint8Array(array);
}
out = "";
len = array.length;
i = 0;
while (i < len) {
c = array[i++];
switch (c >> 4) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
// 0xxxxxxx
out += String.fromCharCode(c);
break;
case 12:
case 13:
// 110x xxxx 10xx xxxx
char2 = array[i++];
out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
break;
case 14:
// 1110 xxxx 10xx xxxx 10xx xxxx
char2 = array[i++];
char3 = array[i++];
out += String.fromCharCode(((c & 0x0F) << 12) |
((char2 & 0x3F) << 6) |
((char3 & 0x3F) << 0));
break;
}
}
return out;
},
6.处理扫码结果
- 每个页面处理扫码结果的逻辑是不一样的,为了减少对页面代码的改动,我的处理方式是这样的
- 每个页面新建一个处理扫码结果的方法
- 进行蓝牙扫描时,获取到当前页面,再拿到这个处理方法,有则执行
//转码
var _code = this.arrayBufferUTF8ToStr(res.value)
//去除左右空格
var code = _code.trim()
//拿到当前页面 (页面栈最后一个元素)
var now_page_index = getCurrentPages().length - 1
var now_page = getCurrentPages()[now_page_index]
//执行页面扫码回调
if(typeof now_page.$vm.handleScanResult == 'function'){
//执行回调
console.log('执行当前页面 handleScanResult 方法')
now_page.$vm.handleScanResult(code)
}
7.蓝牙相关数据初始化
- 在vuex中初始化相关数据
const state = {
connectedBluetoothDevices:[],//已经建立连接状态的设备 长度大于0表示设备连接成功
bluetoothAdapterAvailable: false,//蓝牙适配器是否可用
bluetoothAdapterDiscovering:false,//蓝牙适配器是否处于搜索状态
bluetoothOpenNotify:false,//蓝牙扫码监听是否已经开启
bluetoothCloseScanTimer:null,//关闭蓝牙扫描的延时定时器
}
- 更新相关数据的方法
const mutations = {
//更新 已经建立连接状态的设备 的数据
updateConnectedBluetoothDevices(state,val){
state.connectedBluetoothDevices = val
},
//更新 蓝牙适配器可用状态
updateBluetoothAdapterAvailable(state,val){
state.bluetoothAdapterAvailable = val
},
//更新 蓝牙适配器是否处于搜索状态
updateBluetoothAdapterDiscovering(state,val){
state.bluetoothAdapterDiscovering = val
},
//更新 蓝牙扫码监听是否已经开启
updateBluetoothOpenNotify(state,val){
state.bluetoothOpenNotify = val
},
//更新 关闭蓝牙扫描的延时定时器
updateBlueClosetoothScanTimer(state,val){
state.bluetoothCloseScanTimer = val
},
//重置蓝牙数据
resetBluetoothData(state){
state.connectedBluetoothDevices = []
state.bluetoothAdapterAvailable = false
state.bluetoothAdapterDiscovering = false
state.bluetoothOpenNotify = false
}
}
8.设备页面
<!-- #ifndef H5 -->
<view >
<uni-list>
<uni-list-item title="蓝牙连接" clickable :note="bluetoothAdapterStatus" @click="gotoBluetoothPage"></uni-list-item>
</uni-list>
</view>
<!-- #endif -->
- 读取vuex中的数据,输入当前的蓝牙运行状态
computed:{
//已经建立连接状态的设备
connectedBluetoothDevices(){
return this.$store.state.connectedBluetoothDevices
},
//蓝牙适配器是否可用
bluetoothAdapterAvailable(){
return this.$store.state.bluetoothAdapterAvailable
},
//蓝牙运行状态
bluetoothAdapterStatus(){
//如果不可用 显示
if(!this.bluetoothAdapterAvailable){
return '未开启'
}else if(this.connectedBluetoothDevices.length > 0){
//如果已经开启 且已经连接了设备 则展示其名称
return this.connectedBluetoothDevices[0].name
}else{
return '未连接设备'
}
},
}
9.蓝牙页面
- 展示开关
- 渲染设备列表
- 事件初始化
<template>
<view class="page-wrap">
<!-- 蓝牙适配器状态 -->
<view class="flex-box" style="padding:0 8px;margin-bottom: 10px;">
<view style="font-size: 16px;">蓝牙适配器状态</view>
<view>
<u-switch :value="bluetoothAdapterAvailable" @change="bluetoothAdapterAvailableChange"></u-switch>
</view>
</view>
<!-- #ifdef MP-WEIXIN -->
<!-- 权限状态 开始 -->
<view>
<view class="tip">确保下列权限都已开启</view>
<view class="flex-box row-box">
<text>蓝牙系统开关:</text>
<view @click="handleClickEnabled">
<u-switch disabled :value="bluetoothEnabled"></u-switch>
</view>
</view>
<u-line></u-line>
<view class="flex-box row-box">
<text>地理位置的系统开关:</text>
<view @click="handleClickEnabled">
<u-switch disabled :value="locationEnabled"></u-switch>
</view>
</view>
<u-line></u-line>
<view class="flex-box row-box">
<text>允许微信使用定位:</text>
<view @click="handleClickEnabled">
<u-switch disabled :value="locationAuthorized"></u-switch>
</view>
</view>
<u-line></u-line>
</view>
<!-- 权限状态 结束 -->
<!-- #endif -->
<!-- 已保存设备列表 -->
<view>
<!-- 标题栏 -->
<view class="flex-box" style="margin-top:10px;padding:5px 0px;">
<view class="tip">已保存设备列表</view>
<view style="padding:0 8px 0 4px;" @click="clearSaveBluetoothDevice">
<u-icon name="trash-fill" size="20" color="#999"></u-icon>
</view>
</view>
<!-- 列表 -->
<view style="padding:2px 8px;">
<!-- v-for="(device, index) in save_list" :key="index" -->
<view class="bluetooth-box" :class="{'is-connected':deviceIsConnected(save.deviceId)}"
v-for="(save, index) in saveBluetoothDevice" :key="index" @click="clickSaveDevice(save)">
<view>
<view style="font-size: 18px;">{{save.name}}</view>
<view style="font-size: 14px;">
<text>{{deviceIsConnected(save.deviceId)? '已连接':'已保存'}}</text>
<text style="margin-left:10px;">{{getDeviceServicesStatus}}</text>
</view>
</view>
<view>
<u-icon name="arrow-right" size="22" :color="deviceIsConnected(save.deviceId)? '#fff':'#999'"></u-icon>
</view>
</view>
<view v-if="saveBluetoothDevice.length==0" style="text-align: center;padding:5px;color:#999;">空</view>
</view>
</view>
<!-- 可用设备列表 -->
<view>
<!-- 标题栏 -->
<view class="flex-box" style="margin-top:10px;padding:5px 0px;">
<view class="tip">可用设备列表</view>
<view style="padding:0 8px 0 4px;" @click="reloadDeviceFindList">
<u-loading-icon v-if="bluetoothAdapterDiscovering" mode="circle" size="20"></u-loading-icon>
<u-icon v-else name="reload" size="20"></u-icon>
</view>
</view>
<!-- 列表 -->
<view>
<view v-for="(device, index) in bluetoothDeviceFindList" :key="index">
<view class="device-box" :class="{'is-connected2':deviceIsConnected(device.deviceId)}"
@click="clickDeviceToConnect(device)">
<text>{{device | showDeviceName}}</text>
<text v-if="deviceIsConnected(device.deviceId)">已连接</text>
</view>
<u-line></u-line>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
bluetoothEnabled: false, //蓝牙系统开关
locationEnabled: false, //地理位置的系统开关
locationAuthorized: false, //允许微信使用定位的开关
saveBluetoothDevice:[],//已保存过的蓝牙设备列表
saveBluetoothDevice_key:"saveBluetoothDevice",//保存到本地存储中的key
bluetoothDeviceFindList:[],//扫描到的附近的蓝牙设备
bluetoothConnecting:false,//是否是连接进行中 介于 未连接 与成功连接 之间的状态
delayGetServerTimer:null,//延时获取服务的定时器
}
},
onLoad() {
//1.获取所需权限
this.getNeedAuthority()
//2.获取已保存的蓝牙设备列表
this.getSaveBluetoothDevice()
//3.初始化蓝牙适配器
this.initBluetoothAdapter()
},
onShow(){
},
onUnload() {
//停止搜索设备
this.stopBluetoothDevicesDiscovery()
},
onHide(){
},
filters: {
//展示蓝牙设备的名称 有名称则展示名称,没名称则展示展示其他
showDeviceName(device) {
if (device.name) {
return device.name
} else {
return device.deviceId
}
}
},
methods: {
//获取所需权限
getNeedAuthority(){
//获取系统权限
uni.getSystemInfo({
success: (res)=>{
// #ifdef MP-WEIXIN
this.bluetoothEnabled = res.bluetoothEnabled //蓝牙系统开关
this.locationEnabled = res.locationEnabled //地理位置的系统开关
this.locationAuthorized = res.locationAuthorized //允许微信使用定位的开关
// #endif
}
})
},
//获取已保存的蓝牙设备列表
getSaveBluetoothDevice(){
this.saveBluetoothDevice = uni.getStorageSync(this.saveBluetoothDevice_key) || []
console.log('本地记录 已保存过的蓝牙设备列表',this.saveBluetoothDevice)
},
//初始化蓝牙适配器 H5不支持
initBluetoothAdapter(){
// #ifndef H5
//先获取蓝牙适配器状态 防止重复初始化
console.log('获取本机蓝牙适配器状态')
uni.getBluetoothAdapterState({
//在这里处理成功
success:(res)=>{
console.log('success',res)
//搜寻附近的蓝牙外围设备 参数10表示扫描10秒
this.scanBluetoothDevice(10)
},
//在这里处理失败
fail:(res)=>{
console.log('fail',res)
//res.errCode == 10000代表未进行过初始化 下一步进行初始化操作
if(res.errCode == 10000){
//如果是微信小程序 需要进行权限验证
// #ifdef MP-WEIXIN
// 任何一个未开启,都阻止
if (!this.bluetoothEnabled || !this.locationEnabled || !this.locationAuthorized){
console.log('请先开启相关权限')
uni.showToast({
icon:"error",
title:"请开启相关权限",
duration: 2500
})
return
}
// #endif
// 开始初始化蓝牙适配器
console.log('开始初始化蓝牙适配器')
uni.openBluetoothAdapter({
//在这里处理成功
success:(res)=>{
console.log('success',res)
//监听蓝牙适配器状态变化事件
this.onBluetoothAdapterStateChange()
//监听低功耗蓝牙连接状态的改变事件
this.onConnectionStateChange()
//搜寻附近的蓝牙外围设备
this.scanBluetoothDevice(10)
},
//在这里处理失败
fail: (res) => {
console.log('fail',res)
uni.showToast({
icon: "none",
title: res.errMsg,
duration: 2500
})
},
//为了兼容性考虑,请勿使用 complete 回调
})
}
}
})
// #endif
},
//搜寻附近的蓝牙外围设备
scanBluetoothDevice(scan_duration = 10) {
//先关闭之前的已开启的搜索
this.stopBluetoothDevicesDiscovery()
console.log(`开始搜寻附近的蓝牙外围设备 ${scan_duration}秒`)
//开始搜寻附近的蓝牙外围设备
//此操作比较耗费系统资源,请在 适当时机 调用 uni.stopBluetoothDevicesDiscovery 方法停止搜索
uni.startBluetoothDevicesDiscovery({
//services: ['FEE7'],
allowDuplicatesKey: true,//允许重复上报同一设备
//成功调用在这里处理
success:(res)=>{
//监听寻找到新设备的事件
uni.onBluetoothDeviceFound((devices) => {
//当前设备
var now_devices = devices.devices[0]
//判断是否已经存在列表中
var isInDeviceFindList = this.bluetoothDeviceFindList.some(item=>{
return item.deviceId == now_devices.deviceId
})
//不存在附近设备的列表中
if(!isInDeviceFindList){
//挂载新属性
now_devices.advertisDataStr = this.ab2hex(now_devices.advertisData)
//添加到列表中
this.bluetoothDeviceFindList.push(now_devices)
}
//判断此设备是否存在已保存列表中
var isInSaveBluetoothDevice = this.saveBluetoothDevice.some(item=>{
return item.deviceId == now_devices.deviceId
})
//如果已存在列表中,且未进行过任何连接 且不是连接进行中 则自动连接
if(isInSaveBluetoothDevice && this.connectedBluetoothDevices.length==0 && !this.bluetoothConnecting){
//console.log('模拟点击 自动进行设备连接')
//模拟点击 进行设备连接
this.clickDeviceToConnect(now_devices)
}
})
//延时定时器 自动关闭扫描
var timer = setTimeout(()=>{
//停止搜索设备
this.stopBluetoothDevicesDiscovery()
},scan_duration * 1000)
//全局记录此延时定时器id
this.$store.commit('updateBluetoothCloseScanTimer',timer)
},
fail: (res) => {
//弹出错误提示
uni.showToast({
icon: "none",
title: res.errMsg,
duration: 2500
})
}
})
},
//获取已连接的蓝牙设备列表 只有设备id和名称
getConnectedBluetoothDevices() {
//获取已经建立链接的设备
uni.getConnectedBluetoothDevices({
services: [],
//调用成功在这里处理
success:(res)=>{
//保存设备列表
this.$store.commit('updateConnectedBluetoothDevices',res.devices)
},
//调用失败在这里处理
fail: (res)=> {
//弹出错误提示
uni.showToast({
icon: "none",
title: res.errMsg,
duration: 2500
})
}
})
},
//连接蓝牙设备
createBLEConnection(device){
//显示加载中
uni.showLoading({
title:"连接中"
})
//更新连接状态
this.bluetoothConnecting = true
//进行设备连接
uni.createBLEConnection({
deviceId: device.deviceId,
timeout: 10 * 1000,
success:(res)=>{
//隐藏加载状态
uni.hideLoading()
//更新连接状态
this.bluetoothConnecting = false
//记录已连接的设备 如果之前为连接的话
var index = this.saveBluetoothDevice.findIndex(item=>{
return item.deviceId == device.deviceId
})
//如果存在 则先删除
if(index != -1){
this.saveBluetoothDevice.splice(index,1)
}
//添加到已保存列表中
this.saveBluetoothDevice.unshift({
deviceId: device.deviceId,
name: device.name
})
//保存到本地
uni.setStorageSync(this.saveBluetoothDevice_key,this.saveBluetoothDevice)
//停止搜索设备
this.stopBluetoothDevicesDiscovery()
//延迟获取设备服务
this.delayGetServerTimer = setTimeout(()=>{
//获取设备服务
this.getDeviceServer(device)
},1500)
},
fail: (res) => {
//隐藏加载状态
uni.hideLoading()
//更新连接状态
this.bluetoothConnecting = false
//弹出错误提示
uni.showToast({
icon: "none",
title: res.errMsg,
duration: 2500
})
}
})
},
//获取设备服务
getDeviceServer(device) {
uni.getBLEDeviceServices({
deviceId: device.deviceId,
success:(res)=>{
console.log('获取设备服务成功',res)
//查找目标服务 00001804-0000-1000-8000-00805F9B34FB 目标服务
var scan_server = res.services.find(item=>{
return item.uuid == "0000FEEA-0000-1000-8000-00805F9B34FB"
})
//是由存在目标服务
if(scan_server){
//获取扫码服务服务的特征值
this.getDeviceCharacteristics(device, scan_server)
}else{
console.log('未获取到目标服务')
uni.showToast({
icon:"none",
title:"未获取到目标服务",
duration: 2500
})
}
},
fail: (res) => {
//弹出错误提示
uni.showToast({
icon: "none",
title: res.errMsg,
duration: 2500
})
}
})
},
//获取蓝牙设备某个服务中所有特征值
getDeviceCharacteristics(device, services_item) {
//当前蓝牙扫描是第三个服务
var serviceId = services_item.uuid
uni.getBLEDeviceCharacteristics({
// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
deviceId: device.deviceId,
// 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
serviceId: serviceId,
success:(res)=>{
console.log('特征值列表',res.characteristics)
//特征值列表
var characteristics = res.characteristics
//启用低功耗蓝牙设备特征值变化时的 notify 功能
this.openNotify(device.deviceId, serviceId, characteristics[0].uuid)
},
fail: (res) => {
//弹出错误提示
uni.showToast({
icon: "none",
title: res.errMsg,
duration: 2500
})
},
})
},
//启用低功耗蓝牙设备特征值变化时的 notify 功能
openNotify(deviceId, serviceId, characteristicId) {
console.log('characteristicId',characteristicId)
uni.notifyBLECharacteristicValueChange({
state: true, // 启用 notify 功能
// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
deviceId,
// 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
serviceId,
// 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
characteristicId,
success:(res)=>{
this.$store.commit('updateBluetoothOpenNotify',true)
console.log('notify 功能开启成功 开启监听到特征值变化')
//监听特征值变化
uni.onBLECharacteristicValueChange((res) => {
//扫描到的二维码为
var code = this.arrayBufferUTF8ToStr(res.value)
console.log("监听到特征值变化 ",code)
//处理扫码结果
this.handleBluetoothCode(code)
})
},
fail: (res) => {
console.log('notify 功能开启失败',res)
this.$store.commit('updateBluetoothOpenNotify',false)
//弹出错误提示
uni.showToast({
icon: "none",
title: res.errMsg,
duration: 2500
})
}
})
},
//停止搜索设备
stopBluetoothDevicesDiscovery() {
console.log('停止搜索设备,并清除相关定时器')
uni.stopBluetoothDevicesDiscovery()
//清除延时定时器
clearTimeout(this.bluetoothCloseScanTimer)
},
//断开与低功耗蓝牙设备的连接
closeBLEConnection(deviceId){
//显示操作状态
uni.showLoading({
title:"操作中..."
})
uni.closeBLEConnection({
deviceId,
success:(res)=>{
//隐藏操作状态
uni.hideLoading()
},
fail: (res)=> {
//隐藏操作状态
uni.hideLoading()
//弹出错误提示
uni.showToast({
icon: "none",
title: res.errMsg,
duration: 2500
})
}
})
},
//关闭蓝牙适配器 关闭之后 所有监听失效
closeBluetoothAdapter(){
uni.closeBluetoothAdapter({
success:(res)=>{
//重置vuex中的数据
this.$store.commit('resetBluetoothData')
//清空已扫描到的设备列表
this.deviceFindList = []
//清空定时器
clearTimeout(this.bluetoothCloseScanTimer)
console.log('关闭屏幕常亮')
uni.setKeepScreenOn({
keepScreenOn: false
})
},
fail: (res)=> {
//弹出错误提示
uni.showToast({
icon: "none",
title: res.errMsg,
duration: 2500
})
}
})
},
//监听蓝牙适配器状态变化事件
onBluetoothAdapterStateChange(){
uni.onBluetoothAdapterStateChange((res)=>{
//更新vuex中的数据
this.$store.commit('updateBluetoothAdapterAvailable',res.available)
//bluetoothAdapterDiscovering
this.$store.commit('updateBluetoothAdapterDiscovering',res.discovering)
if(!res.available){
//清除定时器
clearTimeout(this.delayGetServerTimer)
//清空周围的设备列表
this.bluetoothDeviceFindList = []
}
})
},
//监听低功耗蓝牙连接状态的改变事件
onConnectionStateChange(){
uni.onBLEConnectionStateChange((res)=> {
console.log('蓝牙连接状态发生改变',res)
if(!res.connected){
uni.showModal({
title:"提示",
content:"蓝牙已断开",
showCancel:false,
success:(res)=>{
}
})
console.log('关闭屏幕常亮')
uni.setKeepScreenOn({
keepScreenOn: false
})
}else{
uni.showToast({
icon:"none",
title:"蓝牙已连接",
duration: 2500
})
console.log('保持屏幕常亮')
uni.setKeepScreenOn({
keepScreenOn: true
})
}
//获取已连接的蓝牙设备列表
this.getConnectedBluetoothDevices()
})
},
//点击清空已保存的蓝牙设备列表
clearSaveBluetoothDevice(){
//清空已保存的蓝牙设备列表 - 页面
this.saveBluetoothDevice = []
//清空已保存的蓝牙设备列表 - 本地存储
uni.setStorageSync(this.saveBluetoothDevice_key,this.saveBluetoothDevice)
},
//点击重载扫描到的设备列表
reloadDeviceFindList(){
//清空之前扫描到的设备
this.deviceFindList = []
//判断本机蓝牙适配器状态。
if(this.bluetoothAdapterAvailable){
//搜寻附近的蓝牙外围设备
this.scanBluetoothDevice(10)
}else{
console.log('请先开启蓝牙适配器')
uni.showToast({
icon: "none",
title: "请先开启蓝牙适配器",
duration: 2500
})
}
},
//点击蓝牙适配器开关切换
bluetoothAdapterAvailableChange(status){
if(!status){
//关闭蓝牙适配器
this.closeBluetoothAdapter()
}else{
//3.初始化蓝牙适配器
this.initBluetoothAdapter()
}
},
//点击选中蓝牙设备进行连接
clickDeviceToConnect(device) {
console.log('点击选中的设备 ' + device.deviceId)
//一次只能连一个设备
if(this.connectedBluetoothDevices.length > 0){
uni.showToast({
icon: "none",
title: "只能连一个设备",
duration: 2500
})
return
}
//只有这种设备才能连
var target_advertisServiceUUIDs = "0000FEE7-0000-1000-8000-00805F9B34FB"
//如果不符合条件 则禁止连接
if(!device.advertisServiceUUIDs.includes(target_advertisServiceUUIDs)){
uni.showToast({
icon: "none",
title: "不支持该设备",
duration: 2500
})
return
}
//连接设备
this.createBLEConnection(device)
},
//点击已保存过的设备
clickSaveDevice(device){
//判断此设备是否已经连接过
var isConnected = this.deviceIsConnected(device.deviceId)
//如果已经连接 则断开
if(isConnected){
uni.showModal({
title:"提示",
content:"是否断开蓝牙连接",
success: (res)=>{
if (res.confirm) {
console.log('用户点击确定')
} else if (res.cancel) {
console.log('用户点击取消')
}
}
})
}else{
//尝试连接蓝牙设备
this.createBLEConnection(device)
}
},
//点击权限开关
handleClickEnabled(){
uni.showToast({
icon:"none",
title:"请到系统设置界面进行操作",
duration: 2500
})
},
//判断设备id是否已经在连接列表中
deviceIsConnected(deviceId){
return this.connectedBluetoothDevices.some(item=>{
return item.deviceId == deviceId
})
},
ab2hex(buffer) {
const hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function(bit) {
return ('00' + bit.toString(16)).slice(-2)
}
)
return hexArr.join('')
},
arrayBufferUTF8ToStr(array) {
var out, i, len, c;
var char2, char3;
if (array instanceof ArrayBuffer) {
array = new Uint8Array(array);
}
out = "";
len = array.length;
i = 0;
while (i < len) {
c = array[i++];
switch (c >> 4) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
// 0xxxxxxx
out += String.fromCharCode(c);
break;
case 12:
case 13:
// 110x xxxx 10xx xxxx
char2 = array[i++];
out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
break;
case 14:
// 1110 xxxx 10xx xxxx 10xx xxxx
char2 = array[i++];
char3 = array[i++];
out += String.fromCharCode(((c & 0x0F) << 12) |
((char2 & 0x3F) << 6) |
((char3 & 0x3F) << 0));
break;
}
}
return out;
},
//处理蓝牙适配器的扫码结果
handleBluetoothCode(_code = ""){
//去除左右空格
var code = _code.trim()
//通用二维码转码
var code_obj = this.$utils.transformScanResult(code)
//拿到当前页面 (页面栈最后一个元素)
var now_page_index = getCurrentPages().length - 1
var now_page = getCurrentPages()[now_page_index]
//执行页面扫码回调
if(typeof now_page.$vm.handleScanResult == 'function'){
//执行回调
console.log('执行当前页面 handleScanResult 方法')
now_page.$vm.handleScanResult(code_obj)
}
},
},
computed:{
//已经建立连接状态的设备
connectedBluetoothDevices(){
return this.$store.state.connectedBluetoothDevices
},
//蓝牙适配器是否可用
bluetoothAdapterAvailable(){
return this.$store.state.bluetoothAdapterAvailable
},
//蓝牙适配器是否处于搜索状态
bluetoothAdapterDiscovering(){
return this.$store.state.bluetoothAdapterDiscovering
},
//蓝牙是否开启了 Notify
bluetoothOpenNotify(){
return this.$store.state.bluetoothOpenNotify
},
//延时关闭蓝牙扫描的定时器
bluetoothCloseScanTimer(){
return this.$store.state.bluetoothCloseScanTimer
},
//判断设备的服务状态
getDeviceServicesStatus(deviceId){
if(this.bluetoothOpenNotify && this.connectedBluetoothDevices.length > 0){
return '服务运行中'
}else{
return ''
}
},
}
}
</script>
<style scoped lang="scss">
.page-wrap{
padding:10px 0;
font-size: 14px;
.tip {
padding: 3px 8px;
color: $uni-color-primary;
font-size: 16px;
}
.flex-box {
display: flex;
align-items: center;
justify-content: space-between;
}
.row-box{
padding:3px 8px;
}
.bluetooth-box{
background-color: $uni-bg-color-grey;
padding:15px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
&:last-child{
margin-bottom: 0;
}
}
.is-connected{
background-color: $uni-color-primary;
color: #fff;
}
.device-box{
padding:8px 12px;
display: flex;
align-items: center;
justify-content: space-between;
}
.is-connected2{
color: $uni-color-primary;
}
}
.btn-box {
display: flex;
align-items: center;
justify-content: space-between;
}
.btn-box .btn-item {
width: 40%;
}
</style>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了