diff --git a/.claude/skills/design-overview.md b/.claude/skills/design-overview.md new file mode 100644 index 0000000..1e49a74 --- /dev/null +++ b/.claude/skills/design-overview.md @@ -0,0 +1,110 @@ +# Skill: 生成模块设计总览页 + +## 触发方式 +用户说"生成设计总览"、"design overview"或指定某个模块需要生成设计总览时触发。 + +## 输入参数 +- **模块路径**(必须):如 `employee-app/emergency-medical` +- **模块中文名**(必须):如 `应急就医员工端APP` + +## 执行流程 + +### Step 1: 扫描模块页面结构 +1. 列出模块目录下所有 `.html` 文件 +2. 逐个读取每个 HTML 文件,识别: + - 页面标题和主要内容 + - 弹窗/对话框(`display:none` 的 modal、mask、overlay、panel 等) + - Tab 切换 / 状态切换(语音/文字模式、展开/收起等) + - 触发弹窗的 CSS 类名和切换方式 +3. 读取 `doc/PRD.md`(如存在),获取业务主线分组 + +### Step 2: 整理页面形态清单 & 复用已有截图 +1. 检查 `doc/screenshots/` 目录下已有的截图文件 +2. 将已有截图与页面一一对应(按文件名匹配) +3. 标记出缺失的弹窗/状态截图(仅这些需要新截取) +4. **已有截图直接复用,不重新截取** + +### Step 3: 截取缺失的弹窗截图 + +**截图工具优先级**(按可用性依次尝试): +1. **agent-browser**(首选):原生 Rust CLI,截图质量最好 +2. **chrome-devtools MCP**(备选):通过 MCP 协议控制 Chrome +3. **Puppeteer 脚本**(兜底):使用 `_templates/screenshot-modals.js` + +#### 使用 agent-browser 截图: +```bash +agent-browser open "file:///<绝对路径>/<页面>.html" +agent-browser set viewport 390 844 2 +# 触发弹窗状态 +agent-browser snapshot -i # 获取元素 ref +agent-browser click @eN # 或用 evaluate 执行 JS +agent-browser screenshot "<输出路径>.jpg" +agent-browser close +``` + +#### 使用 Puppeteer 脚本截图(兜底方案): +编写 config.json 配置文件,只包含缺失的截图任务: +```json +{ + "baseDir": "<模块路径>", + "tasks": [ + { "page": "xxx.html", "output": "xxx-modal.jpg", + "action": "() => { /* 触发弹窗的JS */ }" } + ] +} +``` +运行:`node _templates/screenshot-modals.js ` + +**关键参数**(Puppeteer): +```javascript +// 视口必须大于 phone-shell 尺寸(390px宽),避免布局溢出 +page.setViewport({ width: 500, height: 900, deviceScaleFactor: 1 }); + +// 只截取 .phone-shell 元素,避免外壳嵌套感 +const shell = await page.$('.phone-shell'); +if (shell) await shell.screenshot({ path, type: 'jpeg', quality: 85 }); +``` + +**截图规范(参考项目背景.md 7.4 节)**: +- 输出格式:JPEG,质量 85%(兼顾清晰度与文件体积) +- deviceScaleFactor 设为 1(设计总览卡片仅 260px 宽,不需要 2x 分辨率) +- 单张截图体积控制在 **60KB 以内**,避免项目膨胀 +- 视口宽度 500px(大于 phone-shell 的 390px 避免溢出),高度 900px +- 只截取 `.phone-shell` 元素的可视区域(不展开滚动内容),保持手机屏幕视角 +- 截图命名规范:`{页面名}-{状态}.jpg`,如 `emergency-chat-confirm.jpg` +- 常规页面截图命名与 HTML 文件名一致(去掉 `.html`),如 `index.jpg` + +### Step 4: 生成 design-overview.html +使用 `_templates/design-overview-template.html` 模板,填充: +- 导航标题:`{模块中文名} · 设计总览` +- 各业务主线 section(id 为 `sec-{英文缩写}`) +- 每张截图一个 card,包含图片、标签名、弹窗/状态标记 +- **截图路径必须与 `doc/screenshots/` 下的实际文件名一致** + +**样式规范(必须与模板一致)**: +- 暗色背景 `#1a1a2e` +- 卡片尺寸 `260×564px`(手机比例 375:812 的 ~70%) +- 每条业务线横向滚动不换行(`overflow-x: auto`) +- 弹窗标记用橙色 tag `tag--modal` +- 状态切换标记用蓝色 tag `tag--state` +- 点击图片灯箱放大查看 + +### Step 5: 注册到导航页 +在根目录 `index.html` 的 `SITE_MAP` 中,找到对应模块的 `pages` 数组,在第一个位置插入: +```javascript +{ file:'../doc/design-overview.html', label:'页面总览' }, +``` + +## 输出物 +1. `{模块目录}/doc/design-overview.html` — 设计总览页 +2. `{模块目录}/doc/screenshots/*-{状态}.jpg` — 新截取的弹窗截图(仅缺失部分) +3. 根 `index.html` SITE_MAP 更新 + +## 注意事项 +- **复用优先**:已有截图绝不重新截取,只补拍缺失的弹窗/状态截图 +- 截图脚本 `_templates/screenshot-modals.js` 放在 _templates 目录,每个模块复用 +- Puppeteer 视口必须设为 `500x900`(大于 phone-shell 的 390px),避免"空白页嵌套小页面"问题 +- 截图只截取 `.phone-shell` 元素,不截整个页面 +- 所有模块的 design-overview 共用同一套 CSS 样式(从模板复制) +- 不要遗漏任何页面形态(弹窗、Tab 状态、展开/收起等) +- 截图配置 json 文件用完后保留在项目根目录,方便后续重新截取 diff --git a/_templates/design-overview-template.html b/_templates/design-overview-template.html new file mode 100644 index 0000000..64c4120 --- /dev/null +++ b/_templates/design-overview-template.html @@ -0,0 +1,82 @@ + + + + + +{{MODULE_NAME}} - 页面设计总览 + + + + + + + + + +
+ + + + + + + diff --git a/emergency-app/onsite/doc/design-overview.html b/emergency-app/onsite/doc/design-overview.html new file mode 100644 index 0000000..7960cd9 --- /dev/null +++ b/emergency-app/onsite/doc/design-overview.html @@ -0,0 +1,218 @@ + + + + + +驻场人员端 - 页面设计总览 + + + + + + + + +
+
+

应急工单

+ 6 页 +
+
+
+
工作台首页
+
工作台首页
+
Tab切换
+
+
+
工单详情(进行中)
+
工单详情(进行中)
+
+
+
进行中·完成处理弹窗
+
进行中·完成处理弹窗
+
弹窗
+
+
+
进行中·联系操作人员
+
进行中·联系操作人员
+
弹窗
+
+
+
进行中·转单选择
+
进行中·转单选择
+
弹窗
+
+
+
工单详情(已完成)
+
工单详情(已完成)
+
+
+
+ +
+ + +
+
+

大病就医

+ 6 页 +
+
+
+
大病就医列表
+
大病就医列表
+
Tab切换
+
+
+
详情(待处理)
+
详情(待处理)
+
+
+
待处理·提交挂号结果
+
待处理·提交挂号结果
+
弹窗
+
+
+
待处理·转单选择
+
待处理·转单选择
+
弹窗
+
+
+
详情(已完成)
+
详情(已完成)
+
+
+
详情(已取消)
+
详情(已取消)
+
+
+
+ +
+ + +
+
+

