| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813 |
- <template>
- <view class="page">
- <!-- <view class="nav-bar" :style="{ paddingTop: statusBarHeight + 'px' }">
- <view class="nav-title">血压仪连接</view>
- <view class="nav-back" @tap="goBack">
- <text class="back-text">返回</text>
- </view>
- </view> -->
- <view class="content">
- <!-- 连接状态 -->
- <view class="status-card" :class="{ connected: connected }">
- <view class="status-icon">{{ connected ? '✓' : '○' }}</view>
- <view class="status-text">{{ statusText }}</view>
- <view v-if="connected && bpWaitUser != null" class="bp-wait-hint">设备就绪 (用户{{ bpWaitUser }}) · LCD 显示「bt」闪烁,可开始测量</view>
- <view v-if="connected && bpMeasureStarted" class="bp-wait-hint bp-measure-ok">血压计已确认,开始量测</view>
- <view v-if="connected && bpCurrentPressure != null" class="bp-pressure-live">测量中 压力: {{ bpCurrentPressure }} mmHg</view>
- <view v-if="bpErrorMsg" class="bp-error-msg">{{ bpErrorMsg }}</view>
- <view v-if="lastBloodPressure.systolic != null" class="measure-value">
- <text class="value-label">血压</text>
- <text class="value-num">{{ lastBloodPressure.systolic }}/{{ lastBloodPressure.diastolic }}</text>
- <text class="value-unit">mmHg</text>
- <text v-if="lastBloodPressure.pulse != null" class="value-extra">脉率 {{ lastBloodPressure.pulse }} 次/分</text>
- <text v-if="lastBloodPressure.ihb === 1" class="value-extra value-ihb">心律不齐</text>
- </view>
- <view v-if="lastBloodPressure.time" class="measure-time">{{ lastBloodPressure.time }}</view>
- <view v-if="lastGlucose.value !== null" class="measure-value">
- <text class="value-label">血糖</text>
- <text class="value-num">{{ lastGlucose.value }}</text>
- <text class="value-unit">{{ lastGlucose.unit }}</text>
- </view>
- <view v-if="lastGlucose.time" class="measure-time">{{ lastGlucose.time }}</view>
- </view>
- <!-- 操作区 -->
- <view class="actions">
- <button class="btn btn-primary" :disabled="scanning" :loading="scanning" @tap="startScan">
- {{ scanning ? '扫描中...' : '扫描设备' }}
- </button>
- <view v-if="deviceList.length > 0" class="device-list">
- <view class="list-title">可选设备(请确保血压仪/血糖仪已开机)</view>
- <view
- v-for="d in deviceList"
- :key="d.deviceId"
- class="device-item"
- :class="{ active: selectedDeviceId === d.deviceId }"
- @tap="selectDevice(d)"
- >
- <radio :checked="selectedDeviceId === d.deviceId" color="#FF5030" />
- <view class="device-info">
- <text class="device-name">{{ d.name || d.localName || '未知设备' }}</text>
- <text class="device-id">{{ d.deviceId }}</text>
- </view>
- </view>
- </view>
- <button
- class="btn btn-connect"
- :disabled="!selectedDeviceId || connecting"
- :loading="connecting"
- @tap="connectDevice"
- >
- {{ connected ? '已连接' : connecting ? '连接中...' : '连接设备' }}
- </button>
- <button v-if="connected" class="btn btn-warn" :disabled="disconnecting" @tap="disconnect">
- 断开连接
- </button>
- </view>
- <!-- 日志(调试用,可折叠) -->
- <view class="log-section">
- <view class="log-title" @tap="showLog = !showLog">
- <text>运行日志</text>
- <text class="log-toggle">{{ showLog ? '收起' : '展开' }}</text>
- </view>
- <scroll-view v-if="showLog" class="log-list" scroll-y>
- <text v-for="(line, i) in logs" :key="i" class="log-line">{{ line }}</text>
- </scroll-view>
- </view>
- </view>
- </view>
- </template>
- <script>
- // uni 蓝牙 API Promise 化
- const $p = (api, opts = {}) =>
- new Promise((res, rej) => {
- opts.success = res;
- opts.fail = rej;
- uni[api](opts);
- });
- // 解析 BLE 血压测量特征值(Bluetooth GATT 0x2A18 格式)
- // 固定 UUID:接收用 0xFFF1,发送用 0xFFF2,同属 Service 0xFFF0
- const BLE_SERVICE_UUID = 'fff0';
- const BLE_CHAR_UUID_NOTIFY = 'fff1'; // 接收
- const BLE_CHAR_UUID_WRITE = 'fff2'; // 发送
- // 血压协议:命令头 0xFD,0xFD;命令尾 0x0D,0x0A
- const BP_HEADER = [0xfd, 0xfd];
- const BP_TAIL = [0x0d, 0x0a];
- // 血压计开机后每 0.5s 发送的“等待上位机连接”包:FD FD A4 01 01 用户 01 0D 0A,用户字节 01=用户1、02=用户2,LCD 显示 "bt" 闪烁
- const BP_WAIT_CMD = 0xa4;
- const BP_WAIT_PACKET_LEN = 9; // FD FD A4 01 01 [user] 01 0D 0A
- // 连接成功后 APP 发送,告知血压计可量测:FD FD FA 05 0D 0A
- const BP_CMD_CONNECT_OK = new Uint8Array([0xfd, 0xfd, 0xfa, 0x05, 0x0d, 0x0a]);
- // 血压计回复确认并开始量测:FD FD 06 0D 0A
- const BP_REPLY_START_MEASURE_LEN = 5;
- // 量测过程中压力信号,每 0.5s 一次:FD FD FB PressureH PressureL 0D 0A,压力 = PressureH*256+PressureL
- const BP_PRESSURE_CMD = 0xfb;
- const BP_PRESSURE_PACKET_LEN = 7;
- // 量测完成测试结果:FD FD FC SYS DIA PUL IHB 0D 0A,SYS=收缩压,DIA=舒张压,PUL=心率,IHB=心律不齐(0x00正常,0x01不齐),未收到回复会连传3次
- const BP_RESULT_CMD = 0xfc;
- const BP_RESULT_PACKET_LEN = 9;
- // 量测错误码:FD FD FD err 0D 0A
- const BP_ERROR_CMD = 0xfd;
- const BP_ERROR_PACKET_LEN = 6;
- const BP_ERROR_MESSAGES = {
- 0x01: 'E-1 人体心跳信号太小或压力突降。量测错误,请根据说明书重新戴好袖带,保持安静,重新量测。',
- 0x02: 'E-2 杂讯干扰。量测错误,请根据说明书重新戴好袖带,保持安静,重新量测。',
- 0x03: 'E-3 充气时间过长。量测错误,请根据说明书重新戴好袖带,保持安静,重新量测。',
- 0x05: 'E-5 测得的结果异常。量测错误,请根据说明书重新戴好袖带,保持安静,重新量测。',
- 0x0e: 'E-E 量测错误,请根据说明书重新戴好袖带,保持安静,重新量测。',
- 0x0b: 'E-B 电源低电压,电池电量低,请更换电池。'
- };
- // 血糖协议:命令头 0xA5,0xA5;命令尾 0x5A,0x5A
- const GLUCOSE_HEADER = [0xa5, 0xa5];
- const GLUCOSE_TAIL = [0x5a, 0x5a];
- function matchHeader(arr, header) {
- if (arr.length < header.length) return false;
- return header.every((b, i) => arr[i] === b);
- }
- function matchTail(arr, tail) {
- if (arr.length < tail.length) return false;
- return tail.every((b, i) => arr[arr.length - tail.length + i] === b);
- }
- // 判断是否为血压计“等待连接”包,若是则返回 { user: 1|2 },否则返回 null
- function parseBpWaitForConnection(arr) {
- if (arr.length !== BP_WAIT_PACKET_LEN) return null;
- if (!matchHeader(arr, BP_HEADER) || !matchTail(arr, BP_TAIL)) return null;
- if (arr[2] !== BP_WAIT_CMD || arr[3] !== 0x01 || arr[4] !== 0x01 || arr[6] !== 0x01) return null;
- const userByte = arr[5];
- if (userByte === 0x01) return { user: 1 };
- if (userByte === 0x02) return { user: 2 };
- return null;
- }
- // 解析量测结果:FD FD FC SYS DIA PUL IHB 0D 0A
- function parseBpResultPacket(arr) {
- if (arr.length !== BP_RESULT_PACKET_LEN || arr[2] !== BP_RESULT_CMD) return null;
- if (!matchHeader(arr, BP_HEADER) || !matchTail(arr, BP_TAIL)) return null;
- const systolic = arr[3];
- const diastolic = arr[4];
- const pulse = arr[5];
- const ihb = arr[6]; // 0x00 正常,0x01 心律不齐
- if (systolic > 300 || diastolic > 300) return null;
- return {
- systolic,
- diastolic,
- pulse,
- ihb: ihb === 0x01 ? 1 : 0,
- time: new Date().toLocaleString()
- };
- }
- // 解析量测错误码:FD FD FD err 0D 0A,返回错误文案或 null
- function parseBpErrorPacket(arr) {
- if (arr.length !== BP_ERROR_PACKET_LEN || arr[2] !== BP_ERROR_CMD) return null;
- if (!matchHeader(arr, BP_HEADER) || !matchTail(arr, BP_TAIL)) return null;
- const code = arr[3];
- return BP_ERROR_MESSAGES[code] || ('错误码 E-' + (code < 10 ? '0' + code.toString(16) : code.toString(16).toUpperCase()));
- }
- // 解析血压数据:命令头 0xFD,0xFD;命令尾 0x0D,0x0A;排除“等待连接”包(A4 01 01 用户 01)
- function parseBloodPressure(buffer) {
- if (!buffer || buffer.byteLength < 8) return null;
- const arr = new Uint8Array(buffer);
- if (!matchHeader(arr, BP_HEADER) || !matchTail(arr, BP_TAIL)) return null;
- if (parseBpWaitForConnection(arr)) return null; // 等待连接包不当作血压数据
- const len = arr.length - BP_HEADER.length - BP_TAIL.length;
- if (len < 4) return null;
- const payload = arr.slice(BP_HEADER.length, BP_HEADER.length + len);
- // 常见格式:收缩压(2字节)、舒张压(2字节)、脉率(1字节,可选),小端
- const systolic = payload[0] | (payload[1] << 8);
- const diastolic = payload[2] | (payload[3] << 8);
- const pulse = payload.length >= 5 ? payload[4] : null;
- if (systolic > 300 || diastolic > 300) return null; // 合理范围校验
- return {
- type: 'bloodPressure',
- systolic,
- diastolic,
- pulse,
- time: new Date().toLocaleString()
- };
- }
- // 解析血糖数据:命令头 0xA5,0xA5;命令尾 0x5A,0x5A;中间为有效载荷
- function parseGlucoseMeasurement(buffer) {
- if (!buffer || buffer.byteLength < 6) return null;
- const arr = new Uint8Array(buffer);
- if (!matchHeader(arr, GLUCOSE_HEADER) || !matchTail(arr, GLUCOSE_TAIL)) return null;
- const len = arr.length - GLUCOSE_HEADER.length - GLUCOSE_TAIL.length;
- if (len < 2) return null;
- const payload = arr.slice(GLUCOSE_HEADER.length, GLUCOSE_HEADER.length + len);
- // 血糖值常见为 2 字节,单位 mmol/L(或需 *0.1)
- const raw = payload[0] | (payload[1] << 8);
- let value = raw;
- if (value > 1000) value = raw * 0.1;
- value = Math.round(value * 10) / 10;
- if (value <= 0 || value > 100) return null;
- return {
- type: 'glucose',
- value,
- unit: 'mmol/L',
- time: new Date().toLocaleString()
- };
- }
- export default {
- data() {
- return {
- scanning: false,
- connecting: false,
- disconnecting: false,
- connected: false,
- deviceList: [],
- selectedDeviceId: '',
- selectedDeviceName: '',
- deviceId: '',
- serviceId: '',
- characteristicId: '',
- notifyServiceId: '',
- notifyCharId: '',
- writeServiceId: '',
- writeCharId: '',
- lastGlucose: {
- value: null,
- unit: 'mmol/L',
- time: ''
- },
- lastBloodPressure: {
- systolic: null,
- diastolic: null,
- pulse: null,
- ihb: null, // 0 正常,1 心律不齐
- time: ''
- },
- // 血压计“等待连接”状态:收到 FD FD A4 01 01 用户 01 0D 0A 时置为 1 或 2,LCD 显示 bt 闪烁
- bpWaitUser: null,
- // 血压计已回复 FD FD 06 0D 0A,已开始量测
- bpMeasureStarted: false,
- // 量测过程中实时压力值(FD FD FB PressureH PressureL 0D 0A),单位 mmHg,收到最终结果后清空
- bpCurrentPressure: null,
- // 量测错误提示(E-1/E-2/E-3/E-5/E-E/E-B),收到成功结果或断开时清空
- bpErrorMsg: '',
- logs: [],
- showLog: true,
- statusText: '未连接',
- statusBarHeight: 0
- };
- },
- onLoad() {
- const sys = uni.getSystemInfoSync();
- this.statusBarHeight = sys.statusBarHeight || 0;
- },
- onUnload() {
- this.closeBLE();
- },
- methods: {
- goBack() {
- uni.navigateBack({ delta: 1 });
- },
- log(msg) {
- const line = `${new Date().toLocaleTimeString()} ${msg}`;
- this.logs.unshift(line);
- if (this.logs.length > 100) this.logs.pop();
- },
- async startScan() {
- this.deviceList = [];
- this.selectedDeviceId = '';
- this.log('打开蓝牙适配器...');
- try {
- await $p('openBluetoothAdapter');
- this.log('开始扫描低功耗蓝牙设备...');
- await $p('startBluetoothDevicesDiscovery', {
- allowDuplicatesKey: false
- });
- this.scanning = true;
- this.statusText = '正在扫描...';
- uni.onBluetoothDeviceFound((res) => {
- const nameContains = (d) => {
- const name = (d.name || d.localName || '').toLowerCase();
- return name.indexOf('bluetooth') !== -1;
- };
- const newDevices = (res.devices || []).filter(
- (d) => nameContains(d) && !this.deviceList.some((x) => x.deviceId === d.deviceId)
- );
- if (newDevices.length) {
- this.deviceList = [...this.deviceList, ...newDevices];
- }
- });
- setTimeout(() => this.stopScan(), 10000);
- } catch (e) {
- this.scanning = false;
- this.statusText = '未连接';
- const msg = (e.errMsg || e.message || String(e)).toLowerCase();
- if (msg.indexOf('bluetooth') !== -1 || msg.indexOf('adapter') !== -1) {
- uni.showToast({ title: '请开启手机蓝牙', icon: 'none' });
- }
- this.log('扫描失败:' + (e.errMsg || e));
- }
- },
- async stopScan() {
- if (!this.scanning) return;
- try {
- await $p('stopBluetoothDevicesDiscovery');
- } catch (e) {}
- this.scanning = false;
- this.statusText = this.connected ? '已连接' : '未连接';
- this.log('扫描已停止,共发现 ' + this.deviceList.length + ' 个设备');
- },
- selectDevice(d) {
- this.selectedDeviceId = d.deviceId;
- this.selectedDeviceName = d.name || d.localName || '未知设备';
- },
- async connectDevice() {
- if (!this.selectedDeviceId) {
- uni.showToast({ title: '请先选择设备', icon: 'none' });
- return;
- }
- this.connecting = true;
- this.log('正在连接 ' + this.selectedDeviceName + '...');
- this.statusText = '连接中...';
- try {
- await $p('createBLEConnection', {
- deviceId: this.selectedDeviceId,
- timeout: 10000
- });
- this.deviceId = this.selectedDeviceId;
- this.connected = true;
- this.statusText = '已连接';
- this.log('连接成功,正在发现服务...');
- await new Promise((r) => setTimeout(r, 600));
- const { services } = await $p('getBLEDeviceServices', {
- deviceId: this.deviceId
- });
- if (!services || services.length === 0) {
- this.log('未发现服务');
- this.connecting = false;
- return;
- }
- // 固定使用 Service 0xFFF0,接收 0xFFF1,发送 0xFFF2
- const svc = services.find((s) => s.uuid.toLowerCase().indexOf(BLE_SERVICE_UUID) !== -1);
- if (!svc) {
- this.log('未发现服务 0xFFF0');
- this.connecting = false;
- return;
- }
- this.serviceId = svc.uuid;
- this.log('使用服务: ' + this.serviceId);
- const { characteristics } = await $p('getBLEDeviceCharacteristics', {
- deviceId: this.deviceId,
- serviceId: this.serviceId
- });
- if (!characteristics || characteristics.length === 0) {
- this.log('未发现特征值');
- this.connecting = false;
- return;
- }
- console.log('特征值: ' + characteristics);
- const notifyChar = characteristics.find(
- (c) => c.uuid.toLowerCase().indexOf(BLE_CHAR_UUID_NOTIFY) !== -1
- );
- const writeChar = characteristics.find(
- (c) => c.uuid.toLowerCase().indexOf(BLE_CHAR_UUID_WRITE) !== -1
- );
- if (!notifyChar) {
- this.log('未找到接收特征 0xFFF1');
- this.connecting = false;
- return;
- }
- this.notifyServiceId = svc.uuid;
- this.notifyCharId = notifyChar.uuid;
- this.characteristicId = notifyChar.uuid;
- if (writeChar) {
- this.writeServiceId = svc.uuid;
- this.writeCharId = writeChar.uuid;
- this.log('发送特征 0xFFF2 已就绪');
- }
- this.log('Service: 0xFFF0, 接收(FFF1), 发送(FFF2)');
- await $p('notifyBLECharacteristicValueChange', {
- deviceId: this.deviceId,
- serviceId: this.notifyServiceId,
- characteristicId: this.notifyCharId,
- state: true
- });
- this.log('已开启数据通知');
- // 对码:连接成功后发送 FD FD FA 05 0D 0A,告知血压计可量测
- if (this.writeCharId) {
- try {
- const ab = new ArrayBuffer(BP_CMD_CONNECT_OK.length);
- console.log('发送',this.writeServiceId,this.writeCharId,ab)
- new Uint8Array(ab).set(BP_CMD_CONNECT_OK);
- await $p('writeBLECharacteristicValue', {
- deviceId: this.deviceId,
- serviceId: this.writeServiceId,
- characteristicId: this.writeCharId,
- value: ab
- });
- this.log('已发送对码:FD FD FA 05 0D 0A,等待血压计确认');
- } catch (err) {
- console.log('发送对码失败: ' + (err.errMsg || err.message || err));
- this.log('发送对码失败: ' + (err.errMsg || err.message || err));
- }
- }
- uni.onBLECharacteristicValueChange((res) => {
- if (res.deviceId !== this.deviceId) return;
- const buf = res.value;
- const arr = new Uint8Array(buf);
- const hex = Array.from(arr).map((b) => ('0' + b.toString(16)).slice(-2)).join(' ');
- this.log('收到数据: ' + hex);
- if (matchHeader(arr, BP_HEADER) && matchTail(arr, BP_TAIL)) {
- // 血压计回复:连接成功并开始量测 FD FD 06 0D 0A
- if (arr.length === BP_REPLY_START_MEASURE_LEN && arr[2] === 0x06) {
- this.bpMeasureStarted = true;
- this.log('血压计已确认,开始量测');
- return;
- }
- // 量测过程中压力信号:FD FD FB PressureH PressureL 0D 0A,压力=PressureH*256+PressureL
- if (arr.length === BP_PRESSURE_PACKET_LEN && arr[2] === BP_PRESSURE_CMD) {
- const pressure = arr[3] * 256 + arr[4];
- this.bpCurrentPressure = pressure;
- this.log('测量中 压力: ' + pressure + ' mmHg');
- return;
- }
- const waitInfo = parseBpWaitForConnection(arr);
- if (waitInfo) {
- this.bpWaitUser = waitInfo.user;
- this.log('血压计就绪 (用户' + waitInfo.user + '),LCD bt 闪烁,可开始测量');
- return;
- }
- // 量测完成测试结果:FD FD FC SYS DIA PUL IHB 0D 0A
- const result = parseBpResultPacket(arr);
- if (result) {
- this.bpCurrentPressure = null;
- this.bpErrorMsg = '';
- this.lastBloodPressure = {
- systolic: result.systolic,
- diastolic: result.diastolic,
- pulse: result.pulse,
- ihb: result.ihb,
- time: result.time
- };
- this.log('血压: ' + result.systolic + '/' + result.diastolic + ' mmHg 脉率' + result.pulse + (result.ihb ? ' 心律不齐' : ''));
- return;
- }
- // 量测错误码:FD FD FD err 0D 0A
- const errMsg = parseBpErrorPacket(arr);
- if (errMsg) {
- this.bpCurrentPressure = null;
- this.bpErrorMsg = errMsg;
- this.log('血压计错误: ' + errMsg);
- uni.showToast({ title: errMsg.slice(0, 20) + '…', icon: 'none', duration: 3000 });
- return;
- }
- const parsed = parseBloodPressure(buf);
- if (parsed) {
- this.bpCurrentPressure = null;
- this.bpErrorMsg = '';
- this.lastBloodPressure = {
- systolic: parsed.systolic,
- diastolic: parsed.diastolic,
- pulse: parsed.pulse,
- ihb: parsed.ihb != null ? parsed.ihb : null,
- time: parsed.time
- };
- this.log('血压: ' + parsed.systolic + '/' + parsed.diastolic + ' mmHg' + (parsed.pulse != null ? ' 脉率' + parsed.pulse : ''));
- }
- return;
- }
- if (matchHeader(arr, GLUCOSE_HEADER) && matchTail(arr, GLUCOSE_TAIL)) {
- const parsed = parseGlucoseMeasurement(buf);
- if (parsed && parsed.value != null) {
- this.lastGlucose = {
- value: parsed.value,
- unit: parsed.unit || 'mmol/L',
- time: parsed.time || new Date().toLocaleString()
- };
- this.log('血糖: ' + this.lastGlucose.value + ' ' + this.lastGlucose.unit);
- }
- }
- });
- uni.showToast({ title: '连接成功', icon: 'success' });
- } catch (e) {
- this.connected = false;
- this.deviceId = '';
- this.statusText = '未连接';
- const errMsg = e.errMsg || e.message || String(e);
- this.log('连接失败: ' + errMsg);
- uni.showToast({ title: '连接失败', icon: 'none' });
- }
- this.connecting = false;
- },
- async disconnect() {
- this.disconnecting = true;
- this.log('断开连接...');
- await this.closeBLE();
- this.connected = false;
- this.deviceId = '';
- this.serviceId = '';
- this.characteristicId = '';
- this.notifyServiceId = '';
- this.notifyCharId = '';
- this.writeServiceId = '';
- this.writeCharId = '';
- this.lastGlucose = { value: null, unit: 'mmol/L', time: '' };
- this.lastBloodPressure = { systolic: null, diastolic: null, pulse: null, ihb: null, time: '' };
- this.bpWaitUser = null;
- this.bpMeasureStarted = false;
- this.bpCurrentPressure = null;
- this.bpErrorMsg = '';
- this.statusText = '未连接';
- this.disconnecting = false;
- this.log('已断开');
- },
- async closeBLE() {
- try {
- if (this.deviceId) {
- await $p('closeBLEConnection', { deviceId: this.deviceId });
- }
- } catch (e) {}
- try {
- await $p('closeBluetoothAdapter');
- } catch (e) {}
- }
- }
- };
- </script>
- <style lang="scss" scoped>
- .page {
- min-height: 100vh;
- background: #f7f7f7;
- }
- .nav-bar {
- position: sticky;
- top: 0;
- z-index: 10;
- display: flex;
- align-items: center;
- justify-content: center;
- height: 88rpx;
- padding: 0 24rpx;
- background: #fff;
- border-bottom: 1rpx solid #eee;
- }
- .nav-title {
- font-size: 36rpx;
- font-weight: 600;
- color: #333;
- }
- .nav-back {
- position: absolute;
- left: 24rpx;
- padding: 10rpx 0;
- }
- .back-text {
- font-size: 30rpx;
- color: #FF5030;
- }
- .content {
- padding: 24rpx;
- padding-bottom: 60rpx;
- }
- .status-card {
- background: #fff;
- border-radius: 24rpx;
- padding: 48rpx;
- margin-bottom: 32rpx;
- text-align: center;
- border: 2rpx solid #eee;
- }
- .status-card.connected {
- border-color: #FF5030;
- background: #fffaf7;
- }
- .status-icon {
- font-size: 56rpx;
- color: #999;
- margin-bottom: 16rpx;
- }
- .status-card.connected .status-icon {
- color: #FF5030;
- }
- .status-text {
- font-size: 28rpx;
- color: #666;
- margin-bottom: 24rpx;
- }
- .bp-wait-hint {
- font-size: 24rpx;
- color: #999;
- margin-bottom: 16rpx;
- }
- .bp-measure-ok {
- color: #07c160;
- }
- .bp-pressure-live {
- font-size: 28rpx;
- color: #FF5030;
- font-weight: 600;
- margin-bottom: 16rpx;
- }
- .bp-error-msg {
- font-size: 24rpx;
- color: #e64340;
- background: #fff5f5;
- padding: 16rpx;
- border-radius: 12rpx;
- margin-bottom: 16rpx;
- line-height: 1.5;
- }
- .value-ihb {
- display: block;
- color: #e64340;
- margin-top: 4rpx;
- }
- .measure-value {
- margin: 24rpx 0 8rpx;
- }
- .value-label {
- display: block;
- font-size: 24rpx;
- color: #999;
- margin-bottom: 4rpx;
- }
- .value-num {
- font-size: 72rpx;
- font-weight: 700;
- color: #FF5030;
- }
- .value-unit {
- font-size: 28rpx;
- color: #999;
- margin-left: 8rpx;
- }
- .value-extra {
- display: block;
- font-size: 26rpx;
- color: #666;
- margin-top: 8rpx;
- }
- .measure-time {
- font-size: 24rpx;
- color: #999;
- }
- .actions {
- background: #fff;
- border-radius: 24rpx;
- padding: 32rpx;
- margin-bottom: 24rpx;
- }
- .btn {
- width: 100%;
- height: 88rpx;
- line-height: 88rpx;
- border-radius: 44rpx;
- font-size: 32rpx;
- margin-bottom: 24rpx;
- }
- .btn:last-child {
- margin-bottom: 0;
- }
- .btn-primary {
- background: #FF5030;
- color: #fff;
- border: none;
- }
- .btn-connect {
- background: #07c160;
- color: #fff;
- border: none;
- }
- .btn-warn {
- background: #fff;
- color: #e64340;
- border: 2rpx solid #e64340;
- }
- .device-list {
- margin: 24rpx 0;
- padding: 0 0 16rpx;
- border-bottom: 1rpx solid #eee;
- }
- .list-title {
- font-size: 26rpx;
- color: #999;
- margin-bottom: 16rpx;
- }
- .device-item {
- display: flex;
- align-items: center;
- padding: 20rpx 0;
- border-radius: 12rpx;
- }
- .device-item.active {
- background: #fff5f0;
- }
- .device-info {
- margin-left: 20rpx;
- display: flex;
- flex-direction: column;
- }
- .device-name {
- font-size: 30rpx;
- color: #333;
- }
- .device-id {
- font-size: 22rpx;
- color: #999;
- margin-top: 4rpx;
- }
- .log-section {
- background: #fff;
- border-radius: 24rpx;
- padding: 24rpx;
- }
- .log-title {
- display: flex;
- justify-content: space-between;
- font-size: 28rpx;
- color: #666;
- padding: 8rpx 0;
- }
- .log-toggle {
- color: #FF5030;
- font-size: 26rpx;
- }
- .log-list {
- max-height: 360rpx;
- margin-top: 16rpx;
- padding: 16rpx;
- background: #f5f5f5;
- border-radius: 12rpx;
- }
- .log-line {
- display: block;
- font-size: 22rpx;
- color: #666;
- line-height: 1.6;
- word-break: break-all;
- }
- </style>
|