uniapp蓝牙搜索连接展示蓝牙设备包含信号显示

参考界面

注意在manifest.json里在 安卓打包配置里permissions里配置蓝牙模块

javascript 复制代码
					"<uses-permission android:name=\"android.permission.BLUETOOTH\"/>",
					//蓝牙管理权限
					"<uses-permission android:name=\"android.permission.BLUETOOTH_ADMIN\"/>",
					// 蓝牙配对权限
					"<uses-permission android:name=\"android.permission.BLUETOOTH_PRIVILEGED\"/>",
					"<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
					"<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
					//用于使用蓝牙扫描附件其他的蓝牙设备 Android 12
					"<uses-permission android:name=\"android.permission.BLUETOOTH_SCAN\"/>",
					//用于允许当前的设备被其他的蓝牙设备所发现 Android 12
					"<uses-permission android:name=\"android.permission.BLUETOOTH_ADVERTISE\"/>",
					//用于连接之前已经配对过的蓝牙设备 Android 12
					"<uses-permission android:name=\"android.permission.BLUETOOTH_CONNECT\"/>"

以下是参考代码

html 复制代码
<template>
	<view class="content" :class="{ 'landscape': isLandscape }">
	<view class="text-area">
			<text class="title">{{title}}</text>
		</view> 

		<!-- 蓝牙状态显示 -->
		<view class="bluetooth-section">
			<view class="status-card">
				<view class="status-header">
					<text class="status-title">蓝牙连接状态</text>
					<view class="status-indicator" :class="bluetoothStatus">
						{{ getStatusText(bluetoothStatus) }}
					</view>
				</view>

				<view class="device-info" v-if="connectedDevice">
					<text class="info-label">已连接设备:</text>
					<text class="info-value">{{ connectedDevice.name }}</text>
					<text class="info-label">设备ID:</text>
					<text class="info-value">{{ connectedDevice.deviceId }}</text>
				</view>

				<view class="no-device" v-else>
					<text class="no-device-text">未连接任何设备</text>
				</view>
			</view>

			<!-- 操作按钮 -->
			<view class="action-buttons" :class="{ 'landscape-buttons': isLandscape }">
				<button class="btn" @click="initBluetooth" :disabled="loading">
					{{ loading ? '操作中...' : '初始化蓝牙' }}
				</button>

				<button class="btn" @click="startBluetoothDiscovery" :disabled="!bluetoothAvailable">
					搜索设备
				</button>

				<button class="btn btn-secondary" @click="stopBluetoothDiscovery" :disabled="!isDiscovering">
					停止搜索
				</button>
				<!-- 添加刷新按钮 -->
				<button class="btn btn-refresh" @click="refreshDevices" :disabled="loading">
					刷新设备列表
				</button>

				<!-- <button class="btn btn-danger" @click="closeBluetooth" :disabled="!bluetoothAvailable">
			  	关闭蓝牙
			  </button> -->
			</view>

			<!-- 服务信息 -->
			<view class="service-section" v-if="services.length > 0">
				<view class="service-card">
					<text class="section-title">设备服务信息</text>

					<view class="service-info">
						<text class="info-label">服务数量:</text>
						<text class="info-value">{{ services.length }} 个</text>

						<text class="info-label">当前服务:</text>
						<text class="info-value">{{ currentServiceId }}</text>

						<text class="info-label">特征值数量:</text>
						<text class="info-value">{{ characteristics.length }} 个</text>
					</view>
				</view>
			</view>

			<!-- 数据通信 -->
			<view class="communication-section" v-if="deviceConnected">
				<view class="communication-card">
					<text class="section-title">数据通信</text>

					<!-- 发送数据 -->
					<view class="send-section">
						<text class="input-label">发送数据:</text>
						<view class="input-group">
							<input class="data-input" v-model="sendData" placeholder="输入要发送的数据" />
							<button class="btn btn-send" @click="sendDataToDevice">发送</button>
						</view>
					</view>

					<!-- 接收数据 -->
					<view class="receive-section">
						<view class="receive-header">
							<text class="receive-title">接收数据 (最近50条)</text>
							<text class="receive-count">{{ receivedData.length }} 条</text>
						</view>

						<scroll-view class="receive-scroll" scroll-y>
							<view v-for="(item, index) in receivedData" :key="index" class="data-item">
								<text class="data-time">{{ item.timestamp }}</text>
								<text class="data-content">{{ item.data }}</text>
							</view>
						</scroll-view>
					</view>
				</view>
			</view>
			<!-- 设备列表 -->
			<!-- 设备列表 -->
			<view class="devices-list" v-if="devices.length > 0">
				<text class="list-title">可用设备 ({{ devices.length }})</text>
				<view class="sort-info">
					<text class="sort-text">按信号强度排序 ▼</text>
				</view>
				<scroll-view class="devices-scroll" scroll-y>
					<view v-for="device in devices" :key="device.deviceId" class="device-item"
						@click="connectToDevice(device)">
						<view class="device-main">
							<text class="device-name">{{ device.name || '未知设备' }}</text>
							<text class="device-id">{{ device.deviceId }}</text>
						</view>
						<view class="device-rssi">
							<view class="rssi-indicator" :class="getRSSILevel(device.RSSI)"></view>
							<text class="rssi-text">{{ getRSSIDisplay(device.RSSI) }}</text>
						</view>
						<view class="connect-indicator" :class="{ 'connected': device.connected }">
							{{ device.connected ? '已连接' : '点击连接' }}
						</view>
					</view>
				</scroll-view>
			</view>

			<!-- 连接状态提示 -->
			<view class="connection-status" v-if="connectionStatus">
				<text :class="['status-text', connectionStatus.type]">
					{{ connectionStatus.message }}
				</text>
			</view>
		</view>
	</view>
