From c7cebc2fc91a71dd93b73f32624ccda9b0d3266b Mon Sep 17 00:00:00 2001 From: ozh Date: Tue, 8 Sep 2026 09:27:20 +0800 Subject: [PATCH] =?UTF-8?q?feat(device):=20=E9=A6=96=E9=A1=B5=E8=AE=BE?= =?UTF-8?q?=E5=A4=87=E5=88=97=E8=A1=A8=E5=8D=A1=E7=89=87=E7=BD=91=E6=A0=BC?= =?UTF-8?q?=20+=20ED713=20=E4=BD=93=E5=BE=81=E8=AF=A6=E6=83=85=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 api/device.js(getDeviceList / getVitalSigns / getHeartRateHistory, 当前 mock,后端就绪后关闭 USE_MOCK) - 新增 components/vital-wave.vue 心率波形组件(canvas 自绘 ECG 风格面积图) - 新增 pages/device/detail.vue ED713 体征仪表盘(心率大数字+波形、六指标 +设备信息卡,加载/错误/重试三态) - pages/index 设备列表改为双列卡片网格(loading/error/empty 三态,型号→图标映射, 点卡片进详情页) - pages.json / constants/routes 新增 DEVICE_DETAIL 路由注册, pages/scene/select up-navbar 启用 auto-back --- api/device.js | 97 +++++++++++ components/vital-wave.vue | 179 ++++++++++++++++++++ constants/routes.js | 1 + pages.json | 6 + pages/device/detail.vue | 343 ++++++++++++++++++++++++++++++++++++++ pages/index/index.vue | 160 +++++++++++++++++- pages/scene/select.vue | 2 +- 7 files changed, 782 insertions(+), 6 deletions(-) create mode 100644 api/device.js create mode 100644 components/vital-wave.vue create mode 100644 pages/device/detail.vue diff --git a/api/device.js b/api/device.js new file mode 100644 index 0000000..3d053c3 --- /dev/null +++ b/api/device.js @@ -0,0 +1,97 @@ +import request from '@/api/http/client' + +// 后端就绪后置为 false 即可恢复真实请求。 +const USE_MOCK = true + +// 模拟设备列表数据(后端 /device/list 就绪后删除)。 +const MOCK_DEVICES = [ + { id: 1, name: '客厅体征监测仪', model: 'ED713', online: true }, + { id: 2, name: '卧室体征监测仪', model: 'ED713', online: false }, + { id: 3, name: '书房跌倒检测雷达', model: 'ED719', online: true }, + { id: 4, name: '走廊跌倒检测雷达', model: 'ED719', online: false } +] + +/** + * 获取指定场所下的设备列表。 + * 约定:data 为数组,每项至少含 { id, name, model, online },model 取值 ED713 | ED719。 + * @param {*} sceneId 场景 id,全部/未选择时可传 null + */ +export function getDeviceList(sceneId) { + if (USE_MOCK) { + // 模拟网络延迟,让 loading 态可见。 + return new Promise((resolve) => setTimeout(() => resolve(MOCK_DEVICES), 500)) + } + return request.get('/device/list', { sceneId }) +} + +// 模拟 ED713 实时体征数据(后端 /device/vitals 就绪后删除)。 +// 字段与 web 端 runTimeInfo.properties 对齐;枚举值见 detail 页映射表。 +const MOCK_VITALS = { + heartRate: 72, + respiratoryRate: 16, + bodyMovement: 3, + moveState: 3, + onbedStatus: 1, + bodyStatus: 2, + bodyPosition: 8 +} + +// 心率读数模拟状态:跨调用保留最近 50 个读数(与 web 端 pageSize:50 一致), +// 每次轮询推进 2~3 个新读数、挤出最旧的——等价于 web 端每 3 秒按 createTime 倒序重拉最新 50 条。 +const heartState = { + series: [], + value: 72 +} + +// 心率读数小幅随机游走(60~90 BPM)。 +function driftHeartRate() { + heartState.value += Math.round((Math.random() - 0.5) * 4) + heartState.value = Math.min(90, Math.max(60, heartState.value)) + return heartState.value +} + +// 初始预填 50 个读数,首屏即有完整曲线。 +for (let i = 0; i < 50; i++) { + heartState.series.push(driftHeartRate()) +} + +// 推进若干新读数并保留最近 50 个。 +function pushHeartReadings() { + const n = 2 + Math.round(Math.random()) + for (let i = 0; i < n; i++) { + heartState.series.push(driftHeartRate()) + } + if (heartState.series.length > 50) { + heartState.series = heartState.series.slice(-50) + } +} + +/** + * 获取 ED713 设备实时体征属性。 + * 约定:data 含 { heartRate, respiratoryRate, bodyMovement, moveState, onbedStatus, bodyStatus, bodyPosition }。 + * @param {*} deviceId 设备 id + */ +export function getVitalSigns(deviceId) { + if (USE_MOCK) { + const heartRate = driftHeartRate() + return new Promise((resolve) => + setTimeout(() => resolve({ ...MOCK_VITALS, heartRate, deviceId }), 300) + ) + } + return request.get('/device/vitals', { deviceId }) +} + +/** + * 获取心率历史序列(与 web 端一致:最近 50 条心率读数、按时间升序喂给面积图)。 + * 约定:data 为 number 数组;每次调用推进若干新读数,曲线随轮询向左滚动。 + * @param {*} deviceId 设备 id + */ +export function getHeartRateHistory(deviceId) { + if (USE_MOCK) { + pushHeartReadings() + return new Promise((resolve) => + setTimeout(() => resolve([...heartState.series]), 300) + ) + } + return request.get('/device/heart-history', { deviceId }) +} diff --git a/components/vital-wave.vue b/components/vital-wave.vue new file mode 100644 index 0000000..94f65ee --- /dev/null +++ b/components/vital-wave.vue @@ -0,0 +1,179 @@ + + + + + + diff --git a/constants/routes.js b/constants/routes.js index 3506cd0..685362a 100644 --- a/constants/routes.js +++ b/constants/routes.js @@ -8,6 +8,7 @@ export const ROUTES = Object.freeze({ MINE: '/pages/mine/index', DEVICE_ADD: '/pages/device/add', DEVICE_CONFIG: '/pages/device/config', + DEVICE_DETAIL: '/pages/device/detail', SCENE_SELECT: '/pages/scene/select' }) diff --git a/pages.json b/pages.json index dade0f7..d5759f9 100644 --- a/pages.json +++ b/pages.json @@ -36,6 +36,12 @@ "navigationStyle": "custom" } }, + { + "path": "pages/device/detail", + "style": { + "navigationStyle": "custom" + } + }, { "path": "pages/scene/select", "style": { diff --git a/pages/device/detail.vue b/pages/device/detail.vue new file mode 100644 index 0000000..016ba9a --- /dev/null +++ b/pages/device/detail.vue @@ -0,0 +1,343 @@ + + + + + + diff --git a/pages/index/index.vue b/pages/index/index.vue index 9a45e31..340de1a 100644 --- a/pages/index/index.vue +++ b/pages/index/index.vue @@ -29,8 +29,39 @@ - - + + + + + + + + + + + + + + + + + + + + + {{ device.name }} + + + + {{ device.online ? '在线' : '离线' }} + + + @@ -40,19 +71,31 @@