diff --git a/apps/miniprogram/native/pkg-veepoo/index.js b/apps/miniprogram/native/pkg-veepoo/index.js index 141c97e..ef2112a 100644 --- a/apps/miniprogram/native/pkg-veepoo/index.js +++ b/apps/miniprogram/native/pkg-veepoo/index.js @@ -71,6 +71,12 @@ Page({ measureError: '', results: {}, hasResults: false, + // 自动测量状态 + autoMeasuring: false, + autoMeasureDone: false, + autoMeasureStatus: {}, + autoMeasureValues: {}, + autoMeasureProgress: 0, }, _authTimer: null, @@ -84,6 +90,8 @@ Page({ _eventChannel: null, _connecting: false, _listenersRegistered: false, + _autoQueue: null, + _autoQueueIndex: 0, // ── 生命周期 ── @@ -278,6 +286,9 @@ Page({ // 认证成功后自动读取 3 天睡眠数据 + 开启自动测量 this._readSleepData(); this._enableAutoMeasurement(); + + // 自动依次测量所有指标(面向中老年人,减少操作) + this._startAutoMeasureQueue(); }, // ── SDK 事件路由 ── @@ -415,6 +426,13 @@ Page({ // ── 测量事件处理 ── _handleMeasureEvent: function (type, data) { + // 自动测量模式下,路由到自动测量处理器 + if (this.data.autoMeasuring) { + this._handleAutoMeasureEvent(type, data); + return; + } + + // 手动测量模式 if (this.data.selectedType !== type || this.data.measurePhase !== 'measuring') return; var content = data.content || {}; @@ -634,4 +652,212 @@ Page({ console.warn('[veepoo-native] 开启体温自动监测失败:', e); } }, + + // ── 自动测量队列 ── + + _startAutoMeasureQueue: function () { + var types = []; + var status = {}; + for (var i = 0; i < MEASURE_TYPES.length; i++) { + types.push(MEASURE_TYPES[i].type); + status[MEASURE_TYPES[i].type] = 'pending'; + } + + this._autoQueue = types; + this._autoQueueIndex = 0; + + this.setData({ + autoMeasuring: true, + autoMeasureDone: false, + autoMeasureStatus: status, + autoMeasureValues: {}, + autoMeasureProgress: 0, + }); + + // eslint-disable-next-line no-undef + console.log('[veepoo-native] 启动自动测量队列,共 ' + types.length + ' 项'); + + var self = this; + setTimeout(function () { + self._startNextAutoMeasure(); + }, 800); + }, + + _startNextAutoMeasure: function () { + if (!this._autoQueue || this._autoQueueIndex >= this._autoQueue.length) { + this._onAutoMeasureComplete(); + return; + } + + var type = this._autoQueue[this._autoQueueIndex]; + this._updateSelectedDisplay(type); + + var status = Object.assign({}, this.data.autoMeasureStatus); + status[type] = 'measuring'; + this.setData({ + autoMeasureStatus: status, + measurePhase: 'measuring', + measureProgress: 0, + measureDisplayValue: '', + measureError: '', + }); + + // eslint-disable-next-line no-undef + console.log('[veepoo-native] 自动测量 [' + (this._autoQueueIndex + 1) + '/' + this._autoQueue.length + ']: ' + type); + + this._lastValues = null; + this._sendMeasureCommand(type, true); + + var self = this; + var timeout = MEASURE_TIMEOUTS[type] || 60000; + this._measureTimer = setTimeout(function () { + // eslint-disable-next-line no-undef + console.warn('[veepoo-native] 自动测量超时: ' + type); + self._onAutoMeasureError(type, '测量超时'); + }, timeout); + }, + + _handleAutoMeasureEvent: function (type, data) { + if (!this._autoQueue || this._autoQueueIndex >= this._autoQueue.length) return; + if (type !== this._autoQueue[this._autoQueueIndex]) return; + + var content = data.content || {}; + var self = this; + + if (content.deviceBusy === true) { self._onAutoMeasureError(type, '设备正忙'); return; } + if (content.notWear === true || data.state === 6) { self._onAutoMeasureError(type, '未佩戴'); return; } + if (data.state === 7) { self._onAutoMeasureError(type, '设备充电中'); return; } + if (data.state === 8) { self._onAutoMeasureError(type, '电量不足'); return; } + if (type === 'pressure' && data.ack === 2) { self._onAutoMeasureError(type, '电量不足'); return; } + if (type === 'pressure' && data.ack === 3) { self._onAutoMeasureError(type, '设备正忙'); return; } + if (type === 'pressure' && data.ack === 4) { self._onAutoMeasureError(type, '未佩戴'); return; } + + var values = self._extractValues(type, content); + if (!values) return; + + self._lastValues = values; + + var progress = data.Progress !== undefined ? data.Progress : 0; + self.setData({ measureProgress: Math.max(progress, 0) }); + + if (progress >= 100) { + self._onAutoMeasureSuccess(type, values); + return; + } + + if (!self._settleTimer) { + self._settleTimer = setTimeout(function () { + if (self._lastValues && self.data.autoMeasuring) { + self._onAutoMeasureSuccess(type, self._lastValues); + } + }, MEASURE_SETTLE_DELAY); + } + }, + + _onAutoMeasureSuccess: function (type, values) { + this._cancelPendingMeasure(); + + var result = { type: type, values: values, measuredAt: Date.now() }; + var newResults = Object.assign({}, this.data.results); + newResults[type] = result; + + var status = Object.assign({}, this.data.autoMeasureStatus); + status[type] = 'done'; + + var newValues = Object.assign({}, this.data.autoMeasureValues); + newValues[type] = this._formatValues(type, values); + + var doneCount = 0; + var keys = Object.keys(status); + for (var i = 0; i < keys.length; i++) { + if (status[keys[i]] === 'done' || status[keys[i]] === 'error') doneCount++; + } + var progress = Math.round((doneCount / this._autoQueue.length) * 100); + + // eslint-disable-next-line no-undef + console.log('[veepoo-native] 自动测量完成: ' + type + ' = ' + newValues[type] + ' (' + progress + '%)'); + + this.setData({ + results: newResults, + hasResults: true, + autoMeasureStatus: status, + autoMeasureValues: newValues, + autoMeasureProgress: progress, + measurePhase: 'idle', + }); + + this._autoQueueIndex++; + var self = this; + setTimeout(function () { + self._startNextAutoMeasure(); + }, 800); + }, + + _onAutoMeasureError: function (type, msg) { + this._cancelPendingMeasure(); + + var status = Object.assign({}, this.data.autoMeasureStatus); + status[type] = 'error'; + + var doneCount = 0; + var keys = Object.keys(status); + for (var i = 0; i < keys.length; i++) { + if (status[keys[i]] === 'done' || status[keys[i]] === 'error') doneCount++; + } + var progress = Math.round((doneCount / this._autoQueue.length) * 100); + + // eslint-disable-next-line no-undef + console.warn('[veepoo-native] 自动测量失败: ' + type + ' - ' + msg + ' (' + progress + '%)'); + + this.setData({ + autoMeasureStatus: status, + autoMeasureProgress: progress, + measurePhase: 'idle', + }); + + this._autoQueueIndex++; + var self = this; + setTimeout(function () { + self._startNextAutoMeasure(); + }, 500); + }, + + _onAutoMeasureComplete: function () { + // eslint-disable-next-line no-undef + console.log('[veepoo-native] 自动测量全部完成'); + + var results = this.data.results; + if (Object.keys(results).length > 0) { + try { + // eslint-disable-next-line no-undef + wx.setStorageSync('hms:veepoo_measure_results', JSON.stringify(results)); + } catch (_) { /* ignore */ } + } + + this.setData({ + autoMeasureDone: true, + autoMeasuring: false, + autoMeasureProgress: 100, + measurePhase: 'idle', + }); + }, + + handleCancelAutoMeasure: function () { + this._cancelPendingMeasure(); + this._autoQueue = null; + + var results = this.data.results; + if (Object.keys(results).length > 0) { + try { + // eslint-disable-next-line no-undef + wx.setStorageSync('hms:veepoo_measure_results', JSON.stringify(results)); + } catch (_) { /* ignore */ } + } + + this.setData({ + autoMeasuring: false, + autoMeasureDone: false, + measurePhase: 'idle', + }); + }, }); diff --git a/apps/miniprogram/native/pkg-veepoo/index.wxml b/apps/miniprogram/native/pkg-veepoo/index.wxml index 73f78b3..b332021 100644 --- a/apps/miniprogram/native/pkg-veepoo/index.wxml +++ b/apps/miniprogram/native/pkg-veepoo/index.wxml @@ -48,7 +48,78 @@ - + + + + + + + + {{deviceName}} + {{batteryLevel}}% + + 取消 + + + + + + {{autoMeasureDone ? '✓ 测量完成!' : '正在自动测量...'}} + 请保持手环佩戴,无需任何操作 + + + + + + + + {{autoMeasureStatus[item.type] === 'done' ? '✓' : autoMeasureStatus[item.type] === 'error' ? '✕' : item.icon}} + + {{item.label}} + + + + {{autoMeasureValues[item.type]}} + {{item.unit}} + + 测量中... + 已跳过 + 等待中 + + + + + + + + + + {{autoMeasureProgress}}% + + + + + 测量数据仅供参考,不作为医学诊断依据。如有不适请及时就医。 + + + + + + 查看结果并返回 + + + 取消自动测量 + + + + + + + diff --git a/apps/miniprogram/native/pkg-veepoo/index.wxss b/apps/miniprogram/native/pkg-veepoo/index.wxss index 0ae178b..dda3eb5 100644 --- a/apps/miniprogram/native/pkg-veepoo/index.wxss +++ b/apps/miniprogram/native/pkg-veepoo/index.wxss @@ -111,6 +111,161 @@ page { /* ═══════════════════════════════════════ 测量页面(就绪态) ═══════════════════════════════════════ */ + +/* ═══ 自动测量进度 ═══ */ +.auto-measure { + padding: 24px 20px 40px; +} + +.auto-measure__header { + text-align: center; + margin-bottom: 28px; +} + +.auto-measure__title { + display: block; + font-family: Georgia, 'Times New Roman', serif; + font-size: 24px; + font-weight: 700; + color: var(--tx); + margin-bottom: 8px; +} + +.auto-measure__subtitle { + display: block; + font-size: 14px; + color: var(--tx3); +} + +.auto-measure__list { + background: var(--card); + border-radius: 16px; + box-shadow: 0 2px 12px rgba(45,42,38,0.06); + overflow: hidden; +} + +.auto-item { + display: flex; + align-items: center; + justify-content: space-between; + padding: 16px 20px; + border-bottom: 1px solid var(--bd); + transition: background 200ms ease; +} + +.auto-item:last-child { + border-bottom: none; +} + +.auto-item--active { + background: rgba(196,98,58,0.04); +} + +.auto-item--done { + background: rgba(91,122,94,0.04); +} + +.auto-item--error { + opacity: 0.6; +} + +.auto-item__left { + display: flex; + align-items: center; + gap: 14px; +} + +.auto-item__icon-wrap { + width: 40px; + height: 40px; + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; +} + +.auto-item--done .auto-item__icon-wrap { + background: var(--acc) !important; +} + +.auto-item--error .auto-item__icon-wrap { + background: var(--tx3) !important; +} + +.auto-item__icon { + font-size: 18px; + color: #fff; + font-weight: 700; +} + +.auto-item__label { + font-size: 16px; + font-weight: 600; + color: var(--tx); +} + +.auto-item__right { + display: flex; + align-items: baseline; + gap: 4px; +} + +.auto-item__value { + font-family: Georgia, 'Times New Roman', serif; + font-size: 22px; + font-weight: 700; +} + +.auto-item__unit { + font-size: 13px; + color: var(--tx3); +} + +.auto-item__status { + font-size: 14px; + color: var(--tx3); +} + +.auto-item__status--active { + color: var(--pri); + font-weight: 600; +} + +.auto-item__status--error { + color: var(--tx3); +} + +/* ── 自动测量进度条 ── */ +.auto-progress { + display: flex; + align-items: center; + gap: 12px; + margin-top: 24px; + padding: 0 4px; +} + +.auto-progress__bar { + flex: 1; + height: 6px; + background: var(--bd); + border-radius: 3px; + overflow: hidden; +} + +.auto-progress__fill { + height: 100%; + background: var(--pri); + border-radius: 3px; + transition: width 0.5s ease-out; +} + +.auto-progress__text { + font-size: 14px; + font-weight: 600; + color: var(--pri); + min-width: 36px; + text-align: right; +} .measure-page { min-height: 100vh; padding-bottom: 40px; @@ -279,6 +434,8 @@ page { flex-direction: column; align-items: center; justify-content: center; + background: var(--bg); + border-radius: 50%; } .gauge__icon-lg {