</template>

<script>
export default {
	data() {
		return {
			bluetoothStatus: 'unavailable',
			bluetoothAvailable: false,
			isDiscovering: false,
			devices: [],
			connectedDevice: null,
			loading: false,
			connectionStatus: null,
			isLandscape: false, // 添加横屏状态
			windowHeight: 0,
			windowWidth: 0,
			// 新增蓝牙服务相关数据
			services: [], // 设备服务列表
			characteristics: [], // 特征值列表
			currentServiceId: '', // 当前选择的服务ID
			currentCharacteristicId: '', // 当前选择的特征值ID
			notifyEnabled: false, // 是否开启通知
			deviceConnected: false, // 设备连接状态
			receivedData: [], // 接收到的数据
			sendData: '' // 要发送的数据
		}
	},
	onLoad() {
		this.initBluetooth();
		this.checkBluetoothStatus();
		this.checkScreenOrientation(); // 检查初始屏幕方向
		this.startOrientationListener(); // 开始监听屏幕方向变化
		// 添加权限检查
		this.checkAndRequestPermissions();
	},
	onUnload() {
		// 页面卸载时关闭蓝牙
		this.closeBluetooth();
	},
	methods: {
		// 获取信号强度显示文本
		getRSSIDisplay(rssi) {
			if (rssi === null || rssi === undefined) return '未知';
			return `${rssi} dBm`;
		},

		// 获取信号强度等级
		getRSSILevel(rssi) {
			if (rssi === null || rssi === undefined) return 'unknown';

			if (rssi >= -50) return 'excellent'; // 优秀
			if (rssi >= -60) return 'good'; // 良好
			if (rssi >= -70) return 'fair'; // 一般
			if (rssi >= -80) return 'weak'; // 较弱
			return 'very-weak'; // 很弱
		},

		// 刷新设备列表
		async refreshDevices() {
			this.loading = true;
			try {
				// 清空当前列表
				this.devices = [];

				// 重新获取已配对设备
				await this.getPairedDevices();

				// 重新开始搜索
				if (this.bluetoothAvailable && !this.isDiscovering) {
					await this.startBluetoothDiscovery();
				}

				this.connectionStatus = {
					message: '设备列表已刷新',
					type: 'success'
				};
			} catch (error) {
				console.error('刷新设备列表失败:', error);
			} finally {
				this.loading = false;
			}
		},
		// 检查并请求权限
		async checkAndRequestPermissions() {
			try {
				// 检查位置权限(蓝牙扫描需要)
				const result = await uni.authorize({
					scope: 'scope.location'
				});
				console.log('位置权限授权结果:', result);

				// 对于 Android 12+,还需要蓝牙扫描权限
				// if (uni.getSystemInfoSync().platform === 'android') {
				// 	// 尝试请求蓝牙相关权限
				// 	await this.requestBluetoothPermissions();
				// }

			} catch (error) {
				console.log('权限授权失败或用户拒绝:', error);
				// 可以在这里引导用户去设置页面开启权限
				// this.showPermissionGuide();
			}
		},

		// 请求蓝牙权限
		async requestBluetoothPermissions() {
			return new Promise((resolve, reject) => {
				// 这里可以使用原生插件或自定义方式请求权限
				// 暂时使用提示方式
				uni.showModal({
					title: '蓝牙权限提示',
					content: '应用需要蓝牙权限来扫描和连接设备,请确保已授予相关权限',
					showCancel: false,
					success: resolve,
					fail: reject
				});
			});
		},
		async getConnectedDevices() {
			try {
				const res = await uni.getConnectedBluetoothDevices({
					services: [] // 空数组表示获取所有已连接设备
				});

				console.log('已连接的蓝牙设备:', res.devices);

				if (res.devices && res.devices.length > 0) {
					// 处理已连接的设备
					res.devices.forEach(device => {
						// 更新设备列表中的连接状态
						this.devices = this.devices.map(d => {
							if (d.deviceId === device.deviceId) {
								return {
									...d,
									connected: true
								};
							}
							return d;
						});

						// 设置当前连接设备
						this.connectedDevice = {
							name: device.name,
							deviceId: device.deviceId
						};
					});

					// 排序设备列表
					this.sortDevicesByRSSI();
				} else {
					console.log('没有已连接的设备');
					this.connectedDevice = null;
				}
			} catch (err) {
				console.error('获取已连接设备失败:', err);
				// 如果API不支持,尝试使用其他方法
				this.fallbackGetConnectedDevices();
			}
		},

		// 备选方法:通过设备列表筛选已连接设备
		fallbackGetConnectedDevices() {
			uni.getBluetoothDevices({
				success: (res) => {
					const connectedDevices = res.devices.filter(device => {
						// 这里可以根据实际需求判断设备是否已连接
						// 有些平台可能没有明确的连接状态标识
						return device.connected || this.isDeviceConnected(device.deviceId);
					});

					if (connectedDevices.length > 0) {
						const device = connectedDevices[0];
						this.connectedDevice = {
							name: device.name,
							deviceId: device.deviceId
						};

						// 更新设备列表连接状态
						this.devices = this.devices.map(d => {
							if (d.deviceId === device.deviceId) {
								return {
									...d,
									connected: true
								};
							}
							return d;
						});

						// 排序设备列表
						this.sortDevicesByRSSI();
					}
				}
			});
		},
		// 检查设备是否已连接
		isDeviceConnected(deviceId) {
			// 这里可以添加您的设备连接判断逻辑
			return this.connectedDevice && this.connectedDevice.deviceId === deviceId;
		},

		// 检查屏幕方向
		checkScreenOrientation() {
			const res = uni.getSystemInfoSync();
			this.windowHeight = res.windowHeight;
			this.windowWidth = res.windowWidth;
			this.isLandscape = res.windowWidth > res.windowHeight;
		},

		// 监听屏幕方向变化
		startOrientationListener() {
			uni.onWindowResize((res) => {
				this.windowHeight = res.size.windowHeight;
				this.windowWidth = res.size.windowWidth;
				this.isLandscape = res.size.windowWidth > res.size.windowHeight;
			});
		},

		// 在页面卸载时取消监听
		beforeDestroy() {
			uni.offWindowResize();
		},
		// 检查蓝牙状态
		async checkBluetoothStatus() {
			try {
				const res = await uni.getBluetoothAdapterState();
				console.log('蓝牙适配器状态:', res);
				this.bluetoothStatus = res.available ? 'enabled' : 'disabled';
				this.bluetoothAvailable = res.available;
				this.isDiscovering = res.discovering;
			} catch (error) {
				console.error('获取蓝牙状态失败:', error);
				this.bluetoothStatus = 'unavailable';
				this.bluetoothAvailable = false;
			}
		},

		// 初始化蓝牙
		async initBluetooth() {
			this.loading = true;
			this.connectionStatus = {
				message: '正在初始化蓝牙...',
				type: 'info'
			};

			try {
				// 先检查权限
				await this.checkAndRequestPermissions();

				// 打开蓝牙适配器
				await uni.openBluetoothAdapter();

				this.connectionStatus = {
					message: '蓝牙初始化成功',
					type: 'success'
				};

				this.bluetoothStatus = 'enabled';
				this.bluetoothAvailable = true;

				// 开始监听蓝牙状态变化
				this.startBluetoothListeners();

				// 先获取已连接设备,再开始搜索
				await this.getConnectedDevices();

				// 如果已经有连接设备,就不需要搜索了
				if (!this.connectedDevice) {
					setTimeout(() => {
						this.startBluetoothDiscovery();
					}, 1000);
				}

			} catch (error) {
				console.error('蓝牙初始化失败:', error);
				this.handleBluetoothError(error);

				// 如果是权限问题,提示用户
				if (error.errCode === 10013) {
					this.showPermissionGuide();
				}
			} finally {
				this.loading = false;
			}
		},

		// 开始监听蓝牙事件
		startBluetoothListeners() {
			// 蓝牙适配器状态变化
			uni.onBluetoothAdapterStateChange((res) => {
				console.log('蓝牙适配器状态变化:', res);
				this.bluetoothAvailable = res.available;
				this.isDiscovering = res.discovering;
				this.bluetoothStatus = res.available ? 'enabled' : 'disabled';
				this.getConnectedDevices();
			});

			// 发现新设备 - 增强日志
			uni.onBluetoothDeviceFound((res) => {
				// console.log('发现新设备详情:', res.devices);

				// 过滤设备,包括经典蓝牙设备
				const filteredDevices = res.devices.filter(device => {

					return device.deviceId && (device.name || device.localName);
				});

				// console.log('过滤后的设备:', filteredDevices);
				this.addOrUpdateDevices(filteredDevices);
			});

			// 蓝牙连接状态变化
			uni.onBLEConnectionStateChange((res) => {
				console.log('蓝牙连接状态变化:', res);
				this.handleConnectionStateChange(res);
			});
		},
		// 添加或更新设备列表,并按信号强度排序
		addOrUpdateDevices(newDevices) {
			newDevices.forEach(newDevice => {
				const existingIndex = this.devices.findIndex(d => d.deviceId === newDevice.deviceId);
				if (existingIndex > -1) {
					// 更新现有设备信息,保留连接状态
					this.devices[existingIndex] = {
						...this.devices[existingIndex],
						...newDevice
					};
				} else {
					// 添加新设备
					this.devices.push({
						...newDevice,
						connected: false
					});
				}
			});

			// 按信号强度排序,信号最强的排在最上面
			this.sortDevicesByRSSI();
		},

		// 按信号强度排序设备
		sortDevicesByRSSI() {
			this.devices.sort((a, b) => {
				// 处理 RSSI 值为 null 或 undefined 的情况
				const rssiA = a.RSSI !== null && a.RSSI !== undefined ? a.RSSI : -999;
				const rssiB = b.RSSI !== null && b.RSSI !== undefined ? b.RSSI : -999;

				// RSSI 值越大表示信号越强,所以降序排列
				return rssiB - rssiA;
			});
		},

		// 开始搜索设备
		async startBluetoothDiscovery() {
			if (!this.bluetoothAvailable) {
				this.connectionStatus = {
					message: '蓝牙未初始化或不可用',
					type: 'error'
				};
				return;
			}

			try {
				// 清空设备列表
				this.devices = [];

				this.connectionStatus = {
					message: '正在搜索蓝牙设备...',
					type: 'info'
				};

				// 先获取已配对设备
				await this.getPairedDevices();

				// 开始搜索新设备
				await uni.startBluetoothDevicesDiscovery({
					allowDuplicatesKey: true, // 改为 true 以获取更多设备
					interval: 1000, // 缩短间隔
					powerLevel: 'high' // 高功率扫描
				});

				this.isDiscovering = true;
				this.connectionStatus = {
					message: '正在搜索蓝牙设备...',
					type: 'info'
				};

				// 30秒后自动停止搜索(给更多时间)
				setTimeout(() => {
					this.stopBluetoothDiscovery();
				}, 30000);

			} catch (error) {
				console.error('开始搜索失败:', error);
				this.connectionStatus = {
					message: `搜索失败: ${error.errMsg || error.message}`,
					type: 'error'
				};
			}
		},
		// 获取已配对设备
		async getPairedDevices() {
			return new Promise((resolve) => {
				uni.getBluetoothDevices({
					success: (res) => {
						console.log('已配对设备:', res.devices);
						if (res.devices && res.devices.length > 0) {
							// 过滤出有效的设备
							const validDevices = res.devices.filter(device =>
								device.name && device.deviceId
							);
							this.addOrUpdateDevices(validDevices);
						}
						resolve();
					},
					fail: (err) => {
						console.error('获取已配对设备失败:', err);
						resolve();
					}
				});
			});
		},

		// 停止搜索设备
		async stopBluetoothDiscovery() {
			try {
				await uni.stopBluetoothDevicesDiscovery();
				this.isDiscovering = false;
				this.connectionStatus = {
					message: '已停止搜索设备',
					type: 'info'
				};
			} catch (error) {
				console.error('停止搜索失败:', error);
			}
		},

		// 连接到设备
		async connectToDevice(device) {
			if (device.connected) {
				this.connectionStatus = {
					message: '设备已连接',
					type: 'info'
				};
				return;
			}

			this.loading = true;
			this.connectionStatus = {
				message: `正在连接 ${device.name || '设备'}...`,
				type: 'info'
			};

			try {
				// 创建蓝牙连接
				await uni.createBLEConnection({
					deviceId: device.deviceId,
					timeout: 10000
				});

				this.connectionStatus = {
					message: '连接成功,正在发现服务...',
					type: 'info'
				};

				// 等待一段时间让连接稳定
				await new Promise(resolve => setTimeout(resolve, 500));

				// 获取蓝牙设备服务
				await this.getBLEDeviceServices(device.deviceId);

				this.deviceConnected = true;

				// 更新设备连接状态
				this.updateDeviceConnectionStatus(device.deviceId, true);

			} catch (error) {
				console.error('连接设备失败:', error);
				this.connectionStatus = {
					message: `连接失败: ${error.errMsg || error.message}`,
					type: 'error'
				};
				this.updateDeviceConnectionStatus(device.deviceId, false);
			} finally {
				this.loading = false;
			}
		},
		// 获取蓝牙设备服务
		async getBLEDeviceServices(deviceId) {
			try {
				const res = await uni.getBLEDeviceServices({
					deviceId: deviceId
				});

				console.log('获取设备服务成功:', res.services);
				this.services = res.services;

				if (res.services && res.services.length > 0) {
					// 默认选择第一个服务
					this.currentServiceId = res.services[0].uuid;
					this.connectionStatus = {
						message: `发现 ${res.services.length} 个服务,正在获取特征值...`,
						type: 'info'
					};

					// 获取该服务的特征值
					await this.getBLEDeviceCharacteristics(deviceId, this.currentServiceId);
				} else {
					this.connectionStatus = {
						message: '未发现任何服务',
						type: 'warning'
					};
				}
			} catch (error) {
				console.error('获取设备服务失败:', error);
				this.connectionStatus = {
					message: `获取服务失败: ${error.errMsg || error.message}`,
					type: 'error'
				};
			}
		},
		// 获取蓝牙设备特征值
		async getBLEDeviceCharacteristics(deviceId, serviceId) {
			try {
				const res = await uni.getBLEDeviceCharacteristics({
					deviceId: deviceId,
					serviceId: serviceId
				});

				console.log('获取特征值成功:', res.characteristics);
				this.characteristics = res.characteristics;

				if (res.characteristics && res.characteristics.length > 0) {
					this.connectionStatus = {
						message: `发现 ${res.characteristics.length} 个特征值`,
						type: 'success'
					};

					// 自动寻找可读写的特征值
					await this.setupCharacteristic(deviceId, serviceId, res.characteristics);
				} else {
					this.connectionStatus = {
						message: '未发现任何特征值',
						type: 'warning'
					};
				}
			} catch (error) {
				console.error('获取特征值失败:', error);
				this.connectionStatus = {
					message: `获取特征值失败: ${error.errMsg || error.message}`,
					type: 'error'
				};
			}
		},
		// 设置特征值(自动寻找合适的特征值并开启通知)
		async setupCharacteristic(deviceId, serviceId, characteristics) {
			// 优先寻找支持 notify 和 write 的特征值
			const suitableChar = characteristics.find(char => {
				return char.properties.notify && char.properties.write;
			});

			if (suitableChar) {
				this.currentCharacteristicId = suitableChar.uuid;

				// 开启通知
				await this.enableBLECharacteristicNotification(deviceId, serviceId, suitableChar.uuid);

				this.connectionStatus = {
					message: '已开启数据通知,可以通信了',
					type: 'success'
				};

			} else {
				// 如果没有同时支持 notify 和 write 的特征值,寻找其他可用特征值
				const notifyChar = characteristics.find(char => char.properties.notify);
				const writeChar = characteristics.find(char => char.properties.write);

				if (notifyChar) {
					await this.enableBLECharacteristicNotification(deviceId, serviceId, notifyChar.uuid);
				}

				if (writeChar) {
					this.currentCharacteristicId = writeChar.uuid;
				}

				this.connectionStatus = {
					message: '已配置可用特征值',
					type: 'info'
				};
			}
		},
		// 开启特征值通知
		async enableBLECharacteristicNotification(deviceId, serviceId, characteristicId) {
			try {
				await uni.notifyBLECharacteristicValueChange({
					deviceId: deviceId,
					serviceId: serviceId,
					characteristicId: characteristicId,
					state: true
				});

				this.notifyEnabled = true;
				console.log('开启通知成功');

				// 监听特征值变化
				uni.onBLECharacteristicValueChange((res) => {
					this.onBLECharacteristicValueChange(res);
				});

			} catch (error) {
				console.error('开启通知失败:', error);
				this.connectionStatus = {
					message: `开启通知失败: ${error.errMsg || error.message}`,
					type: 'error'
				};
			}
		},
		// 接收到蓝牙设备数据
		onBLECharacteristicValueChange(res) {
			console.log('接收到蓝牙数据:', res);

			// 将 ArrayBuffer 转换为十六进制字符串
			const hexString = this.ab2hex(res.value);
			const timestamp = new Date().toLocaleTimeString();

			this.receivedData.unshift({
				timestamp: timestamp,
				data: hexString,
				raw: res.value
			});

			// 限制接收数据条数
			if (this.receivedData.length > 50) {
				this.receivedData = this.receivedData.slice(0, 50);
			}

			console.log(`[${timestamp}] 接收数据: ${hexString}`);
		},
		// 向蓝牙设备发送数据
		async sendDataToDevice() {
			if (!this.deviceConnected || !this.currentCharacteristicId || !this.sendData) {
				this.connectionStatus = {
					message: '请先连接设备并输入要发送的数据',
					type: 'warning'
				};
				return;
			}

			try {
				// 将字符串转换为 ArrayBuffer
				const buffer = this.str2ab(this.sendData);

				await uni.writeBLECharacteristicValue({
					deviceId: this.connectedDevice.deviceId,
					serviceId: this.currentServiceId,
					characteristicId: this.currentCharacteristicId,
					value: buffer
				});

				const timestamp = new Date().toLocaleTimeString();
				this.connectionStatus = {
					message: `数据发送成功: ${this.sendData}`,
					type: 'success'
				};

				console.log(`[${timestamp}] 发送数据: ${this.sendData}`);

				// 清空发送数据
				this.sendData = '';

			} catch (error) {
				console.error('发送数据失败:', error);
				this.connectionStatus = {
					message: `发送失败: ${error.errMsg || error.message}`,
					type: 'error'
				};
			}
		},
		// ArrayBuffer 转十六进制字符串
		ab2hex(buffer) {
			const hexArr = Array.prototype.map.call(
				new Uint8Array(buffer),
				bit => ('00' + bit.toString(16)).slice(-2)
			);
			return hexArr.join(' ');
		},

		// 字符串转 ArrayBuffer
		str2ab(str) {
			const buf = new ArrayBuffer(str.length);
			const bufView = new Uint8Array(buf);
			for (let i = 0; i < str.length; i++) {
				bufView[i] = str.charCodeAt(i);
			}
			return buf;
		},

		// 更新设备连接状态
		updateDeviceConnectionStatus(deviceId, connected) {
			this.devices = this.devices.map(device => {
				if (device.deviceId === deviceId) {
					return {
						...device,
						connected
					};
				}
				return device;
			});

			if (connected) {
				const device = this.devices.find(d => d.deviceId === deviceId);
				if (device) {
					this.connectedDevice = {
						...device
					};
				}
			} else {
				this.connectedDevice = null;
			}

			// 更新连接状态后重新排序
			this.sortDevicesByRSSI();
		},
		// 断开设备连接
		async disconnectDevice() {
			if (!this.connectedDevice) return;

			try {
				// 关闭通知
				if (this.notifyEnabled) {
					await uni.notifyBLECharacteristicValueChange({
						deviceId: this.connectedDevice.deviceId,
						serviceId: this.currentServiceId,
						characteristicId: this.currentCharacteristicId,
						state: false
					});
					this.notifyEnabled = false;
				}

				// 断开连接
				await uni.closeBLEConnection({
					deviceId: this.connectedDevice.deviceId
				});

				// 重置状态
				this.updateDeviceConnectionStatus(this.connectedDevice.deviceId, false);
				this.deviceConnected = false;
				this.services = [];
				this.characteristics = [];
				this.receivedData = [];

				this.connectionStatus = {
					message: '设备已断开连接',
					type: 'info'
				};

			} catch (error) {
				console.error('断开连接失败:', error);
			}
		},
		// 关闭蓝牙
		async closeBluetooth() {
			try {
				// 停止搜索
				if (this.isDiscovering) {
					await this.stopBluetoothDiscovery();
				}

				// 断开已连接的设备
				if (this.connectedDevice) {
					await uni.closeBLEConnection({
						deviceId: this.connectedDevice.deviceId
					});
				}

				// 关闭蓝牙适配器
				await uni.closeBluetoothAdapter();

				this.bluetoothStatus = 'disabled';
				this.bluetoothAvailable = false;
				this.devices = [];
				this.connectedDevice = null;
				this.connectionStatus = {
					message: '蓝牙已关闭',
					type: 'info'
				};

			} catch (error) {
				console.error('关闭蓝牙失败:', error);
			}
		},

		// 获取状态文本
		getStatusText(status) {
			const statusMap = {
				unavailable: '不可用',
				available: '可用',
				enabled: '已开启',
				disabled: '已关闭'
			};
			return statusMap[status] || '未知';
		},
		// 处理蓝牙错误
		handleBluetoothError(error) {
			let errorMessage = '蓝牙操作失败';

			if (error.errCode === 10001) {
				errorMessage = '蓝牙适配器不可用,请检查手机蓝牙是否开启';
			} else if (error.errCode === 10002) {
				errorMessage = '没有找到指定设备';
			} else if (error.errCode === 10003) {
				errorMessage = '连接失败,请重试';
			} else if (error.errCode === 10004) {
				errorMessage = '没有找到指定服务';
			} else if (error.errCode === 10005) {
				errorMessage = '没有找到指定特征值';
			} else if (error.errCode === 10006) {
				errorMessage = '当前连接已断开';
			} else if (error.errCode === 10007) {
				errorMessage = '当前特征值不支持此操作';
			} else if (error.errCode === 10008) {
				errorMessage = '其余所有系统上报的异常';
			} else if (error.errCode === 10009) {
				errorMessage = 'Android 系统特有,系统版本低于 4.3 不支持 BLE';
			} else if (error.errCode === 10012) {
				errorMessage = '连接超时';
			} else if (error.errCode === 10013) {
				errorMessage = '未授权位置权限';
			}

			this.connectionStatus = {
				message: errorMessage,
				type: 'error'
			};
		}

	}
}
</script>

