uniapp_code/pages/index/index.vue

367 lines
8.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="container">
<view class="title-bar">
<text class="title">连接设备</text>
<button
class="scan-btn"
:disabled="isScanning"
@click="toggleScan"
>
{{ isScanning ? '停止扫描' : '开始扫描' }}
</button>
</view>
<view class="status-tip" v-if="isScanning">
<text>正在扫描设备...</text>
<view class="loading"></view>
</view>
<view class="status-tip" v-else-if="foundDevices.length === 0">
<text>未发现设备请点击"开始扫描"</text>
</view>
<view class="device-list">
<view
class="device-item"
v-for="(device, index) in foundDevices"
:key="device.deviceId"
@click="connectDevice(device)"
>
<!-- 设备名称 -->
<view class="device-name">
<text>{{ device.name || '未知设备' }}</text>
<view class="is_bj" v-if="device.is_bj" >吧唧</view>
<text class="device-id">{{ device.deviceId }}</text>
</view>
<!-- 信号强度 -->
<view class="device-rssi">
<text>信号: {{ device.rssi }} dBm</text>
<view class="rssi-bar" :style="{ width: getRssiWidth(device.rssi) }"></view>
</view>
<!-- 连接状态 -->
<view class="device-status" v-if="device.connected">
<text class="connected">已连接</text>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
foundDevices: [],
isScanning: false,
bluetoothEnabled: false,
connectedDeviceId: ''
};
},
onLoad() {
// 页面加载时初始化蓝牙
this.initBluetooth();
},
onUnload() {
// 页面卸载时清理资源
this.stopScan();
uni.closeBluetoothAdapter();
// 移除事件监听
uni.offBluetoothDeviceFound(this.onDeviceFound);
},
methods: {
// 初始化蓝牙适配器
initBluetooth() {
uni.openBluetoothAdapter({
success: () => {
this.bluetoothEnabled = true;
// uni.showToast({ title: '蓝牙初始化成功', icon: 'none' });
},
fail: (err) => {
this.bluetoothEnabled = false;
uni.showModal({
title: '蓝牙开启失败',
content: '请检查设备蓝牙是否开启',
showCancel: false
});
console.error('蓝牙初始化失败:', err);
}
});
},
// 切换扫描状态(开始/停止)
toggleScan() {
if (!this.bluetoothEnabled) {
this.initBluetooth();
return;
}
if (this.isScanning) {
this.stopScan();
} else {
this.startScan();
}
},
// 开始扫描设备
startScan() {
this.isScanning = true;
this.foundDevices = []; // 清空历史列表
// 开始扫描空services表示扫描所有设备
uni.startBluetoothDevicesDiscovery({
services: [],
allowDuplicatesKey: false, // 不允许重复上报
success: () => {
uni.showToast({ title: '开始扫描', icon: 'none' });
// 注册设备发现事件
uni.onBluetoothDeviceFound(this.onDeviceFound);
},
fail: (err) => {
this.isScanning = false;
uni.showToast({ title: '扫描失败', icon: 'none' });
console.error('扫描失败:', err);
}
});
// 10秒后自动停止扫描避免耗电
setTimeout(() => {
if (this.isScanning) this.stopScan();
}, 5000);
},
// 停止扫描
stopScan() {
if (!this.isScanning) return;
uni.stopBluetoothDevicesDiscovery({
success: () => {
this.isScanning = false;
if(this.foundDevices.length == 0){
uni.showToast({
title: `暂未扫描到任何设备`,
icon: 'none'
});
return;
}
uni.showToast({
title: `扫描完成,发现${this.foundDevices.length}台设备`,
icon: 'none'
});
},
fail: (err) => {
console.error('停止扫描失败:', err);
}
});
},
// 设备发现回调(处理去重和数据格式化)
onDeviceFound(res) {
const devices = res.devices || [];
devices.forEach(device => {
var that = this
if (!device.deviceId) return;
const isExist = this.foundDevices.some(
d => d.deviceId === device.deviceId
);
if (isExist) return;
var is_bj = false;
const advData = new Uint8Array(device.advertisData);
// const companyidData = (advData[0]<<8)| advData[1];
const devicenameData = advData.slice(2, 6);
const devicename = String.fromCharCode(...devicenameData)
if(devicename == 'dzbj') is_bj = true;
this.foundDevices.push({
is_bj: is_bj,
name: device.name || '未知设备',
deviceId: device.deviceId,
rssi: device.RSSI,
connected: device.deviceId === this.connectedDeviceId
});
});
},
// 计算信号强度条宽度(-100dBm为0%-30dBm为100%
getRssiWidth(rssi) {
const minRssi = -100;
const maxRssi = -30;
let ratio = (rssi - minRssi) / (maxRssi - minRssi);
ratio = Math.max(0, Math.min(1, ratio)); // 限制在0-1之间
return `${ratio * 100}%`;
},
connectDevice(device) {
var that = this
this.stopScan();
uni.showLoading({ title: '连接中...' });
uni.createBLEConnection({
deviceId:device.deviceId,
success(res) {
that.foundDevices = that.foundDevices.map(d => ({
...d,
connected: d.deviceId === device.deviceId
}));
uni.hideLoading();
uni.showToast({ title: `已连接${device.name}`, icon: 'none' });
uni.navigateTo({
url:'../connect/connect?deviceId='+device.deviceId,
fail: (err) => {
uni.showToast({ title: `连接失败,请稍后重试`, icon: 'error' });
uni.closeBLEConnection({
deviceId:device.deviceId,
success() {
console('断开连接成功')
}
})
}
})
},
fail() {
uni.hideLoading();
uni.showToast({ title: `连接失败`, icon: 'error' });
}
})
}
}
};
</script>
<style scoped>
.container {
padding: 16rpx;
background-color: #f5f5f5;
/* min-height: 100vh; */
}
/* 标题栏 */
.title-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 0;
border-bottom: 1px solid #eee;
margin-bottom: 20rpx;
margin-top: 80rpx;
}
.title {
font-size: 36rpx;
font-weight: bold;
color: #333;
}
.scan-btn {
background-color: #00c900;
color: white;
padding: 0rpx 24rpx;
border-radius: 8rpx;
font-size: 28rpx;
margin-right: 5%;
}
.scan-btn:disabled {
background-color: #ccc;
}
/* 状态提示 */
.status-tip {
text-align: center;
padding: 40rpx 0;
color: #666;
font-size: 28rpx;
}
.loading {
width: 30rpx;
height: 30rpx;
border: 3rpx solid #007aff;
border-top-color: transparent;
border-radius: 50%;
margin: 20rpx auto;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* 设备列表 */
.device-list {
display: flex;
flex-direction: column;
gap: 16rpx;
}
.device-item {
background-color: white;
border-radius: 12rpx;
padding: 24rpx;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.1);
cursor: pointer;
}
.device-name {
display: flex;
justify-content: space-between;
margin-bottom: 16rpx;
}
.device-name text:first-child {
font-size: 32rpx;
color: #333;
}
.is_bj{
padding: 3px 8px;
border-radius: 5px;
color: white;
background-color: rgba(30, 228, 156, 0.4);
position: relative;
right: 30px;
}
.device-id {
font-size: 24rpx;
color: #999;
}
.device-rssi {
display: flex;
flex-direction: column;
gap: 8rpx;
}
.device-rssi text {
font-size: 26rpx;
color: #666;
}
.rssi-bar {
height: 8rpx;
background-color: #eee;
border-radius: 4rpx;
overflow: hidden;
}
.rssi-bar::after {
content: '';
display: block;
height: 100%;
background: linear-gradient(to right, #ff4d4f, #faad14, #52c41a);
}
.device-status {
margin-top: 16rpx;
text-align: right;
}
.connected {
font-size: 26rpx;
color: #07c160;
background-color: #f0fff4;
padding: 4rpx 16rpx;
border-radius: 12rpx;
}
</style>