知识库

+ 2 页 +
+
+
+
知识库首页
+
知识库首页
+
Tab切换
+
+
+
知识详情
+
知识详情
+
+
+
+ +
+ + +
+
+

个人中心

+ 5 页 +
+
+
+
个人中心主页
+
个人中心主页
+
+
+
退出登录确认弹窗
+
退出登录确认弹窗
+
弹窗
+
+
+
修改个人信息
+
修改个人信息
+
+
+
修改信息·头像选择
+
修改信息·头像选择
+
操作列表
+
+
+
修改密码
+
修改密码
+
+
+
+ +
+ + + + + + + diff --git a/emergency-app/onsite/doc/screenshots/edit-profile-avatar.jpg b/emergency-app/onsite/doc/screenshots/edit-profile-avatar.jpg new file mode 100644 index 0000000..6519be9 Binary files /dev/null and b/emergency-app/onsite/doc/screenshots/edit-profile-avatar.jpg differ diff --git a/emergency-app/onsite/doc/screenshots/knowledge-article-detail.jpg b/emergency-app/onsite/doc/screenshots/knowledge-article-detail.jpg index c526e7d..85912be 100644 Binary files a/emergency-app/onsite/doc/screenshots/knowledge-article-detail.jpg and b/emergency-app/onsite/doc/screenshots/knowledge-article-detail.jpg differ diff --git a/emergency-app/onsite/doc/screenshots/profile-index-logout.jpg b/emergency-app/onsite/doc/screenshots/profile-index-logout.jpg new file mode 100644 index 0000000..981a466 Binary files /dev/null and b/emergency-app/onsite/doc/screenshots/profile-index-logout.jpg differ diff --git a/emergency-app/onsite/doc/screenshots/serious-illness-detail-cancelled.jpg b/emergency-app/onsite/doc/screenshots/serious-illness-detail-cancelled.jpg index f4e17aa..fccfd43 100644 Binary files a/emergency-app/onsite/doc/screenshots/serious-illness-detail-cancelled.jpg and b/emergency-app/onsite/doc/screenshots/serious-illness-detail-cancelled.jpg differ diff --git a/emergency-app/onsite/doc/screenshots/serious-illness-detail-done.jpg b/emergency-app/onsite/doc/screenshots/serious-illness-detail-done.jpg index 2049812..9ea9a3e 100644 Binary files a/emergency-app/onsite/doc/screenshots/serious-illness-detail-done.jpg and b/emergency-app/onsite/doc/screenshots/serious-illness-detail-done.jpg differ diff --git a/emergency-app/onsite/doc/screenshots/serious-illness-detail-pending-submit.jpg b/emergency-app/onsite/doc/screenshots/serious-illness-detail-pending-submit.jpg new file mode 100644 index 0000000..1fbc358 Binary files /dev/null and b/emergency-app/onsite/doc/screenshots/serious-illness-detail-pending-submit.jpg differ diff --git a/emergency-app/onsite/doc/screenshots/serious-illness-detail-pending-transfer.jpg b/emergency-app/onsite/doc/screenshots/serious-illness-detail-pending-transfer.jpg new file mode 100644 index 0000000..4a09576 Binary files /dev/null and b/emergency-app/onsite/doc/screenshots/serious-illness-detail-pending-transfer.jpg differ diff --git a/emergency-app/onsite/doc/screenshots/work-order-detail-ongoing-complete.jpg b/emergency-app/onsite/doc/screenshots/work-order-detail-ongoing-complete.jpg new file mode 100644 index 0000000..adcdf51 Binary files /dev/null and b/emergency-app/onsite/doc/screenshots/work-order-detail-ongoing-complete.jpg differ diff --git a/emergency-app/onsite/doc/screenshots/work-order-detail-ongoing-contact.jpg b/emergency-app/onsite/doc/screenshots/work-order-detail-ongoing-contact.jpg new file mode 100644 index 0000000..e844fb3 Binary files /dev/null and b/emergency-app/onsite/doc/screenshots/work-order-detail-ongoing-contact.jpg differ diff --git a/emergency-app/onsite/doc/screenshots/work-order-detail-ongoing-transfer.jpg b/emergency-app/onsite/doc/screenshots/work-order-detail-ongoing-transfer.jpg new file mode 100644 index 0000000..3030f2e Binary files /dev/null and b/emergency-app/onsite/doc/screenshots/work-order-detail-ongoing-transfer.jpg differ diff --git a/emergency-app/onsite/doc/screenshots/work-order-detail-ongoing.jpg b/emergency-app/onsite/doc/screenshots/work-order-detail-ongoing.jpg index c195640..4aa3474 100644 Binary files a/emergency-app/onsite/doc/screenshots/work-order-detail-ongoing.jpg and b/emergency-app/onsite/doc/screenshots/work-order-detail-ongoing.jpg differ diff --git a/emergency-app/onsite/doc/screenshots/work-order-detail.jpg b/emergency-app/onsite/doc/screenshots/work-order-detail.jpg index 9a1a64c..71c7b32 100644 Binary files a/emergency-app/onsite/doc/screenshots/work-order-detail.jpg and b/emergency-app/onsite/doc/screenshots/work-order-detail.jpg differ diff --git a/emergency-app/operator/doc/design-overview.html b/emergency-app/operator/doc/design-overview.html new file mode 100644 index 0000000..671fce0 --- /dev/null +++ b/emergency-app/operator/doc/design-overview.html @@ -0,0 +1,257 @@ + + + + + +操作人员端 - 页面设计总览 + + + + + + + + +
+
+

应急求助

+ 11 页 +
+
+
+
工作台首页
+
工作台首页
+
Tab切换
+
+
+
应急聊天(进行中)
+
应急聊天(进行中)
+
+
+
应急聊天·媒体面板
+
应急聊天·媒体面板
+
面板
+
+
+
结束应急弹窗
+
结束应急弹窗
+
弹窗
+
+
+
应急聊天(已结束)
+
应急聊天(已结束)
+
+
+
驻场派单
+
驻场派单
+
+
+
救护车支援
+
救护车支援
+
+
+
救助信息汇总
+
救助信息汇总
+
+
+
患者定位地图
+
患者定位地图
+
+
+
工单详情(进行中)
+
工单详情(进行中)
+
+
+
工单详情(已完成)
+
工单详情(已完成)
+
+
+
工单详情·补充小结弹窗
+
工单详情·补充小结弹窗
+
弹窗
+
+
+
+ +
+ + +
+
+

大病就医

+ 8 页 +
+
+
+
大病就医列表
+
大病就医列表
+
Tab切换
+
+
+
详情(待确认)
+
详情(待确认)
+
+
+
待确认·确认派单弹窗
+
待确认·确认派单弹窗
+
弹窗
+
+
+
待确认·取消预约弹窗
+
待确认·取消预约弹窗
+
弹窗
+
+
+
详情(已确认)
+
详情(已确认)
+
+
+
已确认·取消预约弹窗
+
已确认·取消预约弹窗
+
弹窗
+
+
+
详情(已完成)
+
详情(已完成)
+
+
+
详情(已取消)
+
详情(已取消)
+
+
+
+ +
+ + +
+
+