<style>
.content {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: flex-start;
	padding: 20rpx;
}

.logo {
	height: 120rpx;
	width: 120rpx;
	margin-top: 40rpx;
	margin-bottom: 30rpx;
}

.text-area {
	display: flex;
	justify-content: center;
	margin-bottom: 40rpx;
}

.title {
	font-size: 36rpx;
	color: #8f8f94;
}

/* 蓝牙相关样式 */
.bluetooth-section {
	width: 100%;
	max-width: 700rpx;
}

.status-card {
	background: white;
	border-radius: 16rpx;
	padding: 30rpx;
	margin-bottom: 30rpx;
	box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
	position: fixed;
	width: 90%;
	z-index: 10000;

}

.status-header {
	display: flex;
	justify-content: space-between;
	align-items: center;
	margin-bottom: 20rpx;

}

.status-title {
	font-size: 32rpx;
	font-weight: bold;
	color: #333;
}

.status-indicator {
	padding: 8rpx 16rpx;
	border-radius: 20rpx;
	font-size: 24rpx;
	color: white;
}

.status-indicator.enabled {
	background-color: #34C759;
}

.status-indicator.disabled {
	background-color: #FF3B30;
}

.status-indicator.unavailable {
	background-color: #FF9500;
}

.status-indicator.available {
	background-color: #007AFF;
}

