examTeamApp/pages/home/home.vue

602 lines
14 KiB
Vue
Raw Normal View History

<template>
2026-01-10 09:17:01 +08:00
<view class="content">
<button type="default" v-show="!shows" @click="initBle">
初始化蓝牙模块
</button>
<scroll-view scroll-y="true" show-scrollbar="true">
<radio-group>
<view v-for="(item, index) in bleDevs" :key="index" v-show="item.name.length > 0 && !shows"
style="padding: 10rpx 20rpx; border-bottom: 1rpx solid #ececec"
v-if="Math.max(100 + item.RSSI, 0) >= 30">
<view style="font-size: 32rpx; color: #333">
<checkbox-group @change="checkboxChange" :data-name="item.name" :data-deviceId="item.deviceId">
<label>
<checkbox :value="item.deviceId">
{{ item.name }}
</checkbox>
</label>
</checkbox-group>
2025-04-26 13:35:30 +08:00
</view>
2026-01-10 09:17:01 +08:00
<view style="font-size: 20rpx; padding: 10rpx 0">
deviceId: {{ item.deviceId }} 信号强度: {{ item.RSSI }}dBm ({{
Math.max(100 + item.RSSI, 0)
}}%)
2025-04-26 13:35:30 +08:00
</view>
</view>
2026-01-10 09:17:01 +08:00
<view class="dis">
<view @tap="connectBle" v-if="!shows" class="pl"> 连接 </view>
<view @tap="close" v-if="shows" class="pl"> 断开 </view>
</view>
2026-01-10 09:17:01 +08:00
</radio-group>
</scroll-view>
<view class="appItems">
<viwe :class="[item.status ? 'item bakBlue' : 'item']" v-for="(item, index) in totalList" :key="index">
<view class="name p_hide">蓝牙名称{{ item.name }}</view>
<view class="txt">蓝牙数据{{ item.text }}</view>
</viwe>
</view>
2026-01-10 09:17:01 +08:00
<view class="items" v-if="shows">
<view class="item" v-for="(item, index) in getData" :key="index">
{{ item.name }}{{ item.txt }}
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
2026-01-10 09:17:01 +08:00
config: {
color: "#333",
backgroundColor: [1, "#fff"],
title: "多设备蓝牙连接",
back: false,
},
title: "Hello",
bleDevs: [],
status: -2, //-2未连接 -1已连接 0连接成功
deviceId: "",
serviceId: "",
2026-01-10 09:17:01 +08:00
characteristicId: "",
2025-04-18 14:53:38 +08:00
2026-01-10 09:17:01 +08:00
sendData: "",
getData: [],
deviceIds: [],
totalList: [], // 全部已连接的设备
timeIndex: 0, // 默认是列表的第一个
timeout: null,
shows: false,
titleTime: "00:00:00",
timer: "",
hour: 0,
minutes: 0,
seconds: 0,
input1: "B",
input2: "",
};
},
2026-01-10 09:17:01 +08:00
destroyed() {
clearInterval(this.timer);
},
2026-01-10 09:17:01 +08:00
onLoad() {},
mounted() {
this.onBLEConnectionStateChange();
},
methods: {
2026-01-10 09:17:01 +08:00
checkboxChange(e) {
if (e.target.value[0] && e.target.dataset.name) {
let item = {
deviceId: e.target.value[0],
name: e.target.dataset.name,
};
this.deviceIds.push(item);
} else {
2026-01-10 09:17:01 +08:00
for (let index = 0; index < this.deviceIds.length; index++) {
let item = this.deviceIds[index];
if (item.deviceId == e.target.dataset.deviceid) {
this.deviceIds.splice(index, 1);
}
}
}
},
2026-01-10 09:17:01 +08:00
onBLEConnectionStateChange() {
uni.onBLEConnectionStateChange((res) => {
// 该方法回调中可以用于处理连接意外断开等异常情况
if (res.connected == false) {
uni.hideLoading();
for (let i = 0; i < this.deviceIds.length; i++) {
if (res.deviceId == this.deviceIds[i].deviceId) {
uni.showToast({
title: this.deviceIds[i].name + " 蓝牙设备断开连接",
icon: "none",
});
}
}
}
});
},
2026-01-10 09:17:01 +08:00
//初始化蓝牙
initBle() {
this.bleDevs = [];
this.deviceIds = [];
uni.openBluetoothAdapter({
2026-01-10 09:17:01 +08:00
success: (res) => {
//已打开
uni.getBluetoothAdapterState({
//蓝牙的匹配状态
success: (res1) => {
this.startBluetoothDeviceDiscovery();
},
fail(error) {
uni.showToast({
icon: "none",
title: "查看手机蓝牙是否打开"
});
},
});
},
fail: (err) => {
//未打开
uni.showToast({
icon: "none",
title: "查看手机蓝牙是否打开"
});
},
});
},
2026-01-10 09:17:01 +08:00
// 开始搜索蓝牙设备
startBluetoothDeviceDiscovery() {
uni.startBluetoothDevicesDiscovery({
2026-01-10 09:17:01 +08:00
success: (res) => {
// console.log("启动成功", res);
// 发现外围设备
this.onBluetoothDeviceFound();
},
fail: (err) => {
// console.log(err, "错误信息");
},
});
},
2026-01-10 09:17:01 +08:00
// 发现外围设备
onBluetoothDeviceFound() {
2026-01-10 09:17:01 +08:00
// console.log("执行到这--发现外围设备")
let that = this
uni.onBluetoothDeviceFound((res) => {
res.devices.forEach(device => {
2026-01-10 09:17:01 +08:00
console.log("name", device.name)
if (device.name.indexOf('PC-C02Pro') != -1|| (device.localName && device.localName.indexOf('PC-C02Pro') != -1) || device.name.indexOf('G02') != -1) {
that.Bluetoothfilter(device)
}
})
2026-01-10 09:17:01 +08:00
// 吧搜索到的设备存储起来,方便我们在页面上展示
// if (this.bleDevs.indexOf(res.devices[0].deviceId) == -1) {
// this.bleDevs.push(res.devices[0]);
// }
// console.log("蓝牙列表", res);
});
},
2026-01-10 09:17:01 +08:00
Bluetoothfilter(item) {
let that = this
2026-01-10 09:17:01 +08:00
const foundDevices = that.bleDevs
const idx = that.inArray(foundDevices, "deviceId", item.deviceId)
if (idx === -1) {
2026-01-10 09:17:01 +08:00
that.bleDevs.push(item);
}
},
inArray(arr, key, val) {
if (!arr || !arr.length || typeof arr != 'object' || !Array.isArray(arr)) {
return -1
}
for (let i = 0; i < arr.length; i++) {
if (!key) {
if (arr[i] == val) {
return i
}
} else if (arr[i][key] === val) {
return i
}
2026-01-10 09:17:01 +08:00
}
return -1;
},
2024-12-09 09:57:51 +08:00
2026-01-10 09:17:01 +08:00
// 多选然后连接
connectBle() {
if (this.deviceIds.length == 0) {
uni.showToast({
title: "请选择连接的设备",
icon: "none"
});
return;
2024-12-09 09:57:51 +08:00
}
2026-01-10 09:17:01 +08:00
this.getData = [];
for (let i = 0; i < this.deviceIds.length; i++) {
this.createBLEConnection(this.deviceIds[i]);
// this.nowLinkLis(this.deviceIds[i]);
}
// this.deviceIds.forEach((item) => {
// // this.createBLEConnection(item);
// this.nowLinkLis(item);
// });
},
2026-01-10 09:17:01 +08:00
//选择设备连接吧deviceId传进来
createBLEConnection(item) {
uni.showLoading({
title: "连接中,请稍等",
mask: true,
});
let that = this;
//连接蓝牙
uni.createBLEConnection({
deviceId: item.deviceId,
success(res) {
that.shows = true;
that.stopBluetoothDevicesDiscovery();
that.getBLEDeviceServices(2, item);
},
fail(res) {
console.log("蓝牙连接失败", res);
uni.showToast({
title: items.name + "蓝牙连接失败",
icon: "none",
});
},
});
},
2026-01-10 09:17:01 +08:00
// 停止搜寻蓝牙设备
stopBluetoothDevicesDiscovery() {
uni.stopBluetoothDevicesDiscovery({
success: (e) => {
this.loading = false;
// console.log("停止搜索蓝牙设备:" + e.errMsg);
},
fail: (e) => {
console.log("停止搜索蓝牙设备失败,错误码:" + e.errCode);
},
});
},
2026-01-10 09:17:01 +08:00
//获取蓝牙的所有服务
getBLEDeviceServices(index, items) {
setTimeout(() => {
uni.getBLEDeviceServices({
// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
deviceId: items.deviceId,
success: (res) => {
// console.log("成功",res)
// console.log("device services:", res);
//这里会获取到好多个services uuid 我们只存储我们需要用到的就行这个uuid一般硬件厂家会给我们提供
console.log("services", res.services);
res.services.forEach((item) => {
if (item.uuid.indexOf("FFF0") != -1) {
items["serviceId"] = item.uuid;
this.getBLEDeviceCharacteristics(index, items);
}
});
},
});
}, 1000);
},
//获取蓝牙特征
getBLEDeviceCharacteristics(index, items) {
// console.log("进入特征");
setTimeout(() => {
uni.getBLEDeviceCharacteristics({
// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
deviceId: items.deviceId,
// 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
serviceId: items.serviceId,
success: (res) => {
console.log("characteristics", res);
res.characteristics.forEach((item) => {
if (
// 2 支持监听 1 支持写入
item.uuid.indexOf(
index == 1 ?
"0000FFF2" :
"0000FFF1"
) != -1
) {
items["characteristicId"] = item.uuid;
if (index == 2) {
this.notifyBLECharacteristicValueChange(items);
}
}
});
if (index == 1) {
this.writeString(this.sendData, items);
}
},
fail: (res) => {
console.log(res);
},
});
}, 0);
},
2026-01-10 09:17:01 +08:00
// 启用 notify 功能
notifyBLECharacteristicValueChange(items) {
let that = this;
2026-01-10 09:17:01 +08:00
uni.notifyBLECharacteristicValueChange({
state: true, // 启用 notify 功能
// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
deviceId: items.deviceId,
// 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
serviceId: items.serviceId,
// 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
characteristicId: items.characteristicId,
success: (res) => {
console.log("启用 notify 功能成功", res);
uni.hideLoading();
// uni.showToast({
// title: items.name + "连接成功",
// icon: "none",
// });
items["status"] = true;
items["text"] = "";
that.totalList.push(items);
uni.onBLECharacteristicValueChange((res) => {
for (let i = 0; i < that.deviceIds.length; i++) {
if (res.deviceId == that.deviceIds[i].deviceId) {
let item = {
name: that.deviceIds[i].name,
txt: "接收到:" + that.ab2hex(res.value),
};
that.getData.unshift(item);
}
}
for (let i = 0; i < that.totalList.length; i++) {
if (res.deviceId == that.totalList[i].deviceId) {
that.totalList[i].text = that.ab2hex(res.value)
}
}
that.totalList = JSON.stringify(that.totalList);
that.totalList = JSON.parse(that.totalList);
});
},
fail: (res) => {
console.log("启用 notify 功能失败", res);
},
});
},
2026-01-10 09:17:01 +08:00
ab2hex(buffer, split) {
var hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function(bit) {
return ('00' + bit.toString(16)).slice(-2)
}
2026-01-10 09:17:01 +08:00
)
return hexArr.join(split);
},
close() {
let that = this;
uni.showModal({
title: "提示",
content: "将断开全部蓝牙连接",
success: function(res) {
if (res.confirm) {
for (let index = 0; index < that.deviceIds.length; index++) {
let item = that.deviceIds[index];
uni.closeBLEConnection({
deviceId: item.deviceId,
success(res) {
console.log("断开蓝牙成功", res);
that.shows = false;
that.totalList = [];
uni.showToast({
title: "断开蓝牙成功",
});
},
fail(res) {
console.log("断开蓝牙失败", res);
},
});
}
}
},
});
},
2026-01-10 09:17:01 +08:00
// 向蓝牙设备发送字符串数据 writeBLECharacteristicValueString
writeString(str, items) {
let that = this;
// console.log("发送字符串数据", str);
// 发送方式一
let buffer = new ArrayBuffer(str.length);
let dataView = new DataView(buffer);
for (let i in str) {
dataView.setUint8(i, str[i].charCodeAt() | 0);
//打印二进制字节
// console.log("dataView.getUint8(i)>>", dataView.getUint8(i));
}
//延迟发送指令
setTimeout(() => {
uni.writeBLECharacteristicValue({
deviceId: items.deviceId,
serviceId: items.serviceId,
characteristicId: items.characteristicId,
value: buffer,
writeType: "write",
success: function(res) {
uni.hideLoading();
// uni.showToast({
// title: "已成功发送",
// });
let item = {
name: items.name,
txt: "已发送:" + str,
};
that.getData.unshift(item);
},
fail: function(res) {
uni.hideLoading();
uni.showToast({
title: "发送失败,可能蓝牙目前不支持写入",
icon: "none",
});
},
});
}, 0);
},
},
};
</script>
<style lang="scss" scoped>
2026-01-10 09:17:01 +08:00
.input3 {
display: flex;
justify-content: space-around;
2026-01-10 09:17:01 +08:00
input {
border: 1rpx solid #ccc;
margin: 20rpx;
text-align: center;
height: 60rpx;
border-radius: 10rpx;
font-size: 50rpx;
}
2026-01-10 09:17:01 +08:00
input:first-child,
input:last-child {
width: 200rpx;
}
}
2026-01-10 09:17:01 +08:00
.bakBlue {
background-color: #007aff !important;
}
2026-01-10 09:17:01 +08:00
.appItems {
padding: 30rpx 0 30rpx 4rpx;
display: flex;
2026-01-10 09:17:01 +08:00
flex-wrap: wrap;
2026-01-10 09:17:01 +08:00
.item {
color: #333;
width: 100%;
height: 60px;
margin: 10rpx 15rpx;
position: relative;
background: #dfdfdf;
border-radius: 10px;
padding: 0 10px;
display: flex;
2026-01-10 09:17:01 +08:00
flex-direction: column;
justify-content: space-evenly;
}
}
2026-01-10 09:17:01 +08:00
.timers {
text-align: center;
margin-top: 30rpx;
2026-01-10 09:17:01 +08:00
.time {
margin-bottom: 40rpx;
2024-09-26 09:18:27 +08:00
width: 100%;
2026-01-10 09:17:01 +08:00
font-size: 80rpx;
font-weight: bold;
2024-09-26 09:18:27 +08:00
}
2026-01-10 09:17:01 +08:00
.btns {
display: flex;
justify-content: space-around;
view {
width: 200rpx;
height: 60rpx;
background-color: #007aff;
color: #fff;
line-height: 60rpx;
border-radius: 10rpx;
}
2026-01-10 09:17:01 +08:00
view:active {
background-color: #2990ff;
}
}
}
2026-01-10 09:17:01 +08:00
.items {
width: 100%;
2026-01-10 09:17:01 +08:00
font-size: 32rpx;
overflow-y: scroll;
height: 300rpx;
background-color: #ccc;
margin: 40rpx 0;
.item {
padding: 4rpx 20rpx 0 20rpx;
}
2026-01-10 09:17:01 +08:00
}
2026-01-10 09:17:01 +08:00
.pl {
margin: 20rpx;
background-color: #007aff;
padding: 10rpx;
}
2026-01-10 09:17:01 +08:00
.classText {
width: 94%;
padding: 10rpx;
margin: 3%;
border: 1rpx solid #ececec;
}
2025-04-26 13:35:30 +08:00
2026-01-10 09:17:01 +08:00
.send {
background-color: #ff3e3e;
color: #fff;
2025-04-26 13:35:30 +08:00
}
2026-01-10 09:17:01 +08:00
.dis {
2025-04-26 13:35:30 +08:00
display: flex;
justify-content: space-between;
2026-01-10 09:17:01 +08:00
color: #fff;
text-align: center;
2025-04-26 13:35:30 +08:00
flex-wrap: wrap;
2026-01-10 09:17:01 +08:00
view {
2025-04-26 13:35:30 +08:00
width: 100%;
2026-01-10 09:17:01 +08:00
border-radius: 8rpx;
font-size: 32rpx;
2025-04-26 13:35:30 +08:00
}
2026-01-10 09:17:01 +08:00
}
2025-04-26 13:35:30 +08:00
2026-01-10 09:17:01 +08:00
.barItems {
width: 100%;
2025-04-26 13:35:30 +08:00
2026-01-10 09:17:01 +08:00
.barItem {
2025-04-26 13:35:30 +08:00
display: flex;
2026-01-10 09:17:01 +08:00
justify-content: space-around;
// border: 1rpx solid #ececec;
height: 100rpx;
padding-top: 20rpx;
align-items: center;
.bar {
width: 300rpx;
2025-04-26 13:35:30 +08:00
display: flex;
2026-01-10 09:17:01 +08:00
justify-content: space-around;
2025-04-26 13:35:30 +08:00
view {
2026-01-10 09:17:01 +08:00
border: 1rpx solid #ececec;
width: 50rpx;
height: 50rpx;
text-align: center;
2025-04-26 13:35:30 +08:00
}
2026-01-10 09:17:01 +08:00
input {
width: 100rpx;
text-align: center;
2025-04-26 13:35:30 +08:00
}
}
}
}
</style>