知识库

+ 2 页 +
+
+
+
知识库首页
+
知识库首页
+
Tab切换
+
+
+
知识详情
+
知识详情
+
+
+
+ +
+ + +
+
+

个人中心

+ 7 页 +
+
+
+
个人中心主页
+
个人中心主页
+
+
+
退出登录确认弹窗
+
退出登录确认弹窗
+
弹窗
+
+
+
修改个人信息
+
修改个人信息
+
+
+
修改密码
+
修改密码
+
+
+
修改密码成功提示
+
修改密码成功提示
+
弹窗
+
+
+
值班信息
+
值班信息
+
+
+
排班配置
+
排班配置
+
+
+
+ +
+ + + + + diff --git a/emergency-app/operator/doc/screenshots/change-pwd-success.jpg b/emergency-app/operator/doc/screenshots/change-pwd-success.jpg new file mode 100644 index 0000000..403786a Binary files /dev/null and b/emergency-app/operator/doc/screenshots/change-pwd-success.jpg differ diff --git a/emergency-app/operator/doc/screenshots/emergency-chat-ongoing.jpg b/emergency-app/operator/doc/screenshots/emergency-chat-ongoing.jpg index 5019441..30fd7e2 100644 Binary files a/emergency-app/operator/doc/screenshots/emergency-chat-ongoing.jpg and b/emergency-app/operator/doc/screenshots/emergency-chat-ongoing.jpg differ diff --git a/emergency-app/operator/doc/screenshots/knowledge-article-detail.jpg b/emergency-app/operator/doc/screenshots/knowledge-article-detail.jpg index 7ce4bd1..5a99b11 100644 Binary files a/emergency-app/operator/doc/screenshots/knowledge-article-detail.jpg and b/emergency-app/operator/doc/screenshots/knowledge-article-detail.jpg differ diff --git a/emergency-app/operator/doc/screenshots/profile-index-logout.jpg b/emergency-app/operator/doc/screenshots/profile-index-logout.jpg new file mode 100644 index 0000000..7935ff2 Binary files /dev/null and b/emergency-app/operator/doc/screenshots/profile-index-logout.jpg differ diff --git a/emergency-app/operator/doc/screenshots/serious-illness-detail-confirmed-cancel.jpg b/emergency-app/operator/doc/screenshots/serious-illness-detail-confirmed-cancel.jpg new file mode 100644 index 0000000..cae6efd Binary files /dev/null and b/emergency-app/operator/doc/screenshots/serious-illness-detail-confirmed-cancel.jpg differ diff --git a/emergency-app/operator/doc/screenshots/serious-illness-detail-pending-cancel.jpg b/emergency-app/operator/doc/screenshots/serious-illness-detail-pending-cancel.jpg new file mode 100644 index 0000000..4181cf4 Binary files /dev/null and b/emergency-app/operator/doc/screenshots/serious-illness-detail-pending-cancel.jpg differ diff --git a/emergency-app/operator/doc/screenshots/serious-illness-detail-pending-confirm.jpg b/emergency-app/operator/doc/screenshots/serious-illness-detail-pending-confirm.jpg new file mode 100644 index 0000000..3650b52 Binary files /dev/null and b/emergency-app/operator/doc/screenshots/serious-illness-detail-pending-confirm.jpg differ diff --git a/emergency-app/operator/doc/screenshots/work-order-detail-dialog.jpg b/emergency-app/operator/doc/screenshots/work-order-detail-dialog.jpg new file mode 100644 index 0000000..eb384b4 Binary files /dev/null and b/emergency-app/operator/doc/screenshots/work-order-detail-dialog.jpg differ diff --git a/emergency-app/professional/doc/design-overview.html b/emergency-app/professional/doc/design-overview.html new file mode 100644 index 0000000..c384ad1 --- /dev/null +++ b/emergency-app/professional/doc/design-overview.html @@ -0,0 +1,238 @@ + + + + + +健康应急APP专业人员端 - 页面设计总览 + + + + + + + + +
+
+

工作台

+ 17 页面形态 +
+
+
+
+
工单列表-进行中
+
+
+
+
工单列表-已结束
+
Tab切换
+
+
+
+
应急聊天-进行中
+
+
+
+
应急聊天-患者信息展开
+
展开状态
+
+
+
+
应急聊天-语音模式
+
模式切换
+
+
+
+
应急聊天-媒体面板
+
面板
+
+
+
+
应急聊天-结束确认
+
弹窗
+
+
+
+
应急聊天-已结束
+
+
+
+
应急聊天已结束-信息展开
+
展开状态
+
+
+
+
工单详情-已完成
+
+
+
+
工单详情-编辑意见弹窗
+
弹窗
+
+
+
+
工单详情-进行中
+
+
+
+
工单详情进行中-编辑弹窗
+
弹窗
+
+
+
+
工单详情进行中-结束确认
+
弹窗
+
+
+
+
患者定位
+
+
+
+
患者定位-资源卡片
+
卡片展开
+
+
+
+
救助意见
+
+
+
+ +
+ + +
+
+

知识库

+ 3 页面形态 +
+
+
+
+
知识库-文章列表
+
+
+
+
知识库-规章制度
+
Tab切换
+
+
+
+
知识详情
+
+
+
+ +
+ + +
+
+

我的