.device-info {
	margin-top: 20rpx;
}

.info-label {
	display: block;
	font-size: 26rpx;
	color: #666;
	margin-bottom: 8rpx;
}

.info-value {
	display: block;
	font-size: 28rpx;
	color: #333;
	font-weight: 500;
	margin-bottom: 16rpx;
	word-break: break-all;
}

.no-device {
	text-align: center;
	padding: 40rpx 0;
}

.no-device-text {
	font-size: 28rpx;
	color: #999;
}

.action-buttons {
	margin-bottom: 30rpx;
	margin-top: 300rpx;
	text-align: center;
	/* 可选,居中显示按钮 */
}

.btn {
	background-color: #007AFF;
	color: white;
	border: none;
	border-radius: 12rpx;
	padding: 24rpx;
	font-size: 28rpx;
	display: inline-block;
	margin: 10rpx;
	/* 添加间距 */
	min-width: 150rpx;
	/* 控制按钮最小宽度 */
}

.btn:disabled {
	background-color: #cccccc;
}

.btn-secondary {
	background-color: #34C759;
}

.btn-danger {
	background-color: #FF3B30;
}

.devices-list {
	background: white;
	border-radius: 16rpx;
	padding: 30rpx;
	box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
}

.list-title {
	font-size: 30rpx;
	font-weight: bold;
	color: #333;
	margin-bottom: 20rpx;
	display: block;
}

