fix: 更新文档截图和页面,新增管理员体重管理文档
@@ -15,7 +15,9 @@
|
||||
│ │ └── 体重记录 → 员工体重详情
|
||||
│ ├── 体重管理详情 → 减重排名
|
||||
│ ├── 体重管理详情 → 目标审核
|
||||
│ │ └── 目标审核页
|
||||
│ │ ├── 目标审核(待审核)
|
||||
│ │ ├── 目标审核(已通过)
|
||||
│ │ └── 目标审核(已驳回)
|
||||
│ ├── 体检进度追踪
|
||||
│ ├── 应急求助工单 → 应急工单详情
|
||||
│ └── 穿戴设备预警记录
|
||||
@@ -33,23 +35,6 @@
|
||||
└── 退出登录(确认弹窗)
|
||||
```
|
||||
|
||||
### 目标审核状态流转图
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> 待审核 : 员工提交减重目标
|
||||
待审核 --> 已通过 : 管理员审核通过
|
||||
待审核 --> 已驳回 : 管理员审核驳回
|
||||
已通过 --> 待签收 : 员工签收确认
|
||||
```
|
||||
|
||||
### 应急工单状态流转图
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> 进行中 : 操作人员派单
|
||||
进行中 --> 已完成 : 驻场人员完成处理
|
||||
```
|
||||
|
||||
## 页面清单
|
||||
|
||||
@@ -63,20 +48,22 @@ stateDiagram-v2
|
||||
| 6 | workbench/exercise-employee-detail.html | 员工运动详情 | 工作台·体重管理 |
|
||||
| 7 | workbench/weight-record.html | 体重记录 | 工作台·体重管理 |
|
||||
| 8 | workbench/weight-employee-detail.html | 员工体重详情 | 工作台·体重管理 |
|
||||
| 9 | workbench/goal-review.html | 目标审核 | 工作台·体重管理 |
|
||||
| 10 | workbench/checkup-progress.html | 体检进度追踪 | 工作台·体检管理 |
|
||||
| 11 | workbench/emergency-orders.html | 应急求助工单 | 工作台·应急管理 |
|
||||
| 12 | workbench/emergency-order-detail.html | 应急工单详情 | 工作台·应急管理 |
|
||||
| 13 | workbench/wearable-alerts.html | 穿戴设备预警记录 | 工作台·预警管理 |
|
||||
| 14 | knowledge/index.html | 知识库首页 | 知识库 |
|
||||
| 15 | knowledge/detail.html | 知识详情 | 知识库 |
|
||||
| 16 | ../ai-assistant/index.html | AI 助手 | AI 助手 |
|
||||
| 17 | ../message/index.html | 消息列表 | 消息 |
|
||||
| 18 | ../message/contacts.html | 通讯录 | 消息 |
|
||||
| 19 | ../message/im-chat.html | 即时通讯 | 消息 |
|
||||
| 20 | profile/index.html | 个人中心主页 | 我的 |
|
||||
| 21 | profile/edit-profile.html | 修改个人信息 | 我的 |
|
||||
| 22 | profile/change-pwd.html | 修改密码 | 我的 |
|
||||
| 9 | workbench/goal-review.html?status=pending | 目标审核(待审核) | 工作台·体重管理 |
|
||||
| 10 | workbench/goal-review.html?status=approved | 目标审核(已通过) | 工作台·体重管理 |
|
||||
| 11 | workbench/goal-review.html?status=rejected | 目标审核(已驳回) | 工作台·体重管理 |
|
||||
| 12 | workbench/checkup-progress.html | 体检进度追踪 | 工作台·体检管理 |
|
||||
| 13 | workbench/emergency-orders.html | 应急求助工单 | 工作台·应急管理 |
|
||||
| 14 | workbench/emergency-order-detail.html | 应急工单详情 | 工作台·应急管理 |
|
||||
| 15 | workbench/wearable-alerts.html | 穿戴设备预警记录 | 工作台·预警管理 |
|
||||
| 16 | knowledge/index.html | 知识库首页 | 知识库 |
|
||||
| 17 | knowledge/detail.html | 知识详情 | 知识库 |
|
||||
| 18 | ../ai-assistant/index.html | AI 助手 | AI 助手 |
|
||||
| 19 | ../message/index.html | 消息列表 | 消息 |
|
||||
| 20 | ../message/contacts.html | 通讯录 | 消息 |
|
||||
| 21 | ../message/im-chat.html | 即时通讯 | 消息 |
|
||||
| 22 | profile/index.html | 个人中心主页 | 我的 |
|
||||
| 23 | profile/edit-profile.html | 修改个人信息 | 我的 |
|
||||
| 24 | profile/change-pwd.html | 修改密码 | 我的 |
|
||||
|
||||
## 逐页说明
|
||||
|
||||
@@ -88,23 +75,22 @@ stateDiagram-v2
|
||||
|
||||

|
||||
|
||||
**功能说明:** 管理员工作台入口,顶部展示用户信息(头像、姓名、角色、部门),下方以功能卡片形式呈现四大管理入口:体重管理详情、体检进度追踪、应急求助工单、穿戴设备预警记录。
|
||||
**功能说明:** 管理员工作台入口,顶部展示用户信息(头像、姓名、角色、单位部门),下方以功能卡片形式呈现四大管理入口:体重管理详情、体检进度追踪、应急求助工单、穿戴设备预警记录。
|
||||
|
||||
**业务规则:**
|
||||
- 用户信息区:头像、姓名(王建国)、角色(健康管理员)、所属部门(安全环保部·健康管理科)
|
||||
- 功能卡片共 4 个,两行两列排列:
|
||||
1. 体重管理详情(蓝色图标)
|
||||
2. 体检进度追踪(绿色图标)
|
||||
3. 应急求助工单(橙色图标,角标数字"3"表示进行中工单数)
|
||||
4. 穿戴设备预警记录(红色图标)
|
||||
- 底部 5 个 Tab:工作台(当前激活,角标"3")、知识库、AI 助手、消息(角标"2")、我的
|
||||
- 用户信息区:头像、姓名、角色、所属单位部门
|
||||
- 功能卡片共 4 个:
|
||||
1. 体重管理详情
|
||||
2. 体检进度追踪
|
||||
3. 应急求助工单
|
||||
4. 穿戴设备预警记录
|
||||
- 底部 5 个 Tab:工作台、知识库、AI 助手、消息、我的
|
||||
|
||||
**交互说明:**
|
||||
- 点击"体重管理详情"卡片:跳转到体重管理详情页
|
||||
- 点击"体检进度追踪"卡片:跳转到体检进度追踪页
|
||||
- 点击"应急求助工单"卡片:跳转到应急求助工单页
|
||||
- 点击"穿戴设备预警记录"卡片:跳转到穿戴设备预警记录页
|
||||
- 点击头像:跳转到个人中心主页
|
||||
- 底部导航栏切换:跳转到对应 Tab 页面
|
||||
|
||||
---
|
||||
@@ -115,15 +101,15 @@ stateDiagram-v2
|
||||
|
||||

|
||||
|
||||
**功能说明:** 体重管理活动总览页面,支持切换不同活动期次,展示活动整体统计数据,并通过三个主 Tab(每日打卡、减重排名、目标审核)分类查看管理数据。
|
||||
**功能说明:** 体重管理活动总览页面,支持切换不同活动,展示活动整体统计数据,并通过三个主 Tab(每日打卡、减重排名、目标审核)分类查看管理数据。
|
||||
|
||||
**业务规则:**
|
||||
- 顶部活动选择器:下拉切换不同活动期次(如"2026年Q2体重管理活动"),切换后刷新活动统计数据
|
||||
- 活动统计区:已进行天数 / 总天数、预期参与人数、实际参与人数
|
||||
- 三个主 Tab:
|
||||
- 每日打卡(默认):按日查看饮食、运动、体重的打卡统计数据
|
||||
- 减重排名:4 个子 Tab(减重总量排名 / 减重率排名 / 综合评分排名 / 行动评分排名),各显示前 20 名
|
||||
- 目标审核:4 个子 Tab(待审核 / 已通过 / 待签收 / 已驳回),显示员工减重目标申请
|
||||
- 减重排名:4 个子 Tab(减重总量排名 / 减重率排名 / 综合评分排名 / 行动评分排名)
|
||||
- 目标审核:3 个子 Tab(待审核 / 已通过 / 已驳回),显示员工减重目标申请
|
||||
- 每日打卡面板:日期导航(左右箭头 + 日历选择器)、三张统计卡片(饮食已打卡XX人/未打卡XX人、运动已打卡XX人/未打卡XX人、体重已打卡XX人/未打卡XX人)
|
||||
- 减重排名:前三名展示金/银/铜牌勋章 + 员工脱敏姓名 + 部门 + 数值
|
||||
- 目标审核卡片:展示员工姓名、初始/目标数据对比、操作按钮
|
||||
@@ -154,7 +140,7 @@ stateDiagram-v2
|
||||
**业务规则:**
|
||||
- 日期导航:支持左右箭头切换日期、日历面板选择日期
|
||||
- 两个 Tab:已记录(角标数字显示已打卡人数)、未记录(角标数字显示未打卡人数)
|
||||
- 员工卡片信息:头像(取姓名首字)、姓名、性别、年龄、部门、身高
|
||||
- 员工卡片信息:姓名、性别、年龄、部门、身高
|
||||
- 已记录员工卡片右侧有"记录详情 >"链接
|
||||
|
||||
**交互说明:**
|
||||
@@ -177,7 +163,6 @@ stateDiagram-v2
|
||||
- 数据对比表(三列):初始数据 / 目标数据 / 当前数据,包含体重、BMI、体脂率、腰围
|
||||
- 变化指标:体重变化、BMI 变化、体脂率变化、腰围变化(绿色数值表示下降)
|
||||
- 当日饮食记录:按餐次展示(早餐、午餐、加餐、晚餐),每条记录显示热量(kcal)、时间、记录来源标签
|
||||
- 记录来源标签颜色:蓝色(智能记录)、紫色(手动记录)
|
||||
|
||||
**交互说明:**
|
||||
- 点击返回:返回饮食记录详情页
|
||||
@@ -193,7 +178,7 @@ stateDiagram-v2
|
||||
**业务规则:**
|
||||
- 日期导航:支持左右箭头切换日期、日历面板选择日期
|
||||
- 两个 Tab:已记录(角标数字)、未记录(角标数字)
|
||||
- 员工卡片信息:头像、姓名、性别、年龄、部门
|
||||
- 员工卡片信息:姓名、性别、年龄、单位部门、身高
|
||||
- 已记录员工卡片右侧有"记录详情 >"链接
|
||||
|
||||
**交互说明:**
|
||||
@@ -215,8 +200,7 @@ stateDiagram-v2
|
||||
- 员工信息:姓名(陈伟)、性别、年龄
|
||||
- 数据对比表:初始数据 / 目标数据 / 当前数据(体重、BMI、体脂率、腰围)
|
||||
- 变化指标:绿色显示下降数值
|
||||
- 当日运动记录:跑步(45min/320kcal/穿戴设备)、骑行(30min/180kcal/手动记录)、游泳(60min/480kcal/器材记录)、步行(8,520步/150kcal/微信运动)
|
||||
- 记录来源标签颜色:蓝色(穿戴设备/器材)、紫色(手动/微信运动)
|
||||
- 当日运动记录:运动名称、运动时长/步数、消耗千卡、时间、来源(穿戴、器械、微信、自记)
|
||||
|
||||
**交互说明:**
|
||||
- 点击返回:返回运动记录详情页
|
||||
@@ -232,7 +216,7 @@ stateDiagram-v2
|
||||
**业务规则:**
|
||||
- 日期导航:支持左右箭头切换日期、日历面板选择日期
|
||||
- 两个 Tab:已记录(角标数字)、未记录(角标数字)
|
||||
- 员工卡片信息:头像、姓名、性别、年龄、部门
|
||||
- 员工卡片信息:姓名、性别、年龄、单位部门、身高
|
||||
- 已记录员工卡片右侧有"记录详情 >"链接
|
||||
|
||||
**交互说明:**
|
||||
@@ -254,38 +238,37 @@ stateDiagram-v2
|
||||
- 员工信息:姓名(张晓红)、性别、年龄、部门
|
||||
- 数据对比表:初始数据 / 目标数据 / 当前数据(体重、BMI、体脂率、腰围)
|
||||
- 变化指标:绿色显示下降数值
|
||||
- 体重记录列表:按时间倒序,每条显示时间、体重、体脂率、腰围、来源标签
|
||||
- 记录来源标签颜色:蓝色(蓝牙秤/身高体重秤)、紫色(体脂仪/手动/穿戴设备)
|
||||
- 体重记录列表:按时间倒序,每条显示时间、体重、体脂率、腰围、来源标签(身高秤、蓝牙称、体脂仪、自记)
|
||||
|
||||
**交互说明:**
|
||||
- 点击返回:返回体重记录页
|
||||
|
||||
---
|
||||
|
||||
### 9. 目标审核
|
||||
### 9. 目标审核(待审核)
|
||||
|
||||

|
||||

|
||||
|
||||
**功能说明:** 管理员审核员工提交的体重管理减重目标申请,可选择通过或驳回,驳回时需填写驳回原因。已审核的目标以只读方式展示审核结果。
|
||||
**功能说明:** 管理员查看待审核的减重目标申请,可选择通过或驳回。驳回时需填写驳回原因。
|
||||
|
||||
**业务规则:**
|
||||
- 通过 URL 参数 `status` 区分页面模式:pending(待审核)/ approved(已通过)/ signed(待签收)/ rejected(已驳回)
|
||||
- 待审核模式:
|
||||
- 展示员工基本信息卡片(姓名、性别、年龄、身高)
|
||||
- 数据对比卡片:初始数据(体重/BMI/体脂率/腰围)、目标数据(绿色)、推荐数据范围(蓝色参考)
|
||||
- 活动时间范围卡片:开始日期 → 结束日期
|
||||
- 底部操作区:单选按钮(通过 / 驳回)、驳回时展开驳回原因文本框、提交审核按钮
|
||||
- 已通过/待签收/已驳回模式:
|
||||
- 以只读方式展示审核结果,包含状态标签、审核人、审核时间
|
||||
- 已驳回额外展示驳回原因
|
||||
- 无操作按钮
|
||||
- 员工信息:姓名、性别、年龄、身高
|
||||
- 申请时间
|
||||
- 数据对比卡片:
|
||||
- 初始数据:体重/ BMI/ 体脂/ 腰围
|
||||
- 目标数据:体重/ BMI/ 体脂/ 腰围
|
||||
- 推荐范围:体重/ BMI/ 体脂/ 腰围
|
||||
- 目标起止日期
|
||||
- 底部操作区:单选按钮(审核通过 / 审核驳回)、驳回原因文本框(默认隐藏)、提交审核按钮
|
||||
|
||||
**交互说明:**
|
||||
- 点击"通过"单选按钮:隐藏驳回原因文本框
|
||||
- 点击"驳回"单选按钮:展开驳回原因文本框(placeholder: "请输入驳回原因...")
|
||||
- 点击"审核通过"单选按钮:选中通过,隐藏驳回原因文本框
|
||||
- 点击"审核驳回"单选按钮:选中驳回,展开驳回原因文本框
|
||||
- 点击"提交审核":弹出确认弹窗
|
||||
- 确认弹窗:标题根据操作变化(通过审核 / 驳回申请),显示简要确认信息,取消按钮关闭弹窗,确认按钮执行操作
|
||||
- 确认后弹出成功弹窗:"审核完成",点击"返回列表"跳转回体重管理详情页
|
||||
- 通过时:"确认通过该目标申请?",确认按钮为蓝色"确认通过"
|
||||
- 驳回时:"确认驳回该目标申请?驳回原因:…",确认按钮为红色"确认驳回"
|
||||
- 确认弹窗点击取消:关闭弹窗
|
||||
- 确认弹窗点击确认:关闭确认弹窗,弹出成功弹窗("审核完成"),点击"返回列表"跳转回体重管理详情页
|
||||
- 点击返回:返回体重管理详情页
|
||||
|
||||
**审核操作表单:**
|
||||
@@ -297,20 +280,61 @@ stateDiagram-v2
|
||||
|
||||
---
|
||||
|
||||
### 10. 目标审核(已通过)
|
||||
|
||||

|
||||
|
||||
**功能说明:** 查看已通过审核的减重目标申请详情,纯只读展示,无操作按钮。
|
||||
|
||||
**业务规则:**
|
||||
- 员工信息:姓名、性别、年龄、身高
|
||||
- 申请时间
|
||||
- 数据对比卡片:
|
||||
- 初始数据:体重/ BMI/ 体脂/ 腰围
|
||||
- 目标数据:体重/ BMI/ 体脂/ 腰围
|
||||
- 推荐范围:体重/ BMI/ 体脂/ 腰围
|
||||
- 目标起止日期
|
||||
- 审核结果卡片:审核结果标签、审核人、审核时间
|
||||
|
||||
**交互说明:**
|
||||
- 点击返回:返回体重管理详情页
|
||||
|
||||
---
|
||||
|
||||
### 11. 目标审核(已驳回)
|
||||
|
||||

|
||||
|
||||
**功能说明:** 查看已被驳回的减重目标申请详情,包含驳回原因,纯只读展示,无操作按钮。
|
||||
|
||||
**业务规则:**
|
||||
- 员工信息:姓名、性别、年龄、身高
|
||||
- 申请时间
|
||||
- 数据对比卡片:
|
||||
- 初始数据:体重/ BMI/ 体脂/ 腰围
|
||||
- 目标数据:体重/ BMI/ 体脂/ 腰围
|
||||
- 推荐范围:体重/ BMI/ 体脂/ 腰围
|
||||
- 目标起止日期
|
||||
- 审核结果卡片:审核结果标签、审核人、审核时间、驳回原因
|
||||
|
||||
**交互说明:**
|
||||
- 点击返回:返回体重管理详情页
|
||||
|
||||
---
|
||||
|
||||
## 三、体检管理业务线
|
||||
|
||||
### 10. 体检进度追踪
|
||||
### 12. 体检进度追踪
|
||||
|
||||

|
||||
|
||||
**功能说明:** 按体检计划期次查看员工的体检预约和完成进度,通过四个分段 Tab(已预约、已检查、未预约、未检查)统计并展示各状态的员工列表。
|
||||
|
||||
**业务规则:**
|
||||
- 顶部体检计划选择器:下拉切换不同体检计划(如"2026年度体检""2025年度体检""2025职业病体检"),切换后刷新各状态统计数据
|
||||
- 顶部体检计划选择器:下拉切换不同体检计划,切换后刷新各状态统计数据
|
||||
- 统计区:预期人数、已预约人数、已检查人数、未预约人数、未检查人数
|
||||
- 四个分段 Tab:已预约(角标数字)、已检查(角标数字)、未预约(角标数字)、未检查(角标数字)
|
||||
- 员工卡片信息:头像(姓名首字)、姓名 + 工号、所属单位 + 部门、联系电话
|
||||
- 数据通过 JS 模拟数据动态渲染
|
||||
- 员工卡片信息:头像、姓名 + 工号、所属单位 + 部门、联系电话
|
||||
|
||||
**交互说明:**
|
||||
- 点击体检计划下拉框:展开计划列表,选择后切换数据
|
||||
@@ -321,17 +345,16 @@ stateDiagram-v2
|
||||
|
||||
## 四、应急管理业务线
|
||||
|
||||
### 11. 应急求助工单
|
||||
### 13. 应急求助工单
|
||||
|
||||

|
||||
|
||||
**功能说明:** 查看所有应急求助工单的处理情况,分"进行中"和"已完成"两个 Tab 展示,支持按月度和求助方式筛选。管理员以只读监控视角查看工单流转。
|
||||
|
||||
**业务规则:**
|
||||
- 两个主 Tab:进行中(角标数字"3")、已完成(角标数字"12")
|
||||
- 筛选栏:月份下拉(全部 / 2026-06 / 2026-05 / 2026-04)、求助方式下拉(全部 / 应急视频 / 应急电话 / 拨120)
|
||||
- 工单卡片信息:求助方式标签(颜色区分:视频粉色、电话橙色、120红色)、时间、求助员工信息(头像、姓名、工号、性别、年龄、单位、部门、电话)、工单详情行(应急中心、操作人员、专业人员)
|
||||
- 数据通过 JS 模拟,筛选条件联动更新两个 Tab 的列表
|
||||
- 两个主 Tab:进行中(角标数字)、已完成(角标数字)
|
||||
- 筛选栏:月份下拉、求助方式下拉(全部 / 应急视频 / 应急电话 / 拨120)
|
||||
- 工单卡片信息:求助方式标签、时间、求助员工信息(头像、姓名、工号、性别、年龄、单位、部门、电话)、工单信息(应急中心、操作人员、专业人员)
|
||||
|
||||
**交互说明:**
|
||||
- 点击进行中/已完成 Tab:切换工单列表
|
||||
@@ -342,7 +365,7 @@ stateDiagram-v2
|
||||
|
||||
---
|
||||
|
||||
### 12. 应急工单详情
|
||||
### 14. 应急工单详情
|
||||
|
||||

|
||||
|
||||
@@ -350,8 +373,8 @@ stateDiagram-v2
|
||||
|
||||
**业务规则:**
|
||||
- 四个可折叠卡片(默认展开):
|
||||
1. 基本信息:工单编号、应急中心、求助员工姓名/工号、单位、部门、求助方式、求助时间、位置信息(含地图图标)、操作人员、专业人员、工单状态(已完成绿色标签)、完成时间
|
||||
2. 救助信息:救助意见(文本)、救助结果(文本)、相关照片(3 张占位图)、补充小结(多条带圆点标记的列表)
|
||||
1. 基本信息:工单编号、应急中心、求助员工姓名/工号、单位、部门、求助方式、求助时间、位置信息、操作人员、专业人员、工单状态(已完成绿色标签)、完成时间
|
||||
2. 救助信息:救助意见(文本)、救助结果(文本)、相关照片、补充小结(多条带圆点标记的列表)
|
||||
3. 派单信息:派单时间、驻场人员、派单状态(已完成)、结束时间、派单说明、驻场说明
|
||||
4. 救护车支持:车牌号、机构、驾驶员、驾驶员电话、护送人员、护送人员电话、目的地、备注
|
||||
- 纯只读展示,无编辑或操作按钮
|
||||
@@ -364,17 +387,15 @@ stateDiagram-v2
|
||||
|
||||
## 五、预警管理业务线
|
||||
|
||||
### 13. 穿戴设备预警记录
|
||||
### 15. 穿戴设备预警记录
|
||||
|
||||

|
||||
|
||||
**功能说明:** 查看所有员工穿戴设备的异常告警记录,支持按月份和告警类型筛选。
|
||||
|
||||
**业务规则:**
|
||||
- 筛选栏:月份下拉(全部 / 2026-06 / 2026-05 / 2026-04)、告警类型下拉(全部 / SOS 求助 / 体温异常 / 心率异常 / 血氧异常 / 压力异常 / 跌倒检测)
|
||||
- 告警卡片信息:告警类型标签(颜色区分类型)、时间戳、异常数值(含单位)、员工信息(头像、姓名、工号、性别、年龄、单位、部门)、位置信息(含定位图标)
|
||||
- 告警类型共 6 种:SOS 求助(红色)、体温异常(橙色)、心率异常(红色)、血氧异常(紫色)、压力异常(蓝色)、跌倒检测(红色)
|
||||
- 数据通过 JS 模拟,筛选条件联动更新列表
|
||||
- 筛选栏:月份下拉、告警类型下拉(全部 / 一键告警 / 体温异常 / 心率异常 / 血氧异常 / 压力异常 / 跌倒)
|
||||
- 告警卡片信息:告警类型标签、时间戳、异常数值(含单位)、员工信息(头像、姓名、工号、性别、年龄、单位、部门)、位置信息
|
||||
|
||||
**交互说明:**
|
||||
- 点击月份下拉:按月份筛选告警记录
|
||||
@@ -385,137 +406,88 @@ stateDiagram-v2
|
||||
|
||||
## 六、知识库模块
|
||||
|
||||
### 14. 知识库首页
|
||||
### 16. 知识库首页
|
||||
|
||||

|
||||
|
||||
**功能说明:** 专业知识参考库,分"知识文章"和"规章制度"两个大类,知识文章下再按二级分类 Tab 组织。
|
||||
|
||||
**业务规则:**
|
||||
- 两大主 Tab:知识文章(默认)/ 规章制度
|
||||
- 知识文章二级分类 Tab(4 个):体检知识(4 篇)、药品知识(2 篇)、疾病知识(2 篇)、健康知识(2 篇)
|
||||
- 知识文章列表:每篇文章展示图标、标题、简介描述、右箭头
|
||||
- 规章制度列表:5 条 PDF 文档条目(员工健康管理办法、职业病防治规则、体检异常处理流程、健康咨询服务规范、应急救援操作标准)
|
||||
- 文章数据通过 JS 对象动态渲染
|
||||
|
||||
**交互说明:**
|
||||
- 点击知识文章/规章制度主 Tab:切换大类
|
||||
- 点击二级分类 Tab:切换知识分类
|
||||
- 点击文章条目:跳转到知识详情页(通过 `?key=<文章标识>` 传递参数)
|
||||
- 点击规章制度条目:打开对应 PDF(原型中为占位链接)
|
||||
**功能说明:** 等待后续规划,提示“功能开发中,敬请期待~”
|
||||
|
||||
---
|
||||
|
||||
### 15. 知识详情
|
||||
### 17. 知识详情
|
||||
|
||||

|
||||
|
||||
**功能说明:** 展示单篇知识文章的详细内容,包括文章基本信息、摘要和要点列表。
|
||||
**功能说明:** 等待后续规划,提示“功能开发中,敬请期待~”
|
||||
|
||||
**业务规则:**
|
||||
- 通过 URL 参数 `key` 确定展示哪篇文章,共有 10 篇文章数据
|
||||
- 文章内容:图标、标题、分类、发布日期、摘要段、要点列表(彩色标题 + 灰色圆角说明卡片)
|
||||
- 体检知识(4 篇):血常规解读、心电图判读、肝功能解读、血糖解读
|
||||
- 药品知识(2 篇):降压药物、他汀类药物
|
||||
- 疾病知识(2 篇):高血压、偏头痛
|
||||
- 健康知识(2 篇):营养指导、运动与心血管健康
|
||||
|
||||
**交互说明:**
|
||||
- 点击返回:返回知识库首页
|
||||
|
||||
---
|
||||
|
||||
## 七、AI 助手
|
||||
|
||||
### 16. AI 助手
|
||||
### 18. AI 助手
|
||||
|
||||

|
||||
|
||||
**功能说明:** 智能健康问答助手,支持文字输入提问,提供快捷问题入口。全角色共享页面。
|
||||
|
||||
**业务规则:**
|
||||
- 欢迎语和三个快捷问题入口(偏头痛鉴别诊断、高血压用药建议、血脂异常处理方案)
|
||||
- AI 对话模拟:用户发送消息后,AI 延迟回复占位文字
|
||||
- 通过 `sessionStorage.appRole` 识别当前角色为 admin,设置正确的 Tab 导航路径
|
||||
|
||||
**交互说明:**
|
||||
- 输入文字后点击发送或按回车:发送消息,显示用户消息气泡(紫色右对齐)
|
||||
- 点击快捷问题标签:自动填入输入框并发送
|
||||
- 底部 Tab 切换:跳转到对应角色页面
|
||||
**功能说明:** 后续规划,暂时显示”开发中,敬请期待~”。
|
||||
|
||||
---
|
||||
|
||||
## 八、消息模块
|
||||
|
||||
### 17. 消息列表
|
||||
### 19. 消息列表
|
||||
|
||||

|
||||

|
||||
|
||||
**功能说明:** 即时通讯消息汇总列表,展示最近对话的联系人及消息预览。全角色共享页面。
|
||||
**功能说明:** 内部消息模块,与当前系统中”咨询专家”、”咨询小助手”、”操作人员”、”专业人员”、”驻场人员”、”健康管理员”进行对话聊天的消息列表。
|
||||
|
||||
**业务规则:**
|
||||
- 6 条消息会话:咨询小助手(未读"2")、李主任(专业人员)、张护士长(专业人员)、陈运营(操作人员)、刘驻场(驻场人员)、系统管理员
|
||||
- 每条展示:头像、姓名、角色标签(颜色区分)、时间、消息预览
|
||||
- 通过 `sessionStorage.appRole` 识别当前角色,设置正确的底部 Tab 导航路径
|
||||
- 消息列表按最新消息时间倒序排列
|
||||
- 每条消息显示联系人头像、姓名、角色标签、最后一条消息预览、时间
|
||||
|
||||
**交互说明:**
|
||||
- 点击消息条目:跳转到即时通讯页
|
||||
- 点击右上角"通讯录":跳转到通讯录页
|
||||
- 底部 Tab 切换:跳转到对应角色页面
|
||||
- 点击消息项:跳转到 IM 聊天页
|
||||
- 点击右上角通讯录图标:跳转到通讯录页
|
||||
- 底部 Tab 导航切换到其他模块
|
||||
|
||||
---
|
||||
|
||||
### 18. 通讯录
|
||||
### 20. 通讯录
|
||||
|
||||

|
||||

|
||||
|
||||
**功能说明:** 组织内联系人列表,按角色分组展示,支持搜索过滤。全角色共享页面。
|
||||
|
||||
**业务规则:**
|
||||
- 6 个联系人分组:小助手、咨询专家、健康管理员、操作人员、专业人员、驻场人员
|
||||
- 每组可折叠/展开,展示成员姓名、角色标签、所属机构
|
||||
- 搜索框支持按姓名实时过滤,空分组自动隐藏
|
||||
**功能说明:** 展示系统内所有可联系的内部人员,支持按角色筛选和姓名搜索。
|
||||
|
||||
**交互说明:**
|
||||
- 点击分组标题:折叠/展开该分组
|
||||
- 在搜索框输入文字:实时过滤联系人
|
||||
- 点击联系人:跳转到即时通讯页
|
||||
- 点击返回:返回消息列表页
|
||||
- 点击联系人:跳转到 IM 聊天页
|
||||
- 输入名称进行模糊搜索
|
||||
- 角色分类可收起展开
|
||||
- 返回按钮:跳转到消息列表
|
||||
|
||||
---
|
||||
|
||||
### 19. 即时通讯
|
||||
### 21. 即时通讯
|
||||
|
||||

|
||||

|
||||
|
||||
**功能说明:** 单聊页面,支持文字和语音输入,可发送图片。全角色共享页面。
|
||||
|
||||
**业务规则:**
|
||||
- 导航栏标题显示当前对话对象名称
|
||||
- 预置示例对话(与"咨询小助手"的聊天记录)
|
||||
- 支持文字输入、语音输入(按住说话)、多媒体附件(拍照/图库)
|
||||
- 发送的消息为蓝色右对齐气泡,接收的消息为白色左对齐气泡
|
||||
**功能说明:** 与内部人员的即时通讯聊天界面。
|
||||
|
||||
**交互说明:**
|
||||
- 在输入框输入文字后点击发送:消息以蓝色气泡展示在右侧
|
||||
- 点击语音切换按钮:切换为按住说话模式
|
||||
- 点击"+"按钮:展开多媒体面板(拍照/图库选项,原型中为占位)
|
||||
- 点击返回:返回消息列表页
|
||||
- 底部输入框输入文字并发送
|
||||
- 支持语音和输入切换
|
||||
- 支持拍照和发送图库照片
|
||||
- 返回按钮:跳转到消息列表
|
||||
|
||||
---
|
||||
|
||||
## 九、个人中心模块
|
||||
|
||||
### 20. 个人中心主页
|
||||
### 22. 个人中心主页
|
||||
|
||||

|
||||
|
||||
**功能说明:** 管理员个人信息展示和功能菜单导航。
|
||||
|
||||
**业务规则:**
|
||||
- 用户信息区:头像、姓名(王建国)、角色(健康管理员)、所属部门(安全环保部·健康管理科)
|
||||
- 用户信息区:头像、姓名、角色、所属单位部门
|
||||
- 菜单项:修改个人信息、修改密码、关于我们
|
||||
- 退出登录按钮(红色文字)
|
||||
- 退出登录按钮
|
||||
|
||||
**交互说明:**
|
||||
- 点击"修改个人信息":跳转到修改个人信息页
|
||||
@@ -525,7 +497,7 @@ stateDiagram-v2
|
||||
|
||||
---
|
||||
|
||||
### 21. 修改个人信息
|
||||
### 23. 修改个人信息
|
||||
|
||||

|
||||
|
||||
@@ -536,28 +508,15 @@ stateDiagram-v2
|
||||
- 点击"保存":验证姓名非空,显示"保存成功"提示(1.5 秒自动消失)
|
||||
- 点击返回:返回个人中心主页
|
||||
|
||||
**表单字段表:**
|
||||
|
||||
| 字段名 | 类型 | 必填 | 校验规则 |
|
||||
|--------|------|------|----------|
|
||||
| 头像 | 图片选择 | 否 | 支持拍照或从相册选择 |
|
||||
| 姓名 | 单行文本 | 是 | 不能为空 |
|
||||
|
||||
---
|
||||
|
||||
### 22. 修改密码
|
||||
### 24. 修改密码
|
||||
|
||||

|
||||
|
||||
**功能说明:** 修改管理员登录密码。
|
||||
|
||||
**交互说明:**
|
||||
- 点击"保存修改":弹出修改成功确认弹窗("密码修改成功,下次登录请使用新密码"),点击确定后跳转回个人中心
|
||||
- 点击"保存修改":弹出修改成功确认弹窗("密码修改成功"),退回到登录页面进行重新登录
|
||||
|
||||
**表单字段表:**
|
||||
|
||||
| 字段名 | 类型 | 必填 | 校验规则 |
|
||||
|--------|------|------|----------|
|
||||
| 当前密码 | 密码输入 | 是 | 不能为空 |
|
||||
| 新密码 | 密码输入 | 是 | 不能为空 |
|
||||
| 确认新密码 | 密码输入 | 是 | 必须与新密码一致 |
|
||||
|
||||
@@ -0,0 +1,258 @@
|
||||
<!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 { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #1a1a2e; color: #e0e0e0; }
|
||||
|
||||
/* 顶部导航 */
|
||||
.nav { position: sticky; top: 0; z-index: 100; background: #16213e; border-bottom: 1px solid #0f3460; padding: 14px 24px; display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
|
||||
.nav__title { font-size: 16px; font-weight: 600; color: #4fc3f7; margin-right: 20px; white-space: nowrap; }
|
||||
.nav__link { font-size: 13px; color: #aaa; text-decoration: none; padding: 5px 12px; border-radius: 20px; transition: all .2s; white-space: nowrap; border: 1px solid transparent; }
|
||||
.nav__link:hover { background: #0f3460; color: #4fc3f7; border-color: #4fc3f7; }
|
||||
|
||||
/* 业务主线区块 */
|
||||
.section { padding: 24px 24px 8px; }
|
||||
.section__header { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; }
|
||||
.section__title { font-size: 15px; font-weight: 600; color: #fff; }
|
||||
.section__count { font-size: 12px; color: #666; background: #2a2a4a; padding: 2px 8px; border-radius: 10px; }
|
||||
|
||||
/* 横向滚动容器 */
|
||||
.scroll-row { display: flex; gap: 16px; overflow-x: auto; padding-bottom: 16px; scrollbar-width: thin; scrollbar-color: #333 transparent; }
|
||||
.scroll-row::-webkit-scrollbar { height: 6px; }
|
||||
.scroll-row::-webkit-scrollbar-track { background: transparent; }
|
||||
.scroll-row::-webkit-scrollbar-thumb { background: #444; border-radius: 3px; }
|
||||
|
||||
/* 卡片 */
|
||||
.card { flex-shrink: 0; width: 260px; cursor: pointer; transition: transform .2s; }
|
||||
.card:hover { transform: scale(1.02); }
|
||||
.card__img { width: 260px; height: 564px; border-radius: 12px; overflow: hidden; border: 2px solid #2a2a4a; background: #111; transition: border-color .2s; }
|
||||
.card:hover .card__img { border-color: #4fc3f7; }
|
||||
.card__img img { width: 100%; height: 100%; object-fit: cover; object-position: top; }
|
||||
.card__label { margin-top: 8px; font-size: 12px; color: #999; text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.card__tags { display: flex; justify-content: center; gap: 4px; margin-top: 4px; flex-wrap: wrap; }
|
||||
.tag { font-size: 10px; padding: 1px 5px; border-radius: 3px; }
|
||||
.tag--modal { background: #4a2800; color: #ffa940; }
|
||||
.tag--state { background: #002a4a; color: #4fc3f7; }
|
||||
|
||||
/* 分隔线 */
|
||||
.divider { height: 1px; background: #2a2a4a; margin: 8px 24px; }
|
||||
|
||||
/* 灯箱 */
|
||||
.lightbox { display: none; position: fixed; inset: 0; z-index: 1000; background: rgba(0,0,0,0.92); align-items: center; justify-content: center; cursor: zoom-out; }
|
||||
.lightbox.show { display: flex; }
|
||||
.lightbox img { max-width: 90vw; max-height: 92vh; border-radius: 8px; }
|
||||
.lightbox__close { position: absolute; top: 16px; right: 20px; color: #fff; font-size: 24px; cursor: pointer; width: 36px; height: 36px; display: flex; align-items: center; justify-content: center; border-radius: 50%; background: rgba(255,255,255,0.1); }
|
||||
.lightbox__name { position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%); color: #ccc; font-size: 13px; background: rgba(0,0,0,0.6); padding: 6px 16px; border-radius: 20px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- 顶部导航 -->
|
||||
<nav class="nav">
|
||||
<span class="nav__title">管理员端 · 设计总览</span>
|
||||
<a class="nav__link" href="#sec-workbench">工作台首页</a>
|
||||
<a class="nav__link" href="#sec-emergency">应急工单</a>
|
||||
<a class="nav__link" href="#sec-weight">体重管理</a>
|
||||
<a class="nav__link" href="#sec-goal">目标审核</a>
|
||||
<a class="nav__link" href="#sec-checkup">体检与预警</a>
|
||||
<a class="nav__link" href="#sec-knowledge">知识库</a>
|
||||
<a class="nav__link" href="#sec-profile">个人中心</a>
|
||||
</nav>
|
||||
|
||||
<!-- 工作台首页 -->
|
||||
<section class="section" id="sec-workbench">
|
||||
<div class="section__header">
|
||||
<h2 class="section__title">工作台首页</h2>
|
||||
<span class="section__count">1 页</span>
|
||||
</div>
|
||||
<div class="scroll-row">
|
||||
<div class="card" onclick="openLightbox('screenshots/index.jpg','工作台首页')">
|
||||
<div class="card__img"><img src="screenshots/index.jpg" alt="工作台首页"></div>
|
||||
<div class="card__label">工作台首页</div>
|
||||
<div class="card__tags"><span class="tag tag--state">功能入口</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<!-- 应急工单管理 -->
|
||||
<section class="section" id="sec-emergency">
|
||||
<div class="section__header">
|
||||
<h2 class="section__title">应急工单管理</h2>
|
||||
<span class="section__count">2 页</span>
|
||||
</div>
|
||||
<div class="scroll-row">
|
||||
<div class="card" onclick="openLightbox('screenshots/emergency-orders.jpg','应急求助工单')">
|
||||
<div class="card__img"><img src="screenshots/emergency-orders.jpg" alt="应急求助工单"></div>
|
||||
<div class="card__label">应急求助工单</div>
|
||||
<div class="card__tags"><span class="tag tag--state">Tab切换</span></div>
|
||||
</div>
|
||||
<div class="card" onclick="openLightbox('screenshots/emergency-order-detail.jpg','应急工单详情')">
|
||||
<div class="card__img"><img src="screenshots/emergency-order-detail.jpg" alt="应急工单详情"></div>
|
||||
<div class="card__label">应急工单详情</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<!-- 体重管理 -->
|
||||
<section class="section" id="sec-weight">
|
||||
<div class="section__header">
|
||||
<h2 class="section__title">体重管理</h2>
|
||||
<span class="section__count">7 页</span>
|
||||
</div>
|
||||
<div class="scroll-row">
|
||||
<div class="card" onclick="openLightbox('screenshots/weight-detail.jpg','体重管理详情')">
|
||||
<div class="card__img"><img src="screenshots/weight-detail.jpg" alt="体重管理详情"></div>
|
||||
<div class="card__label">体重管理详情</div>
|
||||
<div class="card__tags"><span class="tag tag--state">多Tab</span></div>
|
||||
</div>
|
||||
<div class="card" onclick="openLightbox('screenshots/weight-record.jpg','体重记录')">
|
||||
<div class="card__img"><img src="screenshots/weight-record.jpg" alt="体重记录"></div>
|
||||
<div class="card__label">体重记录</div>
|
||||
</div>
|
||||
<div class="card" onclick="openLightbox('screenshots/weight-employee-detail.jpg','员工体重详情')">
|
||||
<div class="card__img"><img src="screenshots/weight-employee-detail.jpg" alt="员工体重详情"></div>
|
||||
<div class="card__label">员工体重详情</div>
|
||||
</div>
|
||||
<div class="card" onclick="openLightbox('screenshots/eat-detail.jpg','饮食记录详情')">
|
||||
<div class="card__img"><img src="screenshots/eat-detail.jpg" alt="饮食记录详情"></div>
|
||||
<div class="card__label">饮食记录详情</div>
|
||||
</div>
|
||||
<div class="card" onclick="openLightbox('screenshots/diet-employee-detail.jpg','员工饮食详情')">
|
||||
<div class="card__img"><img src="screenshots/diet-employee-detail.jpg" alt="员工饮食详情"></div>
|
||||
<div class="card__label">员工饮食详情</div>
|
||||
</div>
|
||||
<div class="card" onclick="openLightbox('screenshots/exercise-detail.jpg','运动记录详情')">
|
||||
<div class="card__img"><img src="screenshots/exercise-detail.jpg" alt="运动记录详情"></div>
|
||||
<div class="card__label">运动记录详情</div>
|
||||
</div>
|
||||
<div class="card" onclick="openLightbox('screenshots/exercise-employee-detail.jpg','员工运动详情')">
|
||||
<div class="card__img"><img src="screenshots/exercise-employee-detail.jpg" alt="员工运动详情"></div>
|
||||
<div class="card__label">员工运动详情</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<!-- 目标审核 -->
|
||||
<section class="section" id="sec-goal">
|
||||
<div class="section__header">
|
||||
<h2 class="section__title">目标审核</h2>
|
||||
<span class="section__count">3 页</span>
|
||||
</div>
|
||||
<div class="scroll-row">
|
||||
<div class="card" onclick="openLightbox('screenshots/goal-review-pending.jpg','目标审核(待审核)')">
|
||||
<div class="card__img"><img src="screenshots/goal-review-pending.jpg" alt="目标审核(待审核)"></div>
|
||||
<div class="card__label">目标审核(待审核)</div>
|
||||
<div class="card__tags"><span class="tag tag--state">Tab切换</span></div>
|
||||
</div>
|
||||
<div class="card" onclick="openLightbox('screenshots/goal-review-approved.jpg','目标审核(已通过)')">
|
||||
<div class="card__img"><img src="screenshots/goal-review-approved.jpg" alt="目标审核(已通过)"></div>
|
||||
<div class="card__label">目标审核(已通过)</div>
|
||||
<div class="card__tags"><span class="tag tag--state">Tab切换</span></div>
|
||||
</div>
|
||||
<div class="card" onclick="openLightbox('screenshots/goal-review-rejected.jpg','目标审核(已驳回)')">
|
||||
<div class="card__img"><img src="screenshots/goal-review-rejected.jpg" alt="目标审核(已驳回)"></div>
|
||||
<div class="card__label">目标审核(已驳回)</div>
|
||||
<div class="card__tags"><span class="tag tag--state">Tab切换</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<!-- 体检与预警 -->
|
||||
<section class="section" id="sec-checkup">
|
||||
<div class="section__header">
|
||||
<h2 class="section__title">体检与预警</h2>
|
||||
<span class="section__count">2 页</span>
|
||||
</div>
|
||||
<div class="scroll-row">
|
||||
<div class="card" onclick="openLightbox('screenshots/checkup-progress.jpg','体检进度追踪')">
|
||||
<div class="card__img"><img src="screenshots/checkup-progress.jpg" alt="体检进度追踪"></div>
|
||||
<div class="card__label">体检进度追踪</div>
|
||||
</div>
|
||||
<div class="card" onclick="openLightbox('screenshots/wearable-alerts.jpg','穿戴设备预警记录')">
|
||||
<div class="card__img"><img src="screenshots/wearable-alerts.jpg" alt="穿戴设备预警记录"></div>
|
||||
<div class="card__label">穿戴设备预警记录</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<!-- 知识库 -->
|
||||
<section class="section" id="sec-knowledge">
|
||||
<div class="section__header">
|
||||
<h2 class="section__title">知识库</h2>
|
||||
<span class="section__count">2 页</span>
|
||||
</div>
|
||||
<div class="scroll-row">
|
||||
<div class="card" onclick="openLightbox('screenshots/knowledge-index.jpg','知识库首页')">
|
||||
<div class="card__img"><img src="screenshots/knowledge-index.jpg" alt="知识库首页"></div>
|
||||
<div class="card__label">知识库首页</div>
|
||||
<div class="card__tags"><span class="tag tag--state">Tab切换</span></div>
|
||||
</div>
|
||||
<div class="card" onclick="openLightbox('screenshots/knowledge-detail.jpg','知识详情')">
|
||||
<div class="card__img"><img src="screenshots/knowledge-detail.jpg" alt="知识详情"></div>
|
||||
<div class="card__label">知识详情</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<!-- 个人中心 -->
|
||||
<section class="section" id="sec-profile">
|
||||
<div class="section__header">
|
||||
<h2 class="section__title">个人中心</h2>
|
||||
<span class="section__count">3 页</span>
|
||||
</div>
|
||||
<div class="scroll-row">
|
||||
<div class="card" onclick="openLightbox('screenshots/profile-index.jpg','个人中心主页')">
|
||||
<div class="card__img"><img src="screenshots/profile-index.jpg" alt="个人中心主页"></div>
|
||||
<div class="card__label">个人中心主页</div>
|
||||
</div>
|
||||
<div class="card" onclick="openLightbox('screenshots/profile-edit-profile.jpg','修改个人信息')">
|
||||
<div class="card__img"><img src="screenshots/profile-edit-profile.jpg" alt="修改个人信息"></div>
|
||||
<div class="card__label">修改个人信息</div>
|
||||
</div>
|
||||
<div class="card" onclick="openLightbox('screenshots/profile-change-pwd.jpg','修改密码')">
|
||||
<div class="card__img"><img src="screenshots/profile-change-pwd.jpg" alt="修改密码"></div>
|
||||
<div class="card__label">修改密码</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div style="height:40px;"></div>
|
||||
|
||||
<!-- 灯箱 -->
|
||||
<div class="lightbox" id="lightbox" onclick="closeLightbox()">
|
||||
<span class="lightbox__close">×</span>
|
||||
<img id="lightbox-img" src="" alt="">
|
||||
<div class="lightbox__name" id="lightbox-name"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function openLightbox(src, name) {
|
||||
document.getElementById('lightbox-img').src = src;
|
||||
document.getElementById('lightbox-name').textContent = name;
|
||||
document.getElementById('lightbox').classList.add('show');
|
||||
}
|
||||
function closeLightbox() {
|
||||
document.getElementById('lightbox').classList.remove('show');
|
||||
}
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') closeLightbox();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,10 +1,15 @@
|
||||
/**
|
||||
* 管理员端(健康应急APP)页面截图脚本
|
||||
* 使用 Puppeteer 批量截取 HTML 原型页面,输出 JPEG(质量 85%)到 doc/screenshots/
|
||||
* 使用 Playwright 批量截取 HTML 原型页面,输出 JPEG 到 doc/screenshots/
|
||||
*
|
||||
* 截图约束(参考项目背景.md §7.4):
|
||||
* - 视口宽度 390px,1x 缩放,全页长图
|
||||
* - JPEG 质量:普通页 80%,长图(>1800px)65%
|
||||
* - 目标体积 ≤ 80KB
|
||||
*
|
||||
* 用法: node doc/screenshot.js
|
||||
*/
|
||||
const puppeteer = require('puppeteer');
|
||||
const { chromium } = require('playwright');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
@@ -12,150 +17,171 @@ const MODULE_DIR = __dirname.replace(/\\doc$/, ''); // admin/
|
||||
const SCREENSHOTS_DIR = path.join(__dirname, 'screenshots');
|
||||
|
||||
// 所有需要截图的页面(相对于模块目录)
|
||||
// 格式:{ file: 'path.html', query: '?key=val', out: 'output-name.jpg' }
|
||||
const PAGES = [
|
||||
// 工作台
|
||||
'workbench/index.html',
|
||||
'workbench/weight-detail.html',
|
||||
'workbench/eat-detail.html',
|
||||
'workbench/diet-employee-detail.html',
|
||||
'workbench/exercise-detail.html',
|
||||
'workbench/exercise-employee-detail.html',
|
||||
'workbench/weight-record.html',
|
||||
'workbench/weight-employee-detail.html',
|
||||
'workbench/goal-review.html',
|
||||
'workbench/checkup-progress.html',
|
||||
'workbench/emergency-orders.html',
|
||||
'workbench/emergency-order-detail.html',
|
||||
'workbench/wearable-alerts.html',
|
||||
{ file: 'workbench/index.html' },
|
||||
{ file: 'workbench/weight-detail.html' },
|
||||
{ file: 'workbench/eat-detail.html' },
|
||||
{ file: 'workbench/diet-employee-detail.html' },
|
||||
{ file: 'workbench/exercise-detail.html' },
|
||||
{ file: 'workbench/exercise-employee-detail.html' },
|
||||
{ file: 'workbench/weight-record.html' },
|
||||
{ file: 'workbench/weight-employee-detail.html' },
|
||||
// 目标审核 - 3 种状态
|
||||
{ file: 'workbench/goal-review.html', query: '?status=pending', out: 'goal-review-pending.jpg' },
|
||||
{ file: 'workbench/goal-review.html', query: '?status=approved', out: 'goal-review-approved.jpg' },
|
||||
{ file: 'workbench/goal-review.html', query: '?status=rejected', out: 'goal-review-rejected.jpg' },
|
||||
// 体检管理
|
||||
{ file: 'workbench/checkup-progress.html' },
|
||||
// 应急管理
|
||||
{ file: 'workbench/emergency-orders.html' },
|
||||
{ file: 'workbench/emergency-order-detail.html' },
|
||||
// 预警管理
|
||||
{ file: 'workbench/wearable-alerts.html' },
|
||||
// 知识库
|
||||
'knowledge/index.html',
|
||||
'knowledge/detail.html',
|
||||
{ file: 'knowledge/index.html' },
|
||||
{ file: 'knowledge/detail.html' },
|
||||
// 个人中心
|
||||
'profile/index.html',
|
||||
'profile/edit-profile.html',
|
||||
'profile/change-pwd.html',
|
||||
{ file: 'profile/index.html' },
|
||||
{ file: 'profile/edit-profile.html' },
|
||||
{ file: 'profile/change-pwd.html' },
|
||||
];
|
||||
|
||||
async function screenshotPage(browser, htmlFile) {
|
||||
const page = await browser.newPage();
|
||||
const filePath = path.join(MODULE_DIR, htmlFile);
|
||||
const fileUrl = `file:///${filePath.replace(/\\/g, '/')}`;
|
||||
// 共享页面(ai-assistant、message),截图输出到各自 screenshots 目录
|
||||
const EMERGENCY_DIR = path.resolve(MODULE_DIR, '..'); // emergency-app/
|
||||
const SHARED_PAGES = [
|
||||
{ file: path.join(EMERGENCY_DIR, 'ai-assistant/index.html'), outDir: path.join(EMERGENCY_DIR, 'ai-assistant/screenshots'), out: 'index.jpg' },
|
||||
{ file: path.join(EMERGENCY_DIR, 'message/index.html'), outDir: path.join(EMERGENCY_DIR, 'message/screenshots'), out: 'index.jpg' },
|
||||
{ file: path.join(EMERGENCY_DIR, 'message/contacts.html'), outDir: path.join(EMERGENCY_DIR, 'message/screenshots'), out: 'contacts.jpg' },
|
||||
{ file: path.join(EMERGENCY_DIR, 'message/im-chat.html'), outDir: path.join(EMERGENCY_DIR, 'message/screenshots'), out: 'im-chat.jpg' },
|
||||
];
|
||||
|
||||
const viewportWidth = 390;
|
||||
const viewportHeight = 844;
|
||||
function getOutputName(entry) {
|
||||
if (entry.out) return entry.out;
|
||||
let name = entry.file.replace('.html', '.jpg');
|
||||
if (name.startsWith('workbench/')) name = name.replace('workbench/', '');
|
||||
else if (name.startsWith('knowledge/')) name = name.replace('knowledge/', 'knowledge-');
|
||||
else if (name.startsWith('profile/')) name = name.replace('profile/', 'profile-');
|
||||
return name;
|
||||
}
|
||||
|
||||
await page.setViewport({ width: viewportWidth, height: viewportHeight, deviceScaleFactor: 2 });
|
||||
async function screenshotPage(browser, entry, outDir) {
|
||||
const context = await browser.newContext({
|
||||
viewport: { width: 390, height: 844 },
|
||||
deviceScaleFactor: 1,
|
||||
});
|
||||
const page = await context.newPage();
|
||||
const filePath = typeof entry.file === 'string' && path.isAbsolute(entry.file)
|
||||
? entry.file
|
||||
: path.join(MODULE_DIR, entry.file);
|
||||
const query = entry.query || '';
|
||||
const fileUrl = `file:///${filePath.replace(/\\/g, '/')}${query}`;
|
||||
|
||||
try {
|
||||
await page.goto(fileUrl, { waitUntil: 'networkidle0', timeout: 15000 });
|
||||
await page.goto(fileUrl, { waitUntil: 'networkidle', timeout: 15000 });
|
||||
|
||||
// 等待页面渲染完成
|
||||
await new Promise(r => setTimeout(r, 500));
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// 注入 CSS:移除 phone-shell 限制,展开全部内容
|
||||
await page.evaluate(() => {
|
||||
const style = document.createElement('style');
|
||||
style.id = 'screenshot-unwrap';
|
||||
style.textContent = `
|
||||
/* 移除手机壳固定高度和装饰 */
|
||||
await page.addStyleTag({
|
||||
content: `
|
||||
.phone-shell {
|
||||
height: auto !important;
|
||||
min-height: auto !important;
|
||||
min-height: 844px !important;
|
||||
max-height: none !important;
|
||||
border-radius: 0 !important;
|
||||
box-shadow: none !important;
|
||||
overflow: visible !important;
|
||||
}
|
||||
/* 滚动区域全部展开 */
|
||||
.scroll-content,
|
||||
[class*="scroll"] {
|
||||
overflow: visible !important;
|
||||
max-height: none !important;
|
||||
height: auto !important;
|
||||
}
|
||||
/* 页面外层背景改为白色 */
|
||||
body {
|
||||
background: #fff !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
/* 隐藏下拉菜单、遮罩等弹窗遮挡元素 */
|
||||
.dropdown-menu, .dropdown-mask, .calendar-mask,
|
||||
.plan-dropdown, .plan-mask {
|
||||
display: none !important;
|
||||
}
|
||||
/* 底部安全区缩到最小 */
|
||||
.bottom-safe {
|
||||
height: 8px !important;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
`,
|
||||
});
|
||||
|
||||
// 等待样式生效
|
||||
await new Promise(r => setTimeout(r, 300));
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
// 获取实际内容高度
|
||||
const bodyHandle = await page.$('.phone-shell');
|
||||
const contentHeight = await page.evaluate((el) => {
|
||||
if (el) return el.scrollHeight;
|
||||
const contentHeight = await page.evaluate(() => {
|
||||
const shell = document.querySelector('.phone-shell');
|
||||
if (shell) return shell.scrollHeight;
|
||||
return document.body.scrollHeight;
|
||||
}, bodyHandle);
|
||||
});
|
||||
|
||||
const finalHeight = Math.max(contentHeight || viewportHeight, 400);
|
||||
await page.setViewport({ width: viewportWidth, height: Math.min(finalHeight + 40, 12000), deviceScaleFactor: 2 });
|
||||
const finalHeight = Math.max(contentHeight || 844, 400);
|
||||
const clipHeight = Math.min(finalHeight + 40, 12000);
|
||||
|
||||
// 使用简洁文件名,与 PRD 引用一致
|
||||
// workbench/index.html → index.jpg, knowledge/index.html → knowledge-index.jpg
|
||||
let outName = htmlFile.replace('.html', '.jpg');
|
||||
if (outName.startsWith('workbench/')) {
|
||||
outName = outName.replace('workbench/', '');
|
||||
} else if (outName.startsWith('knowledge/')) {
|
||||
outName = outName.replace('knowledge/', 'knowledge-');
|
||||
} else if (outName.startsWith('profile/')) {
|
||||
outName = outName.replace('profile/', 'profile-');
|
||||
}
|
||||
const outPath = path.join(SCREENSHOTS_DIR, outName);
|
||||
const outName = entry.out || getOutputName(entry);
|
||||
const outPath = path.join(outDir, outName);
|
||||
|
||||
// 长图适当压缩:>1800px 用 65% 质量,普通用 80%
|
||||
const jpegQuality = finalHeight > 1800 ? 65 : 80;
|
||||
|
||||
await page.screenshot({
|
||||
path: outPath,
|
||||
type: 'jpeg',
|
||||
quality: 85,
|
||||
quality: jpegQuality,
|
||||
fullPage: true,
|
||||
});
|
||||
|
||||
console.log(` ✓ ${htmlFile} → ${outName} (${viewportWidth}×${finalHeight})`);
|
||||
const label = query ? `${entry.file}${query}` : entry.file;
|
||||
console.log(` ✓ ${label} → ${outName} (390×${finalHeight}, q${jpegQuality})`);
|
||||
} catch (err) {
|
||||
console.error(` ✗ ${htmlFile}: ${err.message}`);
|
||||
console.error(` ✗ ${entry.file}: ${err.message}`);
|
||||
} finally {
|
||||
await page.close();
|
||||
await context.close();
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const allPages = [
|
||||
...PAGES.map(e => ({ ...e, _outDir: SCREENSHOTS_DIR })),
|
||||
...SHARED_PAGES.map(e => ({ ...e, _outDir: e.outDir })),
|
||||
];
|
||||
|
||||
console.log('管理员端(健康应急APP)- 页面截图');
|
||||
console.log(`模块目录: ${MODULE_DIR}`);
|
||||
console.log(`输出目录: ${SCREENSHOTS_DIR}`);
|
||||
console.log(`共 ${PAGES.length} 个页面\n`);
|
||||
console.log(`共 ${allPages.length} 个页面(含共享页面)\n`);
|
||||
|
||||
// 确保输出目录存在
|
||||
if (!fs.existsSync(SCREENSHOTS_DIR)) {
|
||||
fs.mkdirSync(SCREENSHOTS_DIR, { recursive: true });
|
||||
// 确保所有输出目录存在
|
||||
const dirs = new Set(allPages.map(e => e._outDir));
|
||||
for (const d of dirs) {
|
||||
if (!fs.existsSync(d)) fs.mkdirSync(d, { recursive: true });
|
||||
}
|
||||
|
||||
const browser = await puppeteer.launch({
|
||||
const browser = await chromium.launch({
|
||||
headless: true,
|
||||
executablePath: 'C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe',
|
||||
args: ['--no-sandbox', '--disable-setuid-sandbox'],
|
||||
});
|
||||
|
||||
try {
|
||||
for (let i = 0; i < PAGES.length; i++) {
|
||||
const pageName = PAGES[i];
|
||||
console.log(`[${i + 1}/${PAGES.length}] ${pageName}`);
|
||||
await screenshotPage(browser, pageName);
|
||||
for (let i = 0; i < allPages.length; i++) {
|
||||
const entry = allPages[i];
|
||||
const label = entry.query ? `${entry.file}${entry.query}` : entry.file;
|
||||
console.log(`[${i + 1}/${allPages.length}] ${label}`);
|
||||
await screenshotPage(browser, entry, entry._outDir);
|
||||
}
|
||||
console.log(`\n✅ 全部完成!共 ${allPages.length} 张截图`);
|
||||
console.log(` 管理员截图: ${SCREENSHOTS_DIR}`);
|
||||
for (const sp of SHARED_PAGES) {
|
||||
console.log(` 共享截图: ${sp.outDir}`);
|
||||
}
|
||||
console.log(`\n✅ 全部完成!共 ${PAGES.length} 张截图,保存在 ${SCREENSHOTS_DIR}`);
|
||||
} catch (err) {
|
||||
console.error('执行出错:', err);
|
||||
process.exit(1);
|
||||
|
||||
|
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 276 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 317 KiB After Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 35 KiB |
@@ -291,7 +291,7 @@ var personData = {
|
||||
],
|
||||
dateStart: '2026-06-15', dateEnd: '2026-09-15',
|
||||
result: {
|
||||
status: 'approved', reviewer: '刘主管', time: '2026-06-16'
|
||||
status: 'approved', reviewer: '刘主管', time: '2026-06-16 10:25:38'
|
||||
}
|
||||
},
|
||||
signed: {
|
||||
@@ -306,7 +306,7 @@ var personData = {
|
||||
],
|
||||
dateStart: '2026-06-12', dateEnd: '2026-09-12',
|
||||
result: {
|
||||
status: 'signed', reviewer: '刘主管', time: '2026-06-14'
|
||||
status: 'signed', reviewer: '刘主管', time: '2026-06-14 14:30:15'
|
||||
}
|
||||
},
|
||||
rejected: {
|
||||
@@ -321,7 +321,7 @@ var personData = {
|
||||
],
|
||||
dateStart: '2026-06-10', dateEnd: '2026-09-10',
|
||||
result: {
|
||||
status: 'rejected', reviewer: '刘主管', time: '2026-06-17', reason: '目标设定偏低,建议调整'
|
||||
status: 'rejected', reviewer: '刘主管', time: '2026-06-17 09:12:47', reason: '目标设定偏低,建议调整'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -114,7 +114,7 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
|
||||
|
||||
<!-- 底部5Tab导航栏 -->
|
||||
<div class="tab-bar">
|
||||
<div class="tab-item active" onclick="location.href='./index.html'"><span class="tab-icon">🏥</span><span class="tab-label">工作台</span><span class="tab-badge">3</span></div>
|
||||
<div class="tab-item active" onclick="location.href='./index.html'"><span class="tab-icon">🏥</span><span class="tab-label">工作台</span></div>
|
||||
<div class="tab-item" onclick="location.href='../knowledge/index.html'"><span class="tab-icon">📚</span><span class="tab-label">知识库</span></div>
|
||||
<div class="tab-item" onclick="location.href='../../ai-assistant/index.html'"><span class="tab-icon">🤖</span><span class="tab-label">AI助手</span></div>
|
||||
<div class="tab-item" onclick="location.href='../../message/index.html'"><span class="tab-icon">💬</span><span class="tab-label">消息</span><span class="tab-badge">2</span></div>
|
||||
|
||||
@@ -324,7 +324,6 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
|
||||
<div class="sub-tabs" id="reviewTabs">
|
||||
<div class="sub-tab active" onclick="switchReviewTab(this,'rv-pending')">待审核<span class="badge">2</span></div>
|
||||
<div class="sub-tab" onclick="switchReviewTab(this,'rv-approved')">已通过</div>
|
||||
<div class="sub-tab" onclick="switchReviewTab(this,'rv-signed')">已通过待签署</div>
|
||||
<div class="sub-tab" onclick="switchReviewTab(this,'rv-rejected')">已驳回</div>
|
||||
</div>
|
||||
|
||||
@@ -422,22 +421,6 @@ body { background: #f0f2f5; display: flex; justify-content: center; align-items:
|
||||
<div style="height:16px;"></div>
|
||||
</div>
|
||||
|
||||
<!-- 已通过待签署列表 -->
|
||||
<div id="rv-signed" style="display:none;">
|
||||
<!-- [交互] 点击卡片跳转至审核详情页面 -->
|
||||
<div class="review-card" onclick="location.href='./goal-review.html?status=signed'" style="cursor:pointer;">
|
||||
<div class="review-card__head">
|
||||
<span class="review-card__name">陈伟</span>
|
||||
<span class="review-card__time">申请时间:2026-06-12 16:20</span>
|
||||
</div>
|
||||
<div class="review-card__basic"><span>男</span><span>36岁</span><span>身高 178cm</span></div>
|
||||
<div class="review-card__row"><div class="review-card__col"><div class="review-card__col-title">初始</div><div class="review-card__col-vals"><span class="review-card__col-val">体重 92kg</span><span class="review-card__col-val">BMI 29.0</span><span class="review-card__col-val">体脂 30.2%</span><span class="review-card__col-val">腰围 102cm</span></div></div></div>
|
||||
<div class="review-card__row"><div class="review-card__col"><div class="review-card__col-title">目标</div><div class="review-card__col-vals"><span class="review-card__col-val" style="color:#52c41a;">体重 80kg</span><span class="review-card__col-val" style="color:#52c41a;">BMI 25.2</span><span class="review-card__col-val" style="color:#52c41a;">体脂 22%</span><span class="review-card__col-val" style="color:#52c41a;">腰围 90cm</span></div></div></div>
|
||||
<div style="text-align:right;margin-top:8px;"><span class="tag tag-orange">待签署</span><span style="font-size:11px;color:#999;margin-left:6px;">审核人:刘主管</span><span style="font-size:11px;color:#999;margin-left:6px;">审核时间:2026-06-14</span></div>
|
||||
</div>
|
||||
<div style="height:16px;"></div>
|
||||
</div>
|
||||
|
||||
<!-- 已驳回列表 -->
|
||||
<div id="rv-rejected" style="display:none;">
|
||||
<!-- [交互] 点击卡片跳转至审核详情页面 -->
|
||||
@@ -672,7 +655,7 @@ function renderRankList(data) {
|
||||
function switchReviewTab(el, panelId) {
|
||||
el.closest('.sub-tabs').querySelectorAll('.sub-tab').forEach(function(t) { t.classList.remove('active'); });
|
||||
el.classList.add('active');
|
||||
['rv-pending','rv-approved','rv-signed','rv-rejected'].forEach(function(id) {
|
||||
['rv-pending','rv-approved','rv-rejected'].forEach(function(id) {
|
||||
document.getElementById(id).style.display = id === panelId ? 'block' : 'none';
|
||||
});
|
||||
}
|
||||
|
||||