feat: 健康监测模块UI优化与员工端PRD文档更新

员工端:新增健康现状入口、健康排查文案优化(打卡→记录)、穿戴监测单位/图表/预警页面优化、环境监测地图缩放按钮
管理端:档案管理/环境监测/体重监测/穿戴监测等模块优化
文档:同步更新员工端PRD文字描述与12张页面截图

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-28 21:55:05 +08:00
co-authored by Claude Opus 4.6
parent 14157f92e4
commit b120bb691c
146 changed files with 2031 additions and 1022 deletions
+34 -28
View File
@@ -11,8 +11,8 @@
├── 健康监测主页
├── 健康排查
│ ├── 健康排查首页(积分/排行榜)
│ ├── 每日健康打卡(问卷填写)
│ ├── 打卡记录(日历+列表)
│ ├── 每日健康记录(问卷填写)
│ ├── 历史记录(日历+列表)
│ ├── 问卷详情(只读回顾)
│ └── 题目设置(自定义显示)
├── 穿戴监测
@@ -49,8 +49,8 @@
| 序号 | 页面/弹窗 | 文件 | 类型 |
|------|-----------|------|------|
| 2 | 健康排查首页 | health-screening/index.html | 页面 |
| 3 | 每日健康打卡 | health-screening/questionnaire-fill.html | 页面 |
| 4 | 打卡记录 | health-screening/checkin-record.html | 页面 |
| 3 | 每日健康记录 | health-screening/questionnaire-fill.html | 页面 |
| 4 | 历史记录 | health-screening/checkin-record.html | 页面 |
| 5 | 问卷详情 | health-screening/questionnaire-detail.html | 页面 |
| 6 | 题目设置 | health-screening/questionnaire-setting.html | 页面 |
@@ -103,15 +103,17 @@
![页面截图](screenshots/index.jpg)
**功能说明:** 员工端健康监测模块的主入口,以卡片形式聚合展示四大子模块的关键指标概览,引导用户进入各子模块。
**功能说明:** 员工端健康监测模块的主入口,以卡片形式聚合展示子模块的关键指标概览,引导用户进入各子模块。
**主要UI元素:**
- 健康现状入口卡片(渐变背景,含人群分类图标如A/B/C/D/E、分类标签如"A类·大病人群"、更新时间)
- 穿戴监测大卡片(含设备图标、模块名称、最近同步时间、4个指标预览格:步数/心率/血氧/体温,各带状态徽章)
- 体重监测大卡片(含模块名称、最近测量时间、3个指标预览格:体重/BMI/体脂率,各带状态徽章)
- 环境监测入口卡片(含模块名称、4类状态徽章:室外空气/室内空气/水质/花粉、更新时间)
- 健康排查入口卡片(含模块名称、积分数值、连续签到天数、更新时间)
- 健康排查入口卡片(含模块名称、积分数值、连续记录天数、更新时间)
**交互说明:**
- 点击"健康现状"卡片进入健康现状页面
- 点击"穿戴监测"卡片进入穿戴监测首页
- 点击"体重监测"卡片进入体重监测首页
- 点击"环境监测"卡片进入环境监测首页
@@ -125,29 +127,29 @@
![页面截图](screenshots/health-screening/index.jpg)
**功能说明:** 展示员工健康打卡积分、连续签到天数和积分排行榜,引导员工进行每日打卡
**功能说明:** 展示员工健康记录积分、连续记录天数和积分排行榜,引导员工进行每日健康记录
**主要UI元素:**
- 积分卡片(显示当前积分、连续签到天数)
- "立即打卡"按钮
- "打卡记录"按钮
- 积分排行榜列表
- 积分卡片(显示当前积分、连续记录天数)
- "立即记录"按钮
- "历史记录"按钮
- 积分排行榜列表(含"我的排名"置顶卡片和完整排名列表)
**交互说明:**
- 点击"立即打卡"跳转到每日健康打卡页面
- 点击"打卡记录"跳转到打卡记录页面
- 点击"立即记录"跳转到每日健康记录页面
- 点击"历史记录"跳转到历史记录页面
---
### 3. 每日健康打卡health-screening/questionnaire-fill.html
### 3. 每日健康记录health-screening/questionnaire-fill.html
![页面截图](screenshots/health-screening/questionnaire-fill.jpg)
**功能说明:** 员工填写每日健康自查问卷,包含身体状况、心理状态、生活习惯三个类别共8个问题。
**功能说明:** 员工填写每日健康自查问卷,包含身体状况、心理状态、生活习惯三个类别共8个问题,每个问题标注填写频率
**主要UI元素:**
- 分类标题(身体状况、心理状态、生活习惯)
- 问题卡片(含必填/选填标签)
- 问题卡片(含必填/选填标签、频率标签如"每日"/"每周"/"每月"
- 单选选项
- 提交按钮
- 成功弹窗(显示积分奖励)
@@ -155,6 +157,7 @@
**业务规则:**
- 必填题目必须全部作答才能提交
- 选填题目可跳过
- 每个问题标注填写频率(每日/每周/每月),指示该问题的出现周期
- 提交成功后弹窗显示获得的积分
**交互说明:**
@@ -164,16 +167,16 @@
---
### 4. 打卡记录(health-screening/checkin-record.html
### 4. 历史记录(health-screening/checkin-record.html
![页面截图](screenshots/health-screening/checkin-record.jpg)
**功能说明:** 以日历视图和列表形式展示员工的打卡历史记录
**功能说明:** 以日历视图和列表形式展示员工的健康记录历史
**主要UI元素:**
- 月份日历(已打卡日期标记)
- 月份日历(已记录日期标记)
- 日历展开/收起按钮
- 历史记录列表(打卡时间、日期、积分)
- 历史记录列表(记录时间、日期、积分)
**交互说明:**
- 点击日历导航按钮切换月份
@@ -260,7 +263,7 @@
- 时间维度Tab(日/周/月/年)
- 当前心率大字显示
- 折线趋势图
- 最高/最低/平均值统计卡片
- 最高/最低/平均值统计卡片(单位:次/分钟)
- "查看预警历史"按钮
**交互说明:**
@@ -276,13 +279,14 @@
**功能说明:** 展示心率预警记录列表,支持按年份和月份筛选。
**主要UI元素:**
- 预警统计卡片(本月预警次数)
- 预警记录列表标题(含总条数)
- 筛选按钮
- 预警记录卡片(时间、等级、数值、持续时长、触发原因)
- 预警记录卡片(时间、等级、数值、持续时长、触发原因、右侧阈值范围显示
- 时间筛选弹窗(年份/月份下拉)
**业务规则:**
- 预警等级:严重(红色)、警告(橙色)
- 每条预警卡片右侧显示正常阈值范围参考
**交互说明:**
- 点击筛选按钮打开时间筛选弹窗
@@ -292,7 +296,9 @@
### 10-11. 血氧详情与预警历史
**血氧详情(wearable/blood-oxygen.html** **血氧预警历史(wearable/blood-oxygen-alerts.html** 的页面结构与心率详情/预警历史一致,展示血氧监测数据和预警记录
**血氧详情(wearable/blood-oxygen.html** 展示血氧监测数据,图表采用柱状图形式(正常/警告/危险三色区分),支持日/周/月/年多时间维度查看,底部时间轴滑块可交互选择具体时间点
**血氧预警历史(wearable/blood-oxygen-alerts.html):** 页面结构与心率预警历史一致,展示血氧预警记录列表,每条卡片右侧显示正常阈值范围。
![血氧详情](screenshots/wearable/blood-oxygen.jpg)
![血氧预警](screenshots/wearable/blood-oxygen-alerts.jpg)
@@ -306,13 +312,13 @@
**血压详情(wearable/blood-pressure.html):** 展示收缩压/舒张压双线图表,统计卡片分别显示收缩压和舒张压的最高/最低值。
**血压预警历史(wearable/blood-pressure-alerts.html):** 预警记录卡片同时显示收缩压和舒张压数值。
**血压预警历史(wearable/blood-pressure-alerts.html):** 预警记录卡片同时显示收缩压和舒张压数值,右侧显示正常阈值范围
---
### 14-15. 体温详情与预警历史
**体温详情(wearable/temperature.html****体温预警历史(wearable/temperature-alerts.html** 的页面结构与心率一致。
**体温详情(wearable/temperature.html****体温预警历史(wearable/temperature-alerts.html** 的页面结构与心率一致,预警历史页面每条卡片右侧显示正常阈值范围
![体温详情](screenshots/wearable/temperature.jpg)
![体温预警](screenshots/wearable/temperature-alerts.jpg)
@@ -321,7 +327,7 @@
### 16-17. 压力详情与预警历史
**压力详情(wearable/stress.html****压力预警历史(wearable/stress-alerts.html** 的页面结构与心率一致,额外显示压力等级评估徽章。
**压力详情(wearable/stress.html****压力预警历史(wearable/stress-alerts.html** 的页面结构与心率一致,额外显示压力等级评估徽章,预警历史页面每条卡片右侧显示正常阈值范围
![压力详情](screenshots/wearable/stress.jpg)
![压力预警](screenshots/wearable/stress-alerts.jpg)
@@ -489,7 +495,7 @@
**主要UI元素:**
- 监测类型Tab(室外空气、室内空气、水质、花粉)
- 地图视图(设备标记、图例、搜索框、定位按钮)
- 地图视图(设备标记、图例、搜索框、定位按钮、缩放按钮+/-
- 列表视图(含筛选和搜索)
- 底部弹窗(设备简要信息)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 283 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 12 KiB

@@ -79,6 +79,32 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
z-index: 10;
}
/* 地图缩放按钮 */
.map-zoom-controls {
position: absolute;
right: 12px;
bottom: 130px;
display: flex;
flex-direction: column;
gap: 8px;
z-index: 10;
}
.map-zoom-btn {
width: 36px; height: 36px;
background: #fff;
border-radius: 50%;
box-shadow: 0 2px 8px rgba(0,0,0,0.18);
display: flex; align-items: center; justify-content: center;
cursor: pointer;
font-size: 20px;
font-weight: 600;
color: #666;
user-select: none;
}
.map-zoom-btn:active {
transform: scale(0.95);
}
/* 图例 */
.map-legend {
position: absolute;
@@ -313,6 +339,18 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
<!-- 设备标记容器 -->
<div id="markerContainer"></div>
<!-- 缩放按钮 -->
<div class="map-zoom-controls">
<div class="map-zoom-btn">+</div>
<div class="map-zoom-btn"></div>
</div>
<!-- 缩放按钮 -->
<div class="map-zoom-controls">
<div class="map-zoom-btn">+</div>
<div class="map-zoom-btn"></div>
</div>
<!-- 定位按钮 -->
<div class="map-locate-btn">
<svg width="18" height="18" fill="none"><circle cx="9" cy="9" r="3" fill="#1aad8d"/><path d="M9 1v3M9 15v3M1 9h3M15 9h3" stroke="#1aad8d" stroke-width="1.5" stroke-linecap="round"/></svg>
@@ -64,7 +64,7 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
<div class="nav-back" onclick="location.href='index.html'">
<svg width="12" height="20" fill="none"><path d="M10 2L2 10l8 8" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
</div>
<div class="nav-title">打卡记录</div>
<div class="nav-title">历史记录</div>
</div>
<div class="scroll-content">
<!-- 日历 -->
@@ -91,7 +91,7 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
</div>
<!-- 历史记录 -->
<div class="history-section">
<div class="history-title">历史记录</div>
<!-- <div class="history-title">历史记录</div>-->
<div class="history-list">
<a class="history-item" href="questionnaire-detail.html">
<div class="history-item__left">
@@ -80,16 +80,16 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
<span class="score-card__num">1,280</span>
<span class="score-card__unit">积分</span>
</div>
<div class="score-card__streak">🔥 连续签到 <strong>7</strong></div>
<div class="score-card__streak">🔥 连续记录 <strong>7</strong></div>
</div>
<div class="score-card__actions">
<button class="btn-checkin" onclick="location.href='questionnaire-fill.html'">
<svg width="18" height="18" fill="none"><path d="M9 1v16M1 9h16" stroke="#667eea" stroke-width="2.5" stroke-linecap="round"/></svg>
立即打卡
立即记录
</button>
<button class="btn-record" onclick="location.href='checkin-record.html'">
<svg width="16" height="16" fill="none"><path d="M3 4h10M3 8h10M3 12h7" stroke="#fff" stroke-width="1.5" stroke-linecap="round"/></svg>
打卡记录
历史记录
</button>
</div>
</div>
@@ -24,6 +24,7 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
.question-card { background: #fff; border-radius: 12px; padding: 16px; margin-bottom: 12px; }
.question-card__header { display: flex; align-items: center; gap: 6px; margin-bottom: 10px; }
.question-card__title { font-size: 14px; font-weight: 600; color: #1a1a1a; line-height: 1.5; flex: 1; }
.question-card__freq { font-size: 11px; color: #999; font-weight: 400; }
.tag-required { font-size: 10px; color: #ff4d4f; background: #fff2f0; padding: 1px 6px; border-radius: 3px; flex-shrink: 0; border: 1px solid #ffccc7; }
.tag-optional { font-size: 10px; color: #999; background: #f5f5f5; padding: 1px 6px; border-radius: 3px; flex-shrink: 0; }
.question-card__options { display: flex; flex-direction: column; gap: 8px; }
@@ -76,7 +77,7 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
<div class="category-title">身体状况</div>
<div class="question-card">
<div class="question-card__header">
<div class="question-card__title">1. 今天您的整体身体感觉如何?</div>
<div class="question-card__title">1. 今天您的整体身体感觉如何?<span class="question-card__freq">(每日)</span></div>
<span class="tag-required">必填</span>
</div>
<div class="question-card__options">
@@ -88,7 +89,7 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
</div>
<div class="question-card">
<div class="question-card__header">
<div class="question-card__title">2. 昨晚的睡眠质量如何?</div>
<div class="question-card__title">2. 昨晚的睡眠质量如何?<span class="question-card__freq">(每日)</span></div>
<span class="tag-required">必填</span>
</div>
<div class="question-card__options">
@@ -100,7 +101,7 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
</div>
<div class="question-card">
<div class="question-card__header">
<div class="question-card__title">3. 是否有头痛、头晕等不适症状?</div>
<div class="question-card__title">3. 是否有头痛、头晕等不适症状?<span class="question-card__freq">(每周)</span></div>
<span class="tag-optional">选填</span>
</div>
<div class="question-card__options">
@@ -114,7 +115,7 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
<div class="category-title">心理状态</div>
<div class="question-card">
<div class="question-card__header">
<div class="question-card__title">4. 今天的情绪状态如何?</div>
<div class="question-card__title">4. 今天的情绪状态如何?<span class="question-card__freq">(每日)</span></div>
<span class="tag-required">必填</span>
</div>
<div class="question-card__options">
@@ -126,7 +127,7 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
</div>
<div class="question-card">
<div class="question-card__header">
<div class="question-card__title">5. 近期是否感到工作压力较大?</div>
<div class="question-card__title">5. 近期是否感到工作压力较大?<span class="question-card__freq">(每月)</span></div>
<span class="tag-optional">选填</span>
</div>
<div class="question-card__options">
@@ -140,7 +141,7 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
<div class="category-title">生活习惯</div>
<div class="question-card">
<div class="question-card__header">
<div class="question-card__title">6. 昨天是否进行了运动锻炼?</div>
<div class="question-card__title">6. 昨天是否进行了运动锻炼?<span class="question-card__freq">(每日)</span></div>
<span class="tag-required">必填</span>
</div>
<div class="question-card__options">
@@ -152,7 +153,7 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
</div>
<div class="question-card">
<div class="question-card__header">
<div class="question-card__title">7. 昨天的饮食是否规律?</div>
<div class="question-card__title">7. 昨天的饮食是否规律?<span class="question-card__freq">(每日)</span></div>
<span class="tag-required">必填</span>
</div>
<div class="question-card__options">
@@ -164,7 +165,7 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
</div>
<div class="question-card">
<div class="question-card__header">
<div class="question-card__title">8. 昨天饮水量是否充足(8杯以上)?</div>
<div class="question-card__title">8. 昨天饮水量是否充足(8杯以上)?<span class="question-card__freq">(每周)</span></div>
<span class="tag-optional">选填</span>
</div>
<div class="question-card__options">
@@ -0,0 +1,172 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>健康现状 - 员工端 | 健康长庆原型</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #f0f2f5; display: flex; justify-content: center; align-items: flex-start; min-height: 100vh; padding: 20px; font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif; }
.phone-shell { width: 390px; height: 844px; background: #f5f7fa; border-radius: 44px; box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0; overflow: hidden; position: relative; display: flex; flex-direction: column; }
.status-bar { height: 44px; display: flex; align-items: center; justify-content: space-between; padding: 0 24px; flex-shrink: 0; background: #fff; }
.status-bar .time { font-size: 15px; font-weight: 700; color: #333; }
.status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar { height: 44px; background: #fff; display: flex; align-items: center; padding: 0 16px; flex-shrink: 0; border-bottom: 1px solid #f0f0f0; }
.nav-back { width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; cursor: pointer; }
.nav-title { flex: 1; text-align: center; font-size: 17px; font-weight: 600; color: #1a1a1a; margin-right: 28px; }
.scroll-content { flex: 1; overflow-y: auto; padding: 0; }
.scroll-content::-webkit-scrollbar { display: none; }
/* 仪表盘区域 */
.gauge-section { background: linear-gradient(180deg, #f0f0ff 0%, #f5f7fa 100%); padding: 32px 16px 20px; display: flex; flex-direction: column; align-items: center; }
.gauge-wrap { position: relative; width: 200px; height: 120px; margin-bottom: 12px; }
.gauge-wrap svg { width: 100%; height: 100%; }
.gauge-current { position: absolute; bottom: 0; left: 50%; transform: translateX(-50%); text-align: center; }
.gauge-current__letter { font-size: 36px; font-weight: 800; color: #ff4d4f; }
.gauge-current__label { font-size: 11px; color: #999; margin-top: 2px; }
.gauge-badge { position: absolute; top: 10px; right: -10px; width: 40px; height: 40px; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 20px; font-weight: 800; color: #fff; }
.gauge-badge--A { background: #ff4d4f; }
/* 人群标签 */
.crowd-tag { display: inline-flex; align-items: center; padding: 6px 16px; border-radius: 20px; font-size: 15px; font-weight: 700; margin-bottom: 8px; }
.crowd-tag--A { color: #ff4d4f; background: #fff1f0; border: 1px solid #ffccc7; }
.crowd-tag--B { color: #fa8c16; background: #fff7e6; border: 1px solid #ffd591; }
.crowd-tag--C { color: #faad14; background: #fffbe6; border: 1px solid #ffe58f; }
.crowd-tag--D { color: #1890ff; background: #e6f7ff; border: 1px solid #91d5ff; }
.crowd-tag--E { color: #52c41a; background: #f6ffed; border: 1px solid #b7eb8f; }
.update-time { font-size: 12px; color: #999; }
/* 说明区 */
.note-section { padding: 16px; }
.note-card { background: #fff; border-radius: 12px; padding: 14px 16px; }
.note-card__title { font-size: 13px; font-weight: 600; color: #1a1a1a; margin-bottom: 6px; display: flex; align-items: center; gap: 6px; }
.note-card__text { font-size: 12px; color: #666; line-height: 1.8; }
/* 信息卡片 */
.info-section { padding: 0 16px 16px; }
.info-section__title { font-size: 15px; font-weight: 700; color: #1a1a1a; margin-bottom: 10px; }
.info-card { background: #fff; border-radius: 12px; overflow: hidden; margin-bottom: 12px; }
.info-row { display: flex; align-items: flex-start; padding: 12px 16px; border-bottom: 1px solid #f5f5f5; }
.info-row:last-child { border-bottom: none; }
.info-row__label { width: 80px; font-size: 13px; color: #999; flex-shrink: 0; }
.info-row__value { flex: 1; font-size: 13px; color: #1a1a1a; text-align: right; }
.info-row__value--highlight { color: #ff4d4f; font-weight: 600; }
.info-row__value--primary { color: #1890ff; font-weight: 600; }
/* 分类信息 */
.category-header { display: flex; align-items: center; gap: 6px; padding: 14px 16px 10px; font-size: 14px; font-weight: 600; color: #1a1a1a; }
.category-header__bar { width: 3px; height: 16px; border-radius: 2px; background: #722ed1; }
.bottom-safe { height: 34px; flex-shrink: 0; }
</style>
</head>
<body>
<div class="phone-shell">
<div class="status-bar">
<span class="time">9:41</span>
<span class="icons">
<svg width="16" height="16" fill="none"><rect x="1" y="4" width="3" height="8" rx="1" fill="#333"/><rect x="5" y="3" width="3" height="9" rx="1" fill="#333"/><rect x="9" y="1" width="3" height="11" rx="1" fill="#333"/><rect x="13" y="0" width="3" height="12" rx="1" fill="#333" opacity=".3"/></svg>
<svg width="16" height="16" fill="none"><path d="M8 3C5.5 3 3.2 4 1.5 5.5l1.4 1.4C4.3 5.5 6.1 5 8 5s3.7.5 5.1 1.9l1.4-1.4C12.8 4 10.5 3 8 3zm0 4c-1.4 0-2.6.5-3.5 1.3L6 9.8c.6-.5 1.3-.8 2-.8s1.4.3 2 .8l1.5-1.5C10.6 7.5 9.4 7 8 7zm0 4c-.6 0-1 .4-1 1s.4 1 1 1 1-.4 1-1-.4-1-1-1z" fill="#333"/></svg>
<svg width="22" height="12" fill="none"><rect x=".5" y=".5" width="19" height="11" rx="2" stroke="#333"/><rect x="20" y="3.5" width="1.5" height="5" rx=".75" fill="#333"/><rect x="2" y="2" width="14" height="8" rx="1" fill="#333"/></svg>
</span>
</div>
<div class="nav-bar">
<div class="nav-back" onclick="history.back()">
<svg width="12" height="20" fill="none"><path d="M10 2L2 10l8 8" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
</div>
<div class="nav-title">健康现状</div>
</div>
<div class="scroll-content">
<!-- 仪表盘 -->
<div class="gauge-section">
<div class="gauge-wrap">
<svg viewBox="0 0 200 120" fill="none">
<!-- 背景弧线 E-D-C-B-A -->
<path d="M20 110 A80 80 0 0 1 52 42" stroke="#e8e8e8" stroke-width="12" stroke-linecap="round"/>
<path d="M56 38 A80 80 0 0 1 90 18" stroke="#e8e8e8" stroke-width="12" stroke-linecap="round"/>
<path d="M94 16 A80 80 0 0 1 110 16" stroke="#e8e8e8" stroke-width="12" stroke-linecap="round"/>
<path d="M114 18 A80 80 0 0 1 148 38" stroke="#e8e8e8" stroke-width="12" stroke-linecap="round"/>
<path d="M152 42 A80 80 0 0 1 180 110" stroke="#ff4d4f" stroke-width="12" stroke-linecap="round"/>
<!-- 刻度标签 -->
<text x="14" y="115" font-size="12" fill="#ccc" font-weight="600">E</text>
<text x="42" y="38" font-size="12" fill="#ccc" font-weight="600">D</text>
<text x="95" y="12" font-size="12" fill="#ccc" font-weight="600">C</text>
<text x="150" y="38" font-size="12" fill="#ccc" font-weight="600">B</text>
<text x="180" y="115" font-size="12" fill="#ccc" font-weight="600">A</text>
</svg>
<div class="gauge-current">
<div class="gauge-current__letter">A</div>
<div class="gauge-current__label">当前属于</div>
</div>
<div class="gauge-badge gauge-badge--A">A</div>
</div>
<div class="crowd-tag crowd-tag--A">A类人群</div>
<div class="update-time">更新时间:2026-03-26 14:36</div>
</div>
<!-- 说明 -->
<div class="note-section">
<div class="note-card">
<div class="note-card__title">💬 说明</div>
<div class="note-card__text">现状评估的人群分类按A、B、C、D、E五个分类进行分类。<br>E类:健康人群 | D类:风险人群 | C类:异常人群 | B类:慢病人群 | A类:大病人群</div>
</div>
</div>
<!-- 住院信息 -->
<div class="info-section">
<div class="info-section__title">住院信息</div>
<div class="info-card">
<div class="info-row">
<span class="info-row__label">住院状态</span>
<span class="info-row__value"><span class="info-row__value--highlight">治疗中</span><br><span style="font-size:12px;color:#999;">宁夏宝石花医院</span></span>
</div>
<div class="info-row">
<span class="info-row__label">住院时间</span>
<span class="info-row__value">2026-03-26</span>
</div>
<div class="info-row">
<span class="info-row__label">住院次数</span>
<span class="info-row__value">4</span>
</div>
<div class="info-row">
<span class="info-row__label">联系人</span>
<span class="info-row__value">张智博</span>
</div>
<div class="info-row">
<span class="info-row__label">联系电话</span>
<span class="info-row__value info-row__value--primary">15735365522</span>
</div>
</div>
</div>
<!-- 分类信息 -->
<div class="info-section">
<div class="info-section__title">分类信息(大病信息)</div>
<div class="info-card">
<div class="category-header"><span class="category-header__bar"></span>肺癌</div>
<div class="info-row">
<span class="info-row__label">确诊年份</span>
<span class="info-row__value">2022年</span>
</div>
<div class="info-row">
<span class="info-row__label">确诊医院</span>
<span class="info-row__value">宁夏宝石花医院</span>
</div>
<div class="info-row">
<span class="info-row__label">大病状态</span>
<span class="info-row__value">可控</span>
</div>
<div class="info-row">
<span class="info-row__label">治疗情况</span>
<span class="info-row__value">目前属于血糖偏高,可控阶段,按时服药,定期复诊。</span>
</div>
</div>
</div>
</div>
<div class="bottom-safe"></div>
</div>
</body>
</html>
+30 -2
View File
@@ -74,7 +74,25 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
.entry-arrow { width: 16px; height: 16px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; }
.entry-time-tag { font-size: 10px; color: #bbb; margin-top: 3px; }
/* 底部安全区 */
/* 健康现状卡片 */
.health-status-card { background: linear-gradient(135deg, #f0f5ff 0%, #e6fffb 100%); border-radius: 16px; padding: 14px 16px; margin-bottom: 12px; display: flex; align-items: center; gap: 14px; cursor: pointer; transition: transform 0.15s; text-decoration: none; }
.health-status-card:active { transform: scale(0.98); }
.health-status__icon { width: 44px; height: 44px; border-radius: 50%; background: #fff; display: flex; align-items: center; justify-content: center; font-size: 20px; font-weight: 800; flex-shrink: 0; box-shadow: 0 2px 8px rgba(0,0,0,0.08); }
.health-status__icon--A { color: #ff4d4f; border: 2px solid #ff4d4f; }
.health-status__icon--B { color: #fa8c16; border: 2px solid #fa8c16; }
.health-status__icon--C { color: #faad14; border: 2px solid #faad14; }
.health-status__icon--D { color: #1890ff; border: 2px solid #1890ff; }
.health-status__icon--E { color: #52c41a; border: 2px solid #52c41a; }
.health-status__info { flex: 1; min-width: 0; }
.health-status__title { font-size: 14px; font-weight: 600; color: #1a1a1a; margin-bottom: 3px; }
.health-status__desc { font-size: 12px; color: #666; }
.health-status__tag { display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 20px; font-size: 11px; font-weight: 600; }
.health-status__tag--A { color: #ff4d4f; background: #fff1f0; }
.health-status__tag--B { color: #fa8c16; background: #fff7e6; }
.health-status__tag--C { color: #faad14; background: #fffbe6; }
.health-status__tag--D { color: #1890ff; background: #e6f7ff; }
.health-status__tag--E { color: #52c41a; background: #f6ffed; }
.health-status__arrow { width: 16px; height: 16px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; }
.bottom-safe { height: 20px; flex-shrink: 0; }
</style>
</head>
@@ -97,6 +115,16 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
<div class="scroll-content">
<!-- 健康现状 -->
<a class="health-status-card" href="health-status.html">
<div class="health-status__icon health-status__icon--A">A</div>
<div class="health-status__info">
<div class="health-status__title">健康现状 <span class="health-status__tag health-status__tag--A">A类·大病人群</span></div>
<div class="health-status__desc">更新时间:2026-05-26 10:30</div>
</div>
<div class="health-status__arrow"><svg width="7" height="12" fill="none"><path d="M1 1l5 5-5 5" stroke="#ccc" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
</a>
<!-- 穿戴监测 大卡片 -->
<a class="core-card" href="wearable/index.html">
@@ -195,7 +223,7 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
<div class="entry-name">健康排查</div>
<div class="entry-metrics">
<span class="entry-metric">积分 <strong>1,280</strong></span>
<span class="entry-metric">连续签到 <strong>7</strong></span>
<span class="entry-metric">连续记录 <strong>7</strong></span>
</div>
<div class="entry-time-tag">05-25 08:00 更新</div>
</div>
@@ -205,6 +205,24 @@ body {
/* 底部安全区 */
.bottom-safe { height: 20px; flex-shrink: 0; }
/* 右侧阈值范围 */
.alert-card__threshold {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 4px;
flex-shrink: 0;
padding-top: 4px;
}
.alert-card__threshold-item {
font-size: 11px;
color: #bbb;
}
.alert-card__threshold-item span {
font-weight: 600;
color: #666;
}
/* 筛选按钮 */
.filter-btn {
display: flex;
@@ -401,41 +419,6 @@ body {
<!-- 滚动内容区 -->
<div class="scroll-content">
<!-- 预警统计卡片 -->
<div class="stats-card">
<div class="stats-card__left">
<div class="stats-card__label">本月预警</div>
<div class="stats-card__count-row">
<span class="stats-card__count">0</span>
<span class="stats-card__unit"></span>
</div>
</div>
<div class="stats-card__right">
<!-- 血氧水滴图标,绿色背景表示正常 -->
<div class="stats-card__icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M12 2C12 2 5 9.5 5 14a7 7 0 0 0 14 0C19 9.5 12 2 12 2z" fill="#52c41a" opacity="0.85"/>
<path d="M9 13.5a3 3 0 0 0 3 3" stroke="#fff" stroke-width="1.5" stroke-linecap="round" opacity="0.7"/>
</svg>
</div>
<!-- <div class="stats-card__recent-label">最近预警时间</div>-->
<div class="stats-card__unit">无预警,请继续保持</div>
</div>
</div>
<!-- 空状态:本月无预警记录 -->
<!-- <div class="empty-state">-->
<!-- &lt;!&ndash; 大图标:绿色盾牌表示健康安全 &ndash;&gt;-->
<!-- <div class="empty-state__icon">-->
<!-- <svg width="40" height="40" viewBox="0 0 24 24" fill="none">-->
<!-- <path d="M12 2L3 7v5c0 5.25 3.75 10.15 9 11.35C17.25 22.15 21 17.25 21 12V7L12 2z" fill="#52c41a" opacity="0.15" stroke="#52c41a" stroke-width="1.5" stroke-linejoin="round"/>-->
<!-- &lt;!&ndash; 对勾 &ndash;&gt;-->
<!-- <polyline points="9 12 11 14 15 10" stroke="#52c41a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>-->
<!-- </svg>-->
<!-- </div>-->
<!-- <div class="empty-state__text">暂无预警记录,请继续保持</div>-->
<!-- </div>-->
<!-- 预警记录列表标题 + 筛选按钮 -->
<div style="display: flex; align-items: center; justify-content: space-between; padding: 14px 16px 8px;">
<div class="section-title" style="margin: 0; padding: 0;">预警记录(共 1 条)</div>
@@ -455,36 +438,20 @@ body {
<div class="alert-card__dot alert-card__dot--critical"></div>
</div>
<div class="alert-card__body">
<!-- 时间 + 严重等级标签 -->
<!-- 时间 -->
<div class="alert-card__header">
<span class="alert-card__time">2026-03-08 03:22</span>
<span class="alert-card__badge alert-card__badge--critical">严重</span>
</div>
<!-- 血氧监测值 -->
<div class="alert-card__value-row">
<span class="alert-card__value">93</span>
<span class="alert-card__value-unit">%</span>
</div>
<!-- 持续时长 + 血氧标签 -->
<div class="alert-card__detail-row">
<!-- 时钟图标 + 持续时长 -->
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<polyline points="12 6 12 12 16 14"/>
</svg>
<span>持续 8 分钟</span>
</div>
<!-- 血氧水滴图标 + 标签 -->
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<path d="M12 2C12 2 6 9 6 14a6 6 0 0 0 12 0C18 9 12 2 12 2z"/>
</svg>
<span>血氧</span>
</div>
</div>
<!-- 触发原因 -->
<div class="alert-card__reason">触发原因:血氧低于 95% 阈值</div>
</div>
<!-- 右侧阈值范围 -->
<div class="alert-card__threshold">
<div class="alert-card__threshold-item">上限 <span>100%</span></div>
<div class="alert-card__threshold-item">下限 <span>95%</span></div>
</div>
</div>
@@ -37,17 +37,27 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
.chart-wrap { position: relative; background: #fff; padding: 6px 16px 0; }
.chart-box { position: relative; height: 160px; }
.chart-svg { width: 100%; height: 100%; display: block; }
.chart-grid line { stroke: #ececec; stroke-width: 1; }
.chart-path { fill: none; stroke: var(--theme); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.chart-dot { fill: var(--theme); }
.chart-grid line { stroke: #f0f0f0; stroke-width: 0.5; stroke-dasharray: none; }
.chart-bar { rx: 2; }
.chart-bar--normal { fill: #95de64; }
.chart-bar--warn { fill: #fa8c16; }
.chart-bar--danger { fill: #ff7875; }
.y-axis-labels { position: absolute; top: 6px; right: 0; width: 56px; height: 208px; pointer-events: none; }
.y-axis-label { position: absolute; right: 0; transform: translateY(50%); font-size: 12px; color: #666; }
.x-axis { display: grid; gap: 0; padding: 8px 14px 0 0; font-size: 12px; color: #666; }
.x-axis span { text-align: center; white-space: nowrap; }
.x-axis span { text-align: center; white-space: nowrap; cursor: pointer; transition: all 0.15s; }
.x-axis span.active { color: #1a1a1a; font-weight: 700; font-size: 14px; }
.x-axis--dense { font-size: 10px; }
.timeline-knob-row { position: relative; margin: 0; height: 24px; }
.timeline-track { position: absolute; left: 0; right: 0; top: 10px; height: 1px; background: #e8e8e8; }
.timeline-knob { position: absolute; right: 16px; top: -4px; width: 24px; height: 24px; border-radius: 50%; background: #fff; box-shadow: 0 2px 10px rgba(0,0,0,0.12); }
.x-axis--dense span.active { font-size: 12px; }
.timeline-knob-row { position: relative; margin: 0; height: 32px; }
.timeline-track { position: absolute; left: 0; right: 0; top: 14px; height: 2px; background: #e8e8e8; border-radius: 1px; }
.timeline-knob { position: absolute; top: 4px; width: 28px; height: 28px; border-radius: 50%; background: #fff; box-shadow: 0 2px 10px rgba(0,0,0,0.15); cursor: grab; transition: left 0.15s; display: flex; align-items: center; justify-content: center; }
.timeline-knob::after { content: ''; width: 10px; height: 10px; border-radius: 50%; background: #e0e0e0; }
.timeline-knob:active { cursor: grabbing; box-shadow: 0 3px 14px rgba(0,0,0,0.2); }
/* 选中柱子指示线 */
.chart-indicator { stroke: #ffaaa5; stroke-width: 1; stroke-dasharray: 3 2; }
.chart-bar--selected { opacity: 1; }
.chart-bar--dimmed { opacity: 0.5; }
.card { margin: 8px 12px 0; background: #fff; border-radius: 22px; padding: 14px 12px; display: flex; gap: 0; }
.metric-row { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 6px 6px; }
@@ -105,15 +115,15 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
<div class="chart-box">
<svg class="chart-svg" id="chart" viewBox="0 0 358 258" preserveAspectRatio="none">
<g class="chart-grid" id="chartGrid"></g>
<path id="line" class="chart-path"></path>
<circle id="dot" class="chart-dot" r="3.2"></circle>
<g id="chartBars"></g>
<line id="chartIndicator" class="chart-indicator" x1="0" y1="0" x2="0" y2="258" style="display:none;"/>
</svg>
<div class="y-axis-labels" id="yAxisLabels"></div>
</div>
<div class="x-axis" id="xAxis"></div>
<div class="timeline-knob-row">
<div class="timeline-knob-row" id="timelineRow">
<div class="timeline-track"></div>
<div class="timeline-knob"></div>
<div class="timeline-knob" id="timelineKnob"></div>
</div>
</div>
</div>
@@ -161,56 +171,56 @@ var dataMap = {
rangeText: '00:00-23:59',
unit: '%',
decimals: 0,
points: [97,97,96,96,95,96,96,97,98,98,97,97,98,99,98,97,97,96,97,98,98,97,97,97],
points: [97,97,96,96,95,96,88,96,97,98,98,97,89,98,99,98,97,97,96,97,88,98,97,97],
totalMode: 'last',
yTicks: [100, 98, 96, 94],
yTicks: [100, 90, 80],
xLabels: ['0:00','6:00','12:00','18:00','24:00'],
status: '正常',
max: { v:99, hint:'10:10' },
min: { v:95, hint:'02:50' },
avg: { v:97, hint:'全天' }
min: { v:88, hint:'06:30' },
avg: { v:96, hint:'全天' }
},
week: {
dateText: '4月27日-5月3日',
rangeText: '周一-周日',
unit: '%',
decimals: 0,
points: [97,96,98,97,97,98,98],
totalMode: 'avg',
yTicks: [100, 98, 96, 94],
ranges: [{min:92,max:98},{min:90,max:97},{min:93,max:99},{min:91,max:98},{min:86,max:95},{min:93,max:99},{min:94,max:98}],
totalMode: 'range',
yTicks: [100, 90, 80],
xLabels: ['周一','周二','周三','周四','周五','周六','周日'],
status: '正常',
max: { v:98, hint:'周三' },
min: { v:96, hint:'周' },
avg: { v:97, hint:'本周' }
max: { v:99, hint:'周三' },
min: { v:86, hint:'周' },
avg: { v:96, hint:'本周' }
},
month: {
dateText: '2026年4月',
rangeText: '4月1日-4月30日',
unit: '%',
decimals: 0,
points: [97,97,96,96,95,96,96,97,98,98,97,97,98,99,98,97,97,96,97,98,98,97,97,97,97,97,96,96,95,96],
totalMode: 'avg',
yTicks: [100, 98, 96, 94],
ranges: [{min:92,max:98},{min:91,max:97},{min:90,max:98},{min:88,max:96},{min:93,max:99},{min:91,max:98},{min:92,max:97},{min:93,max:99},{min:94,max:98},{min:90,max:97},{min:91,max:98},{min:92,max:99},{min:93,max:98},{min:94,max:99},{min:91,max:97},{min:90,max:96},{min:86,max:94},{min:91,max:97},{min:92,max:98},{min:93,max:99},{min:94,max:98},{min:92,max:97},{min:91,max:98},{min:93,max:98},{min:92,max:97},{min:91,max:98},{min:90,max:97},{min:89,max:96},{min:87,max:95},{min:91,max:97}],
totalMode: 'range',
yTicks: [100, 90, 80],
xLabels: ['1日','7日','14日','21日','30日'],
status: '正常',
max: { v:99, hint:'04-14' },
min: { v:95, hint:'04-05' },
avg: { v:97, hint:'本月' }
min: { v:86, hint:'04-17' },
avg: { v:96, hint:'本月' }
},
year: {
dateText: '2026年',
rangeText: '1月-12月',
unit: '%',
decimals: 0,
points: [97,97,96,96,97,96,96,97,98,97,96,97],
totalMode: 'avg',
yTicks: [100, 98, 96, 94],
ranges: [{min:91,max:98},{min:90,max:97},{min:89,max:97},{min:86,max:99},{min:88,max:98},{min:91,max:97},{min:92,max:98},{min:93,max:99},{min:91,max:98},{min:92,max:97},{min:90,max:98},{min:91,max:97}],
totalMode: 'range',
yTicks: [100, 90, 80],
xLabels: ['1月','2月','3月','4月','5月','6月','7月','8月','9月','10月','11月','12月'],
status: '正常',
max: { v:98, hint:'01月' },
min: { v:96, hint:'03月' },
avg: { v:97, hint:'全年' }
max: { v:99, hint:'04月' },
min: { v:86, hint:'04月' },
avg: { v:96, hint:'全年' }
}
};
@@ -234,43 +244,72 @@ function formatValue(val, decimals) {
return String(val);
}
function renderChart(cfg) {
var chartWidth = 330;
function renderChart(cfg, mode) {
var chartWidth = 310;
var chartHeight = 208;
var startY = 22;
var stepX = chartWidth / (cfg.points.length - 1 || 1);
var maxY = cfg.yTicks[0];
var yMin = cfg.yTicks[cfg.yTicks.length - 1];
var yMax = cfg.yTicks[0];
var grid = document.getElementById('chartGrid');
var yAxis = document.getElementById('yAxisLabels');
var xAxis = document.getElementById('xAxis');
var lineEl = document.getElementById('line');
var dotEl = document.getElementById('dot');
var barsGroup = document.getElementById('chartBars');
grid.innerHTML = '';
cfg.yTicks.forEach(function(_, index) {
var y = startY + (chartHeight / (cfg.yTicks.length - 1)) * index;
grid.innerHTML += '<line x1="0" y1="' + y.toFixed(1) + '" x2="330" y2="' + y.toFixed(1) + '"></line>';
grid.innerHTML += '<line x1="0" y1="' + y.toFixed(1) + '" x2="' + chartWidth + '" y2="' + y.toFixed(1) + '"></line>';
});
yAxis.innerHTML = cfg.yTicks.map(function(tick, index) {
return '<span class="y-axis-label" style="top:' + (index * 52) + 'px">' + formatValue(tick, 0) + '</span>';
return '<span class="y-axis-label" style="top:' + (index * 52) + 'px">' + tick + '%</span>';
}).join('');
var pts = cfg.points.map(function(v, i) {
var x = i * stepX;
var y = startY + chartHeight - (v / maxY) * chartHeight;
return { x: x, y: y };
});
var barsHtml = '';
var threshold = 90;
var thresholdY = startY + ((yMax - threshold) / (yMax - yMin)) * chartHeight;
var d = pts.map(function(p, index) {
return (index === 0 ? 'M' : 'L') + p.x.toFixed(1) + ',' + p.y.toFixed(1);
}).join(' ');
lineEl.setAttribute('d', d);
var last = pts[pts.length - 1];
dotEl.setAttribute('cx', last.x.toFixed(1));
dotEl.setAttribute('cy', last.y.toFixed(1));
if (mode === 'day') {
// 日视图:单值柱子从顶部垂下
var barGap = 2;
var barWidth = Math.max(3, (chartWidth / cfg.points.length) - barGap);
var totalBarSpace = barWidth + barGap;
var offsetX = (chartWidth - cfg.points.length * totalBarSpace) / 2;
cfg.points.forEach(function(v, i) {
var x = offsetX + i * totalBarSpace;
var valY = startY + ((yMax - v) / (yMax - yMin)) * chartHeight;
var h = (startY + chartHeight) - valY;
var colorClass = v < 90 ? 'chart-bar--warn' : 'chart-bar--normal';
barsHtml += '<rect class="chart-bar ' + colorClass + '" x="' + x.toFixed(1) + '" y="' + valY.toFixed(1) + '" width="' + barWidth.toFixed(1) + '" height="' + h.toFixed(1) + '" rx="1.5"/>';
});
} else {
// 周/月/年:范围柱状图,每根柱子从min到max,90分界线分色
var data = cfg.ranges;
var barGap = mode === 'month' ? 2 : 6;
var barWidth = Math.max(4, (chartWidth / data.length) - barGap);
var totalBarSpace = barWidth + barGap;
var offsetX = (chartWidth - data.length * totalBarSpace) / 2;
data.forEach(function(r, i) {
var x = offsetX + i * totalBarSpace;
var topY = startY + ((yMax - r.max) / (yMax - yMin)) * chartHeight;
var botY = startY + ((yMax - r.min) / (yMax - yMin)) * chartHeight;
if (r.max <= threshold) {
// 全部在90以下,整根橙色
barsHtml += '<rect class="chart-bar chart-bar--warn" x="' + x.toFixed(1) + '" y="' + topY.toFixed(1) + '" width="' + barWidth.toFixed(1) + '" height="' + (botY - topY).toFixed(1) + '" rx="2"/>';
} else if (r.min >= threshold) {
// 全部在90以上,整根绿色
barsHtml += '<rect class="chart-bar chart-bar--normal" x="' + x.toFixed(1) + '" y="' + topY.toFixed(1) + '" width="' + barWidth.toFixed(1) + '" height="' + (botY - topY).toFixed(1) + '" rx="2"/>';
} else {
// 跨越90线:上半绿色,下半橙色
var greenH = thresholdY - topY;
var orangeH = botY - thresholdY;
barsHtml += '<rect class="chart-bar chart-bar--normal" x="' + x.toFixed(1) + '" y="' + topY.toFixed(1) + '" width="' + barWidth.toFixed(1) + '" height="' + greenH.toFixed(1) + '" rx="2"/>';
barsHtml += '<rect class="chart-bar chart-bar--warn" x="' + x.toFixed(1) + '" y="' + thresholdY.toFixed(1) + '" width="' + barWidth.toFixed(1) + '" height="' + orangeH.toFixed(1) + '" rx="2"/>';
}
});
}
barsGroup.innerHTML = barsHtml;
xAxis.className = 'x-axis' + (cfg.xLabels.length > 7 ? ' x-axis--dense' : '');
xAxis.style.gridTemplateColumns = 'repeat(' + cfg.xLabels.length + ', 1fr)';
@@ -281,11 +320,18 @@ function renderChart(cfg) {
function renderMode(mode) {
var cfg = dataMap[mode];
var value = getValue(cfg);
document.getElementById('dateText').textContent = cfg.dateText;
document.getElementById('rangeText').textContent = cfg.rangeText;
document.getElementById('mainValue').textContent = formatValue(value, cfg.decimals);
document.getElementById('mainUnit').textContent = cfg.unit;
if (cfg.totalMode === 'range') {
// 周/月/年显示范围
document.getElementById('mainValue').textContent = cfg.min.v + '% - ' + cfg.max.v + '%';
document.getElementById('mainUnit').textContent = '';
} else {
var value = getValue(cfg);
document.getElementById('mainValue').textContent = formatValue(value, cfg.decimals);
document.getElementById('mainUnit').textContent = cfg.unit;
}
document.getElementById('statusBadge').textContent = cfg.status;
document.getElementById('maxValue').textContent = formatValue(cfg.max.v, cfg.decimals);
@@ -295,14 +341,153 @@ function renderMode(mode) {
document.getElementById('avgValue').textContent = formatValue(cfg.avg.v, cfg.decimals);
document.getElementById('avgHint').textContent = cfg.avg.hint;
renderChart(cfg);
renderChart(cfg, mode);
// 初始化选中状态
selectBar(currentMode === 'day' ? cfg.points.length - 1 : cfg.ranges.length - 1);
}
var currentMode = 'day';
var selectedIndex = -1;
// 选中某根柱子
function selectBar(idx) {
var cfg = dataMap[currentMode];
var count = currentMode === 'day' ? cfg.points.length : cfg.ranges.length;
if (idx < 0) idx = 0;
if (idx >= count) idx = count - 1;
selectedIndex = idx;
// 更新柱子高亮
var bars = document.querySelectorAll('#chartBars rect');
bars.forEach(function(b) { b.classList.remove('chart-bar--dimmed', 'chart-bar--selected'); });
// 对于范围模式每根柱子可能有2个rect
var barRects = [];
if (currentMode === 'day') {
barRects = [bars[idx]];
} else {
// 范围模式:找到对应index的rect(可能1或2个)
var data = cfg.ranges;
var rectIdx = 0;
for (var i = 0; i < data.length; i++) {
var rectsForBar = (data[i].min >= 90 || data[i].max <= 90) ? 1 : 2;
if (i === idx) {
for (var r = 0; r < rectsForBar; r++) barRects.push(bars[rectIdx + r]);
break;
}
rectIdx += rectsForBar;
}
}
bars.forEach(function(b) { if (barRects.indexOf(b) === -1) b.classList.add('chart-bar--dimmed'); });
barRects.forEach(function(b) { if (b) b.classList.add('chart-bar--selected'); });
// 指示线
var indicator = document.getElementById('chartIndicator');
var chartWidth = 310;
if (currentMode === 'day') {
var barGap = 2;
var barWidth = Math.max(3, (chartWidth / cfg.points.length) - barGap);
var totalBarSpace = barWidth + barGap;
var offsetX = (chartWidth - cfg.points.length * totalBarSpace) / 2;
var cx = offsetX + idx * totalBarSpace + barWidth / 2;
} else {
var data = cfg.ranges;
var barGap2 = currentMode === 'month' ? 2 : 6;
var barWidth2 = Math.max(4, (chartWidth / data.length) - barGap2);
var totalBarSpace2 = barWidth2 + barGap2;
var offsetX2 = (chartWidth - data.length * totalBarSpace2) / 2;
var cx = offsetX2 + idx * totalBarSpace2 + barWidth2 / 2;
}
indicator.setAttribute('x1', cx.toFixed(1));
indicator.setAttribute('x2', cx.toFixed(1));
indicator.style.display = '';
// 更新滚轮位置
var knob = document.getElementById('timelineKnob');
var row = document.getElementById('timelineRow');
var rowWidth = row.offsetWidth - 28;
var knobLeft = (idx / (count - 1)) * rowWidth;
knob.style.left = knobLeft.toFixed(1) + 'px';
// 更新X轴高亮
var xSpans = document.querySelectorAll('#xAxis span');
xSpans.forEach(function(s) { s.classList.remove('active'); });
// 映射选中index到xLabel index
var xCount = cfg.xLabels.length;
var mappedX = Math.round(idx / (count - 1) * (xCount - 1));
if (xSpans[mappedX]) xSpans[mappedX].classList.add('active');
// 更新顶部rangeText
updateSummaryForSelection(cfg, idx);
}
function updateSummaryForSelection(cfg, idx) {
if (currentMode === 'day') {
var hour = Math.floor(idx * 24 / cfg.points.length);
var hourEnd = Math.floor((idx + 1) * 24 / cfg.points.length);
document.getElementById('rangeText').textContent = hour + ':00 - ' + hourEnd + ':00';
document.getElementById('mainValue').textContent = cfg.points[idx];
document.getElementById('mainUnit').textContent = '%';
} else {
var r = cfg.ranges[idx];
document.getElementById('mainValue').textContent = r.min + '% - ' + r.max + '%';
document.getElementById('mainUnit').textContent = '';
if (currentMode === 'week') {
var days = ['周一','周二','周三','周四','周五','周六','周日'];
document.getElementById('rangeText').textContent = days[idx] || '';
} else if (currentMode === 'month') {
document.getElementById('rangeText').textContent = (idx + 1) + '日';
} else if (currentMode === 'year') {
document.getElementById('rangeText').textContent = (idx + 1) + '月';
}
}
}
// 滚轮拖动
(function() {
var knob = document.getElementById('timelineKnob');
var row = document.getElementById('timelineRow');
var dragging = false;
function getIndexFromX(clientX) {
var rect = row.getBoundingClientRect();
var x = clientX - rect.left - 14;
var width = rect.width - 28;
var ratio = Math.max(0, Math.min(1, x / width));
var cfg = dataMap[currentMode];
var count = currentMode === 'day' ? cfg.points.length : cfg.ranges.length;
return Math.round(ratio * (count - 1));
}
knob.addEventListener('mousedown', function(e) { dragging = true; e.preventDefault(); });
knob.addEventListener('touchstart', function(e) { dragging = true; });
document.addEventListener('mousemove', function(e) { if (dragging) selectBar(getIndexFromX(e.clientX)); });
document.addEventListener('touchmove', function(e) { if (dragging) selectBar(getIndexFromX(e.touches[0].clientX)); });
document.addEventListener('mouseup', function() { dragging = false; });
document.addEventListener('touchend', function() { dragging = false; });
// 点击轨道也可选择
row.addEventListener('click', function(e) { selectBar(getIndexFromX(e.clientX)); });
})();
// X轴标签点击选择
document.getElementById('xAxis').addEventListener('click', function(e) {
var span = e.target.closest('span');
if (!span) return;
var spans = Array.from(document.querySelectorAll('#xAxis span'));
var xIdx = spans.indexOf(span);
if (xIdx === -1) return;
var cfg = dataMap[currentMode];
var count = currentMode === 'day' ? cfg.points.length : cfg.ranges.length;
var barIdx = Math.round(xIdx / (cfg.xLabels.length - 1) * (count - 1));
selectBar(barIdx);
});
document.querySelectorAll('.time-tab').forEach(function(tab) {
tab.onclick = function() {
document.querySelectorAll('.time-tab').forEach(function(item) { item.classList.remove('active'); });
tab.classList.add('active');
renderMode(tab.getAttribute('data-mode'));
currentMode = tab.getAttribute('data-mode');
renderMode(currentMode);
};
});
@@ -188,6 +188,24 @@ body {
.bottom-safe { height: 20px; flex-shrink: 0; }
/* 右侧阈值范围 */
.alert-card__threshold {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 4px;
flex-shrink: 0;
padding-top: 4px;
}
.alert-card__threshold-item {
font-size: 11px;
color: #bbb;
}
.alert-card__threshold-item span {
font-weight: 600;
color: #666;
}
.filter-btn {
display: flex;
align-items: center;
@@ -235,26 +253,6 @@ body {
<div class="scroll-content">
<div class="stats-card">
<div class="stats-card__left">
<div class="stats-card__label">本月预警</div>
<div class="stats-card__count-row">
<span class="stats-card__count">4</span>
<span class="stats-card__unit"></span>
</div>
</div>
<div class="stats-card__right">
<div class="stats-card__icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M12 2C8 2 4 5 4 9c0 5 8 13 8 13s8-8 8-13c0-4-4-7-8-7z" fill="#7c5cfc" stroke="#7c5cfc" stroke-width="1.5"/>
<circle cx="12" cy="9" r="3" fill="#fff"/>
</svg>
</div>
<div class="stats-card__recent-label">最近预警时间</div>
<div class="stats-card__recent-time">2026-05-11 08:45</div>
</div>
</div>
<div style="display: flex; align-items: center; justify-content: space-between; padding: 14px 16px 8px;">
<div class="section-title" style="margin: 0; padding: 0;">预警记录(共 4 条)</div>
<button class="filter-btn">
@@ -274,28 +272,15 @@ body {
<div class="alert-card__body">
<div class="alert-card__header">
<span class="alert-card__time">2026-05-11 08:45</span>
<span class="alert-card__badge alert-card__badge--critical">严重</span>
</div>
<div class="alert-card__value-row">
<span class="alert-card__value">158/98</span>
<span class="alert-card__value-unit">mmHg</span>
</div>
<div class="alert-card__detail-row">
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<polyline points="12 6 12 12 16 14"/>
</svg>
<span>持续 15 分钟</span>
</div>
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<path d="M12 2C8 2 4 5 4 9c0 5 8 13 8 13s8-8 8-13c0-4-4-7-8-7z"/>
</svg>
<span>血压</span>
</div>
</div>
<div class="alert-card__reason">触发原因:收缩压超过 150 mmHg 阈值</div>
</div>
<div class="alert-card__threshold">
<div class="alert-card__threshold-item">上限 <span>140/90</span></div>
<div class="alert-card__threshold-item">下限 <span>90/60</span></div>
</div>
</div>
@@ -307,28 +292,15 @@ body {
<div class="alert-card__body">
<div class="alert-card__header">
<span class="alert-card__time">2026-05-08 14:20</span>
<span class="alert-card__badge alert-card__badge--warning">警告</span>
</div>
<div class="alert-card__value-row">
<span class="alert-card__value">142/92</span>
<span class="alert-card__value-unit">mmHg</span>
</div>
<div class="alert-card__detail-row">
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<polyline points="12 6 12 12 16 14"/>
</svg>
<span>持续 8 分钟</span>
</div>
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<path d="M12 2C8 2 4 5 4 9c0 5 8 13 8 13s8-8 8-13c0-4-4-7-8-7z"/>
</svg>
<span>血压</span>
</div>
</div>
<div class="alert-card__reason">触发原因:收缩压超过 140 mmHg 阈值</div>
</div>
<div class="alert-card__threshold">
<div class="alert-card__threshold-item">上限 <span>140/90</span></div>
<div class="alert-card__threshold-item">下限 <span>90/60</span></div>
</div>
</div>
@@ -340,28 +312,15 @@ body {
<div class="alert-card__body">
<div class="alert-card__header">
<span class="alert-card__time">2026-05-05 07:30</span>
<span class="alert-card__badge alert-card__badge--warning">警告</span>
</div>
<div class="alert-card__value-row">
<span class="alert-card__value">145/90</span>
<span class="alert-card__value-unit">mmHg</span>
</div>
<div class="alert-card__detail-row">
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<polyline points="12 6 12 12 16 14"/>
</svg>
<span>持续 10 分钟</span>
</div>
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<path d="M12 2C8 2 4 5 4 9c0 5 8 13 8 13s8-8 8-13c0-4-4-7-8-7z"/>
</svg>
<span>血压</span>
</div>
</div>
<div class="alert-card__reason">触发原因:收缩压超过 140 mmHg 阈值</div>
</div>
<div class="alert-card__threshold">
<div class="alert-card__threshold-item">上限 <span>140/90</span></div>
<div class="alert-card__threshold-item">下限 <span>90/60</span></div>
</div>
</div>
@@ -373,28 +332,15 @@ body {
<div class="alert-card__body">
<div class="alert-card__header">
<span class="alert-card__time">2026-05-02 19:15</span>
<span class="alert-card__badge alert-card__badge--warning">警告</span>
</div>
<div class="alert-card__value-row">
<span class="alert-card__value">138/94</span>
<span class="alert-card__value-unit">mmHg</span>
</div>
<div class="alert-card__detail-row">
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<polyline points="12 6 12 12 16 14"/>
</svg>
<span>持续 6 分钟</span>
</div>
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<path d="M12 2C8 2 4 5 4 9c0 5 8 13 8 13s8-8 8-13c0-4-4-7-8-7z"/>
</svg>
<span>血压</span>
</div>
</div>
<div class="alert-card__reason">触发原因:舒张压超过 90 mmHg 阈值</div>
</div>
<div class="alert-card__threshold">
<div class="alert-card__threshold-item">上限 <span>140/90</span></div>
<div class="alert-card__threshold-item">下限 <span>90/60</span></div>
</div>
</div>
@@ -204,6 +204,24 @@ body {
/* 底部安全区 */
.bottom-safe { height: 20px; flex-shrink: 0; }
/* 右侧阈值范围 */
.alert-card__threshold {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 4px;
flex-shrink: 0;
padding-top: 4px;
}
.alert-card__threshold-item {
font-size: 11px;
color: #bbb;
}
.alert-card__threshold-item span {
font-weight: 600;
color: #666;
}
/* 筛选按钮 */
.filter-btn {
display: flex;
@@ -400,27 +418,6 @@ body {
<!-- 滚动内容区 -->
<div class="scroll-content">
<!-- 预警统计卡片 -->
<div class="stats-card">
<div class="stats-card__left">
<div class="stats-card__label">本月预警</div>
<div class="stats-card__count-row">
<span class="stats-card__count">3</span>
<span class="stats-card__unit"></span>
</div>
</div>
<div class="stats-card__right">
<!-- 预警心形图标 -->
<div class="stats-card__icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" fill="#ff4d4f" stroke="#ff4d4f" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<div class="stats-card__recent-label">最近预警时间</div>
<div class="stats-card__recent-time">2026-04-28 16:32</div>
</div>
</div>
<!-- 预警记录列表标题 + 筛选按钮 -->
<div style="display: flex; align-items: center; justify-content: space-between; padding: 14px 16px 8px;">
<div class="section-title" style="margin: 0; padding: 0;">预警记录(共 3 条)</div>
@@ -440,36 +437,20 @@ body {
<div class="alert-card__dot alert-card__dot--critical"></div>
</div>
<div class="alert-card__body">
<!-- 时间 + 等级标签 -->
<!-- 时间 -->
<div class="alert-card__header">
<span class="alert-card__time">2026-04-28 16:32</span>
<span class="alert-card__badge alert-card__badge--critical">严重</span>
</div>
<!-- 监测值 -->
<div class="alert-card__value-row">
<span class="alert-card__value">142</span>
<span class="alert-card__value-unit">bpm</span>
<span class="alert-card__value-unit">次/分钟</span>
</div>
<!-- 持续时长 + 心率标签 -->
<div class="alert-card__detail-row">
<!-- 时钟图标 + 持续时长 -->
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<polyline points="12 6 12 12 16 14"/>
</svg>
<span>持续 5 分钟</span>
</div>
<!-- 心率标签 -->
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/>
</svg>
<span>心率</span>
</div>
</div>
<!-- 触发原因 -->
<div class="alert-card__reason">触发原因:心率超过 140 bpm 阈值</div>
</div>
<!-- 右侧阈值范围 -->
<div class="alert-card__threshold">
<div class="alert-card__threshold-item">上限 <span>140 次/分钟</span></div>
<div class="alert-card__threshold-item">下限 <span>50 次/分钟</span></div>
</div>
</div>
@@ -480,34 +461,20 @@ body {
<div class="alert-card__dot alert-card__dot--warning"></div>
</div>
<div class="alert-card__body">
<!-- 时间 + 等级标签 -->
<!-- 时间 -->
<div class="alert-card__header">
<span class="alert-card__time">2026-04-25 14:18</span>
<span class="alert-card__badge alert-card__badge--warning">警告</span>
</div>
<!-- 监测值 -->
<div class="alert-card__value-row">
<span class="alert-card__value">128</span>
<span class="alert-card__value-unit">bpm</span>
<span class="alert-card__value-unit">次/分钟</span>
</div>
<!-- 持续时长 + 心率标签 -->
<div class="alert-card__detail-row">
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<polyline points="12 6 12 12 16 14"/>
</svg>
<span>持续 3 分钟</span>
</div>
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/>
</svg>
<span>心率</span>
</div>
</div>
<!-- 触发原因 -->
<div class="alert-card__reason">触发原因:心率超过 120 bpm 阈值</div>
</div>
<!-- 右侧阈值范围 -->
<div class="alert-card__threshold">
<div class="alert-card__threshold-item">上限 <span>120 次/分钟</span></div>
<div class="alert-card__threshold-item">下限 <span>50 次/分钟</span></div>
</div>
</div>
@@ -518,34 +485,20 @@ body {
<div class="alert-card__dot alert-card__dot--warning"></div>
</div>
<div class="alert-card__body">
<!-- 时间 + 等级标签 -->
<!-- 时间 -->
<div class="alert-card__header">
<span class="alert-card__time">2026-04-20 09:45</span>
<span class="alert-card__badge alert-card__badge--warning">警告</span>
</div>
<!-- 监测值 -->
<div class="alert-card__value-row">
<span class="alert-card__value">126</span>
<span class="alert-card__value-unit">bpm</span>
<span class="alert-card__value-unit">次/分钟</span>
</div>
<!-- 持续时长 + 心率标签 -->
<div class="alert-card__detail-row">
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<polyline points="12 6 12 12 16 14"/>
</svg>
<span>持续 2 分钟</span>
</div>
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/>
</svg>
<span>心率</span>
</div>
</div>
<!-- 触发原因 -->
<div class="alert-card__reason">触发原因:心率超过 120 bpm 阈值</div>
</div>
<!-- 右侧阈值范围 -->
<div class="alert-card__threshold">
<div class="alert-card__threshold-item">上限 <span>120 次/分钟</span></div>
<div class="alert-card__threshold-item">下限 <span>50 次/分钟</span></div>
</div>
</div>
@@ -98,7 +98,7 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
<span class="main-row__value" id="mainValue">72</span>
<span class="main-row__unit" id="mainUnit">bpm</span>
</div>
<div class="main-badge" id="statusBadge">正常</div>
</div>
<div class="chart-wrap">
@@ -122,7 +122,7 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
<div class="metric-row">
<div class="metric-left">
<div class="metric-left__label">最高</div>
<div class="metric-left__value"><span class="metric-left__num" id="maxValue">126</span><span class="metric-left__unit">bpm</span></div>
<div class="metric-left__value"><span class="metric-left__num" id="maxValue">126</span><span class="metric-left__unit">次/分钟</span></div>
</div>
<div class="metric-right" id="maxHint">14:20</div>
</div>
@@ -130,7 +130,7 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
<div class="metric-row">
<div class="metric-left">
<div class="metric-left__label">最低</div>
<div class="metric-left__value"><span class="metric-left__num" id="minValue">68</span><span class="metric-left__unit">bpm</span></div>
<div class="metric-left__value"><span class="metric-left__num" id="minValue">68</span><span class="metric-left__unit">次/分钟</span></div>
</div>
<div class="metric-right" id="minHint">03:40</div>
</div>
@@ -138,7 +138,7 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
<div class="metric-row">
<div class="metric-left">
<div class="metric-left__label">平均</div>
<div class="metric-left__value"><span class="metric-left__num" id="avgValue">82</span><span class="metric-left__unit">bpm</span></div>
<div class="metric-left__value"><span class="metric-left__num" id="avgValue">82</span><span class="metric-left__unit">次/分钟</span></div>
</div>
<div class="metric-right" id="avgHint">全天</div>
</div>
@@ -159,7 +159,7 @@ var dataMap = {
day: {
dateText: '4月30日 周四',
rangeText: '00:00-23:59',
unit: 'bpm',
unit: '次/分钟',
decimals: 0,
points: [84,82,80,78,76,74,72,70,68,75,79,83,88,96,102,126,108,94,90,87,85,84,83,72],
totalMode: 'last',
@@ -174,7 +174,7 @@ var dataMap = {
week: {
dateText: '4月27日-5月3日',
rangeText: '周一-周日',
unit: 'bpm',
unit: '次/分钟',
decimals: 0,
points: [78, 80, 82, 79, 83, 81, 80],
totalMode: 'avg',
@@ -189,7 +189,7 @@ var dataMap = {
month: {
dateText: '2026年4月',
rangeText: '4月1日-4月30日',
unit: 'bpm',
unit: '次/分钟',
decimals: 0,
points: [79,81,80,82,83,78,80,81,82,79,80,81,82,83,79,80,81,82,80,79,81,82,80,79,81,82,80,79,81,80],
totalMode: 'avg',
@@ -204,7 +204,7 @@ var dataMap = {
year: {
dateText: '2026年',
rangeText: '1月-12月',
unit: 'bpm',
unit: '次/分钟',
decimals: 0,
points: [79,80,81,80,79,78,79,80,81,80,79,80],
totalMode: 'avg',
@@ -290,7 +290,7 @@ function renderMode(mode) {
document.getElementById('rangeText').textContent = cfg.rangeText;
document.getElementById('mainValue').textContent = formatValue(value, cfg.decimals);
document.getElementById('mainUnit').textContent = cfg.unit;
document.getElementById('statusBadge').textContent = cfg.status;
document.getElementById('maxValue').textContent = formatValue(cfg.max.v, 0);
document.getElementById('maxHint').textContent = cfg.max.hint;
@@ -209,6 +209,24 @@ body {
/* 底部安全区 */
.bottom-safe { height: 20px; flex-shrink: 0; }
/* 右侧阈值范围 */
.alert-card__threshold {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 4px;
flex-shrink: 0;
padding-top: 4px;
}
.alert-card__threshold-item {
font-size: 11px;
color: #bbb;
}
.alert-card__threshold-item span {
font-weight: 600;
color: #666;
}
/* 筛选按钮 */
.filter-btn {
display: flex;
@@ -405,30 +423,6 @@ body {
<!-- 滚动内容区 -->
<div class="scroll-content">
<!-- 预警统计卡片 -->
<div class="stats-card">
<div class="stats-card__left">
<div class="stats-card__label">本月预警</div>
<!-- 本月预警次数:橙色大字 -->
<div class="stats-card__count-row">
<span class="stats-card__count">2</span>
<span class="stats-card__unit"></span>
</div>
</div>
<div class="stats-card__right">
<!-- 压力预警图标:橙色脑波/压力符号 -->
<div class="stats-card__icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<!-- 简化压力波形图标 -->
<path d="M3 12 L6 7 L9 15 L12 9 L15 14 L18 10 L21 12"
stroke="#fa8c16" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<div class="stats-card__recent-label">最近预警时间</div>
<div class="stats-card__recent-time">2026-04-30 16:30</div>
</div>
</div>
<!-- 预警记录列表标题 + 筛选按钮 -->
<div style="display: flex; align-items: center; justify-content: space-between; padding: 14px 16px 8px;">
<div class="section-title" style="margin: 0; padding: 0;">预警记录(共 2 条)</div>
@@ -448,36 +442,20 @@ body {
<div class="alert-card__dot alert-card__dot--warning"></div>
</div>
<div class="alert-card__body">
<!-- 时间 + 警告等级标签 -->
<!-- 时间 -->
<div class="alert-card__header">
<span class="alert-card__time">2026-04-30 16:30</span>
<span class="alert-card__badge alert-card__badge--warning">警告</span>
</div>
<!-- 压力监测值 -->
<div class="alert-card__value-row">
<span class="alert-card__value">82</span>
<span class="alert-card__value-unit">压力值</span>
</div>
<!-- 持续时长 + 压力标签 -->
<div class="alert-card__detail-row">
<!-- 时钟图标 + 持续时长 -->
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<polyline points="12 6 12 12 16 14"/>
</svg>
<span>持续 25 分钟</span>
</div>
<!-- 压力类型标签 -->
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<path d="M3 12 L6 8 L9 14 L12 9 L15 13 L18 10 L21 12"/>
</svg>
<span>压力</span>
</div>
</div>
<!-- 触发原因 -->
<div class="alert-card__reason">触发原因:压力值超过 80 阈值</div>
</div>
<!-- 右侧阈值范围 -->
<div class="alert-card__threshold">
<div class="alert-card__threshold-item">上限 <span>80</span></div>
<div class="alert-card__threshold-item">下限 <span>0</span></div>
</div>
</div>
@@ -488,36 +466,20 @@ body {
<div class="alert-card__dot alert-card__dot--critical"></div>
</div>
<div class="alert-card__body">
<!-- 时间 + 严重等级标签 -->
<!-- 时间 -->
<div class="alert-card__header">
<span class="alert-card__time">2026-04-22 15:10</span>
<span class="alert-card__badge alert-card__badge--critical">严重</span>
</div>
<!-- 压力监测值 -->
<div class="alert-card__value-row">
<span class="alert-card__value">88</span>
<span class="alert-card__value-unit">压力值</span>
</div>
<!-- 持续时长 + 压力标签 -->
<div class="alert-card__detail-row">
<!-- 时钟图标 + 持续时长 -->
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<polyline points="12 6 12 12 16 14"/>
</svg>
<span>持续 40 分钟</span>
</div>
<!-- 压力类型标签 -->
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<path d="M3 12 L6 8 L9 14 L12 9 L15 13 L18 10 L21 12"/>
</svg>
<span>压力</span>
</div>
</div>
<!-- 触发原因 -->
<div class="alert-card__reason">触发原因:压力值超过 85 阈值</div>
</div>
<!-- 右侧阈值范围 -->
<div class="alert-card__threshold">
<div class="alert-card__threshold-item">上限 <span>85</span></div>
<div class="alert-card__threshold-item">下限 <span>0</span></div>
</div>
</div>
@@ -204,6 +204,24 @@ body {
/* 底部安全区 */
.bottom-safe { height: 20px; flex-shrink: 0; }
/* 右侧阈值范围 */
.alert-card__threshold {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 4px;
flex-shrink: 0;
padding-top: 4px;
}
.alert-card__threshold-item {
font-size: 11px;
color: #bbb;
}
.alert-card__threshold-item span {
font-weight: 600;
color: #666;
}
/* 筛选按钮 */
.filter-btn {
display: flex;
@@ -400,27 +418,6 @@ body {
<!-- 滚动内容区 -->
<div class="scroll-content">
<!-- 预警统计卡片 -->
<div class="stats-card">
<div class="stats-card__left">
<div class="stats-card__label">本月预警</div>
<div class="stats-card__count-row">
<span class="stats-card__count">1</span>
<span class="stats-card__unit"></span>
</div>
</div>
<div class="stats-card__right">
<!-- 体温预警图标 -->
<div class="stats-card__icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M14 14.76V3.5a2.5 2.5 0 0 0-5 0v11.26a4.5 4.5 0 1 0 5 0z" stroke="#faad14" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<div class="stats-card__recent-label">最近预警时间</div>
<div class="stats-card__recent-time">2026-04-15 13:28</div>
</div>
</div>
<!-- 预警记录列表标题 + 筛选按钮 -->
<div style="display: flex; align-items: center; justify-content: space-between; padding: 14px 16px 8px;">
<div class="section-title" style="margin: 0; padding: 0;">预警记录(共 1 条)</div>
@@ -440,36 +437,20 @@ body {
<div class="alert-card__dot alert-card__dot--warning"></div>
</div>
<div class="alert-card__body">
<!-- 时间 + 警告等级标签 -->
<!-- 时间 -->
<div class="alert-card__header">
<span class="alert-card__time">2026-04-15 13:28</span>
<span class="alert-card__badge alert-card__badge--warning">警告</span>
</div>
<!-- 体温监测值 -->
<div class="alert-card__value-row">
<span class="alert-card__value">37.8</span>
<span class="alert-card__value-unit">°C</span>
</div>
<!-- 持续时长 + 体温标签 -->
<div class="alert-card__detail-row">
<!-- 时钟图标 + 持续时长 -->
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<polyline points="12 6 12 12 16 14"/>
</svg>
<span>持续 45 分钟</span>
</div>
<!-- 体温标签 -->
<div class="alert-card__detail-item">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#bbb" stroke-width="2">
<path d="M14 14.76V3.5a2.5 2.5 0 0 0-5 0v11.26a4.5 4.5 0 1 0 5 0z"/>
</svg>
<span>体温</span>
</div>
</div>
<!-- 触发原因 -->
<div class="alert-card__reason">触发原因:体温超过 37.3°C 阈值</div>
</div>
<!-- 右侧阈值范围 -->
<div class="alert-card__threshold">
<div class="alert-card__threshold-item">上限 <span>37.3°C</span></div>
<div class="alert-card__threshold-item">下限 <span>35.0°C</span></div>
</div>
</div>