.devices-scroll {
	max-height: 500rpx;
}

.device-item {
	display: flex;
	justify-content: space-between;
	align-items: center;
	padding: 24rpx;
	border-bottom: 1rpx solid #f0f0f0;
	background: #fafafa;
	border-radius: 12rpx;
	margin-bottom: 16rpx;
	transition: all 0.3s ease;
}

.device-item:active {
	background: #f0f0f0;
	transform: scale(0.98);
}

.device-main {
	flex: 1;
	min-width: 0;
	/* 防止内容溢出 */
}

.device-name {
	display: block;
	font-size: 28rpx;
	color: #333;
	font-weight: 500;
	margin-bottom: 8rpx;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.device-id {
	display: block;
	font-size: 22rpx;
	color: #666;
	word-break: break-all;
	font-family: monospace;
}

.device-rssi {
	margin: 0 20rpx;
}

.rssi-text {
	font-size: 24rpx;
	color: #999;
}

.connect-indicator {
	padding: 8rpx 16rpx;
	border-radius: 12rpx;
	font-size: 22rpx;
	color: white;
	background-color: #007AFF;
}

.connect-indicator.connected {
	background-color: #34C759;
}

.connection-status {
	margin-top: 20rpx;
	padding: 20rpx;
	border-radius: 12rpx;
	text-align: center;
}

.status-text {
	font-size: 26rpx;
	font-weight: 500;
}

.status-text.success {
	color: #34C759;
}

.status-text.error {
	color: #FF3B30;
}

.status-text.warning {
	color: #FF9500;
}

.status-text.info {
	color: #007AFF;
}

/* 横屏样式 */
.landscape {
	flex-direction: row;
	flex-wrap: wrap;
}

.landscape .text-area {
	width: 100%;
	margin-bottom: 20rpx;
}

.landscape .bluetooth-section {
	display: flex;
	flex-direction: row;
	flex-wrap: wrap;
	width: 100%;
}

.landscape .status-card {
	flex: 1;
	min-width: 400rpx;
	margin-right: 20rpx;
}

.landscape .action-buttons {
	flex: 1;
	min-width: 300rpx;
	display: flex;
	flex-direction: column;
	justify-content: flex-start;
}

.landscape .action-buttons.landscape-buttons {
	flex-direction: row;
	flex-wrap: wrap;
}

.landscape .action-buttons.landscape-buttons .btn {
	flex: 1;
	min-width: 200rpx;
	margin-right: 10rpx;
	margin-bottom: 10rpx;
}

.landscape .devices-list {
	width: 100%;
	margin-top: 20rpx;
}

/* 平板大屏适配 */
@media screen and (min-width: 768px) {
	.bluetooth-section {
		max-width: 1200rpx;
	}

	.landscape .status-card,
	.landscape .action-buttons {
		min-width: 500rpx;
	}
}

.service-section,
.communication-section {
	width: 100%;
	margin-top: 30rpx;
}

.service-card,
.communication-card {
	background: white;
	border-radius: 16rpx;
	padding: 30rpx;
	margin-bottom: 30rpx;
	box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
}

.section-title {
	font-size: 30rpx;
	font-weight: bold;
	color: #333;
	margin-bottom: 20rpx;
	display: block;
}

.send-section {
	margin-bottom: 30rpx;
}

.input-label {
	font-size: 26rpx;
	color: #666;
	margin-bottom: 10rpx;
	display: block;
}

.input-group {
	display: flex;
	gap: 20rpx;
}

.data-input {
	flex: 1;
	border: 1rpx solid #ddd;
	border-radius: 8rpx;
	padding: 20rpx;
	font-size: 26rpx;
}

.btn-send {
	background-color: #34C759;
	min-width: 120rpx;
}

.receive-header {
	display: flex;
	justify-content: space-between;
	align-items: center;
	margin-bottom: 15rpx;
}

.receive-title {
	font-size: 26rpx;
	color: #333;
	font-weight: 500;
}

.receive-count {
	font-size: 24rpx;
	color: #666;
}

.receive-scroll {
	max-height: 400rpx;
	border: 1rpx solid #f0f0f0;
	border-radius: 8rpx;
	padding: 15rpx;
}

.data-item {
	padding: 15rpx 0;
	border-bottom: 1rpx solid #f5f5f5;
}

.data-item:last-child {
	border-bottom: none;
}

.data-time {
	font-size: 22rpx;
	color: #999;
	margin-right: 15rpx;
}

.data-content {
	font-size: 24rpx;
	color: #333;
	word-break: break-all;
}

.sort-info {
	margin-bottom: 15rpx;
	text-align: center;
}

.sort-text {
	font-size: 24rpx;
	color: #007AFF;
	background: rgba(0, 122, 255, 0.1);
	padding: 8rpx 16rpx;
	border-radius: 20rpx;
}

.device-rssi {
	margin: 0 20rpx;
	display: flex;
	flex-direction: column;
	align-items: center;
	min-width: 120rpx;
}

.rssi-indicator {
	width: 60rpx;
	height: 8rpx;
	border-radius: 4rpx;
	margin-bottom: 8rpx;
	transition: all 0.3s ease;
}

.rssi-indicator.excellent {
	width: 80rpx;
	background: linear-gradient(90deg, #34C759, #34C759);
	box-shadow: 0 0 8rpx rgba(52, 199, 89, 0.5);
}

.rssi-indicator.good {
	width: 70rpx;
	background: linear-gradient(90deg, #34C759 70%, #FFD60A 100%);
}

.rssi-indicator.fair {
	width: 60rpx;
	background: linear-gradient(90deg, #FFD60A 50%, #FF9500 100%);
}

.rssi-indicator.weak {
	width: 50rpx;
	background: linear-gradient(90deg, #FF9500 30%, #FF3B30 100%);
}

.rssi-indicator.very-weak {
	width: 40rpx;
	background: #FF3B30;
}

.rssi-indicator.unknown {
	width: 40rpx;
	background: #8E8E93;
}

.rssi-text {
	font-size: 22rpx;
	color: #666;
	text-align: center;
}

.btn-refresh {
	background-color: #FF9500;
}
</style>

需要注意的是如果要实现整正的与蓝牙设备连接通信,手机需要开启定位服务和蓝牙服务,同时app需要申请位置授权,主要针对安卓12版本,下一篇博文我将进入具体实战demo中

相关推荐
Cisyam^1 小时前
Bright Data Web Scraper 实战:构建 TikTok 与 LinkedIn Web Scraping 自动化 Skill(2026)
运维·前端·自动化
xhbh6661 小时前
Windows网络转发如何配置?netsh命令完整指南
服务器·网络·windows·ip·端口流量转发·路由端口转发·ip隐藏
李剑一2 小时前
开箱即用!Vue3+TS 视频组件完整代码,自动提取视频第一帧做封面。妈妈再也不用担心我手动截封面了
前端
志栋智能2 小时前
超自动化巡检:敏捷运维体系中的重要一环
运维·服务器·网络·云原生·容器·kubernetes·自动化
小杰3122 小时前
网络框架源码阅读技巧
服务器·网络·c++·reactor·zlmediakit·zltoolkit
原来是猿2 小时前
应用层【协议再识/序列化与反序列化】
linux·运维·服务器·网络·网络协议·tcp/ip
天草二十六_简村人2 小时前
对接AI大模型之nginx代理配置SSE接口
运维·网络·nginx·http·阿里云·ai·云计算
盐多碧咸。。2 小时前
echarts折线图矩形选择 框选图表
前端·javascript·echarts
羽沢312 小时前
Canvas学习一
前端·css·学习·canvas