+ 7 页面形态 +
+
+
+
+
个人中心
+
+
+
+
个人中心-退出确认
+
弹窗
+
+
+
+
修改个人信息
+
+
+
+
修改信息-头像选择
+
ActionSheet
+
+
+
+
修改密码
+
+
+
+
修改密码-成功确认
+
弹窗
+
+
+
+
值班信息
+
+
+
+ +
+ + + + + + + diff --git a/emergency-app/professional/doc/screenshots/emergency-chat-done-info.jpg b/emergency-app/professional/doc/screenshots/emergency-chat-done-info.jpg new file mode 100644 index 0000000..57f794b Binary files /dev/null and b/emergency-app/professional/doc/screenshots/emergency-chat-done-info.jpg differ diff --git a/emergency-app/professional/doc/screenshots/emergency-chat-ongoing-confirm.jpg b/emergency-app/professional/doc/screenshots/emergency-chat-ongoing-confirm.jpg new file mode 100644 index 0000000..c0925a4 Binary files /dev/null and b/emergency-app/professional/doc/screenshots/emergency-chat-ongoing-confirm.jpg differ diff --git a/emergency-app/professional/doc/screenshots/emergency-chat-ongoing-info.jpg b/emergency-app/professional/doc/screenshots/emergency-chat-ongoing-info.jpg new file mode 100644 index 0000000..3011fb3 Binary files /dev/null and b/emergency-app/professional/doc/screenshots/emergency-chat-ongoing-info.jpg differ diff --git a/emergency-app/professional/doc/screenshots/emergency-chat-ongoing-media.jpg b/emergency-app/professional/doc/screenshots/emergency-chat-ongoing-media.jpg new file mode 100644 index 0000000..48426eb Binary files /dev/null and b/emergency-app/professional/doc/screenshots/emergency-chat-ongoing-media.jpg differ diff --git a/emergency-app/professional/doc/screenshots/emergency-chat-ongoing-voice.jpg b/emergency-app/professional/doc/screenshots/emergency-chat-ongoing-voice.jpg new file mode 100644 index 0000000..8c7ce7d Binary files /dev/null and b/emergency-app/professional/doc/screenshots/emergency-chat-ongoing-voice.jpg differ diff --git a/emergency-app/professional/doc/screenshots/index-done.jpg b/emergency-app/professional/doc/screenshots/index-done.jpg new file mode 100644 index 0000000..0e1ec4e Binary files /dev/null and b/emergency-app/professional/doc/screenshots/index-done.jpg differ diff --git a/emergency-app/professional/doc/screenshots/index.jpg b/emergency-app/professional/doc/screenshots/index.jpg index 3c17671..77d97a3 100644 Binary files a/emergency-app/professional/doc/screenshots/index.jpg and b/emergency-app/professional/doc/screenshots/index.jpg differ diff --git a/emergency-app/professional/doc/screenshots/knowledge-index-rule.jpg b/emergency-app/professional/doc/screenshots/knowledge-index-rule.jpg new file mode 100644 index 0000000..9218ca6 Binary files /dev/null and b/emergency-app/professional/doc/screenshots/knowledge-index-rule.jpg differ diff --git a/emergency-app/professional/doc/screenshots/patient-location-card.jpg b/emergency-app/professional/doc/screenshots/patient-location-card.jpg new file mode 100644 index 0000000..89bf86e Binary files /dev/null and b/emergency-app/professional/doc/screenshots/patient-location-card.jpg differ diff --git a/emergency-app/professional/doc/screenshots/profile-change-pwd-modal.jpg b/emergency-app/professional/doc/screenshots/profile-change-pwd-modal.jpg new file mode 100644 index 0000000..403786a Binary files /dev/null and b/emergency-app/professional/doc/screenshots/profile-change-pwd-modal.jpg differ diff --git a/emergency-app/professional/doc/screenshots/profile-edit-sheet.jpg b/emergency-app/professional/doc/screenshots/profile-edit-sheet.jpg new file mode 100644 index 0000000..1c12578 Binary files /dev/null and b/emergency-app/professional/doc/screenshots/profile-edit-sheet.jpg differ diff --git a/emergency-app/professional/doc/screenshots/profile-index-logout.jpg b/emergency-app/professional/doc/screenshots/profile-index-logout.jpg new file mode 100644 index 0000000..13b7824 Binary files /dev/null and b/emergency-app/professional/doc/screenshots/profile-index-logout.jpg differ diff --git a/emergency-app/professional/doc/screenshots/work-order-detail-edit.jpg b/emergency-app/professional/doc/screenshots/work-order-detail-edit.jpg new file mode 100644 index 0000000..5111d57 Binary files /dev/null and b/emergency-app/professional/doc/screenshots/work-order-detail-edit.jpg differ diff --git a/emergency-app/professional/doc/screenshots/work-order-detail-ongoing-confirm.jpg b/emergency-app/professional/doc/screenshots/work-order-detail-ongoing-confirm.jpg new file mode 100644 index 0000000..e876551 Binary files /dev/null and b/emergency-app/professional/doc/screenshots/work-order-detail-ongoing-confirm.jpg differ diff --git a/emergency-app/professional/doc/screenshots/work-order-detail-ongoing-edit.jpg b/emergency-app/professional/doc/screenshots/work-order-detail-ongoing-edit.jpg new file mode 100644 index 0000000..2d518c3 Binary files /dev/null and b/emergency-app/professional/doc/screenshots/work-order-detail-ongoing-edit.jpg differ diff --git a/employee-app/emergency-medical/doc/design-overview.html b/employee-app/emergency-medical/doc/design-overview.html new file mode 100644 index 0000000..5f45061 --- /dev/null +++ b/employee-app/emergency-medical/doc/design-overview.html @@ -0,0 +1,288 @@ + + + + + +应急就医 - 页面设计总览 + + + + + + + + +
+
+

入口与总览

+ 2 +
+
+
+
+
应急就医首页
+
+
+
+
紧急联系人
+
+
+
+ +
+ +
+
+

应急资源地图

+ 6 +
+
+
+
+
油田医院地图(默认)
+
+
+
+
医疗点地图
+
+
+
+
AED 地图
+
+
+
+
救护车地图
+
+
+
+
急救包地图
+
+
+
+
模拟急救面板
+
弹窗
+
+
+
+ +
+ + +
+
+

资源详情页

+ 6 +
+
+
+
+
油田医院详情
+
展开/收起
+
+
+
+
医疗点详情
+
+
+
+
AED 详情
+
+
+
+
救护车详情
+
+
+
+
急救包详情
+
+
+
+
急救提示书
+
+
+
+ +
+ + +
+
+

应急求助

+ 5 +
+
+
+
+
应急视频对话(文字模式)
+
+
+
+
语音输入模式
+
状态切换
+
+
+
+
加号菜单展开
+
状态切换
+
+
+
+
结束应急确认弹窗
+
弹窗
+
+
+
+
应急求助记录
+
+
+
+ +
+ + +
+
+

急救教学

+ 1 +
+
+
+
+
急救教学视频列表
+
+
+
+ +
+ + +
+
+

大病就医预约

+ 11 +
+
+
+
+
列表 · 待确认
+
+
+
+
列表 · 已确认
+
+
+
+
列表 · 已完成
+
+
+
+
列表 · 已取消
+
+
+
+
发起预约表单
+
+
+
+
详情 · 待确认
+
+
+
+
取消弹窗(待确认)
+
弹窗
+
+
+
+
详情 · 已确认
+
+
+
+
取消弹窗(已确认)
+
弹窗
+
+
+
+
详情 · 已完成
+
+
+
+
详情 · 已取消
+
+
+
+ +
+ + + + + + + diff --git a/employee-app/emergency-medical/doc/screenshots/appointment-detail-confirmed-modal.jpg b/employee-app/emergency-medical/doc/screenshots/appointment-detail-confirmed-modal.jpg new file mode 100644 index 0000000..f077f84 Binary files /dev/null and b/employee-app/emergency-medical/doc/screenshots/appointment-detail-confirmed-modal.jpg differ diff --git a/employee-app/emergency-medical/doc/screenshots/appointment-detail-pending-modal.jpg b/employee-app/emergency-medical/doc/screenshots/appointment-detail-pending-modal.jpg new file mode 100644 index 0000000..1faba1f Binary files /dev/null and b/employee-app/emergency-medical/doc/screenshots/appointment-detail-pending-modal.jpg differ diff --git a/employee-app/emergency-medical/doc/screenshots/emergency-chat-modal.jpg b/employee-app/emergency-medical/doc/screenshots/emergency-chat-modal.jpg new file mode 100644 index 0000000..9f8a853 Binary files /dev/null and b/employee-app/emergency-medical/doc/screenshots/emergency-chat-modal.jpg differ diff --git a/employee-app/emergency-medical/doc/screenshots/emergency-chat-plus.jpg b/employee-app/emergency-medical/doc/screenshots/emergency-chat-plus.jpg new file mode 100644 index 0000000..284a624 Binary files /dev/null and b/employee-app/emergency-medical/doc/screenshots/emergency-chat-plus.jpg differ diff --git a/employee-app/emergency-medical/doc/screenshots/emergency-chat-voice.jpg b/employee-app/emergency-medical/doc/screenshots/emergency-chat-voice.jpg new file mode 100644 index 0000000..7600034 Binary files /dev/null and b/employee-app/emergency-medical/doc/screenshots/emergency-chat-voice.jpg differ diff --git a/employee-app/emergency-medical/doc/screenshots/resource-map-simulate.jpg b/employee-app/emergency-medical/doc/screenshots/resource-map-simulate.jpg new file mode 100644 index 0000000..602261e Binary files /dev/null and b/employee-app/emergency-medical/doc/screenshots/resource-map-simulate.jpg differ diff --git a/index.html b/index.html index 2be436e..1f3159b 100644 --- a/index.html +++ b/index.html @@ -559,6 +559,7 @@ const SITE_MAP = [ { file:'consult-flowchart.html', label:'咨询流程图' } ]}, { name:'应急就医', dir:'employee-app/emergency-medical', entry:'index.html', prd:'employee-app/emergency-medical/doc/PRD.md', done:true, pages:[ + { file:'doc/design-overview.html', label:'页面总览' }, { file:'index.html', label:'应急就医主页' }, { file:'emergency-chat.html', label:'应急对话' }, { file:'resource-map.html', label:'应急资源地图' }, @@ -645,6 +646,7 @@ const SITE_MAP = [ ]}, { role:'应急操作人员', id:'operator', modules:[ {name:'工作台',dir:'emergency-app/operator/workbench',prd:'emergency-app/operator/doc/PRD.md',done:true,pages:[ + {file:'../doc/design-overview.html',label:'页面总览'}, {file:'index.html',label:'工作台首页(应急求助)'}, {file:'serious-illness-list.html',label:'大病就医首页'}, {file:'emergency-chat.html',label:'应急聊天'}, @@ -673,6 +675,7 @@ const SITE_MAP = [ ]}, { role:'应急专业人员', id:'professional', modules:[ { name:'工作台', dir:'emergency-app/professional/workbench', prd:'emergency-app/professional/doc/PRD.md', done:true, pages:[ + {file:'../doc/design-overview.html',label:'页面总览'}, {file:'index.html',label:'工作台首页'}, {file:'emergency-chat-ongoing.html',label:'应急聊天(进行中)'}, {file:'emergency-chat-done.html',label:'应急聊天(已结束)'}, @@ -692,6 +695,7 @@ const SITE_MAP = [ ]}, { role:'应急驻场人员', id:'onsite', modules:[ {name:'工作台',dir:'emergency-app/onsite/workbench',prd:'emergency-app/onsite/doc/PRD.md',done:true,pages:[ + {file:'../doc/design-overview.html',label:'页面总览'}, {file:'index.html',label:'工作台首页(应急求助)'}, {file:'serious-illness-list.html',label:'大病就医首页'}, {file:'work-order-detail-ongoing.html',label:'工单详情(待处理)'}, @@ -968,7 +972,7 @@ tabNav.addEventListener('click', e => { /* ===== Markdown 预览(新标签页打开) ===== */ function openMdPreview(filePath, title) { - window.open('md-viewer.html?file=' + encodeURIComponent(filePath) + '&title=' + encodeURIComponent(title || ''), '_blank'); + window.open('_templates/md-viewer.html?file=' + encodeURIComponent(filePath) + '&title=' + encodeURIComponent(title || ''), '_blank'); } diff --git a/md-viewer.html b/md-viewer.html deleted file mode 100644 index e51b9e0..0000000 --- a/md-viewer.html +++ /dev/null @@ -1,184 +0,0 @@ - - - - - -文档预览 - - - - -
- - 文档预览 - -
-
-
加载中...
-
- - - - - diff --git a/package-lock.json b/package-lock.json index 2b2d52c..b618309 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,21 +1,46 @@ { "name": "platform-prototype", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "dependencies": { - "chrome-devtools-mcp": "^0.21.0" - }, + "name": "platform-prototype", + "version": "1.0.0", + "license": "ISC", "devDependencies": { - "playwright": "^1.59.1", - "puppeteer-core": "^24.43.0" + "puppeteer": "^24.43.1" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" } }, "node_modules/@puppeteer/browsers": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.13.1.tgz", - "integrity": "sha512-zmS4RTK9fbrc++WlAJhxYbfz3IjDeOmkK/CwwbLmk7ydfS9e2CiEeRJHEPvjDVElO/bwXbidwGA37Bsm6LzCnQ==", + "version": "2.13.2", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.13.2.tgz", + "integrity": "sha512-5EUZSUIc37H6aIXyWO0Z4y8NlF8NnjgmqeQgOGiswAU7pY0HOo16ho4+alIWmSfdZnjqBRawMsP3I5YqLSn6kw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -42,14 +67,14 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.6.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.2.tgz", - "integrity": "sha512-sokuT28dxf9JT5Kady1fsXOvI4HVpjZa95NKT5y9PNTIrs2AsobR4GFAA90ZG8M+nxVRLysCXsVj6eGC7Vbrlw==", + "version": "25.9.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz", + "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "undici-types": "~7.19.0" + "undici-types": ">=7.24.0 <7.24.7" } }, "node_modules/@types/yauzl": { @@ -99,6 +124,13 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, "node_modules/ast-types": { "version": "0.13.4", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", @@ -128,9 +160,9 @@ } }, "node_modules/bare-events": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", - "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.3.tgz", + "integrity": "sha512-HdUm8EMQBLaJvGUdidNNbqpA1kYkwNcb+MYxkxCLAPJGQzlv9J0C24h8V65Z4c5GLd/JEALDvpFCQgpLJqc0zw==", "dev": true, "license": "Apache-2.0", "peerDependencies": { @@ -244,17 +276,14 @@ "node": "*" } }, - "node_modules/chrome-devtools-mcp": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/chrome-devtools-mcp/-/chrome-devtools-mcp-0.21.0.tgz", - "integrity": "sha512-d+iqrRmcwpRFV3Q4DRCF2LCoq+WCRU3GhISKQ9v8g+1C2Uh8upj3urkjxNO4QIjhBMIYei/VQ1OQLFceby80Og==", - "license": "Apache-2.0", - "bin": { - "chrome-devtools": "build/src/bin/chrome-devtools.js", - "chrome-devtools-mcp": "build/src/bin/chrome-devtools-mcp.js" - }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" + "node": ">=6" } }, "node_modules/chromium-bidi": { @@ -306,6 +335,33 @@ "dev": true, "license": "MIT" }, + "node_modules/cosmiconfig": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.1.tgz", + "integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/data-uri-to-buffer": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", @@ -373,6 +429,26 @@ "once": "^1.4.0" } }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -487,21 +563,6 @@ "pend": "~1.2.0" } }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -571,6 +632,23 @@ "node": ">= 14" } }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ip-address": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", @@ -581,6 +659,13 @@ "node": ">= 12" } }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -591,6 +676,40 @@ "node": ">=8" } }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, "node_modules/lru-cache": { "version": "7.18.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", @@ -669,6 +788,38 @@ "node": ">= 14" } }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -676,37 +827,12 @@ "dev": true, "license": "MIT" }, - "node_modules/playwright": { - "version": "1.59.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", - "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "playwright-core": "1.59.1" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "node_modules/playwright-core": { - "version": "1.59.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", - "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=18" - } + "license": "ISC" }, "node_modules/progress": { "version": "2.0.3", @@ -756,14 +882,36 @@ "once": "^1.3.1" } }, - "node_modules/puppeteer-core": { - "version": "24.43.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.43.0.tgz", - "integrity": "sha512-cCRNXsUlhyPoKDz6+TiSpfZpRS3mD6Y1YFKhkdr6ik6TMfuJb7fAtXq9ThUFc4sphxObDk3BuAvdxc1Y6YOnqQ==", + "node_modules/puppeteer": { + "version": "24.43.1", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.43.1.tgz", + "integrity": "sha512-/FSOViCrqRdb1HDocpsM9Z1giA71gTQPUt3SpHGVRALKAy/rJr1fLFYZW9F23qPxqVxTHQnbh/5B5opJST3kAw==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@puppeteer/browsers": "2.13.2", + "chromium-bidi": "14.0.0", + "cosmiconfig": "^9.0.0", + "devtools-protocol": "0.0.1608973", + "puppeteer-core": "24.43.1", + "typed-query-selector": "^2.12.2" + }, + "bin": { + "puppeteer": "lib/cjs/puppeteer/node/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/puppeteer/node_modules/puppeteer-core": { + "version": "24.43.1", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.43.1.tgz", + "integrity": "sha512-T5ScUMAsmhdNbgDR41AGESYeS6V9MSgetkSnVhhW+gXvzC42VesKCn5ld87gAZDJ6vLHL9GkRvY9WtQWSnwFbw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "2.13.1", + "@puppeteer/browsers": "2.13.2", "chromium-bidi": "14.0.0", "debug": "^4.4.3", "devtools-protocol": "0.0.1608973", @@ -785,10 +933,20 @@ "node": ">=0.10.0" } }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/semver": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", - "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", + "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", "dev": true, "license": "ISC", "bin": { @@ -953,9 +1111,9 @@ "license": "MIT" }, "node_modules/undici-types": { - "version": "7.19.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", - "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", "dev": true, "license": "MIT", "optional": true @@ -993,9 +1151,9 @@ "license": "ISC" }, "node_modules/ws": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", - "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index 6556345..7e15242 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,16 @@ { - "dependencies": { - "chrome-devtools-mcp": "^0.21.0" - }, + "name": "platform-prototype", + "version": "1.0.0", + "main": "server.js", "devDependencies": { - "playwright": "^1.59.1", - "puppeteer-core": "^24.43.0" - } + "puppeteer": "^24.43.1" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node server.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "" } diff --git a/本地服务器独立微服务化方案.md b/本地服务器独立微服务化方案.md deleted file mode 100644 index a3aae61..0000000 --- a/本地服务器独立微服务化方案.md +++ /dev/null @@ -1,392 +0,0 @@ -# 本地服务器独立微服务化方案 - -> **项目**:智能餐养系统 - 边缘服务节点重构 -> **版本**:v1.0 -> **日期**:2026-05-26 -> **作者**:技术团队 -> **范围**:面向技术团队的设计与落地方案 -> **周期**:1 个月落地,边跑边迁 - ---- - -## 一、定位与边界 - -**本地服务器 ≠ 业务系统**。本次重构将其重新定位为:**部署在每个食堂边缘的独立微服务,只做"硬件 ↔ 云端"的可靠中转桥梁**,与现有"健康长庆"业务系统、小程序后端、admin 后台**完全解耦**。 - -| 做(In Scope) | 不做(Out of Scope) | 不混用(独立部署) | -| --- | --- | --- | -| 任务化数据中转 | 业务规则、菜品算法 | 不依赖小程序后端 | -| 任务链状态管理与重试 | 用户登录鉴权 | 不调用四合一业务接口 | -| 终端设备接入与协议适配 | 报表/统计/分析 | 不耦合 admin 业务模块 | -| 本地资源缓存(视频/人脸/升级包) | 任何 UI 业务逻辑 | 独立进程、独立升级 | -| 设备远程升级 | | | - -**一句话原则**:本地服务器只回答两个问题——*云端要的数据收齐了吗?云端下发的指令落地了吗?* 其他一概不管。 - ---- - -## 二、总体架构 - - -### 2.1 系统拓扑 - -```mermaid -flowchart TB - subgraph Cloud["云端"] - CloudAPI[云端任务网关
Spring Boot] - CloudDB[(任务主库
PostgreSQL)] - OpsUI[运维后台
Vue3 + Ant Design Vue
复用 admin 技术栈] - end - - subgraph Edge["食堂本地 - 每个食堂一套"] - LS[本地服务器微服务
Spring Boot 独立 JAR] - LSDB[(本地任务库
SQLite)] - Cache[本地资源缓存
视频/人脸库/升级包] - end - - subgraph Devices["终端硬件"] - T1[点餐终端] - T2[入库秤/配比秤] - T3[智能货柜/绑盘机] - T4[人脸设备] - T5[显示大屏] - end - - OpsUI --> CloudAPI - CloudAPI <-->|MQTT QoS2 持久会话| LS - CloudAPI --- CloudDB - LS --- LSDB - LS --- Cache - LS <-->|HTTP/MQTT 局域网| T1 - LS <-->|HTTP/MQTT 局域网| T2 - LS <-->|HTTP/MQTT 局域网| T3 - LS <-->|HTTP/MQTT 局域网| T4 - LS -->|本地 HTTP / RTSP| T5 -``` - -### 2.2 通信协议选型(针对弱网) - -| 链路 | 协议 | 选型理由 | -| --- | --- | --- | -| 云端 ↔ 本地服务器 | **MQTT QoS 2 + Persistent Session**(EMQX) | 弱网友好、断线自动续传、消息至少一次且不重复 | -| 大文件下发(视频/升级包) | HTTPS 断点续传(Range) | 弱网重连不重头,降低带宽浪费 | -| 本地服务器 ↔ 终端 | HTTP / MQTT(局域网) | 局域网稳定,简单优先,按设备类型适配 | -| 运维后台 ↔ 云端 | HTTPS REST | 复用 admin 现有 axios + token 鉴权 | - -### 2.3 技术栈 - -| 模块 | 选型 | 备注 | -| --- | --- | --- | -| **云端任务网关** | Spring Boot 3 + Spring Integration MQTT | 与现有 Java 后端栈一致 | -| **本地服务器微服务** | Spring Boot 3,打包成单 JAR | 一键部署,内存占用控制在 512MB 以内 | -| **MQTT Broker** | EMQX 5(集群) | 支持 QoS 2、共享订阅、规则引擎 | -| **云端数据库** | **PostgreSQL 15** ✅ 推荐 / MySQL 8 备选 | PG 的 JSONB 对 `target_scope` 等字段更友好,CTE/窗口函数利于运维查询 | -| **本地任务库** | SQLite + WAL 模式 | 零运维,适合边缘场景,单文件备份 | -| **运维后台** | Vue 3 + Vite + TypeScript + Ant Design Vue | **复用现有 admin 工程技术栈与登录体系** | -| **配置中心 / 注册** | 暂不引入,本地服务器走配置文件 + 远程下发 | 边缘节点数量级小,过早引入 Nacos 增加复杂度 | -| **部署** | Docker 镜像 + 一键安装脚本 | 边缘节点需支持离线部署 | - ---- - -## 三、核心机制:全链路任务化 - -### 3.1 设计原则 - -每一次数据流转(无论下行还是上行)都是一个**有状态、可追溯、可重试**的任务。云端与本地各持一份任务记录,通过 `task_id` 关联,任意时刻**任意任务卡在哪一跳一查就知道**。 - -### 3.2 任务状态机 - -```mermaid -stateDiagram-v2 - [*] --> Created: 云端创建任务 - Created --> Dispatched: 推送到本地服务器 - Dispatched --> LocalReceived: 本地落库 ACK - LocalReceived --> TerminalDispatched: 分发到终端 - TerminalDispatched --> TerminalDone: 终端确认完成 - TerminalDone --> CloudReported: 上报云端 - CloudReported --> [*]: 任务完成 - - Dispatched --> RetryPending: 网络中断 - LocalReceived --> RetryPending: 终端离线 - TerminalDispatched --> RetryPending: 终端执行失败 - RetryPending --> Dispatched: 自动指数退避重试 - RetryPending --> Failed: 达到最大重试次数 - Failed --> Dispatched: 运维后台人工触发重试 -``` - -**重试策略**:指数退避 `1s → 5s → 30s → 2min → 10min`,默认最多 5 次,达到上限转 `Failed` 并触发告警。 - -### 3.3 数据库 Schema(核心三表) - -> 以下 DDL 以 PostgreSQL 为例,MySQL 版本仅类型微调(JSONB → JSON、ENUM → VARCHAR + CHECK)。 - -```sql --- ============================================ --- 1. 任务主表 --- ============================================ -CREATE TABLE task ( - task_id UUID PRIMARY KEY, - task_type VARCHAR(32) NOT NULL, - -- 'DISH_DOWN','FACE_DOWN','UPGRADE_DOWN','VIDEO_DOWN','MEAL_UP',... - direction VARCHAR(8) NOT NULL, -- 'DOWN' / 'UP' - target_scope JSONB NOT NULL, - -- {"org_id":1,"canteen_ids":[10,11],"device_ids":["T01","T02"]} - -- 支持二级单位/食堂/设备级定向下发 - payload_ref VARCHAR(255), -- 大字段指针(对象存储 key 或外部表 ID) - total_count INT NOT NULL DEFAULT 0, - success_count INT NOT NULL DEFAULT 0, - failed_count INT NOT NULL DEFAULT 0, - state VARCHAR(24) NOT NULL, - -- CREATED/DISPATCHED/LOCAL_RECEIVED/TERMINAL_DISPATCHED/ - -- TERMINAL_DONE/CLOUD_REPORTED/RETRY_PENDING/FAILED - retry_count INT NOT NULL DEFAULT 0, - max_retry INT NOT NULL DEFAULT 5, - next_retry_at TIMESTAMP, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - finished_at TIMESTAMP, - creator VARCHAR(64) -); -CREATE INDEX idx_task_state_retry ON task(state, next_retry_at); -CREATE INDEX idx_task_target ON task USING GIN (target_scope); - --- ============================================ --- 2. 任务跳转流转表(全链路审计) --- ============================================ -CREATE TABLE task_hop ( - hop_id BIGSERIAL PRIMARY KEY, - task_id UUID NOT NULL REFERENCES task(task_id), - hop_name VARCHAR(24) NOT NULL, - -- CLOUD_OUT/LOCAL_IN/LOCAL_OUT/TERMINAL_IN/TERMINAL_DONE/ - -- LOCAL_REPORT/CLOUD_IN - status VARCHAR(8) NOT NULL, -- 'OK','FAIL','TIMEOUT' - err_code VARCHAR(32), - err_msg TEXT, - occurred_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); -CREATE INDEX idx_task_hop_task ON task_hop(task_id, occurred_at); - --- ============================================ --- 3. 任务明细表(批量任务的逐条结果) --- ============================================ -CREATE TABLE task_item ( - item_id BIGSERIAL PRIMARY KEY, - task_id UUID NOT NULL REFERENCES task(task_id), - biz_key VARCHAR(128) NOT NULL, -- 业务键,例如菜品 ID、人脸 ID - status VARCHAR(8) NOT NULL, -- 'OK','FAIL','PENDING' - err_msg TEXT, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - UNIQUE (task_id, biz_key) -); -CREATE INDEX idx_task_item_task ON task_item(task_id, status); -``` - -**这套表的关键能力**: - -- 任意任务卡在哪一跳,一查 `task_hop` 就知道 -- 1000 条菜品下发,失败的 3 条精准告诉你是哪 3 条(`task_item.status='FAIL'`) -- 本地服务器同样跑这套 Schema(SQLite 简化版),云端拉取本地 hop 状态做对账 - -### 3.4 可靠性三大不变量 - -1. **先持久化再前进**:每一跳必须本地落库成功后才回 ACK;落库失败 → 让上游重发 -2. **幂等键 = `task_id` + `biz_key`**:重发不会产生重复数据;终端拿到同一个 task_id 直接覆盖原记录 -3. **任务永不丢**:本地服务器即使断电重启,任务从最近持久化的状态继续往下走;运维后台可强制重放 - -### 3.5 接口契约(节选) - -**云端 → 本地:任务下发** - -``` -Topic: tenant/{org_id}/canteen/{canteen_id}/task/down -QoS: 2 -Payload: -{ - "task_id": "uuid", - "task_type": "DISH_DOWN", - "target_scope": { "device_ids": ["T01","T02"] }, - "payload_ref": "https://oss.../dish-snapshot-2026052601.json", - "items_count": 487, - "issued_at": "2026-05-26T10:00:00Z" -} -``` - -**本地 → 云端:跳转回执** - -``` -Topic: tenant/{org_id}/canteen/{canteen_id}/task/hop -Payload: -{ - "task_id": "uuid", - "hop_name": "TERMINAL_DONE", - "status": "OK", - "success_count": 485, - "failed_count": 2, - "failed_items": [ - {"biz_key":"DISH_1024","err_msg":"image download timeout"} - ] -} -``` - -**本地 → 云端:就餐数据上传** - -``` -Topic: tenant/{org_id}/canteen/{canteen_id}/task/up -Payload: 同 task 主表结构 + items 数组 -``` - ---- - -## 四、运维后台 - -> 复用现有 admin 工程的 **Vue 3 + Vite + TypeScript + Ant Design Vue + Pinia + axios** 技术栈,**沿用 admin 登录体系与权限模型**,作为 admin 工程内的一组独立路由模块(`/edge-ops/*`),与业务模块隔离。 - -### 4.1 功能模块 - -``` -运维后台 (admin 工程内 /edge-ops 路由组) -├── 任务总览 -│ ├── 实时大盘:各食堂在线状态、今日任务数、失败率、堆积量 -│ └── 异常告警:失败率突增、任务堆积超阈值、设备掉线 -├── 任务管理 -│ ├── 任务列表(按食堂/类型/状态/时间筛选) -│ ├── 任务详情(全链路流转图 + 明细 + 错误堆栈) -│ ├── 一键重试(单任务/批量) -│ └── 手动创建任务(应急下发) -├── 设备管理 -│ ├── 食堂/本地服务器健康状态 -│ ├── 终端设备清单(在线、版本、最后心跳) -│ └── 远程软件升级触发 -├── 资源管理 -│ ├── 视频/人脸库/升级包上传与下发 -│ └── 各食堂本地缓存命中情况 -├── 日志与查询 -│ ├── 实时日志流(SSE 推送) -│ └── 数据库可视化查询(只读 SQL + 预设查询模板) -└── 系统配置 - ├── 重试策略、超时阈值 - └── 二级单位/食堂组织树 -``` - -### 4.2 关键页面示意:任务详情 - -``` -[任务 #abc123 · 菜品下发 · 目标:总部食堂 + 5 个分食堂] -状态: ⚠ 部分失败 进度: 487/500 耗时: 2m13s - -全链路流转: - ✓ 云端创建 10:23:01 - ✓ 推送本地 10:23:02 (6/6 食堂) - ✓ 本地落库 10:23:03 (6/6 食堂) - ⚠ 终端分发 10:23:05 (5 食堂成功 / 1 食堂超时,自动重试中) - └─ 失败明细: 分食堂C - 终端 T07 离线 - -操作: [一键重试] [跳过失败项] [取消任务] [导出日志] -``` - -### 4.3 路由与权限 - -- 在 `src/router/index.ts` 的 `modules` 数组中新增「边缘运维」分组,`meta.menuKey = 'edge-ops'` -- 路由命名:`/edge-ops/dashboard`、`/edge-ops/tasks`、`/edge-ops/devices`、`/edge-ops/resources`、`/edge-ops/logs`、`/edge-ops/config` -- 权限模型:复用 admin 已有 `role`,新增 `edge:read`、`edge:retry`、`edge:upgrade`、`edge:config` 四类细粒度权限 -- 仅限内部运维人员访问,不对客户开放 - ---- - -## 五、1 个月落地计划(边跑边迁) - -```mermaid -gantt - title 落地排期(2026-05-26 至 2026-06-22) - dateFormat YYYY-MM-DD - section 第1周 框架 - 项目骨架/任务模型/DB Schema :a1, 2026-05-26, 3d - 云端↔本地通信层(MQTT QoS2) :a2, after a1, 4d - section 第2周 三大功能迁移 - 菜品/营养数据下发改造 :b1, after a2, 3d - 就餐数据上传改造 :b2, after a2, 3d - 视频资源下发改造 :b3, after b1, 4d - section 第3周 运维后台 + 联调 - 运维后台前后端 :c1, after b3, 5d - 全链路联调 + 弱网压测 :c2, after b3, 7d - section 第4周 灰度 + 切换 - 单食堂灰度试运行 :d1, after c2, 3d - 分批切换 + 监控 + 回滚预案 :d2, after d1, 4d -``` - -### 边跑边迁策略 - -- **新老并存**:旧 MQ 链路保留,新任务系统**双写一段时间**(老链路真实下发,新链路影子运行) -- **每日对账**:跑对账脚本比对新老链路数据一致性,差异 > 0 立即告警 -- **按食堂灰度**:第 4 周第 1-3 天选 1-2 个食堂切换,稳定后批量推 -- **回滚开关**:每个食堂有独立的 `use_new_pipeline` 开关,出问题秒级切回老链路 -- **冻结老链路功能**:迁移期间老链路只修 P0 bug,不再加新功能 - -### 验收标准 - -| 指标 | 目标 | -| --- | --- | -| 弱网模拟丢包 30% 下任务最终成功率 | ≥ 99.9% | -| 任务平均端到端延迟(局域网正常) | ≤ 3s | -| 任意失败任务可定位到具体跳转 + 失败明细 | 100% | -| 运维后台一键重试可用率 | 100% | -| 灰度食堂连续 3 天无 P0 故障 | 通过 | - ---- - -## 六、风险与对策 - -| 风险 | 影响 | 对策 | -| --- | --- | --- | -| 弱网导致云端推送积压 | 数据延迟 | MQTT 持久会话 + 本地服务器主动拉取兜底 + 队列水位告警 | -| 本地服务器断电/磁盘损坏 | 任务丢失 | SQLite 启用 WAL + 每日云端快照备份 + 关键任务云端可重放 | -| 终端固件升级失败变砖 | 设备瘫痪 | 双分区 A/B 升级 + 失败自动回滚 + 升级前强制健康检查 | -| 1 个月排期紧张 | 延期 | **第 1 周架构定型不可压缩**;运维后台 V1 可砍范围(先保任务列表 + 重试 + 日志,大盘和告警 V1.5 补) | -| 与四合一系统数据不一致 | 业务事故 | 双写期间每日对账脚本,差异 > 0 告警;切换前必须对账连续 3 天为 0 | -| MQTT Broker 单点 | 整体瘫痪 | EMQX 至少双节点集群 + VIP/DNS 漂移 | -| 边缘节点版本不一致 | 兼容问题 | 本地服务器协议层做版本协商,云端兼容旧版本 N-1 | - ---- - -## 七、待办与开放问题 - -1. **EMQX 部署形态**:云端机房自建 vs 公有云托管,需运维侧拉齐 -2. **对象存储**:大文件(视频/升级包)走哪个 OSS,断点续传 SDK 选型 -3. **告警通道**:钉钉机器人 / 企业微信 / 短信,需明确接入哪一种 -4. **灰度食堂选择**:建议选 1 个网络条件好的 + 1 个网络条件差的,覆盖两种典型场景 -5. **历史任务数据迁移**:老 MQ 在途消息如何处理,是否需要补录到新任务表 - ---- - -## 八、附录 - -### 8.1 与现有 admin 工程的接入点 - -- **新增路由模块**:`src/views/edge-ops/`(dashboard / tasks / devices / resources / logs / config) -- **新增 API 聚合**:`src/api/edge-ops.ts`,baseURL `/api/edge`,走云端任务网关 -- **菜单配置**:`MainLayout.vue` 新增「边缘运维」一级菜单,key = `edge-ops` -- **权限**:复用 `src/stores/user.ts` 的角色与权限校验,新增四个权限位 -- **登录态**:完全沿用 admin 现有 token + axios 拦截器机制,**不新建鉴权体系** - -### 8.2 文件清单(新增) - -``` -zhican/admin/ -├── src/views/edge-ops/ -│ ├── dashboard/index.vue -│ ├── tasks/index.vue -│ ├── tasks/detail.vue -│ ├── devices/index.vue -│ ├── resources/index.vue -│ ├── logs/index.vue -│ └── config/index.vue -├── src/api/edge-ops.ts -└── docs/本地服务器独立微服务化方案.md (本文件) - -服务端(独立工程): -zhican-edge/ -├── edge-cloud-gateway/ 云端任务网关 Spring Boot -├── edge-local-server/ 本地服务器 Spring Boot(单 JAR) -└── edge-common/ 协议/DTO/任务模型共享包 -``` - diff --git a/项目背景.md b/项目背景.md index 4d98e6b..464212c 100644 --- a/项目背景.md +++ b/项目背景.md @@ -478,7 +478,7 @@ window.location.href = '../health-consult/consult-list.html'; **PRD 中引用方式:** - 在每个页面/弹窗的三级标题下方紧跟插入:`![页面截图](screenshots/xxx.jpg)` -- 使用相对路径(基于 `doc/` 目录),md-viewer.html 会自动补全为完整路径 +- 使用相对路径(基于 `doc/` 目录),`_templates/md-viewer.html` 会自动补全为完整路径 **截图更新时机:** - 与 PRD 文档同步,仅在用户明确要求更新 PRD 时重新截取