feat: 补齐营养边缘服务原型
This commit is contained in:
@@ -1,5 +1,20 @@
|
||||
# 钀ュ吇鐢ㄩ绠$悊绔?CHANGELOG
|
||||
|
||||
## [2026-06-24] feat: 边缘服务管理原型补齐与导航入口调整
|
||||
|
||||
- **边缘服务器管理**:
|
||||
- 新增 `edge-server-management.html`,替代原边缘节点列表页面,统一展示边缘服务器状态、版本、心跳与操作入口。
|
||||
- 新增 `edge-server-detail.html`,补齐服务器基础信息、运行状态、同步任务与接口调用概览。
|
||||
- **数据上报与下发**:
|
||||
- 新增 `edge-report-management.html`,用于展示边缘服务上报任务、数据类型、状态与处理结果。
|
||||
- 新增 `edge-dispatch-management.html` 与 `edge-dispatch-detail.html`,承载数据下发任务列表与任务详情。
|
||||
- **异常告警中心**:
|
||||
- 新增 `edge-alert-center.html`,替代原告警中心页面,聚合边缘服务异常、处理状态与处置入口。
|
||||
- **导航与文档入口**:
|
||||
- `index.html` 的营养管理模块新增边缘服务相关 6 个页面入口。
|
||||
- 导航页新增项目资料入口,指向缓存中转服务器功能架构与数据流转方案。
|
||||
- `_templates/md-viewer.html` 适配项目资料预览展示。
|
||||
|
||||
## [2026-06-17] feat: 净菜包装页新增弹窗与食材表联动调整
|
||||
|
||||
- **`sup-pkg-label.html`**:
|
||||
|
||||
@@ -1,161 +0,0 @@
|
||||
# 溯源总览模块优化方案
|
||||
|
||||
> 整理时间:2026-05-22 | 状态:待讨论 | 关联页面:trace-query.html、trace-timeline.html
|
||||
|
||||
---
|
||||
|
||||
## 一、核心问题诊断
|
||||
|
||||
### 问题 1:模块定位与内容严重不匹配
|
||||
|
||||
"溯源总览"作为营养管理的**第一个一级 Tab**,目前只有 2 个页面(查询 + 链路可视化)。对比其他模块(绿色种采 10+ 页、健康生产 10+ 页、营养用餐 8+ 页),内容极度单薄。更关键的是——**叫"总览"却没有总览 Dashboard**,用户进入后直接看到的是一个查询列表,完全没有全局视角。
|
||||
|
||||
### 问题 2:trace-query 的数据粒度混乱(最致命)
|
||||
|
||||
表格的每一行粒度定义混乱:
|
||||
|
||||
| 列名 | 粒度 | 矛盾点 |
|
||||
|------|------|--------|
|
||||
| 餐品溯源码 / 菜品名称 | 餐品级 | 一道菜一个码 |
|
||||
| 来源类型 / 产地/供应商 / 种植批次 | **食材级** | 一道菜有多种食材,各有不同来源 |
|
||||
|
||||
以"宫保鸡丁"为例,它包含 4 种食材(鸡胸肉=外采、花生=外采、辣椒=自种、葱姜蒜=自种),但表中只显示了一条记录,来源标记为"自产养殖"——这只对应了鸡胸肉一种食材,**其他 3 种食材的溯源信息完全丢失**。
|
||||
|
||||
如果某天的花生检出黄曲霉毒素超标,从这个表里根本看不出来。
|
||||
|
||||
### 问题 3:trace-timeline 的信息负荷过重
|
||||
|
||||
鸡胸肉链路有 **14 个节点**,水平排列后管道极长。分支节点(如净菜加工 x4、按单烹制 x3)本质上是一个节点分裂出多条并行链路,但当前设计**只用角标数字表示**,点击后详情是把所有分支挤在一个卡片里(`分支A/B/C/D`),无法直观理解分支走向和收敛。
|
||||
|
||||
### 问题 4:两个页面之间的衔接断裂
|
||||
|
||||
trace-query 中每行的"查看食材链路"都指向同一个 `trace-timeline.html`(硬编码的宫保鸡丁数据),无论点哪道菜都看到同样的内容。作为原型,至少应该在页面状态上表达"跳转后应展示对应菜品"的意图。
|
||||
|
||||
---
|
||||
|
||||
## 二、重构优化方案
|
||||
|
||||
建议将"溯源总览"模块从 2 页扩展为 **4~5 页**:
|
||||
|
||||
```
|
||||
溯源总览(一级菜单)
|
||||
├── 溯源看板 trace-dashboard.html 【新增】总览 Dashboard
|
||||
├── 餐品溯源查询 trace-query.html 【重构】修正数据粒度
|
||||
├── 食材溯源查询 trace-ingredient.html 【新增】食材维度独立页
|
||||
├── 链路可视化 trace-timeline.html 【优化】交互减负
|
||||
└── 溯源码管理 trace-code.html 【新增,可选】溯源码规则配置
|
||||
```
|
||||
|
||||
### 2.1 新增:溯源看板(trace-dashboard)
|
||||
|
||||
作为模块的**入口页和默认页**,提供全局统计视角。
|
||||
|
||||
**顶部统计卡片行(4~5 张):**
|
||||
- 今日溯源覆盖率(已溯源菜品数 / 总菜品数,如 95.2%)
|
||||
- 检测合格率(合格食材数 / 已检食材数,如 99.8%)
|
||||
- 自产食材占比(自种+自养数量 / 总食材数,如 42%)
|
||||
- 异常待处理数(检测不合格 + 链路断裂 + 数据缺失)
|
||||
- 可选:今日已完成溯源链路数
|
||||
|
||||
**中部双列布局:**
|
||||
- 左:近 7 天溯源趋势折线图(溯源覆盖率 + 合格率双曲线)
|
||||
- 右:食材来源分布饼图(自产种植 / 自产养殖 / 外采占比)
|
||||
|
||||
**底部:**
|
||||
- 近期异常事件列表(检测不合格的食材、链路中断记录、溯源码异常等)
|
||||
- 快捷入口卡片(跳转餐品查询、食材查询、链路可视化)
|
||||
|
||||
### 2.2 重构:餐品溯源查询(trace-query)
|
||||
|
||||
**核心改动:修正数据粒度为纯餐品级,食材信息下沉到展开行。**
|
||||
|
||||
表格列重设计:
|
||||
|
||||
| 餐品溯源码 | 菜品名称 | 就餐日期 | 餐次 | 食材数量 | 来源组成 | 检测结论 | 烹制厨师 | 上餐时间 | 操作 |
|
||||
|---|---|---|---|---|---|---|---|---|---|
|
||||
| TR-20260422-001 | 宫保鸡丁 | 2026-04-22 | 午餐 | 4种 | 自种2 外采2 | 全部合格 | 张厨师 | 11:30 | 展开 \| 查看链路 |
|
||||
|
||||
关键变化:
|
||||
- **删除**原来的"来源类型""产地/供应商""种植/养殖批次"(食材级信息不应出现在餐品级表中)
|
||||
- **新增**"食材数量"列 + "来源组成"列(标签组合展示,如 `自种×2 外采×2`)
|
||||
- **新增**"展开"操作:点击后在行下方展开食材子表
|
||||
|
||||
**展开后的食材子表示意:**
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 🥩 鸡胸肉 8.5kg 外采 鲜肉直供 批次:JXRJ-.. 检测:合格 │
|
||||
│ 🥜 花生 1.2kg 外采 华盛干货 批次:HS-.. 检测:合格 │
|
||||
│ 🌶️ 辣椒 0.8kg 自种 南郊基地 批次:LJ-.. 检测:合格 │
|
||||
│ 🧄 葱姜蒜 0.5kg 自种 南郊基地 批次:CJS-.. 检测:合格 │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**筛选栏补充:**
|
||||
- 新增"餐次"筛选(早餐/午餐/晚餐/加餐)
|
||||
- "就餐日期"改为日期范围选择(默认今天)
|
||||
- 新增"食堂"筛选(对应多食堂场景)
|
||||
|
||||
### 2.3 新增:食材溯源查询(trace-ingredient)
|
||||
|
||||
**独立的食材维度查询页**,从 trace-timeline 的"食材维度 Tab"中抽离为完整页面。
|
||||
|
||||
表格列设计:
|
||||
|
||||
| 食材溯源码 | 食材名称 | 来源类型 | 产地/供应商 | 入库日期 | 入库量 | 剩余库存 | 关联菜品数 | 检测结论 | 操作 |
|
||||
|---|---|---|---|---|---|---|---|---|---|
|
||||
| ST-EX-XR01-MT-... | 鸡胸肉 | 外采 | 鲜肉直供公司 | 04-15 | 80kg | 32kg | 4道 | 合格 | 查看链路 \| 关联菜品 |
|
||||
|
||||
页面价值:
|
||||
- 追踪**一批食材被分配到了哪些菜品**(正向追溯)
|
||||
- 检测不合格时快速定位**受影响的所有菜品和订单**(反向召回)
|
||||
- 解决了 trace-query 中食材信息被压缩的问题
|
||||
|
||||
### 2.4 优化:链路可视化(trace-timeline)
|
||||
|
||||
**优化点 1:14 个节点太拥挤 → 分段折叠**
|
||||
|
||||
将 19 种节点归类为 5 个阶段,默认展示阶段概要,点击展开看细节:
|
||||
|
||||
```
|
||||
[采供阶段] ────→ [存储阶段] ────→ [加工阶段] ────→ [烹制上餐] ────→ [餐后追踪]
|
||||
采供入库 毛菜入库 净菜加工 按单烹制 余量处置
|
||||
卫生检测 毛菜存储 净菜入库 上餐监控 营养检验
|
||||
净菜内供 用户取餐
|
||||
净菜进柜
|
||||
净菜组配
|
||||
```
|
||||
|
||||
每个阶段用"超级节点"表示(阶段名 + 节点数 + 整体状态),点击展开该阶段详细节点管道。
|
||||
|
||||
**优化点 2:分支角标看不懂 → 分支卡片列表**
|
||||
|
||||
点击分支节点时,详情区域从单卡片改为**分支卡片列表**,每条分支单独一张卡片,标注分支走向(如"→ 宫保鸡丁""→ 辣子鸡"),用颜色/线条标注"当前追踪的是哪条分支"。
|
||||
|
||||
**优化点 3:食材维度数据不完整**
|
||||
|
||||
- 提供食材搜索框,支持按食材名称/溯源码搜索
|
||||
- 展示该食材的完整溯源链路(从源头到所有去向菜品)
|
||||
- 或者:将食材维度移到 trace-ingredient 页面,trace-timeline 专注餐品维度
|
||||
|
||||
### 2.5 可选新增:溯源码管理(trace-code)
|
||||
|
||||
管理溯源码的编码规则、前缀配置、批次规则等系统配置页面。当前溯源码格式 `ST-{来源}-{供应商编号}-{分类}-{品名}-{重量}-{日期}-{序号}` 是隐含在数据中的,如果需要运维人员管理这些规则,可以做一个配置页。优先级较低。
|
||||
|
||||
---
|
||||
|
||||
## 三、与文档 8.5 节已有建议的关系
|
||||
|
||||
文档 8.5 节的 8 条建议(导出筛选结果、快速预览浮层、PDF报告导出、数据一致性检查、视频播放器、食材历史链路、缩略图导航、分支收敛可视化)属于**细节优化**,可以在重构后的新页面中一并实现。但如果不先解决数据粒度混乱和模块定位不清的根本问题,单独做那些优化效果有限。
|
||||
|
||||
---
|
||||
|
||||
## 四、优先级
|
||||
|
||||
| 优先级 | 页面 | 工作量 | 理由 |
|
||||
|--------|------|--------|------|
|
||||
| P0 | trace-dashboard 新建 | 中 | 模块入口必须有总览 |
|
||||
| P0 | trace-query 重构 | 中 | 数据粒度问题是硬伤 |
|
||||
| P1 | trace-ingredient 新建 | 中 | 食材维度是溯源核心能力 |
|
||||
| P1 | trace-timeline 优化 | 大 | 可视化体验提升 |
|
||||
| P2 | trace-code 新建 | 小 | 系统配置,可后期补 |
|
||||
@@ -514,554 +514,7 @@ sequenceDiagram
|
||||
| 设备升级失败 | 设备版本不一致或不可用 | 云端展示失败状态,支持重新下发或人工处理 |
|
||||
| 边缘节点磁盘不足 | 现场数据可能无法继续保存 | 运维告警,限制大文件缓存,优先保障业务流水 |
|
||||
|
||||
## 12. 管理端需要补充的产品能力
|
||||
|
||||
为了让边缘节点真正可运营,云端管理端建议增加以下能力。
|
||||
|
||||
### 12.0 模块定位
|
||||
|
||||
边缘节点管理模块不是单纯的“设备监控页”,而是云端针对“食堂现场运行状态”的统一运营入口。它承接的是边缘节点相关的日常管理、异常发现、同步补偿和升级跟踪需求。
|
||||
|
||||
该模块需要解决四类产品问题:
|
||||
|
||||
1. 食堂节点是否在线、是否健康、是否还能承接现场业务。
|
||||
2. 云端下发的数据和任务是否真正到达食堂并生效。
|
||||
3. 现场产生的数据是否已经成功回传云端,是否存在积压和失败。
|
||||
4. 节点、设备、升级、文件、人脸等异常发生后,管理端是否有明确的处理入口。
|
||||
|
||||
典型使用对象:
|
||||
|
||||
- 云端管理员:关注全部食堂的节点健康和同步状态。
|
||||
- 业务运营人员:关注任务是否下发成功、现场是否按计划执行。
|
||||
- 运维人员:关注节点、设备、升级、磁盘、服务异常。
|
||||
|
||||
### 12.1 边缘节点列表
|
||||
|
||||
#### 12.1.1 页面用途
|
||||
|
||||
用于按食堂维度集中查看所有边缘节点的在线情况、运行健康度、同步状态和基础资源情况,是进入节点详情和异常处理的总入口。
|
||||
|
||||
#### 12.1.2 筛选项建议
|
||||
|
||||
- 所属区域
|
||||
- 所属食堂
|
||||
- 节点状态
|
||||
- 同步状态
|
||||
- 服务版本
|
||||
- 是否存在告警
|
||||
- 最近心跳时间区间
|
||||
|
||||
#### 12.1.3 列表字段建议
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| 节点名称 | 便于运营识别的节点名称 |
|
||||
| 节点编号 | 系统内唯一编号 |
|
||||
| 所属食堂 | 节点归属食堂 |
|
||||
| 部署位置 | 机房、后厨、配餐间等,可选 |
|
||||
| 在线状态 | 在线、离线、异常 |
|
||||
| 健康状态 | 正常、告警、严重 |
|
||||
| 最后心跳时间 | 最近一次成功心跳时间 |
|
||||
| 服务版本 | 边缘节点当前服务版本 |
|
||||
| 本地数据库状态 | 正常、告警、不可用 |
|
||||
| 磁盘剩余空间 | 用于判断缓存和日志是否存在风险 |
|
||||
| 绑定设备数 | 当前节点关联的设备总数 |
|
||||
| 在线设备数 | 当前在线设备数 |
|
||||
| 未同步数据量 | 本地待上行同步的数据量 |
|
||||
| 下发失败数 | 最近周期内下行失败条数 |
|
||||
| 最后成功同步时间 | 最近一次完整成功同步时间 |
|
||||
| 当前告警数 | 当前未恢复告警数量 |
|
||||
|
||||
#### 12.1.4 列表操作建议
|
||||
|
||||
- 查看详情
|
||||
- 查看设备
|
||||
- 查看同步记录
|
||||
- 查看告警
|
||||
- 手动重连检测
|
||||
- 重新下发基础资料
|
||||
- 导出节点状态
|
||||
|
||||
#### 12.1.5 状态规则建议
|
||||
|
||||
| 状态 | 说明 |
|
||||
|---|---|
|
||||
| 在线 | 最近心跳正常,关键服务可用 |
|
||||
| 离线 | 超过阈值未收到心跳 |
|
||||
| 异常 | 节点在线,但存在数据库异常、MQ 异常、磁盘不足等问题 |
|
||||
|
||||
健康状态建议与在线状态分离:节点可能在线,但已出现同步积压、磁盘不足或服务异常,此时应展示为“告警”或“严重”。
|
||||
|
||||
#### 12.1.6 页面布局建议
|
||||
|
||||
边缘节点列表页建议采用典型的“统计区 + 筛选区 + 列表区”三段式布局。
|
||||
|
||||
页面自上而下建议为:
|
||||
|
||||
1. 页面标题区:展示“边缘节点管理”标题、页面说明、数据更新时间。
|
||||
2. 顶部统计区:展示当前节点运行总览。
|
||||
3. 筛选区:支持按食堂、状态、版本、告警等条件筛选。
|
||||
4. 列表区:展示节点清单和行级操作。
|
||||
5. 详情入口区:点击节点名称或“查看详情”进入节点详情页。
|
||||
|
||||
#### 12.1.7 顶部统计区建议
|
||||
|
||||
建议顶部使用 6 个统计卡,优先展示最能反映全局风险的指标:
|
||||
|
||||
| 统计项 | 说明 |
|
||||
|---|---|
|
||||
| 节点总数 | 当前纳入管理的边缘节点数量 |
|
||||
| 在线节点数 | 最近心跳正常的节点数量 |
|
||||
| 离线节点数 | 超过阈值未心跳的节点数量 |
|
||||
| 异常节点数 | 在线但存在告警或服务异常的节点数量 |
|
||||
| 积压节点数 | 存在未同步积压的节点数量 |
|
||||
| 严重告警节点数 | 存在严重告警且未恢复的节点数量 |
|
||||
|
||||
统计卡交互建议:
|
||||
|
||||
- 点击“在线节点数”自动筛选在线节点。
|
||||
- 点击“离线节点数”自动筛选离线节点。
|
||||
- 点击“积压节点数”自动筛选存在同步积压的节点。
|
||||
|
||||
#### 12.1.8 筛选区布局建议
|
||||
|
||||
筛选区建议分为两行:
|
||||
|
||||
- 第一行放高频筛选项:所属食堂、节点状态、同步状态、是否有告警。
|
||||
- 第二行放辅助筛选项:服务版本、心跳时间区间、关键字搜索。
|
||||
|
||||
按钮建议:
|
||||
|
||||
- 查询
|
||||
- 重置
|
||||
- 导出
|
||||
|
||||
关键字搜索建议支持:
|
||||
|
||||
- 节点名称
|
||||
- 节点编号
|
||||
- 食堂名称
|
||||
|
||||
#### 12.1.9 列表区布局建议
|
||||
|
||||
列表区建议采用表格形式,字段顺序建议如下:
|
||||
|
||||
1. 节点名称
|
||||
2. 所属食堂
|
||||
3. 在线状态
|
||||
4. 健康状态
|
||||
5. 服务版本
|
||||
6. 在线设备数 / 绑定设备数
|
||||
7. 未同步数据量
|
||||
8. 下发失败数
|
||||
9. 最近心跳时间
|
||||
10. 最后成功同步时间
|
||||
11. 当前告警数
|
||||
12. 操作
|
||||
|
||||
展示规则建议:
|
||||
|
||||
- 在线状态使用强识别色标,例如绿色、灰色、红色。
|
||||
- 健康状态使用标签区分正常、告警、严重。
|
||||
- “在线设备数 / 绑定设备数”建议显示为 `12 / 18` 形式,方便快速判断设备在线率。
|
||||
- 未同步数据量和当前告警数大于 0 时,应支持点击进入对应记录页。
|
||||
|
||||
#### 12.1.10 行级操作建议
|
||||
|
||||
列表行级操作建议分为主操作和更多操作:
|
||||
|
||||
主操作:
|
||||
|
||||
- 查看详情
|
||||
- 查看同步
|
||||
|
||||
更多操作:
|
||||
|
||||
- 查看设备
|
||||
- 查看告警
|
||||
- 手动重连检测
|
||||
- 重新下发基础资料
|
||||
- 导出节点状态
|
||||
|
||||
交互规则建议:
|
||||
|
||||
1. “查看详情”为默认主入口,点击节点名称也进入详情页。
|
||||
2. “手动重连检测”只对离线或异常节点开放。
|
||||
3. “重新下发基础资料”应二次确认,并明确影响范围。
|
||||
|
||||
#### 12.1.11 页面状态说明
|
||||
|
||||
需要明确以下页面状态:
|
||||
|
||||
| 状态 | 展示建议 |
|
||||
|---|---|
|
||||
| 首次加载中 | 显示骨架屏或表格 loading |
|
||||
| 无数据 | 提示当前暂无边缘节点数据,可配合引导说明 |
|
||||
| 无筛选结果 | 提示未找到符合条件的节点,支持一键重置筛选 |
|
||||
| 部分异常 | 列表正常展示,同时突出异常节点 |
|
||||
| 接口失败 | 展示错误提示和重试按钮 |
|
||||
|
||||
#### 12.1.12 权限建议
|
||||
|
||||
边缘节点列表页建议按角色控制操作权限:
|
||||
|
||||
| 操作 | 云端管理员 | 业务运营 | 运维人员 |
|
||||
|---|---|---|---|
|
||||
| 查看列表 | 是 | 是 | 是 |
|
||||
| 查看详情 | 是 | 是 | 是 |
|
||||
| 查看同步记录 | 是 | 是 | 是 |
|
||||
| 重新下发基础资料 | 是 | 否 | 是 |
|
||||
| 手动重连检测 | 否 | 否 | 是 |
|
||||
| 导出节点状态 | 是 | 是 | 是 |
|
||||
|
||||
### 12.2 节点详情
|
||||
|
||||
#### 12.2.1 页面用途
|
||||
|
||||
用于查看单个边缘节点的完整运行画像,是故障排查、同步补偿和升级追踪的主要工作台。
|
||||
|
||||
#### 12.2.2 页面结构建议
|
||||
|
||||
建议采用 Tab 结构,至少包括以下视图:
|
||||
|
||||
- 基本信息
|
||||
- 设备列表
|
||||
- 同步记录
|
||||
- 下发记录
|
||||
- 文件缓存
|
||||
- 升级记录
|
||||
- 告警记录
|
||||
|
||||
#### 12.2.3 基本信息
|
||||
|
||||
建议展示:
|
||||
|
||||
- 节点名称、节点编号、所属食堂、部署位置
|
||||
- 节点 IP、安装时间、最近启动时间
|
||||
- 服务版本、配置版本
|
||||
- 最近心跳时间
|
||||
- 本地数据库状态
|
||||
- MQ 或同步连接状态
|
||||
- CPU、内存、磁盘占用
|
||||
- 本地缓存大小
|
||||
- 未同步数据量
|
||||
- 最近同步成功时间
|
||||
|
||||
#### 12.2.4 设备列表
|
||||
|
||||
建议字段:
|
||||
|
||||
- 设备名称
|
||||
- 设备编号
|
||||
- 设备类型
|
||||
- 所属业务场景
|
||||
- 绑定位置
|
||||
- 在线状态
|
||||
- 最近活跃时间
|
||||
- 当前版本
|
||||
- 最近升级结果
|
||||
|
||||
建议操作:
|
||||
|
||||
- 查看设备详情
|
||||
- 查看设备日志
|
||||
- 触发重新拉取配置
|
||||
- 触发重新下发升级任务
|
||||
|
||||
#### 12.2.5 同步记录
|
||||
|
||||
建议按“上行同步”和“下行同步”分开展示。
|
||||
|
||||
上行同步建议字段:
|
||||
|
||||
- 业务类型
|
||||
- 业务编号
|
||||
- 发生时间
|
||||
- 同步状态
|
||||
- 失败次数
|
||||
- 最后失败原因
|
||||
- 最近处理时间
|
||||
|
||||
下行同步建议字段:
|
||||
|
||||
- 下发类型
|
||||
- 下发批次号
|
||||
- 下发时间
|
||||
- 应用状态
|
||||
- 失败原因
|
||||
- 处理完成时间
|
||||
|
||||
#### 12.2.6 文件缓存
|
||||
|
||||
建议展示:
|
||||
|
||||
- 文件类型
|
||||
- 文件名称
|
||||
- 文件版本
|
||||
- 文件大小
|
||||
- 下载状态
|
||||
- 校验状态
|
||||
- 最近下载时间
|
||||
- 来源任务
|
||||
|
||||
#### 12.2.7 升级记录
|
||||
|
||||
建议展示:
|
||||
|
||||
- 升级任务编号
|
||||
- 升级对象
|
||||
- 目标版本
|
||||
- 下发时间
|
||||
- 当前状态
|
||||
- 完成时间
|
||||
- 失败原因
|
||||
|
||||
#### 12.2.8 告警记录
|
||||
|
||||
建议展示:
|
||||
|
||||
- 告警类型
|
||||
- 告警等级
|
||||
- 首次发生时间
|
||||
- 最近发生时间
|
||||
- 当前状态
|
||||
- 处理人
|
||||
- 处理说明
|
||||
|
||||
### 12.3 同步监控
|
||||
|
||||
#### 12.3.1 页面用途
|
||||
|
||||
用于从“数据同步”视角观察整个云边体系的运行情况,重点关注数据是否积压、哪些业务同步失败、失败是否已处理。
|
||||
|
||||
#### 12.3.2 页面结构建议
|
||||
|
||||
建议至少分为三个视图:
|
||||
|
||||
- 总览看板
|
||||
- 上行同步记录
|
||||
- 下行同步记录
|
||||
|
||||
#### 12.3.3 总览看板
|
||||
|
||||
建议展示:
|
||||
|
||||
- 在线节点数 / 离线节点数
|
||||
- 存在同步异常的食堂数
|
||||
- 上行待同步总量
|
||||
- 下行失败总量
|
||||
- 超过阈值积压的节点数
|
||||
- 最近 24 小时失败趋势
|
||||
|
||||
#### 12.3.4 上行同步记录
|
||||
|
||||
页面用途:查看现场到云端的数据回传情况。
|
||||
|
||||
筛选项建议:
|
||||
|
||||
- 食堂
|
||||
- 节点
|
||||
- 业务类型
|
||||
- 同步状态
|
||||
- 发生时间
|
||||
- 是否多次失败
|
||||
|
||||
字段建议:
|
||||
|
||||
- 食堂
|
||||
- 节点
|
||||
- 业务类型
|
||||
- 业务编号
|
||||
- 发生时间
|
||||
- 当前状态
|
||||
- 失败次数
|
||||
- 最后处理时间
|
||||
- 失败原因
|
||||
|
||||
操作建议:
|
||||
|
||||
- 查看详情
|
||||
- 手动重试
|
||||
- 导出记录
|
||||
|
||||
#### 12.3.5 下行同步记录
|
||||
|
||||
页面用途:查看云端数据、任务、升级包等下发到边缘节点的情况。
|
||||
|
||||
筛选项建议:
|
||||
|
||||
- 食堂
|
||||
- 节点
|
||||
- 下发类型
|
||||
- 批次号
|
||||
- 应用状态
|
||||
- 下发时间
|
||||
|
||||
字段建议:
|
||||
|
||||
- 食堂
|
||||
- 节点
|
||||
- 下发类型
|
||||
- 批次号
|
||||
- 下发时间
|
||||
- 当前状态
|
||||
- 完成时间
|
||||
- 失败原因
|
||||
|
||||
操作建议:
|
||||
|
||||
- 查看详情
|
||||
- 重新下发
|
||||
- 标记忽略
|
||||
|
||||
#### 12.3.6 业务规则
|
||||
|
||||
| 操作 | 规则建议 |
|
||||
|---|---|
|
||||
| 手动重试 | 仅针对上行失败或待处理记录,不允许对成功记录重复重试 |
|
||||
| 重新下发 | 仅针对下行失败、旧版本无效或人工确认需重推的记录 |
|
||||
| 标记忽略 | 仅针对确认无需再处理的失败记录,需记录操作人和原因 |
|
||||
| 查看详情 | 需要展示完整业务上下文、失败原因和最近处理轨迹 |
|
||||
|
||||
### 12.4 下发记录与补偿记录
|
||||
|
||||
#### 12.4.1 页面用途
|
||||
|
||||
该页面用于补充“同步监控”偏数据状态的不足,更聚焦于“业务下发是否真正落地”和“失败后如何补偿”。
|
||||
|
||||
适合跟踪的下发对象包括:
|
||||
|
||||
- 基础资料下发
|
||||
- 人脸批次下发
|
||||
- 生产任务下发
|
||||
- 升级任务下发
|
||||
- 文件缓存下发
|
||||
|
||||
#### 12.4.2 下发记录字段建议
|
||||
|
||||
- 下发单号或批次号
|
||||
- 下发类型
|
||||
- 目标食堂
|
||||
- 目标节点
|
||||
- 关联业务对象
|
||||
- 发起人
|
||||
- 发起时间
|
||||
- 当前状态
|
||||
- 失败节点数
|
||||
- 完成节点数
|
||||
|
||||
#### 12.4.3 补偿记录字段建议
|
||||
|
||||
- 补偿单号
|
||||
- 原始失败记录
|
||||
- 补偿方式
|
||||
- 补偿发起人
|
||||
- 补偿时间
|
||||
- 补偿结果
|
||||
- 备注
|
||||
|
||||
#### 12.4.4 补偿操作建议
|
||||
|
||||
- 单节点重新下发
|
||||
- 批量重新下发
|
||||
- 跳过单个失败节点
|
||||
- 重新生成下发批次
|
||||
- 终止补偿
|
||||
|
||||
#### 12.4.5 业务规则
|
||||
|
||||
1. 重新下发应避免对已成功节点重复覆盖,除非用户明确选择“强制覆盖”。
|
||||
2. 人脸、菜品、物料等基础资料重新下发时,应支持按食堂或按节点粒度操作。
|
||||
3. 生产任务重新下发时,应校验任务是否已开始或已完成。
|
||||
4. 升级任务补偿时,应区分“重新通知设备”和“重新分发升级包”。
|
||||
|
||||
### 12.5 告警中心
|
||||
|
||||
#### 12.5.1 页面用途
|
||||
|
||||
告警中心用于集中承接边缘节点和设备运行中的所有异常,让运营和运维人员明确知道“出了什么问题、影响哪些食堂、应该由谁处理、当前处理到哪一步”。
|
||||
|
||||
#### 12.5.2 告警分类建议
|
||||
|
||||
- 节点类告警:节点离线、服务异常、数据库异常、磁盘不足
|
||||
- 连接类告警:MQ 连接异常、网络断连
|
||||
- 设备类告警:设备离线、设备异常、设备版本异常
|
||||
- 同步类告警:上行积压、下行失败、多次重试失败
|
||||
- 数据类告警:人脸版本落后、主数据应用失败、任务状态冲突
|
||||
- 升级类告警:升级失败、升级包校验失败、设备未执行升级
|
||||
|
||||
#### 12.5.3 告警字段建议
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| 告警编号 | 唯一标识 |
|
||||
| 告警类型 | 节点类、设备类、同步类等 |
|
||||
| 告警名称 | 例如“节点离线”“上行积压超 30 分钟” |
|
||||
| 告警等级 | 提醒、警告、严重 |
|
||||
| 所属食堂 | 受影响食堂 |
|
||||
| 所属节点 | 受影响节点 |
|
||||
| 受影响对象 | 设备、业务批次、升级任务等 |
|
||||
| 首次发生时间 | 首次触发时间 |
|
||||
| 最近发生时间 | 最近一次出现时间 |
|
||||
| 持续时长 | 当前已持续多久 |
|
||||
| 当前状态 | 待处理、处理中、已恢复、已忽略 |
|
||||
| 处理人 | 当前负责人 |
|
||||
| 处理说明 | 处理结果或备注 |
|
||||
|
||||
#### 12.5.4 告警操作建议
|
||||
|
||||
- 查看详情
|
||||
- 指派处理人
|
||||
- 标记处理中
|
||||
- 标记已恢复
|
||||
- 标记忽略
|
||||
- 发起关联补偿
|
||||
- 导出告警
|
||||
|
||||
#### 12.5.5 告警规则建议
|
||||
|
||||
1. 同一节点同一类型短时间内重复触发时,应合并告警,避免刷屏。
|
||||
2. 告警恢复后,应保留恢复时间和恢复方式。
|
||||
3. “已忽略”不代表问题已解决,只代表本次不再继续处理,需记录原因。
|
||||
4. 严重告警应支持通知策略,例如站内通知、短信、企业微信等。
|
||||
|
||||
### 12.6 页面间联动建议
|
||||
|
||||
### 12.7 管理端页面结构建议
|
||||
|
||||
为了便于后续落地为原型页面,建议在云端管理端中单独建设“边缘节点管理”一级菜单,下设以下二级页面:
|
||||
|
||||
- 边缘节点列表
|
||||
- 同步监控
|
||||
- 下发与补偿记录
|
||||
- 告警中心
|
||||
|
||||
页面结构建议如下:
|
||||
|
||||
| 页面 | 定位 | 进入方式 | 典型动作 |
|
||||
|---|---|---|---|
|
||||
| 边缘节点列表 | 看节点总体运行情况 | 一级菜单进入 | 查看详情、筛选节点、查看异常 |
|
||||
| 节点详情 | 看单个食堂节点全貌 | 从列表进入 | 看设备、看同步、看告警、看升级 |
|
||||
| 同步监控 | 看全局同步运行情况 | 一级菜单进入 | 查失败记录、重试、重下发 |
|
||||
| 下发与补偿记录 | 看批次落地和补偿闭环 | 一级菜单进入或从同步失败进入 | 重新下发、生成补偿 |
|
||||
| 告警中心 | 看异常处理闭环 | 一级菜单进入或从节点/同步记录跳转 | 指派、处理、恢复、忽略 |
|
||||
|
||||
建议交互关系:
|
||||
|
||||
1. 运营人员通常从“边缘节点列表”进入,先看是哪个食堂有问题,再进入“节点详情”。
|
||||
2. 如果问题是数据没有上下行,则进入“同步监控”或“下发与补偿记录”。
|
||||
3. 如果问题已经触发告警,则从“告警中心”统一分派和跟踪。
|
||||
4. 节点详情页应作为多个页面的汇聚页,承接设备、升级、同步、告警的下钻查看。
|
||||
|
||||
为了提升管理端的处理效率,建议页面之间保持明确联动:
|
||||
|
||||
- 边缘节点列表可跳转到节点详情、同步记录、告警记录。
|
||||
- 同步监控中的失败记录可直接跳转补偿记录。
|
||||
- 告警中心中的同步类告警可直接关联到同步记录。
|
||||
- 升级失败告警可直接关联到升级记录和设备详情。
|
||||
- 人脸版本落后告警可直接进入对应食堂的人脸重新下发入口。
|
||||
|
||||
## 13. 数据范围与保留策略
|
||||
## 12. 数据范围与保留策略
|
||||
|
||||
| 数据类型 | 边缘节点保留建议 | 云端保留建议 |
|
||||
|---|---|---|
|
||||
@@ -1073,7 +526,7 @@ sequenceDiagram
|
||||
| 视频/图片过程记录 | 按容量和业务规则保留 | 按合规和存储策略保留 |
|
||||
| 升级包 | 保留当前版本和上一版本 | 长期保留可用版本 |
|
||||
|
||||
## 14. 产品验收标准
|
||||
## 13. 产品验收标准
|
||||
|
||||
| 验收项 | 验收标准 |
|
||||
|---|---|
|
||||
@@ -1085,7 +538,7 @@ sequenceDiagram
|
||||
| 数据不重复 | 重复上报或重复下发不会生成重复业务数据 |
|
||||
| 文件可分发 | 升级包可由边缘节点缓存并分发给现场设备 |
|
||||
|
||||
## 15. 待确认问题
|
||||
## 14. 待确认问题
|
||||
|
||||
1. 边缘节点是否允许现场管理员在断网时新增或修改临时菜品、临时用户、临时任务。
|
||||
2. 一个大型食堂是否可能部署多台边缘节点,如果可能,需要定义主备或分片规则。
|
||||
@@ -1096,11 +549,11 @@ sequenceDiagram
|
||||
7. 云端对边缘节点实时状态的要求是秒级、分钟级还是仅异常告警即可。
|
||||
8. 就餐营养分析哪些结果必须本地实时计算,哪些可以等待云端汇总计算。
|
||||
|
||||
## 16. 技术实现附录
|
||||
## 15. 技术实现附录
|
||||
|
||||
以下内容用于辅助研发理解产品方案,不作为产品功能主线。
|
||||
|
||||
### 16.1 MQ 与 HTTP 通信方案
|
||||
### 15.1 MQ 与 HTTP 通信方案
|
||||
|
||||
主链路建议采用 MQ。边缘节点主动连接云端 Broker,可以规避云端无法直接访问食堂内网的问题。
|
||||
|
||||
@@ -1121,7 +574,7 @@ sequenceDiagram
|
||||
- 边缘上行积压时,调用云端 `/sync/push` 批量补传。
|
||||
- 文件下载优先走 HTTP 或对象存储,不走 MQ。
|
||||
|
||||
### 16.2 同步对象分层
|
||||
### 15.2 同步对象分层
|
||||
|
||||
| 层级 | 数据类型 | 同步方向 | 特点 |
|
||||
|---|---|---|---|
|
||||
@@ -1131,7 +584,7 @@ sequenceDiagram
|
||||
| 文件数据 | 人脸图、日志、视频、升级包 | 双向 | MQ 只传元数据,文件走对象存储或分片下载 |
|
||||
| 统计数据 | 营养摄入、余量统计、生产统计 | 边缘可算 + 云端汇总 | 本地用于实时展示,云端用于全局分析 |
|
||||
|
||||
### 16.3 通用消息 Envelope
|
||||
### 15.3 通用消息 Envelope
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -1150,7 +603,7 @@ sequenceDiagram
|
||||
}
|
||||
```
|
||||
|
||||
### 16.4 同步状态
|
||||
### 15.4 同步状态
|
||||
|
||||
| 状态 | 说明 |
|
||||
|---|---|
|
||||
@@ -1161,7 +614,7 @@ sequenceDiagram
|
||||
| DEAD | 多次失败进入死信 |
|
||||
| IGNORED | 已忽略,例如旧版本消息 |
|
||||
|
||||
### 16.5 边缘节点本地数据表方向
|
||||
### 15.5 边缘节点本地数据表方向
|
||||
|
||||
| 表类型 | 示例 |
|
||||
|---|---|
|
||||
|
||||
@@ -1,242 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>营养管理 - 上报故障告警中心 | 健康CQ原型</title>
|
||||
<style>
|
||||
*{box-sizing:border-box;margin:0;padding:0;}
|
||||
body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif;background:#F5F7FA;color:#333;min-width:1280px;}
|
||||
:root{--primary:#1890FF;--primary-light:#E6F7FF;--success:#52C41A;--warning:#FAAD14;--danger:#FF4D4F;--text-primary:#333;--text-secondary:#666;--text-placeholder:#999;--border:#E8E8E8;--font-xs:11px;--font-sm:13px;--font-base:15px;--font-lg:17px;--font-xl:20px;--font-xxl:24px;--radius-sm:4px;--radius-md:8px;--shadow-sm:0 1px 4px rgba(0,0,0,.08);--shadow-md:0 4px 12px rgba(0,0,0,.12);}
|
||||
.top-bar{display:flex;align-items:center;height:56px;background:#001529;padding:0 24px;position:fixed;top:0;left:0;right:0;z-index:100;}
|
||||
.top-bar__logo{display:flex;align-items:center;gap:8px;color:#fff;font-size:var(--font-lg);font-weight:600;white-space:nowrap;width:240px;flex-shrink:0;}
|
||||
.top-bar__logo svg{width:28px;height:28px;}
|
||||
.top-bar__nav{display:flex;align-items:center;gap:4px;flex:1;}
|
||||
.top-bar__nav a{color:rgba(255,255,255,.65);text-decoration:none;padding:6px 14px;border-radius:var(--radius-sm);font-size:var(--font-sm);transition:all .2s;}
|
||||
.top-bar__nav a:hover{color:#fff;background:rgba(255,255,255,.08);}
|
||||
.top-bar__nav a.active{color:#fff;background:var(--primary);}
|
||||
.top-bar__right{display:flex;align-items:center;gap:16px;color:rgba(255,255,255,.65);font-size:var(--font-sm);}
|
||||
.btn-back{color:rgba(255,255,255,.65);text-decoration:none;}
|
||||
.btn-back:hover{color:#fff;}
|
||||
.avatar{width:32px;height:32px;border-radius:50%;background:#1890FF;display:flex;align-items:center;justify-content:center;color:#fff;font-size:var(--font-sm);}
|
||||
.sidebar{position:fixed;top:56px;left:0;bottom:0;width:240px;background:#001529;overflow-y:auto;z-index:90;padding-top:8px;}
|
||||
.sidebar-menu__title{color:rgba(255,255,255,.35);font-size:var(--font-xs);padding:16px 24px 8px;text-transform:uppercase;letter-spacing:.5px;}
|
||||
.sidebar-item{display:flex;align-items:center;gap:8px;padding:10px 24px;color:rgba(255,255,255,.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
|
||||
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,.06);}
|
||||
.sidebar-item.active{color:#fff;background:var(--primary);}
|
||||
.mi{display:inline-block;width:20px;text-align:center;filter:grayscale(1);opacity:.75;margin-right:2px;}
|
||||
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
|
||||
.page-header{display:flex;align-items:center;justify-content:space-between;background:#fff;border-radius:var(--radius-md);padding:18px 24px;margin-bottom:16px;box-shadow:var(--shadow-sm);}
|
||||
.page-header__title{font-size:var(--font-xl);font-weight:700;}
|
||||
.page-header__desc{margin-top:6px;font-size:var(--font-sm);color:var(--text-secondary);}
|
||||
.page-header__actions{display:flex;gap:8px;align-items:center;}
|
||||
.btn{height:36px;padding:0 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;border:none;display:inline-flex;align-items:center;gap:6px;white-space:nowrap;}
|
||||
.btn-primary{background:var(--primary);color:#fff;}
|
||||
.btn-default{background:#fff;color:var(--text-primary);border:1px solid var(--border);}
|
||||
.btn-default:hover{color:var(--primary);border-color:var(--primary);}
|
||||
.stats-grid{display:grid;grid-template-columns:repeat(6,1fr);gap:12px;margin-bottom:16px;}
|
||||
.stat-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);padding:16px 18px;}
|
||||
.stat-card__value{font-size:var(--font-xxl);font-weight:700;line-height:1;}
|
||||
.stat-card__label{font-size:var(--font-sm);color:var(--text-secondary);margin-top:8px;}
|
||||
.layout-grid{display:grid;grid-template-columns:.8fr 1.2fr;gap:16px;margin-bottom:16px;}
|
||||
.card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
|
||||
.card__header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--border);}
|
||||
.card__title{font-size:var(--font-base);font-weight:600;}
|
||||
.card__body{padding:16px 20px;}
|
||||
.category-list{display:grid;grid-template-columns:1fr 1fr;gap:12px;}
|
||||
.category-card{border:1px solid var(--border);border-radius:var(--radius-sm);padding:12px;background:#FAFCFF;}
|
||||
.category-card__name{font-size:var(--font-sm);font-weight:600;}
|
||||
.category-card__count{font-size:var(--font-xl);font-weight:700;margin-top:8px;}
|
||||
.process-list{display:flex;flex-direction:column;gap:12px;}
|
||||
.process-item{display:grid;grid-template-columns:100px 1fr 80px;gap:12px;align-items:center;}
|
||||
.process-label{font-size:var(--font-sm);color:var(--text-secondary);}
|
||||
.process-bar{height:8px;background:#F0F2F5;border-radius:4px;overflow:hidden;}
|
||||
.process-bar span{display:block;height:100%;background:var(--primary);border-radius:4px;}
|
||||
.process-num{font-size:var(--font-sm);font-weight:600;text-align:right;}
|
||||
.filter-bar{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);padding:18px 20px;display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-bottom:16px;}
|
||||
.filter-item{display:flex;align-items:center;gap:8px;}
|
||||
.filter-item label{font-size:var(--font-sm);color:var(--text-secondary);white-space:nowrap;}
|
||||
.filter-input{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);outline:none;min-width:160px;}
|
||||
.filter-select{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);outline:none;min-width:120px;background:#fff;}
|
||||
.table-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
|
||||
.table-card__header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--border);}
|
||||
.table-card__title{font-size:var(--font-base);font-weight:600;}
|
||||
.table-wrap{overflow-x:auto;}
|
||||
.table-wrap table{min-width:1500px;}
|
||||
table{width:100%;border-collapse:collapse;}
|
||||
thead th{background:#FAFAFA;padding:12px 16px;text-align:left;font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);white-space:nowrap;}
|
||||
tbody td{padding:14px 16px;font-size:var(--font-sm);color:var(--text-primary);border-bottom:1px solid var(--border);white-space:nowrap;vertical-align:middle;}
|
||||
tbody tr:hover{background:#F8FBFF;}
|
||||
thead th:last-child,tbody td.td-actions{position:sticky;right:0;background:#fff;box-shadow:-4px 0 8px rgba(0,0,0,.04);z-index:2;}
|
||||
thead th:last-child{background:#FAFAFA;}
|
||||
tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
.tag{display:inline-flex;align-items:center;padding:2px 8px;border-radius:10px;font-size:var(--font-xs);}
|
||||
.tag-green{background:#F6FFED;color:var(--success);}
|
||||
.tag-blue{background:var(--primary-light);color:var(--primary);}
|
||||
.tag-orange{background:#FFF7E6;color:var(--warning);}
|
||||
.tag-red{background:#FFF1F0;color:var(--danger);}
|
||||
.tag-gray{background:#F5F5F5;color:#999;}
|
||||
.link{color:var(--primary);cursor:pointer;font-size:var(--font-sm);text-decoration:none;}
|
||||
.link:hover{color:#40a9ff;}
|
||||
.drawer-mask{position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:1000;display:none;}
|
||||
.drawer-mask.show{display:block;}
|
||||
.drawer{position:absolute;right:0;top:0;bottom:0;width:560px;background:#fff;box-shadow:-6px 0 24px rgba(0,0,0,.12);display:flex;flex-direction:column;}
|
||||
.drawer__header{display:flex;align-items:center;justify-content:space-between;padding:18px 24px;border-bottom:1px solid var(--border);}
|
||||
.drawer__title{font-size:var(--font-lg);font-weight:600;}
|
||||
.drawer__close{border:none;background:none;font-size:22px;cursor:pointer;color:var(--text-placeholder);}
|
||||
.drawer__body{padding:20px 24px;overflow-y:auto;flex:1;}
|
||||
.drawer-section{margin-bottom:20px;}
|
||||
.drawer-section__title{font-size:var(--font-sm);font-weight:600;margin-bottom:10px;padding-left:8px;border-left:3px solid var(--primary);}
|
||||
.detail-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px 18px;}
|
||||
.detail-item{display:flex;flex-direction:column;gap:3px;}
|
||||
.detail-label{font-size:var(--font-xs);color:var(--text-placeholder);}
|
||||
.detail-value{font-size:var(--font-sm);color:var(--text-primary);}
|
||||
.timeline{display:flex;flex-direction:column;gap:12px;}
|
||||
.timeline-item{border-left:2px solid var(--primary-light);padding-left:12px;font-size:var(--font-sm);}
|
||||
.timeline-time{color:var(--text-placeholder);font-size:var(--font-xs);margin-bottom:4px;}
|
||||
.toast{position:fixed;top:76px;left:50%;transform:translateX(-50%) translateY(-12px);background:#fff;border-left:4px solid var(--primary);box-shadow:var(--shadow-md);padding:10px 18px;border-radius:var(--radius-sm);font-size:var(--font-sm);opacity:0;pointer-events:none;transition:all .25s;z-index:1200;}
|
||||
.toast.show{opacity:1;transform:translateX(-50%) translateY(0);}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header class="top-bar">
|
||||
<div class="top-bar__logo"><svg viewBox="0 0 28 28" fill="none"><rect width="28" height="28" rx="6" fill="#1890FF"/><path d="M7 14h14M14 7v14" stroke="#fff" stroke-width="2.5" stroke-linecap="round"/></svg>营养管理</div>
|
||||
<nav class="top-bar__nav"><a href="trace-query.html">溯源总览</a><a href="arc-employee.html">档案</a><a href="farm-land.html">绿色种采</a><a href="sup-hygiene-check.html">卫生供应</a><a href="prod-qc-daily.html">健康生产</a><a href="din-dict-mgmt.html">营养用餐</a><a href="ano-dirty.html">异常事件</a><a href="edge-node-list.html" class="active">边缘服务器</a></nav>
|
||||
<div class="top-bar__right"><a href="../../index.html" class="btn-back">← 返回首页</a><div class="avatar">管</div><span>系统管理员 ▾</span></div>
|
||||
</header>
|
||||
|
||||
<aside class="sidebar">
|
||||
<div class="sidebar-menu__title">边缘服务器</div>
|
||||
<a href="edge-alarm-center.html" class="sidebar-item active"><span class="mi">⚠</span>告警中心</a>
|
||||
<a href="edge-sync-monitor.html" class="sidebar-item"><span class="mi">🔄</span>同步监控</a>
|
||||
<a href="edge-dispatch-compensation.html" class="sidebar-item"><span class="mi">📦</span>下发与补偿</a>
|
||||
<a href="edge-node-list.html" class="sidebar-item"><span class="mi">🧭</span>边缘服务器列表</a>
|
||||
</aside>
|
||||
|
||||
<main class="main">
|
||||
<section class="page-header">
|
||||
<div><div class="page-header__title">上报故障告警中心</div><div class="page-header__desc">默认故障入口,集中识别食堂上报故障、服务器离线、数据断流和恢复异常,形成分派、跟踪、恢复闭环。</div></div>
|
||||
<div class="page-header__actions"><button class="btn btn-default" onclick="showToast('正在导出告警')">导出告警</button><button class="btn btn-primary" onclick="showToast('已刷新告警状态')">刷新状态</button></div>
|
||||
</section>
|
||||
|
||||
<section class="stats-grid">
|
||||
<div class="stat-card"><div class="stat-card__value">6</div><div class="stat-card__label">故障食堂数</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value" style="color:var(--danger)">3</div><div class="stat-card__label">数据断流食堂</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value" style="color:var(--danger)">8</div><div class="stat-card__label">严重故障</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value" style="color:var(--warning)">19</div><div class="stat-card__label">待处理故障</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value">7</div><div class="stat-card__label">待指派</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value" style="color:var(--success)">36</div><div class="stat-card__label">今日已恢复</div></div>
|
||||
</section>
|
||||
|
||||
<section class="layout-grid">
|
||||
<div class="card">
|
||||
<div class="card__header"><div class="card__title">故障分类</div><span class="tag tag-orange">未恢复</span></div>
|
||||
<div class="card__body">
|
||||
<div class="category-list">
|
||||
<div class="category-card"><div class="category-card__name">服务器类</div><div class="category-card__count" style="color:var(--danger)">7</div></div>
|
||||
<div class="category-card"><div class="category-card__name">上报连接类</div><div class="category-card__count" style="color:var(--warning)">5</div></div>
|
||||
<div class="category-card"><div class="category-card__name">设备类</div><div class="category-card__count">9</div></div>
|
||||
<div class="category-card"><div class="category-card__name">上报同步类</div><div class="category-card__count" style="color:var(--warning)">13</div></div>
|
||||
<div class="category-card"><div class="category-card__name">上报数据类</div><div class="category-card__count">4</div></div>
|
||||
<div class="category-card"><div class="category-card__name">升级类</div><div class="category-card__count" style="color:var(--danger)">4</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card__header"><div class="card__title">处理状态分布</div><span class="tag tag-blue">当前周期</span></div>
|
||||
<div class="card__body">
|
||||
<div class="process-list">
|
||||
<div class="process-item"><div class="process-label">待处理</div><div class="process-bar"><span style="width:45%;background:var(--warning)"></span></div><div class="process-num">19</div></div>
|
||||
<div class="process-item"><div class="process-label">处理中</div><div class="process-bar"><span style="width:33%;background:var(--primary)"></span></div><div class="process-num">14</div></div>
|
||||
<div class="process-item"><div class="process-label">已恢复</div><div class="process-bar"><span style="width:86%;background:var(--success)"></span></div><div class="process-num">36</div></div>
|
||||
<div class="process-item"><div class="process-label">已忽略</div><div class="process-bar"><span style="width:12%;background:#999"></span></div><div class="process-num">5</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="filter-bar">
|
||||
<div class="filter-item"><label>告警类型</label><select class="filter-select"><option>全部</option><option>服务器类</option><option>连接类</option><option>设备类</option><option>同步类</option><option>数据类</option><option>升级类</option></select></div>
|
||||
<div class="filter-item"><label>告警等级</label><select class="filter-select"><option>全部</option><option>提醒</option><option>警告</option><option>严重</option></select></div>
|
||||
<div class="filter-item"><label>所属食堂</label><select class="filter-select"><option>全部</option><option>采气一厂食堂</option><option>炼化中心食堂</option></select></div>
|
||||
<div class="filter-item"><label>当前状态</label><select class="filter-select"><option>全部</option><option>待处理</option><option>处理中</option><option>已恢复</option><option>已忽略</option></select></div>
|
||||
<div class="filter-item"><label>关键字</label><input class="filter-input" placeholder="告警编号 / 服务器 / 对象"></div>
|
||||
<button class="btn btn-primary" onclick="showToast('已查询告警')">查询</button>
|
||||
<button class="btn btn-default" onclick="showToast('已重置筛选条件')">重置</button>
|
||||
</section>
|
||||
|
||||
<section class="table-card">
|
||||
<div class="table-card__header"><div class="table-card__title">告警列表</div><button class="btn btn-default" onclick="showToast('已批量标记处理中')">批量标记处理中</button></div>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead><tr><th>告警编号</th><th>告警类型</th><th>故障现象</th><th>等级</th><th>所属食堂</th><th>所属服务器</th><th>受影响业务</th><th>首次发生</th><th>最近发生</th><th>持续时长</th><th>当前状态</th><th>处理人</th><th>操作</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>ALM-20260623-081</td><td>服务器类</td><td>服务器离线</td><td><span class="tag tag-red">严重</span></td><td>炼化中心食堂</td><td>炼化中心餐线服务器</td><td>全部现场设备</td><td>2026-06-23 13:54:09</td><td>2026-06-23 14:32:00</td><td>38 分钟</td><td><span class="tag tag-orange">待处理</span></td><td>未指派</td><td class="td-actions"><a class="link" onclick="openDrawer('ALM-20260623-081')">详情</a> <a class="link" href="edge-sync-monitor.html">查看链路</a> <a class="link" href="edge-node-detail.html">服务器详情</a></td></tr>
|
||||
<tr><td>ALM-20260623-078</td><td>同步类</td><td>上行积压超 30 分钟</td><td><span class="tag tag-orange">警告</span></td><td>采气一厂食堂</td><td>采气一厂后厨服务器</td><td>就餐流水 / 称重记录</td><td>2026-06-23 13:45:00</td><td>2026-06-23 14:28:00</td><td>47 分钟</td><td><span class="tag tag-blue">处理中</span></td><td>运维张工</td><td class="td-actions"><a class="link" onclick="openDrawer('ALM-20260623-078')">详情</a> <a class="link" href="edge-sync-monitor.html">查看链路</a> <a class="link" onclick="showToast('已标记恢复')">标记恢复</a></td></tr>
|
||||
<tr><td>ALM-20260623-072</td><td>数据类</td><td>人脸版本落后</td><td><span class="tag tag-orange">警告</span></td><td>采气一厂食堂</td><td>采气一厂后厨服务器</td><td>FACE-BATCH-0623-04</td><td>2026-06-23 10:12:00</td><td>2026-06-23 14:20:00</td><td>4 小时</td><td><span class="tag tag-orange">待处理</span></td><td>未指派</td><td class="td-actions"><a class="link" onclick="openDrawer('ALM-20260623-072')">详情</a> <a class="link" href="edge-dispatch-compensation.html">发起补偿</a> <a class="link" onclick="ignoreAlarm()">忽略</a></td></tr>
|
||||
<tr><td>ALM-20260623-066</td><td>升级类</td><td>设备未执行升级</td><td><span class="tag tag-red">严重</span></td><td>炼化中心食堂</td><td>炼化中心餐线服务器</td><td>餐线营养秤 02</td><td>2026-06-23 11:10:00</td><td>2026-06-23 14:00:00</td><td>2 小时 50 分钟</td><td><span class="tag tag-orange">待处理</span></td><td>未指派</td><td class="td-actions"><a class="link" onclick="openDrawer('ALM-20260623-066')">详情</a> <a class="link" href="edge-dispatch-compensation.html">关联补偿</a></td></tr>
|
||||
<tr><td>ALM-20260623-049</td><td>设备类</td><td>设备离线</td><td><span class="tag tag-orange">警告</span></td><td>输气站小食堂</td><td>输气站小食堂服务器</td><td>结算终端 01</td><td>2026-06-23 09:28:00</td><td>2026-06-23 09:48:00</td><td>20 分钟</td><td><span class="tag tag-green">已恢复</span></td><td>运维李工</td><td class="td-actions"><a class="link" onclick="openDrawer('ALM-20260623-049')">详情</a></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<div class="drawer-mask" id="drawerMask">
|
||||
<div class="drawer">
|
||||
<div class="drawer__header"><div class="drawer__title">告警详情</div><button class="drawer__close" onclick="closeDrawer()">×</button></div>
|
||||
<div class="drawer__body">
|
||||
<div class="drawer-section">
|
||||
<div class="drawer-section__title">基础信息</div>
|
||||
<div class="detail-grid">
|
||||
<div class="detail-item"><div class="detail-label">告警编号</div><div class="detail-value" id="alarmNo">ALM-20260623-081</div></div>
|
||||
<div class="detail-item"><div class="detail-label">告警等级</div><div class="detail-value"><span class="tag tag-red">严重</span></div></div>
|
||||
<div class="detail-item"><div class="detail-label">所属食堂</div><div class="detail-value">炼化中心食堂</div></div>
|
||||
<div class="detail-item"><div class="detail-label">所属服务器</div><div class="detail-value">炼化中心餐线服务器</div></div>
|
||||
<div class="detail-item"><div class="detail-label">受影响对象</div><div class="detail-value">全部现场设备</div></div>
|
||||
<div class="detail-item"><div class="detail-label">当前状态</div><div class="detail-value"><span class="tag tag-orange">待处理</span></div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="drawer-section">
|
||||
<div class="drawer-section__title">处理轨迹</div>
|
||||
<div class="timeline">
|
||||
<div class="timeline-item"><div class="timeline-time">2026-06-23 14:32:00</div><div>系统合并同类告警,持续时间更新为 38 分钟。</div></div>
|
||||
<div class="timeline-item"><div class="timeline-time">2026-06-23 14:10:00</div><div>自动重连检测失败,等待人工介入。</div></div>
|
||||
<div class="timeline-item"><div class="timeline-time">2026-06-23 13:54:09</div><div>服务器心跳超阈值未上报,触发严重告警。</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="drawer-section">
|
||||
<div class="drawer-section__title">处理操作</div>
|
||||
<div style="display:flex;gap:8px;flex-wrap:wrap">
|
||||
<button class="btn btn-primary" onclick="showToast('已标记处理中')">标记处理中</button>
|
||||
<button class="btn btn-default" onclick="showToast('已指派处理人')">指派处理人</button>
|
||||
<button class="btn btn-default" onclick="showToast('已标记恢复')">标记已恢复</button>
|
||||
<button class="btn btn-default" onclick="location.href='edge-dispatch-compensation.html'">发起关联补偿</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="toast" id="toast">操作已完成</div>
|
||||
<script>
|
||||
function showToast(text){
|
||||
var toast=document.getElementById('toast');
|
||||
toast.textContent=text;
|
||||
toast.classList.add('show');
|
||||
setTimeout(function(){toast.classList.remove('show');},1800);
|
||||
}
|
||||
function openDrawer(no){
|
||||
document.getElementById('alarmNo').textContent=no;
|
||||
document.getElementById('drawerMask').classList.add('show');
|
||||
}
|
||||
function closeDrawer(){
|
||||
document.getElementById('drawerMask').classList.remove('show');
|
||||
}
|
||||
function ignoreAlarm(){
|
||||
if(window.confirm('确认忽略该告警?忽略不代表问题已解决,需要记录原因。')){showToast('告警已标记忽略');}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,531 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>营养管理 - 异常告警中心 | 健康CQ原型</title>
|
||||
<style>
|
||||
*{box-sizing:border-box;margin:0;padding:0;}
|
||||
body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif;background:#F5F7FA;color:#333;min-width:1280px;}
|
||||
:root{--primary:#1890FF;--primary-light:#E6F7FF;--success:#52C41A;--warning:#FAAD14;--danger:#FF4D4F;--text-primary:#333;--text-secondary:#666;--text-placeholder:#999;--border:#E8E8E8;--font-xs:11px;--font-sm:13px;--font-base:15px;--font-lg:17px;--font-xl:20px;--font-xxl:24px;--radius-sm:4px;--radius-md:8px;--radius-lg:12px;--shadow-sm:0 1px 4px rgba(0,0,0,.08);--shadow-md:0 4px 12px rgba(0,0,0,.12);}
|
||||
.top-bar{display:flex;align-items:center;height:56px;background:#001529;padding:0 24px;position:fixed;top:0;left:0;right:0;z-index:100;}
|
||||
.top-bar__logo{display:flex;align-items:center;gap:8px;color:#fff;font-size:var(--font-lg);font-weight:600;white-space:nowrap;width:240px;flex-shrink:0;}
|
||||
.top-bar__logo svg{width:28px;height:28px;}
|
||||
.top-bar__nav{display:flex;align-items:center;gap:4px;flex:1;}
|
||||
.top-bar__nav a{color:rgba(255,255,255,.65);text-decoration:none;padding:6px 14px;border-radius:var(--radius-sm);font-size:var(--font-sm);transition:all .2s;}
|
||||
.top-bar__nav a:hover{color:#fff;background:rgba(255,255,255,.08);}
|
||||
.top-bar__nav a.active{color:#fff;background:var(--primary);}
|
||||
.top-bar__right{display:flex;align-items:center;gap:16px;color:rgba(255,255,255,.65);font-size:var(--font-sm);}
|
||||
.btn-back{color:rgba(255,255,255,.65);text-decoration:none;}
|
||||
.btn-back:hover{color:#fff;}
|
||||
.avatar{width:32px;height:32px;border-radius:50%;background:#1890FF;display:flex;align-items:center;justify-content:center;color:#fff;font-size:var(--font-sm);}
|
||||
.sidebar{position:fixed;top:56px;left:0;bottom:0;width:240px;background:#001529;overflow-y:auto;z-index:90;padding-top:8px;}
|
||||
.sidebar-menu__title{color:rgba(255,255,255,.35);font-size:var(--font-xs);padding:16px 24px 8px;text-transform:uppercase;letter-spacing:.5px;}
|
||||
.sidebar-item{display:flex;align-items:center;gap:8px;padding:10px 24px;color:rgba(255,255,255,.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
|
||||
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,.06);}
|
||||
.sidebar-item.active{color:#fff;background:var(--primary);}
|
||||
.mi{display:inline-block;width:20px;text-align:center;filter:grayscale(1);opacity:.75;margin-right:2px;}
|
||||
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
|
||||
.page-header{display:flex;align-items:center;justify-content:space-between;background:#fff;border-radius:var(--radius-md);padding:18px 24px;margin-bottom:16px;box-shadow:var(--shadow-sm);}
|
||||
.page-header__title{font-size:var(--font-xl);font-weight:700;}
|
||||
.page-header__desc{margin-top:6px;font-size:var(--font-sm);color:var(--text-secondary);}
|
||||
.page-header__actions{display:flex;gap:8px;align-items:center;}
|
||||
.btn{height:36px;padding:0 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;border:none;display:inline-flex;align-items:center;gap:6px;white-space:nowrap;}
|
||||
.btn-primary{background:var(--primary);color:#fff;}
|
||||
.btn-default{background:#fff;color:var(--text-primary);border:1px solid var(--border);}
|
||||
.btn-default:hover{color:var(--primary);border-color:var(--primary);}
|
||||
.stats-grid{display:grid;grid-template-columns:repeat(6,1fr);gap:12px;margin-bottom:16px;}
|
||||
.stat-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);padding:16px 18px;}
|
||||
.stat-card__value{font-size:var(--font-xxl);font-weight:700;line-height:1;}
|
||||
.stat-card__label{font-size:var(--font-sm);color:var(--text-secondary);margin-top:8px;}
|
||||
.stat-card__hint{font-size:var(--font-xs);color:var(--text-placeholder);margin-top:8px;}
|
||||
.summary-grid{display:grid;grid-template-columns:1.1fr .9fr;gap:16px;margin-bottom:16px;}
|
||||
.card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
|
||||
.card__header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--border);}
|
||||
.card__title{font-size:var(--font-base);font-weight:600;}
|
||||
.card__body{padding:16px 20px;}
|
||||
.type-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:12px;}
|
||||
.type-card{border:1px solid var(--border);border-radius:var(--radius-sm);padding:14px;background:#FAFCFF;}
|
||||
.type-card__name{font-size:var(--font-sm);font-weight:600;}
|
||||
.type-card__count{font-size:var(--font-xl);font-weight:700;margin-top:8px;}
|
||||
.type-card__desc{font-size:var(--font-xs);color:var(--text-secondary);margin-top:6px;line-height:1.6;}
|
||||
.progress-list{display:flex;flex-direction:column;gap:12px;}
|
||||
.progress-item{display:grid;grid-template-columns:92px 1fr 76px;gap:10px;align-items:center;}
|
||||
.progress-label{font-size:var(--font-sm);color:var(--text-secondary);}
|
||||
.progress-bar{height:8px;background:#F0F2F5;border-radius:4px;overflow:hidden;}
|
||||
.progress-bar span{display:block;height:100%;border-radius:4px;background:var(--primary);}
|
||||
.progress-num{font-size:var(--font-sm);font-weight:600;text-align:right;}
|
||||
.filter-bar{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);padding:18px 20px;display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-bottom:16px;}
|
||||
.filter-item{display:flex;align-items:center;gap:8px;}
|
||||
.filter-item label{font-size:var(--font-sm);color:var(--text-secondary);white-space:nowrap;}
|
||||
.filter-input,.filter-select{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);outline:none;background:#fff;}
|
||||
.filter-input{min-width:170px;}
|
||||
.filter-select{min-width:126px;}
|
||||
.table-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
|
||||
.table-card__header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--border);}
|
||||
.table-card__title{font-size:var(--font-base);font-weight:600;}
|
||||
.table-wrap{overflow-x:auto;}
|
||||
.table-wrap table{min-width:1560px;}
|
||||
table{width:100%;border-collapse:collapse;}
|
||||
thead th{background:#FAFAFA;padding:12px 16px;text-align:left;font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);white-space:nowrap;}
|
||||
tbody td{padding:14px 16px;font-size:var(--font-sm);color:var(--text-primary);border-bottom:1px solid var(--border);white-space:nowrap;vertical-align:middle;}
|
||||
tbody tr:hover{background:#F8FBFF;}
|
||||
thead th:last-child,tbody td.td-actions{position:sticky;right:0;background:#fff;box-shadow:-4px 0 8px rgba(0,0,0,.04);z-index:2;}
|
||||
thead th:last-child{background:#FAFAFA;}
|
||||
tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
.tag{display:inline-flex;align-items:center;padding:2px 8px;border-radius:10px;font-size:var(--font-xs);}
|
||||
.tag-green{background:#F6FFED;color:var(--success);}
|
||||
.tag-blue{background:var(--primary-light);color:var(--primary);}
|
||||
.tag-orange{background:#FFF7E6;color:var(--warning);}
|
||||
.tag-red{background:#FFF1F0;color:var(--danger);}
|
||||
.tag-gray{background:#F5F5F5;color:#999;}
|
||||
.link{color:var(--primary);cursor:pointer;font-size:var(--font-sm);text-decoration:none;}
|
||||
.link:hover{color:#40a9ff;}
|
||||
.drawer-mask{position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:1000;display:none;}
|
||||
.drawer-mask.show{display:block;}
|
||||
.drawer{position:absolute;right:0;top:0;bottom:0;width:580px;background:#fff;box-shadow:-6px 0 24px rgba(0,0,0,.12);display:flex;flex-direction:column;}
|
||||
.drawer__header{display:flex;align-items:center;justify-content:space-between;padding:18px 24px;border-bottom:1px solid var(--border);}
|
||||
.drawer__title{font-size:var(--font-lg);font-weight:600;}
|
||||
.drawer__close{border:none;background:none;font-size:22px;cursor:pointer;color:var(--text-placeholder);}
|
||||
.drawer__body{padding:20px 24px;overflow-y:auto;flex:1;}
|
||||
.drawer-section{margin-bottom:20px;}
|
||||
.drawer-section__title{font-size:var(--font-sm);font-weight:600;margin-bottom:10px;padding-left:8px;border-left:3px solid var(--primary);}
|
||||
.detail-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px 18px;}
|
||||
.detail-item{display:flex;flex-direction:column;gap:3px;}
|
||||
.detail-label{font-size:var(--font-xs);color:var(--text-placeholder);}
|
||||
.detail-value{font-size:var(--font-sm);color:var(--text-primary);line-height:1.6;}
|
||||
.detail-table{width:100%;border-collapse:collapse;border:1px solid var(--border);border-radius:var(--radius-sm);overflow:hidden;}
|
||||
.detail-table td{padding:10px 12px;border-bottom:1px solid var(--border);font-size:var(--font-sm);vertical-align:top;}
|
||||
.detail-table tr:last-child td{border-bottom:none;}
|
||||
.detail-table td:first-child{width:120px;background:#FAFAFA;color:var(--text-secondary);}
|
||||
.timeline{display:flex;flex-direction:column;gap:12px;}
|
||||
.timeline-item{border-left:2px solid var(--primary-light);padding-left:12px;font-size:var(--font-sm);}
|
||||
.timeline-time{color:var(--text-placeholder);font-size:var(--font-xs);margin-bottom:4px;}
|
||||
.action-row{display:flex;gap:8px;flex-wrap:wrap;}
|
||||
.toast{position:fixed;top:76px;left:50%;transform:translateX(-50%) translateY(-12px);background:#fff;border-left:4px solid var(--primary);box-shadow:var(--shadow-md);padding:10px 18px;border-radius:var(--radius-sm);font-size:var(--font-sm);opacity:0;pointer-events:none;transition:all .25s;z-index:1200;}
|
||||
.toast.show{opacity:1;transform:translateX(-50%) translateY(0);}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header class="top-bar">
|
||||
<div class="top-bar__logo"><svg viewBox="0 0 28 28" fill="none"><rect width="28" height="28" rx="6" fill="#1890FF"/><path d="M7 14h14M14 7v14" stroke="#fff" stroke-width="2.5" stroke-linecap="round"/></svg>营养管理</div>
|
||||
<nav class="top-bar__nav"><a href="trace-query.html">溯源总览</a><a href="arc-employee.html">档案</a><a href="farm-land.html">绿色种采</a><a href="sup-hygiene-check.html">卫生供应</a><a href="prod-qc-daily.html">健康生产</a><a href="din-dict-mgmt.html">营养用餐</a><a href="ano-dirty.html">异常事件</a><a href="edge-server-management.html" class="active">边缘服务器</a></nav>
|
||||
<div class="top-bar__right"><a href="../../index.html" class="btn-back">← 返回首页</a><div class="avatar">管</div><span>系统管理员 ▾</span></div>
|
||||
</header>
|
||||
|
||||
<aside class="sidebar">
|
||||
<div class="sidebar-menu__title">边缘服务器</div>
|
||||
<a href="edge-server-management.html" class="sidebar-item"><span class="mi">🖥</span>边缘服务器管理</a>
|
||||
<a href="edge-report-management.html" class="sidebar-item"><span class="mi">📗</span>数据上报管理</a>
|
||||
<a href="edge-dispatch-management.html" class="sidebar-item"><span class="mi">📝</span>数据下发管理</a>
|
||||
<a href="edge-alert-center.html" class="sidebar-item active"><span class="mi">⚠</span>异常告警中心</a>
|
||||
</aside>
|
||||
|
||||
<main class="main">
|
||||
<section class="page-header">
|
||||
<div>
|
||||
<div class="page-header__title">异常告警中心</div>
|
||||
<div class="page-header__desc">统一收拢数据上报失败、数据下发失败、服务器异常、设备异常等边缘服务器运行告警,并提供差异化详情查看入口。</div>
|
||||
</div>
|
||||
<div class="page-header__actions">
|
||||
<button class="btn btn-default" onclick="showToast('正在导出异常告警')">导出告警</button>
|
||||
<button class="btn btn-primary" onclick="showToast('已刷新最新告警状态')">刷新状态</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="stats-grid">
|
||||
<div class="stat-card"><div class="stat-card__value">48</div><div class="stat-card__label">当日告警总数</div><div class="stat-card__hint">已去重的事件级告警</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value" style="color:var(--danger)">6</div><div class="stat-card__label">严重告警</div><div class="stat-card__hint">需人工优先介入</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value" style="color:var(--warning)">14</div><div class="stat-card__label">待处理</div><div class="stat-card__hint">尚未指派或未恢复</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value" style="color:var(--warning)">9</div><div class="stat-card__label">处理中</div><div class="stat-card__hint">已进入恢复跟踪</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value" style="color:var(--success)">25</div><div class="stat-card__label">今日已恢复</div><div class="stat-card__hint">含自动恢复与人工恢复</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value">4</div><div class="stat-card__label">高风险服务器</div><div class="stat-card__hint">同日多类告警叠加</div></div>
|
||||
</section>
|
||||
|
||||
<section class="summary-grid">
|
||||
<div class="card">
|
||||
<div class="card__header"><div class="card__title">告警类型分布</div><span class="tag tag-orange">今日累计</span></div>
|
||||
<div class="card__body">
|
||||
<div class="type-grid">
|
||||
<div class="type-card"><div class="type-card__name">上报失败告警</div><div class="type-card__count" style="color:var(--warning)">17</div><div class="type-card__desc">重点关注消息超时、重试耗尽、数据堆积。</div></div>
|
||||
<div class="type-card"><div class="type-card__name">下发失败告警</div><div class="type-card__count" style="color:var(--danger)">8</div><div class="type-card__desc">涉及配置、升级、增量包接收失败与应用失败。</div></div>
|
||||
<div class="type-card"><div class="type-card__name">服务器异常告警</div><div class="type-card__count" style="color:var(--danger)">11</div><div class="type-card__desc">覆盖离线、磁盘、服务进程、网络链路异常。</div></div>
|
||||
<div class="type-card"><div class="type-card__name">设备异常告警</div><div class="type-card__count">12</div><div class="type-card__desc">覆盖终端离线、版本滞后、称重设备不可用等。</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card__header"><div class="card__title">处理状态分布</div><span class="tag tag-blue">当前周期</span></div>
|
||||
<div class="card__body">
|
||||
<div class="progress-list">
|
||||
<div class="progress-item"><div class="progress-label">待处理</div><div class="progress-bar"><span style="width:42%;background:var(--warning)"></span></div><div class="progress-num">14</div></div>
|
||||
<div class="progress-item"><div class="progress-label">处理中</div><div class="progress-bar"><span style="width:28%;background:var(--primary)"></span></div><div class="progress-num">9</div></div>
|
||||
<div class="progress-item"><div class="progress-label">已恢复</div><div class="progress-bar"><span style="width:74%;background:var(--success)"></span></div><div class="progress-num">25</div></div>
|
||||
<div class="progress-item"><div class="progress-label">已忽略</div><div class="progress-bar"><span style="width:10%;background:#999"></span></div><div class="progress-num">3</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="filter-bar">
|
||||
<div class="filter-item"><label>告警类型</label><select class="filter-select"><option>全部</option><option>上报失败告警</option><option>下发失败告警</option><option>服务器异常告警</option><option>设备异常告警</option></select></div>
|
||||
<div class="filter-item"><label>告警等级</label><select class="filter-select"><option>全部</option><option>提醒</option><option>警告</option><option>严重</option></select></div>
|
||||
<div class="filter-item"><label>当前状态</label><select class="filter-select"><option>全部</option><option>待处理</option><option>处理中</option><option>已恢复</option><option>已忽略</option></select></div>
|
||||
<div class="filter-item"><label>所属食堂</label><select class="filter-select"><option>全部</option><option>采气一厂食堂</option><option>炼化中心食堂</option><option>总部一号食堂</option></select></div>
|
||||
<div class="filter-item"><label>发生日期</label><input class="filter-input" type="date"></div>
|
||||
<div class="filter-item"><label>关键字</label><input class="filter-input" placeholder="告警编号 / 服务器 / 设备"></div>
|
||||
<button class="btn btn-primary" onclick="showToast('已按条件刷新告警列表')">查询</button>
|
||||
<button class="btn btn-default" onclick="showToast('已重置筛选条件')">重置</button>
|
||||
</section>
|
||||
|
||||
<section class="table-card">
|
||||
<div class="table-card__header"><div class="table-card__title">边缘服务器异常告警列表</div><button class="btn btn-default" onclick="showToast('已批量标记为处理中')">批量标记处理中</button></div>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead><tr><th>告警编号</th><th>告警类型</th><th>告警主题</th><th>等级</th><th>所属食堂</th><th>边缘服务器</th><th>影响对象</th><th>影响业务</th><th>首次发生</th><th>最近发生</th><th>当前状态</th><th>处理人</th><th>操作</th></tr></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>ALM-20260624-078</td>
|
||||
<td>上报失败告警</td>
|
||||
<td>就餐流水重试超阈值</td>
|
||||
<td><span class="tag tag-orange">警告</span></td>
|
||||
<td>采气一厂食堂</td>
|
||||
<td><a class="link" href="edge-server-detail.html">采气一厂后厨服务器</a></td>
|
||||
<td>MEAL-20260624-0178</td>
|
||||
<td><a class="link" href="edge-report-management.html">数据上报</a></td>
|
||||
<td>2026-06-24 08:45:00</td>
|
||||
<td>2026-06-24 09:18:33</td>
|
||||
<td><span class="tag tag-blue">处理中</span></td>
|
||||
<td>运维张工</td>
|
||||
<td class="td-actions"><a class="link" onclick="openDrawer('reportFail')">查看详情</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ALM-20260624-083</td>
|
||||
<td>下发失败告警</td>
|
||||
<td>升级包应用超时</td>
|
||||
<td><span class="tag tag-red">严重</span></td>
|
||||
<td>炼化中心食堂</td>
|
||||
<td><a class="link" href="edge-server-detail.html">炼化中心餐线服务器</a></td>
|
||||
<td>UPG-20260624-002</td>
|
||||
<td><a class="link" href="edge-dispatch-management.html">数据下发</a></td>
|
||||
<td>2026-06-24 11:06:12</td>
|
||||
<td>2026-06-24 11:18:20</td>
|
||||
<td><span class="tag tag-orange">待处理</span></td>
|
||||
<td>未指派</td>
|
||||
<td class="td-actions"><a class="link" onclick="openDrawer('dispatchFail')">查看详情</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ALM-20260624-091</td>
|
||||
<td>服务器异常告警</td>
|
||||
<td>边缘服务器离线</td>
|
||||
<td><span class="tag tag-red">严重</span></td>
|
||||
<td>炼化中心食堂</td>
|
||||
<td><a class="link" href="edge-server-detail.html">炼化中心餐线服务器</a></td>
|
||||
<td>EDGE-CQ-024</td>
|
||||
<td>接入、上报、下发全链路</td>
|
||||
<td>2026-06-24 08:54:09</td>
|
||||
<td>2026-06-24 09:21:20</td>
|
||||
<td><span class="tag tag-orange">待处理</span></td>
|
||||
<td>未指派</td>
|
||||
<td class="td-actions"><a class="link" onclick="openDrawer('serverError')">查看详情</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ALM-20260624-066</td>
|
||||
<td>设备异常告警</td>
|
||||
<td>餐线营养秤离线</td>
|
||||
<td><span class="tag tag-orange">警告</span></td>
|
||||
<td>采气一厂食堂</td>
|
||||
<td><a class="link" href="edge-server-detail.html">采气一厂后厨服务器</a></td>
|
||||
<td>LINE-SCALE-002</td>
|
||||
<td>称重采集、就餐校验</td>
|
||||
<td>2026-06-24 08:42:00</td>
|
||||
<td>2026-06-24 09:12:00</td>
|
||||
<td><span class="tag tag-orange">待处理</span></td>
|
||||
<td>运维李工</td>
|
||||
<td class="td-actions"><a class="link" onclick="openDrawer('deviceError')">查看详情</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ALM-20260624-044</td>
|
||||
<td>设备异常告警</td>
|
||||
<td>人脸终端增量包滞后</td>
|
||||
<td><span class="tag tag-green">提醒</span></td>
|
||||
<td>总部一号食堂</td>
|
||||
<td><a class="link" href="edge-server-detail.html">总部一号食堂服务器</a></td>
|
||||
<td>FACE-TERM-003</td>
|
||||
<td>人脸识别、就餐放行</td>
|
||||
<td>2026-06-24 07:48:12</td>
|
||||
<td>2026-06-24 08:01:30</td>
|
||||
<td><span class="tag tag-green">已恢复</span></td>
|
||||
<td>平台自动恢复</td>
|
||||
<td class="td-actions"><a class="link" onclick="openDrawer('deviceRecovered')">查看详情</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<div class="drawer-mask" id="drawerMask">
|
||||
<div class="drawer">
|
||||
<div class="drawer__header"><div class="drawer__title" id="drawerTitle">告警详情</div><button class="drawer__close" onclick="closeDrawer()">×</button></div>
|
||||
<div class="drawer__body" id="drawerBody"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="toast" id="toast">操作已完成</div>
|
||||
<script>
|
||||
var alarmDetails={
|
||||
reportFail:{
|
||||
title:'上报失败告警详情',
|
||||
basic:[
|
||||
['告警编号','ALM-20260624-078'],
|
||||
['告警类型','上报失败告警'],
|
||||
['告警等级','警告'],
|
||||
['所属食堂','采气一厂食堂'],
|
||||
['边缘服务器','采气一厂后厨服务器'],
|
||||
['当前状态','处理中'],
|
||||
['关联业务单号','MEAL-20260624-0178'],
|
||||
['处理人','运维张工']
|
||||
],
|
||||
sections:[
|
||||
{title:'上报上下文',type:'table',rows:[
|
||||
['数据类型','就餐流水'],
|
||||
['来源设备','结算终端 01'],
|
||||
['首次失败时间','2026-06-24 08:45:00'],
|
||||
['最近重试时间','2026-06-24 09:18:33'],
|
||||
['失败原因','MQ 连接超时,备用通道切换失败'],
|
||||
['重试情况','已自动重试 3 次,仍未成功']
|
||||
]},
|
||||
{title:'影响范围',type:'table',rows:[
|
||||
['影响服务器','1 台'],
|
||||
['影响终端','结算终端 01、后厨服务 1 条链路'],
|
||||
['影响业务','就餐流水未上送中心,营养统计延迟'],
|
||||
['建议动作','检查食堂出口网络与 MQ 连通性']
|
||||
]}
|
||||
],
|
||||
timeline:[
|
||||
['2026-06-24 09:18:33','第 3 次自动重试失败,系统保留本地缓存等待人工介入。'],
|
||||
['2026-06-24 09:05:21','边缘服务器尝试切换备用上报通道失败。'],
|
||||
['2026-06-24 08:45:00','检测到就餐流水超过阈值未成功上报,生成告警。']
|
||||
],
|
||||
actions:[
|
||||
{label:'查看上报日志',href:'edge-report-management.html'},
|
||||
{label:'查看服务器详情',href:'edge-server-detail.html'},
|
||||
{label:'标记已恢复',action:"showToast('已标记为上报恢复')"}
|
||||
]
|
||||
},
|
||||
dispatchFail:{
|
||||
title:'下发失败告警详情',
|
||||
basic:[
|
||||
['告警编号','ALM-20260624-083'],
|
||||
['告警类型','下发失败告警'],
|
||||
['告警等级','严重'],
|
||||
['所属食堂','炼化中心食堂'],
|
||||
['边缘服务器','炼化中心餐线服务器'],
|
||||
['当前状态','待处理'],
|
||||
['关联任务','UPG-20260624-002'],
|
||||
['处理人','未指派']
|
||||
],
|
||||
sections:[
|
||||
{title:'下发任务信息',type:'table',rows:[
|
||||
['任务类型','升级任务'],
|
||||
['下发方式','单次'],
|
||||
['计划执行时间','2026-06-24 11:00:00'],
|
||||
['接收状态','服务器已接收,终端应用失败'],
|
||||
['失败阶段','终端应用升级包'],
|
||||
['失败原因','升级校验通过,但终端回执超时']
|
||||
]},
|
||||
{title:'接收与应用结果',type:'table',rows:[
|
||||
['目标服务器','2 台'],
|
||||
['成功服务器','1 台'],
|
||||
['失败服务器','1 台'],
|
||||
['失败终端','餐线营养秤 02'],
|
||||
['补偿建议','生成补发任务并延后 10 分钟执行']
|
||||
]}
|
||||
],
|
||||
timeline:[
|
||||
['2026-06-24 11:18:20','终端应用超时,系统升级状态转为部分失败。'],
|
||||
['2026-06-24 11:06:45','目标边缘服务器完成升级包接收。'],
|
||||
['2026-06-24 11:00:00','中心服务器发起升级下发任务。']
|
||||
],
|
||||
actions:[
|
||||
{label:'查看下发任务',href:'edge-dispatch-detail.html'},
|
||||
{label:'发起补发',action:"showToast('已生成补发任务草稿')"},
|
||||
{label:'指派处理人',action:"showToast('已打开指派处理人流程')"}
|
||||
]
|
||||
},
|
||||
serverError:{
|
||||
title:'服务器异常告警详情',
|
||||
basic:[
|
||||
['告警编号','ALM-20260624-091'],
|
||||
['告警类型','服务器异常告警'],
|
||||
['告警等级','严重'],
|
||||
['所属食堂','炼化中心食堂'],
|
||||
['边缘服务器','炼化中心餐线服务器'],
|
||||
['当前状态','待处理'],
|
||||
['服务器编号','EDGE-CQ-024'],
|
||||
['处理人','未指派']
|
||||
],
|
||||
sections:[
|
||||
{title:'服务器运行状态',type:'table',rows:[
|
||||
['最近心跳','2026-06-24 08:54:09'],
|
||||
['部署位置','炼化中心后厨机房'],
|
||||
['服务版本','v2.2.8'],
|
||||
['磁盘使用率','91%'],
|
||||
['网络状态','链路中断,心跳丢失'],
|
||||
['本地缓存','仍有 56 条待上报记录']
|
||||
]},
|
||||
{title:'影响评估',type:'table',rows:[
|
||||
['影响上报','就餐流水、称重记录全部中断'],
|
||||
['影响下发','中心任务无法继续回执'],
|
||||
['影响终端','餐线秤 2 台、结算终端 1 台'],
|
||||
['建议动作','现场检查供电、网络、磁盘与服务进程']
|
||||
]}
|
||||
],
|
||||
timeline:[
|
||||
['2026-06-24 09:21:20','连续 3 个心跳周期无响应,系统升级为严重告警。'],
|
||||
['2026-06-24 09:02:18','磁盘达到 91%,缓存清理任务未成功执行。'],
|
||||
['2026-06-24 08:54:09','最后一次成功心跳上报。']
|
||||
],
|
||||
actions:[
|
||||
{label:'查看服务器详情',href:'edge-server-detail.html'},
|
||||
{label:'查看上报日志',href:'edge-report-management.html'},
|
||||
{label:'标记处理中',action:"showToast('已标记服务器异常处理中')"}
|
||||
]
|
||||
},
|
||||
deviceError:{
|
||||
title:'设备异常告警详情',
|
||||
basic:[
|
||||
['告警编号','ALM-20260624-066'],
|
||||
['告警类型','设备异常告警'],
|
||||
['告警等级','警告'],
|
||||
['所属食堂','采气一厂食堂'],
|
||||
['边缘服务器','采气一厂后厨服务器'],
|
||||
['当前状态','待处理'],
|
||||
['设备编号','LINE-SCALE-002'],
|
||||
['处理人','运维李工']
|
||||
],
|
||||
sections:[
|
||||
{title:'设备状态信息',type:'table',rows:[
|
||||
['设备名称','餐线营养秤 02'],
|
||||
['设备类型','餐线营养秤'],
|
||||
['绑定位置','一号餐线'],
|
||||
['最近在线','2026-06-24 08:42:18'],
|
||||
['当前版本','v1.7.6'],
|
||||
['故障现象','终端离线 35 分钟,称重采集停止']
|
||||
]},
|
||||
{title:'业务影响',type:'table',rows:[
|
||||
['影响业务','称重采集、营养校验'],
|
||||
['备用方案','人工补录或切换相邻秤位'],
|
||||
['关联下发','FACE-BATCH-0624-04 无直接影响'],
|
||||
['建议动作','现场检查终端电源、网口与网线']
|
||||
]}
|
||||
],
|
||||
timeline:[
|
||||
['2026-06-24 09:12:00','设备持续离线超阈值,告警保持待处理。'],
|
||||
['2026-06-24 08:55:10','边缘服务器检测到设备连接中断。'],
|
||||
['2026-06-24 08:42:18','设备最后一次成功回传称重状态。']
|
||||
],
|
||||
actions:[
|
||||
{label:'查看服务器详情',href:'edge-server-detail.html'},
|
||||
{label:'查看关联告警',action:"showToast('已聚合同设备历史告警')"},
|
||||
{label:'标记已恢复',action:"showToast('已标记设备恢复')"}
|
||||
]
|
||||
},
|
||||
deviceRecovered:{
|
||||
title:'设备异常恢复详情',
|
||||
basic:[
|
||||
['告警编号','ALM-20260624-044'],
|
||||
['告警类型','设备异常告警'],
|
||||
['告警等级','提醒'],
|
||||
['所属食堂','总部一号食堂'],
|
||||
['边缘服务器','总部一号食堂服务器'],
|
||||
['当前状态','已恢复'],
|
||||
['设备编号','FACE-TERM-003'],
|
||||
['处理人','平台自动恢复']
|
||||
],
|
||||
sections:[
|
||||
{title:'恢复信息',type:'table',rows:[
|
||||
['设备名称','人脸终端 03'],
|
||||
['异常现象','增量包接收滞后'],
|
||||
['恢复时间','2026-06-24 08:01:30'],
|
||||
['恢复方式','平台自动补推 + 终端自校验'],
|
||||
['影响范围','就餐识别短暂延迟,无需人工补录']
|
||||
]}
|
||||
],
|
||||
timeline:[
|
||||
['2026-06-24 08:01:30','系统自动补推增量包成功,终端恢复最新版本。'],
|
||||
['2026-06-24 07:56:40','检测到终端回执落后于边缘服务器版本。'],
|
||||
['2026-06-24 07:48:12','首次生成版本滞后提醒。']
|
||||
],
|
||||
actions:[
|
||||
{label:'查看下发任务',href:'edge-dispatch-detail.html'},
|
||||
{label:'查看服务器详情',href:'edge-server-detail.html'},
|
||||
{label:'关闭详情',action:'closeDrawer()'}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
function showToast(text){
|
||||
var toast=document.getElementById('toast');
|
||||
toast.textContent=text;
|
||||
toast.classList.add('show');
|
||||
setTimeout(function(){toast.classList.remove('show');},1800);
|
||||
}
|
||||
|
||||
function renderTable(rows){
|
||||
var html='<table class="detail-table">';
|
||||
rows.forEach(function(row){
|
||||
html+='<tr><td>'+row[0]+'</td><td>'+row[1]+'</td></tr>';
|
||||
});
|
||||
html+='</table>';
|
||||
return html;
|
||||
}
|
||||
|
||||
function renderTimeline(items){
|
||||
var html='<div class="timeline">';
|
||||
items.forEach(function(item){
|
||||
html+='<div class="timeline-item"><div class="timeline-time">'+item[0]+'</div><div>'+item[1]+'</div></div>';
|
||||
});
|
||||
html+='</div>';
|
||||
return html;
|
||||
}
|
||||
|
||||
function renderActions(actions){
|
||||
var html='<div class="action-row">';
|
||||
actions.forEach(function(action){
|
||||
if(action.href){
|
||||
html+='<button class="btn btn-default" onclick="location.href=\''+action.href+'\'">'+action.label+'</button>';
|
||||
}else{
|
||||
html+='<button class="btn btn-primary" onclick="'+action.action+'">'+action.label+'</button>';
|
||||
}
|
||||
});
|
||||
html+='</div>';
|
||||
return html;
|
||||
}
|
||||
|
||||
function openDrawer(key){
|
||||
var data=alarmDetails[key];
|
||||
if(!data){return;}
|
||||
document.getElementById('drawerTitle').textContent=data.title;
|
||||
var basicHtml='<div class="drawer-section"><div class="drawer-section__title">基础信息</div><div class="detail-grid">';
|
||||
data.basic.forEach(function(item){
|
||||
basicHtml+='<div class="detail-item"><div class="detail-label">'+item[0]+'</div><div class="detail-value">'+item[1]+'</div></div>';
|
||||
});
|
||||
basicHtml+='</div></div>';
|
||||
var sectionHtml='';
|
||||
data.sections.forEach(function(section){
|
||||
sectionHtml+='<div class="drawer-section"><div class="drawer-section__title">'+section.title+'</div>'+renderTable(section.rows)+'</div>';
|
||||
});
|
||||
var timelineHtml='<div class="drawer-section"><div class="drawer-section__title">处理轨迹</div>'+renderTimeline(data.timeline)+'</div>';
|
||||
var actionHtml='<div class="drawer-section"><div class="drawer-section__title">处理操作</div>'+renderActions(data.actions)+'</div>';
|
||||
document.getElementById('drawerBody').innerHTML=basicHtml+sectionHtml+timelineHtml+actionHtml;
|
||||
document.getElementById('drawerMask').classList.add('show');
|
||||
}
|
||||
|
||||
function closeDrawer(){
|
||||
document.getElementById('drawerMask').classList.remove('show');
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,199 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>营养管理 - 故障恢复与补偿 | 健康CQ原型</title>
|
||||
<style>
|
||||
*{box-sizing:border-box;margin:0;padding:0;}
|
||||
body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif;background:#F5F7FA;color:#333;min-width:1280px;}
|
||||
:root{--primary:#1890FF;--primary-light:#E6F7FF;--success:#52C41A;--warning:#FAAD14;--danger:#FF4D4F;--text-primary:#333;--text-secondary:#666;--text-placeholder:#999;--border:#E8E8E8;--font-xs:11px;--font-sm:13px;--font-base:15px;--font-lg:17px;--font-xl:20px;--font-xxl:24px;--radius-sm:4px;--radius-md:8px;--shadow-sm:0 1px 4px rgba(0,0,0,.08);--shadow-md:0 4px 12px rgba(0,0,0,.12);}
|
||||
.top-bar{display:flex;align-items:center;height:56px;background:#001529;padding:0 24px;position:fixed;top:0;left:0;right:0;z-index:100;}
|
||||
.top-bar__logo{display:flex;align-items:center;gap:8px;color:#fff;font-size:var(--font-lg);font-weight:600;white-space:nowrap;width:240px;flex-shrink:0;}
|
||||
.top-bar__logo svg{width:28px;height:28px;}
|
||||
.top-bar__nav{display:flex;align-items:center;gap:4px;flex:1;}
|
||||
.top-bar__nav a{color:rgba(255,255,255,.65);text-decoration:none;padding:6px 14px;border-radius:var(--radius-sm);font-size:var(--font-sm);transition:all .2s;}
|
||||
.top-bar__nav a:hover{color:#fff;background:rgba(255,255,255,.08);}
|
||||
.top-bar__nav a.active{color:#fff;background:var(--primary);}
|
||||
.top-bar__right{display:flex;align-items:center;gap:16px;color:rgba(255,255,255,.65);font-size:var(--font-sm);}
|
||||
.btn-back{color:rgba(255,255,255,.65);text-decoration:none;}
|
||||
.btn-back:hover{color:#fff;}
|
||||
.avatar{width:32px;height:32px;border-radius:50%;background:#1890FF;display:flex;align-items:center;justify-content:center;color:#fff;font-size:var(--font-sm);}
|
||||
.sidebar{position:fixed;top:56px;left:0;bottom:0;width:240px;background:#001529;overflow-y:auto;z-index:90;padding-top:8px;}
|
||||
.sidebar-menu__title{color:rgba(255,255,255,.35);font-size:var(--font-xs);padding:16px 24px 8px;text-transform:uppercase;letter-spacing:.5px;}
|
||||
.sidebar-item{display:flex;align-items:center;gap:8px;padding:10px 24px;color:rgba(255,255,255,.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
|
||||
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,.06);}
|
||||
.sidebar-item.active{color:#fff;background:var(--primary);}
|
||||
.mi{display:inline-block;width:20px;text-align:center;filter:grayscale(1);opacity:.75;margin-right:2px;}
|
||||
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
|
||||
.page-header{display:flex;align-items:center;justify-content:space-between;background:#fff;border-radius:var(--radius-md);padding:18px 24px;margin-bottom:16px;box-shadow:var(--shadow-sm);}
|
||||
.page-header__title{font-size:var(--font-xl);font-weight:700;}
|
||||
.page-header__desc{margin-top:6px;font-size:var(--font-sm);color:var(--text-secondary);}
|
||||
.page-header__actions{display:flex;gap:8px;align-items:center;}
|
||||
.btn{height:36px;padding:0 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;border:none;display:inline-flex;align-items:center;gap:6px;white-space:nowrap;}
|
||||
.btn-primary{background:var(--primary);color:#fff;}
|
||||
.btn-default{background:#fff;color:var(--text-primary);border:1px solid var(--border);}
|
||||
.btn-default:hover{color:var(--primary);border-color:var(--primary);}
|
||||
.btn-danger{background:#fff;color:var(--danger);border:1px solid var(--danger);}
|
||||
.stats-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:12px;margin-bottom:16px;}
|
||||
.stat-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);padding:16px 18px;}
|
||||
.stat-card__value{font-size:var(--font-xxl);font-weight:700;line-height:1;}
|
||||
.stat-card__label{font-size:var(--font-sm);color:var(--text-secondary);margin-top:8px;}
|
||||
.tabs{display:flex;background:#fff;border-radius:var(--radius-md) var(--radius-md) 0 0;box-shadow:var(--shadow-sm);overflow:hidden;}
|
||||
.tab{padding:14px 22px;font-size:var(--font-sm);cursor:pointer;border-bottom:2px solid transparent;color:var(--text-secondary);}
|
||||
.tab.active{color:var(--primary);border-bottom-color:var(--primary);font-weight:600;}
|
||||
.tab-panel{display:none;background:#fff;border-radius:0 0 var(--radius-md) var(--radius-md);box-shadow:var(--shadow-sm);padding:18px 20px;}
|
||||
.tab-panel.active{display:block;}
|
||||
.filter-bar{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-bottom:16px;}
|
||||
.filter-item{display:flex;align-items:center;gap:8px;}
|
||||
.filter-item label{font-size:var(--font-sm);color:var(--text-secondary);white-space:nowrap;}
|
||||
.filter-input{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);outline:none;min-width:160px;}
|
||||
.filter-select{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);outline:none;min-width:120px;background:#fff;}
|
||||
.table-wrap{overflow-x:auto;}
|
||||
.table-wrap table{min-width:1360px;}
|
||||
table{width:100%;border-collapse:collapse;}
|
||||
thead th{background:#FAFAFA;padding:12px 16px;text-align:left;font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);white-space:nowrap;}
|
||||
tbody td{padding:14px 16px;font-size:var(--font-sm);color:var(--text-primary);border-bottom:1px solid var(--border);white-space:nowrap;vertical-align:middle;}
|
||||
tbody tr:hover{background:#F8FBFF;}
|
||||
thead th:last-child,tbody td.td-actions{position:sticky;right:0;background:#fff;box-shadow:-4px 0 8px rgba(0,0,0,.04);z-index:2;}
|
||||
thead th:last-child{background:#FAFAFA;}
|
||||
tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
.tag{display:inline-flex;align-items:center;padding:2px 8px;border-radius:10px;font-size:var(--font-xs);}
|
||||
.tag-green{background:#F6FFED;color:var(--success);}
|
||||
.tag-blue{background:var(--primary-light);color:var(--primary);}
|
||||
.tag-orange{background:#FFF7E6;color:var(--warning);}
|
||||
.tag-red{background:#FFF1F0;color:var(--danger);}
|
||||
.tag-gray{background:#F5F5F5;color:#999;}
|
||||
.link{color:var(--primary);cursor:pointer;font-size:var(--font-sm);text-decoration:none;}
|
||||
.link:hover{color:#40a9ff;}
|
||||
.split-layout{display:grid;grid-template-columns:1.3fr .7fr;gap:16px;margin-top:16px;}
|
||||
.card{background:#fff;border:1px solid var(--border);border-radius:var(--radius-md);overflow:hidden;}
|
||||
.card__header{padding:14px 16px;border-bottom:1px solid var(--border);font-size:var(--font-base);font-weight:600;display:flex;justify-content:space-between;align-items:center;}
|
||||
.card__body{padding:16px;}
|
||||
.step-list{display:flex;flex-direction:column;gap:12px;}
|
||||
.step-item{display:grid;grid-template-columns:28px 1fr;gap:10px;align-items:start;}
|
||||
.step-num{width:24px;height:24px;border-radius:50%;background:var(--primary-light);color:var(--primary);display:flex;align-items:center;justify-content:center;font-size:var(--font-xs);font-weight:700;}
|
||||
.step-title{font-size:var(--font-sm);font-weight:600;}
|
||||
.step-desc{font-size:var(--font-xs);color:var(--text-secondary);margin-top:4px;line-height:1.6;}
|
||||
.toast{position:fixed;top:76px;left:50%;transform:translateX(-50%) translateY(-12px);background:#fff;border-left:4px solid var(--primary);box-shadow:var(--shadow-md);padding:10px 18px;border-radius:var(--radius-sm);font-size:var(--font-sm);opacity:0;pointer-events:none;transition:all .25s;z-index:1200;}
|
||||
.toast.show{opacity:1;transform:translateX(-50%) translateY(0);}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header class="top-bar">
|
||||
<div class="top-bar__logo"><svg viewBox="0 0 28 28" fill="none"><rect width="28" height="28" rx="6" fill="#1890FF"/><path d="M7 14h14M14 7v14" stroke="#fff" stroke-width="2.5" stroke-linecap="round"/></svg>营养管理</div>
|
||||
<nav class="top-bar__nav"><a href="trace-query.html">溯源总览</a><a href="arc-employee.html">档案</a><a href="farm-land.html">绿色种采</a><a href="sup-hygiene-check.html">卫生供应</a><a href="prod-qc-daily.html">健康生产</a><a href="din-dict-mgmt.html">营养用餐</a><a href="ano-dirty.html">异常事件</a><a href="edge-node-list.html" class="active">边缘服务器</a></nav>
|
||||
<div class="top-bar__right"><a href="../../index.html" class="btn-back">← 返回首页</a><div class="avatar">管</div><span>系统管理员 ▾</span></div>
|
||||
</header>
|
||||
|
||||
<aside class="sidebar">
|
||||
<div class="sidebar-menu__title">边缘服务器</div>
|
||||
<a href="edge-alarm-center.html" class="sidebar-item"><span class="mi">⚠</span>告警中心</a>
|
||||
<a href="edge-sync-monitor.html" class="sidebar-item"><span class="mi">🔄</span>同步监控</a>
|
||||
<a href="edge-dispatch-compensation.html" class="sidebar-item active"><span class="mi">📦</span>下发与补偿</a>
|
||||
<a href="edge-node-list.html" class="sidebar-item"><span class="mi">🧭</span>边缘服务器列表</a>
|
||||
</aside>
|
||||
|
||||
<main class="main">
|
||||
<section class="page-header">
|
||||
<div><div class="page-header__title">故障恢复与补偿</div><div class="page-header__desc">针对食堂上报故障后的恢复动作,跟踪补发、重试、补偿和最终恢复结果。</div></div>
|
||||
<div class="page-header__actions"><button class="btn btn-default" onclick="showToast('正在导出下发记录')">导出记录</button><button class="btn btn-primary" onclick="showToast('已创建补偿批次草稿')">新建补偿批次</button></div>
|
||||
</section>
|
||||
|
||||
<section class="stats-grid">
|
||||
<div class="stat-card"><div class="stat-card__value">126</div><div class="stat-card__label">今日恢复任务</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value" style="color:var(--success)">118</div><div class="stat-card__label">恢复成功批次</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value" style="color:var(--warning)">6</div><div class="stat-card__label">部分恢复成功</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value" style="color:var(--danger)">2</div><div class="stat-card__label">恢复失败批次</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value">14</div><div class="stat-card__label">补偿处理中</div></div>
|
||||
</section>
|
||||
|
||||
<section class="tabs">
|
||||
<div class="tab active" onclick="switchTab('dispatch',this)">恢复下发记录</div>
|
||||
<div class="tab" onclick="switchTab('compensation',this)">补偿处理记录</div>
|
||||
</section>
|
||||
|
||||
<section id="dispatch" class="tab-panel active">
|
||||
<div class="filter-bar">
|
||||
<div class="filter-item"><label>下发类型</label><select class="filter-select"><option>全部</option><option>基础资料</option><option>人脸批次</option><option>生产任务</option><option>升级任务</option><option>文件缓存</option></select></div>
|
||||
<div class="filter-item"><label>目标食堂</label><select class="filter-select"><option>全部</option><option>采气一厂食堂</option><option>炼化中心食堂</option></select></div>
|
||||
<div class="filter-item"><label>当前状态</label><select class="filter-select"><option>全部</option><option>已完成</option><option>部分失败</option><option>下发失败</option><option>下发中</option></select></div>
|
||||
<div class="filter-item"><label>批次号</label><input class="filter-input" placeholder="请输入批次号"></div>
|
||||
<button class="btn btn-primary" onclick="showToast('已查询下发记录')">查询</button>
|
||||
<button class="btn btn-default" onclick="showToast('已重置筛选条件')">重置</button>
|
||||
</div>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead><tr><th>批次号</th><th>下发类型</th><th>目标食堂</th><th>目标服务器</th><th>关联业务对象</th><th>发起人</th><th>发起时间</th><th>恢复状态</th><th>失败服务器数</th><th>完成服务器数</th><th>操作</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>FACE-BATCH-0623-04</td><td>人脸批次</td><td>采气一厂食堂</td><td>采气一厂后厨服务器</td><td>人脸增量 326 人</td><td>系统自动</td><td>2026-06-23 10:00:00</td><td><span class="tag tag-red">下发失败</span></td><td>1</td><td>0</td><td class="td-actions"><a class="link" href="edge-alarm-center.html">查看故障</a> <a class="link" onclick="showToast('已发起单服务器重新下发')">单服务器重发</a></td></tr>
|
||||
<tr><td>UPG-20260623-002</td><td>升级任务</td><td>炼化中心食堂</td><td>炼化中心餐线服务器</td><td>餐线营养秤 v1.8.2</td><td>运维李工</td><td>2026-06-23 11:00:00</td><td><span class="tag tag-orange">部分失败</span></td><td>2</td><td>14</td><td class="td-actions"><a class="link" href="edge-alarm-center.html">查看故障</a> <a class="link" onclick="showToast('已重新通知离线设备')">重新通知设备</a></td></tr>
|
||||
<tr><td>TASK-20260623-021</td><td>生产任务</td><td>总部一号食堂</td><td>总部一号食堂服务器</td><td>净菜组配任务 18 条</td><td>营养运营</td><td>2026-06-23 09:12:00</td><td><span class="tag tag-green">已完成</span></td><td>0</td><td>1</td><td class="td-actions"><a class="link">查看回执</a></td></tr>
|
||||
<tr><td>BASE-20260623-003</td><td>基础资料</td><td>全部食堂</td><td>28 个服务器</td><td>菜品/食材/物料更新</td><td>系统自动</td><td>2026-06-23 09:30:00</td><td><span class="tag tag-green">已完成</span></td><td>0</td><td>28</td><td class="td-actions"><a class="link">查看回执</a></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="split-layout">
|
||||
<div class="card">
|
||||
<div class="card__header">恢复失败明细 <span class="tag tag-orange">示例选中:UPG-20260623-002</span></div>
|
||||
<div class="card__body table-wrap">
|
||||
<table style="min-width:760px"><thead><tr><th>服务器</th><th>设备</th><th>失败原因</th><th>最近处理</th><th>操作</th></tr></thead><tbody>
|
||||
<tr><td>炼化中心餐线服务器</td><td>餐线营养秤 02</td><td>设备离线</td><td>2026-06-23 14:00:00</td><td class="td-actions"><a class="link" onclick="showToast('已重新通知设备')">重新通知</a></td></tr>
|
||||
<tr><td>炼化中心餐线服务器</td><td>结算终端 01</td><td>升级包校验失败</td><td>2026-06-23 13:58:21</td><td class="td-actions"><a class="link" onclick="showToast('已重新分发升级包')">重新分发包</a></td></tr>
|
||||
</tbody></table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card__header">恢复补偿规则</div>
|
||||
<div class="card__body">
|
||||
<div class="step-list">
|
||||
<div class="step-item"><div class="step-num">1</div><div><div class="step-title">避免覆盖成功服务器</div><div class="step-desc">默认只对失败服务器重新下发,强制覆盖需单独确认。</div></div></div>
|
||||
<div class="step-item"><div class="step-num">2</div><div><div class="step-title">校验任务状态</div><div class="step-desc">生产任务已开始或已完成时,进入异常纠错而非直接覆盖。</div></div></div>
|
||||
<div class="step-item"><div class="step-num">3</div><div><div class="step-title">区分升级补偿</div><div class="step-desc">升级失败可选择重新通知设备或重新分发升级包。</div></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="compensation" class="tab-panel">
|
||||
<div class="filter-bar">
|
||||
<div class="filter-item"><label>补偿方式</label><select class="filter-select"><option>全部</option><option>单服务器重新下发</option><option>批量重新下发</option><option>跳过失败服务器</option></select></div>
|
||||
<div class="filter-item"><label>补偿结果</label><select class="filter-select"><option>全部</option><option>成功</option><option>失败</option><option>处理中</option><option>已终止</option></select></div>
|
||||
<div class="filter-item"><label>原始批次</label><input class="filter-input" placeholder="请输入原始批次号"></div>
|
||||
<button class="btn btn-primary" onclick="showToast('已查询补偿记录')">查询</button>
|
||||
</div>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead><tr><th>补偿单号</th><th>原始失败记录</th><th>补偿方式</th><th>补偿发起人</th><th>补偿时间</th><th>补偿结果</th><th>备注</th><th>操作</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>CMP-20260623-009</td><td>FACE-BATCH-0623-04</td><td>单服务器重新下发</td><td>运维张工</td><td>2026-06-23 14:20:00</td><td><span class="tag tag-orange">处理中</span></td><td>等待服务器恢复 MQ 连接</td><td class="td-actions"><a class="link" href="edge-sync-monitor.html">查看链路</a> <a class="link" onclick="confirmStop()">终止补偿</a></td></tr>
|
||||
<tr><td>CMP-20260623-006</td><td>UPG-20260623-002</td><td>重新通知设备</td><td>运维李工</td><td>2026-06-23 13:10:00</td><td><span class="tag tag-red">失败</span></td><td>设备仍离线</td><td class="td-actions"><a class="link" onclick="showToast('已重新生成补偿批次')">重新生成批次</a></td></tr>
|
||||
<tr><td>CMP-20260622-018</td><td>BASE-20260622-011</td><td>批量重新下发</td><td>系统管理员</td><td>2026-06-22 18:20:00</td><td><span class="tag tag-green">成功</span></td><td>3 个服务器补偿完成</td><td class="td-actions"><a class="link">查看回执</a></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<div class="toast" id="toast">操作已完成</div>
|
||||
<script>
|
||||
function switchTab(id,el){
|
||||
document.querySelectorAll('.tab').forEach(function(tab){tab.classList.remove('active');});
|
||||
document.querySelectorAll('.tab-panel').forEach(function(panel){panel.classList.remove('active');});
|
||||
el.classList.add('active');
|
||||
document.getElementById(id).classList.add('active');
|
||||
}
|
||||
function showToast(text){
|
||||
var toast=document.getElementById('toast');
|
||||
toast.textContent=text;
|
||||
toast.classList.add('show');
|
||||
setTimeout(function(){toast.classList.remove('show');},1800);
|
||||
}
|
||||
function confirmStop(){
|
||||
if(window.confirm('确认终止该补偿单?终止后需记录处理原因。')){showToast('补偿单已终止');}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>营养管理 - 数据下发详情 | 健康CQ原型</title>
|
||||
<style>
|
||||
*{box-sizing:border-box;margin:0;padding:0;}
|
||||
body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif;background:#F5F7FA;color:#333;min-width:1280px;}
|
||||
:root{--primary:#1890FF;--primary-light:#E6F7FF;--success:#52C41A;--warning:#FAAD14;--danger:#FF4D4F;--text-primary:#333;--text-secondary:#666;--text-placeholder:#999;--border:#E8E8E8;--font-xs:11px;--font-sm:13px;--font-base:15px;--font-lg:17px;--font-xl:20px;--font-xxl:24px;--radius-sm:4px;--radius-md:8px;--radius-lg:12px;--shadow-sm:0 1px 4px rgba(0,0,0,.08);--shadow-md:0 4px 12px rgba(0,0,0,.12);}
|
||||
.top-bar{display:flex;align-items:center;height:56px;background:#001529;padding:0 24px;position:fixed;top:0;left:0;right:0;z-index:100;}
|
||||
.top-bar__logo{display:flex;align-items:center;gap:8px;color:#fff;font-size:var(--font-lg);font-weight:600;white-space:nowrap;width:240px;flex-shrink:0;}
|
||||
.top-bar__logo svg{width:28px;height:28px;}
|
||||
.top-bar__nav{display:flex;align-items:center;gap:4px;flex:1;}
|
||||
.top-bar__nav a{color:rgba(255,255,255,.65);text-decoration:none;padding:6px 14px;border-radius:var(--radius-sm);font-size:var(--font-sm);transition:all .2s;}
|
||||
.top-bar__nav a:hover{color:#fff;background:rgba(255,255,255,.08);}
|
||||
.top-bar__nav a.active{color:#fff;background:var(--primary);}
|
||||
.top-bar__right{display:flex;align-items:center;gap:16px;color:rgba(255,255,255,.65);font-size:var(--font-sm);}
|
||||
.btn-back{color:rgba(255,255,255,.65);text-decoration:none;}
|
||||
.btn-back:hover{color:#fff;}
|
||||
.avatar{width:32px;height:32px;border-radius:50%;background:#1890FF;display:flex;align-items:center;justify-content:center;color:#fff;font-size:var(--font-sm);}
|
||||
.sidebar{position:fixed;top:56px;left:0;bottom:0;width:240px;background:#001529;overflow-y:auto;z-index:90;padding-top:8px;}
|
||||
.sidebar-menu__title{color:rgba(255,255,255,.35);font-size:var(--font-xs);padding:16px 24px 8px;text-transform:uppercase;letter-spacing:.5px;}
|
||||
.sidebar-item{display:flex;align-items:center;gap:8px;padding:10px 24px;color:rgba(255,255,255,.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
|
||||
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,.06);}
|
||||
.sidebar-item.active{color:#fff;background:var(--primary);}
|
||||
.mi{display:inline-block;width:20px;text-align:center;filter:grayscale(1);opacity:.75;margin-right:2px;}
|
||||
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
|
||||
.breadcrumb{font-size:var(--font-sm);color:var(--text-secondary);margin-bottom:12px;}
|
||||
.breadcrumb a{color:var(--primary);text-decoration:none;}
|
||||
.page-header{display:flex;align-items:flex-start;justify-content:space-between;background:#fff;border-radius:var(--radius-md);padding:20px 24px;margin-bottom:16px;box-shadow:var(--shadow-sm);}
|
||||
.page-header__title{font-size:var(--font-xl);font-weight:700;}
|
||||
.page-header__desc{margin-top:6px;font-size:var(--font-sm);color:var(--text-secondary);}
|
||||
.page-header__actions{display:flex;gap:8px;flex-wrap:wrap;}
|
||||
.btn{height:36px;padding:0 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;border:none;display:inline-flex;align-items:center;gap:6px;white-space:nowrap;}
|
||||
.btn-primary{background:var(--primary);color:#fff;}
|
||||
.btn-default{background:#fff;color:var(--text-primary);border:1px solid var(--border);}
|
||||
.btn-default:hover{color:var(--primary);border-color:var(--primary);}
|
||||
.stats-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:12px;margin-bottom:16px;}
|
||||
.stat-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);padding:16px 18px;}
|
||||
.stat-card__value{font-size:var(--font-xxl);font-weight:700;line-height:1;}
|
||||
.stat-card__label{font-size:var(--font-sm);color:var(--text-secondary);margin-top:8px;}
|
||||
.card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
|
||||
.card__header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--border);}
|
||||
.card__title{font-size:var(--font-base);font-weight:600;}
|
||||
.card__body{padding:18px 20px;}
|
||||
.info-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:16px 24px;}
|
||||
.info-item{display:flex;flex-direction:column;gap:4px;}
|
||||
.info-label{font-size:var(--font-xs);color:var(--text-placeholder);}
|
||||
.info-value{font-size:var(--font-sm);color:var(--text-primary);font-weight:500;}
|
||||
.tabs{display:flex;background:#fff;border-radius:var(--radius-md) var(--radius-md) 0 0;box-shadow:var(--shadow-sm);overflow:hidden;}
|
||||
.tab{padding:14px 22px;font-size:var(--font-sm);cursor:pointer;border-bottom:2px solid transparent;color:var(--text-secondary);}
|
||||
.tab.active{color:var(--primary);border-bottom-color:var(--primary);font-weight:600;}
|
||||
.tab-panel{display:none;background:#fff;border-radius:0 0 var(--radius-md) var(--radius-md);box-shadow:var(--shadow-sm);padding:18px 20px;}
|
||||
.tab-panel.active{display:block;}
|
||||
.table-wrap{overflow-x:auto;}
|
||||
.table-wrap table{min-width:1240px;}
|
||||
table{width:100%;border-collapse:collapse;}
|
||||
thead th{background:#FAFAFA;padding:12px 16px;text-align:left;font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);white-space:nowrap;}
|
||||
tbody td{padding:14px 16px;font-size:var(--font-sm);color:var(--text-primary);border-bottom:1px solid var(--border);white-space:nowrap;vertical-align:middle;}
|
||||
tbody tr:hover{background:#F8FBFF;}
|
||||
thead th:last-child,tbody td.td-actions{position:sticky;right:0;background:#fff;box-shadow:-4px 0 8px rgba(0,0,0,.04);z-index:2;}
|
||||
thead th:last-child{background:#FAFAFA;}
|
||||
tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
.tag{display:inline-flex;align-items:center;padding:2px 8px;border-radius:10px;font-size:var(--font-xs);}
|
||||
.tag-green{background:#F6FFED;color:var(--success);}
|
||||
.tag-blue{background:var(--primary-light);color:var(--primary);}
|
||||
.tag-orange{background:#FFF7E6;color:var(--warning);}
|
||||
.tag-red{background:#FFF1F0;color:var(--danger);}
|
||||
.tag-gray{background:#F5F5F5;color:#999;}
|
||||
.link{color:var(--primary);cursor:pointer;font-size:var(--font-sm);text-decoration:none;}
|
||||
.link:hover{color:#40a9ff;}
|
||||
.toast{position:fixed;top:76px;left:50%;transform:translateX(-50%) translateY(-12px);background:#fff;border-left:4px solid var(--primary);box-shadow:var(--shadow-md);padding:10px 18px;border-radius:var(--radius-sm);font-size:var(--font-sm);opacity:0;pointer-events:none;transition:all .25s;z-index:1200;}
|
||||
.toast.show{opacity:1;transform:translateX(-50%) translateY(0);}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header class="top-bar">
|
||||
<div class="top-bar__logo"><svg viewBox="0 0 28 28" fill="none"><rect width="28" height="28" rx="6" fill="#1890FF"/><path d="M7 14h14M14 7v14" stroke="#fff" stroke-width="2.5" stroke-linecap="round"/></svg>营养管理</div>
|
||||
<nav class="top-bar__nav"><a href="trace-query.html">溯源总览</a><a href="arc-employee.html">档案</a><a href="farm-land.html">绿色种采</a><a href="sup-hygiene-check.html">卫生供应</a><a href="prod-qc-daily.html">健康生产</a><a href="din-dict-mgmt.html">营养用餐</a><a href="ano-dirty.html">异常事件</a><a href="edge-server-management.html" class="active">边缘服务器</a></nav>
|
||||
<div class="top-bar__right"><a href="../../index.html" class="btn-back">← 返回首页</a><div class="avatar">管</div><span>系统管理员 ▾</span></div>
|
||||
</header>
|
||||
|
||||
<aside class="sidebar">
|
||||
<div class="sidebar-menu__title">边缘服务器</div>
|
||||
<a href="edge-server-management.html" class="sidebar-item"><span class="mi">🧭</span>边缘服务器管理</a>
|
||||
<a href="edge-report-management.html" class="sidebar-item"><span class="mi">📡</span>数据上报管理</a>
|
||||
<a href="edge-dispatch-management.html" class="sidebar-item active"><span class="mi">📦</span>数据下发管理</a>
|
||||
<a href="edge-alert-center.html" class="sidebar-item"><span class="mi">⚠</span>异常告警中心</a>
|
||||
</aside>
|
||||
|
||||
<main class="main">
|
||||
<div class="breadcrumb"><a href="edge-dispatch-management.html">数据下发管理</a> / 数据下发详情</div>
|
||||
|
||||
<section class="page-header">
|
||||
<div><div class="page-header__title">FACE-BATCH-0624-04</div><div class="page-header__desc">人脸增量任务 · 单次下发 · 目标为采气一厂食堂边缘服务器,需跟踪每台边缘服务器接收与终端应用情况。</div></div>
|
||||
<div class="page-header__actions"><button class="btn btn-default" onclick="location.href='edge-alert-center.html'">查看告警</button><button class="btn btn-default" onclick="showToast('已生成补发任务草稿')">生成补发任务</button><button class="btn btn-primary" onclick="showToast('已发起强制重试')">强制重试</button></div>
|
||||
</section>
|
||||
|
||||
<section class="stats-grid">
|
||||
<div class="stat-card"><div class="stat-card__value">1</div><div class="stat-card__label">目标服务器</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value" style="color:var(--success)">1</div><div class="stat-card__label">已接收服务器</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value" style="color:var(--warning)">2</div><div class="stat-card__label">待应用终端</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value" style="color:var(--danger)">1</div><div class="stat-card__label">异常告警</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value">326</div><div class="stat-card__label">人脸增量数</div></div>
|
||||
</section>
|
||||
|
||||
<section class="card" style="margin-bottom:16px;">
|
||||
<div class="card__header"><div class="card__title">任务信息</div><span class="tag tag-orange">进行中</span></div>
|
||||
<div class="card__body">
|
||||
<div class="info-grid">
|
||||
<div class="info-item"><div class="info-label">任务编号</div><div class="info-value">FACE-BATCH-0624-04</div></div>
|
||||
<div class="info-item"><div class="info-label">下发类型</div><div class="info-value">人脸增量</div></div>
|
||||
<div class="info-item"><div class="info-label">下发方式</div><div class="info-value">单次</div></div>
|
||||
<div class="info-item"><div class="info-label">计划执行时间</div><div class="info-value">2026-06-24 08:40:00</div></div>
|
||||
<div class="info-item"><div class="info-label">目标范围</div><div class="info-value">采气一厂食堂 1 台边缘服务器</div></div>
|
||||
<div class="info-item"><div class="info-label">发起人</div><div class="info-value">系统自动</div></div>
|
||||
<div class="info-item"><div class="info-label">数据版本</div><div class="info-value">FACE-VER-20260624-04</div></div>
|
||||
<div class="info-item"><div class="info-label">失败策略</div><div class="info-value">失败自动重试 3 次</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="tabs">
|
||||
<div class="tab active" onclick="switchTab('receive',this)">服务器接收情况</div>
|
||||
<div class="tab" onclick="switchTab('terminal',this)">终端应用情况</div>
|
||||
<div class="tab" onclick="switchTab('fail',this)">失败明细</div>
|
||||
<div class="tab" onclick="switchTab('log',this)">执行日志</div>
|
||||
</section>
|
||||
|
||||
<section id="receive" class="tab-panel active">
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead><tr><th>边缘服务器</th><th>所属食堂</th><th>接收状态</th><th>接收时间</th><th>应用状态</th><th>应用完成时间</th><th>剩余终端</th><th>关联告警</th><th>操作</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td><a class="link" href="edge-server-detail.html">采气一厂后厨服务器</a></td><td>采气一厂食堂</td><td><span class="tag tag-green">已接收</span></td><td>2026-06-24 08:40:12</td><td><span class="tag tag-orange">部分待应用</span></td><td>—</td><td>2 台</td><td><a class="link" href="edge-alert-center.html">1 条</a></td><td class="td-actions"><a class="link" onclick="showToast('已打开接收轨迹')">接收轨迹</a></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="terminal" class="tab-panel">
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead><tr><th>终端设备</th><th>设备编号</th><th>应用状态</th><th>应用时间</th><th>失败原因</th><th>当前版本</th><th>操作</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>人脸识别终端 01</td><td>FACE-TERM-001</td><td><span class="tag tag-green">已完成</span></td><td>2026-06-24 08:41:12</td><td>—</td><td>v3.2.0</td><td class="td-actions"><a class="link" onclick="showToast('已查看终端回执')">终端回执</a></td></tr>
|
||||
<tr><td>人脸识别终端 02</td><td>FACE-TERM-002</td><td><span class="tag tag-orange">待应用</span></td><td>—</td><td>终端离线</td><td>v3.2.0</td><td class="td-actions"><a class="link" onclick="showToast('已发送重新通知')">重新通知</a></td></tr>
|
||||
<tr><td>人脸识别终端 03</td><td>FACE-TERM-003</td><td><span class="tag tag-orange">待应用</span></td><td>—</td><td>终端忙碌中</td><td>v3.2.0</td><td class="td-actions"><a class="link" onclick="showToast('已加入终端重试队列')">加入重试</a></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="fail" class="tab-panel">
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead><tr><th>对象层级</th><th>对象名称</th><th>失败阶段</th><th>失败原因</th><th>最近处理时间</th><th>建议动作</th><th>操作</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>终端设备</td><td>人脸识别终端 02</td><td>终端应用</td><td>终端离线,未获取到增量包</td><td>2026-06-24 09:12:10</td><td>待上线后重新通知</td><td class="td-actions"><a class="link" onclick="showToast('已重新通知终端')">重新通知</a></td></tr>
|
||||
<tr><td>终端设备</td><td>人脸识别终端 03</td><td>终端应用</td><td>本地特征库忙碌,应用超时</td><td>2026-06-24 09:08:22</td><td>延后 5 分钟自动重试</td><td class="td-actions"><a class="link" onclick="showToast('已调整重试策略')">调整策略</a></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="log" class="tab-panel">
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead><tr><th>时间</th><th>执行阶段</th><th>处理结果</th><th>说明</th><th>操作</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>2026-06-24 08:40:12</td><td>边缘服务器接收</td><td><span class="tag tag-green">成功</span></td><td>采气一厂后厨服务器完成批次接收与校验</td><td class="td-actions"><a class="link" onclick="showToast('已复制批次轨迹编号')">复制轨迹号</a></td></tr>
|
||||
<tr><td>2026-06-24 08:41:12</td><td>终端应用</td><td><span class="tag tag-green">部分完成</span></td><td>1 台终端完成应用,2 台待应用</td><td class="td-actions"><a class="link" href="edge-server-detail.html">服务器详情</a></td></tr>
|
||||
<tr><td>2026-06-24 09:12:10</td><td>异常补偿</td><td><span class="tag tag-orange">处理中</span></td><td>系统已对离线终端触发重连检测</td><td class="td-actions"><a class="link" href="edge-alert-center.html">查看告警</a></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<div class="toast" id="toast">操作已完成</div>
|
||||
<script>
|
||||
function switchTab(id,el){
|
||||
document.querySelectorAll('.tab').forEach(function(tab){tab.classList.remove('active');});
|
||||
document.querySelectorAll('.tab-panel').forEach(function(panel){panel.classList.remove('active');});
|
||||
el.classList.add('active');
|
||||
document.getElementById(id).classList.add('active');
|
||||
}
|
||||
function showToast(text){
|
||||
var toast=document.getElementById('toast');
|
||||
toast.textContent=text;
|
||||
toast.classList.add('show');
|
||||
setTimeout(function(){toast.classList.remove('show');},1800);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,197 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>营养管理 - 数据下发管理 | 健康CQ原型</title>
|
||||
<style>
|
||||
*{box-sizing:border-box;margin:0;padding:0;}
|
||||
body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif;background:#F5F7FA;color:#333;min-width:1280px;}
|
||||
:root{--primary:#1890FF;--primary-light:#E6F7FF;--success:#52C41A;--warning:#FAAD14;--danger:#FF4D4F;--text-primary:#333;--text-secondary:#666;--text-placeholder:#999;--border:#E8E8E8;--font-xs:11px;--font-sm:13px;--font-base:15px;--font-lg:17px;--font-xl:20px;--font-xxl:24px;--radius-sm:4px;--radius-md:8px;--radius-lg:12px;--shadow-sm:0 1px 4px rgba(0,0,0,.08);--shadow-md:0 4px 12px rgba(0,0,0,.12);}
|
||||
.top-bar{display:flex;align-items:center;height:56px;background:#001529;padding:0 24px;position:fixed;top:0;left:0;right:0;z-index:100;}
|
||||
.top-bar__logo{display:flex;align-items:center;gap:8px;color:#fff;font-size:var(--font-lg);font-weight:600;white-space:nowrap;width:240px;flex-shrink:0;}
|
||||
.top-bar__logo svg{width:28px;height:28px;}
|
||||
.top-bar__nav{display:flex;align-items:center;gap:4px;flex:1;}
|
||||
.top-bar__nav a{color:rgba(255,255,255,.65);text-decoration:none;padding:6px 14px;border-radius:var(--radius-sm);font-size:var(--font-sm);transition:all .2s;}
|
||||
.top-bar__nav a:hover{color:#fff;background:rgba(255,255,255,.08);}
|
||||
.top-bar__nav a.active{color:#fff;background:var(--primary);}
|
||||
.top-bar__right{display:flex;align-items:center;gap:16px;color:rgba(255,255,255,.65);font-size:var(--font-sm);}
|
||||
.btn-back{color:rgba(255,255,255,.65);text-decoration:none;}
|
||||
.btn-back:hover{color:#fff;}
|
||||
.avatar{width:32px;height:32px;border-radius:50%;background:#1890FF;display:flex;align-items:center;justify-content:center;color:#fff;font-size:var(--font-sm);}
|
||||
.sidebar{position:fixed;top:56px;left:0;bottom:0;width:240px;background:#001529;overflow-y:auto;z-index:90;padding-top:8px;}
|
||||
.sidebar-menu__title{color:rgba(255,255,255,.35);font-size:var(--font-xs);padding:16px 24px 8px;text-transform:uppercase;letter-spacing:.5px;}
|
||||
.sidebar-item{display:flex;align-items:center;gap:8px;padding:10px 24px;color:rgba(255,255,255,.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
|
||||
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,.06);}
|
||||
.sidebar-item.active{color:#fff;background:var(--primary);}
|
||||
.mi{display:inline-block;width:20px;text-align:center;filter:grayscale(1);opacity:.75;margin-right:2px;}
|
||||
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
|
||||
.page-header{display:flex;align-items:center;justify-content:space-between;background:#fff;border-radius:var(--radius-md);padding:18px 24px;margin-bottom:16px;box-shadow:var(--shadow-sm);}
|
||||
.page-header__title{font-size:var(--font-xl);font-weight:700;}
|
||||
.page-header__desc{margin-top:6px;font-size:var(--font-sm);color:var(--text-secondary);}
|
||||
.page-header__actions{display:flex;gap:8px;}
|
||||
.stats-grid{display:grid;grid-template-columns:repeat(6,1fr);gap:12px;margin-bottom:16px;}
|
||||
.stat-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);padding:16px 18px;}
|
||||
.stat-card__value{font-size:var(--font-xxl);font-weight:700;line-height:1;}
|
||||
.stat-card__label{font-size:var(--font-sm);color:var(--text-secondary);margin-top:8px;}
|
||||
.stat-card__hint{font-size:var(--font-xs);color:var(--text-placeholder);margin-top:8px;}
|
||||
.filter-bar{background:#fff;border-radius:var(--radius-md);padding:18px 24px;margin-bottom:16px;box-shadow:var(--shadow-sm);display:flex;align-items:center;gap:14px;flex-wrap:wrap;}
|
||||
.filter-item{display:flex;align-items:center;gap:8px;}
|
||||
.filter-item label{font-size:var(--font-sm);color:var(--text-secondary);white-space:nowrap;}
|
||||
.filter-input,.filter-select{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);outline:none;background:#fff;}
|
||||
.filter-input{min-width:160px;}
|
||||
.filter-select{min-width:128px;}
|
||||
.btn{height:36px;padding:0 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;border:none;display:inline-flex;align-items:center;gap:6px;white-space:nowrap;}
|
||||
.btn-primary{background:var(--primary);color:#fff;}
|
||||
.btn-default{background:#fff;color:var(--text-primary);border:1px solid var(--border);}
|
||||
.btn-default:hover{color:var(--primary);border-color:var(--primary);}
|
||||
.table-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
|
||||
.table-card__header{display:flex;align-items:center;justify-content:space-between;padding:16px 24px;border-bottom:1px solid var(--border);}
|
||||
.table-card__title{font-size:var(--font-base);font-weight:600;}
|
||||
.table-wrap{overflow-x:auto;}
|
||||
.table-wrap table{min-width:1460px;}
|
||||
table{width:100%;border-collapse:collapse;}
|
||||
thead th{background:#FAFAFA;padding:12px 16px;text-align:left;font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);white-space:nowrap;}
|
||||
tbody td{padding:14px 16px;font-size:var(--font-sm);color:var(--text-primary);border-bottom:1px solid var(--border);white-space:nowrap;vertical-align:middle;}
|
||||
tbody tr:hover{background:#F8FBFF;}
|
||||
thead th:last-child,tbody td.td-actions{position:sticky;right:0;background:#fff;box-shadow:-4px 0 8px rgba(0,0,0,.04);z-index:2;}
|
||||
thead th:last-child{background:#FAFAFA;}
|
||||
tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
.tag{display:inline-flex;align-items:center;padding:2px 8px;border-radius:10px;font-size:var(--font-xs);}
|
||||
.tag-green{background:#F6FFED;color:var(--success);}
|
||||
.tag-blue{background:var(--primary-light);color:var(--primary);}
|
||||
.tag-orange{background:#FFF7E6;color:var(--warning);}
|
||||
.tag-red{background:#FFF1F0;color:var(--danger);}
|
||||
.tag-gray{background:#F5F5F5;color:#999;}
|
||||
.link{color:var(--primary);cursor:pointer;font-size:var(--font-sm);text-decoration:none;}
|
||||
.link:hover{color:#40a9ff;}
|
||||
.modal-mask{position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:1000;display:none;align-items:center;justify-content:center;}
|
||||
.modal-mask.show{display:flex;}
|
||||
.modal{width:820px;background:#fff;border-radius:12px;box-shadow:var(--shadow-md);overflow:hidden;}
|
||||
.modal__header{display:flex;align-items:center;justify-content:space-between;padding:18px 24px;border-bottom:1px solid var(--border);}
|
||||
.modal__title{font-size:var(--font-lg);font-weight:600;}
|
||||
.modal__close{border:none;background:none;font-size:22px;cursor:pointer;color:var(--text-placeholder);}
|
||||
.modal__body{padding:20px 24px;}
|
||||
.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px 18px;}
|
||||
.form-item{display:flex;flex-direction:column;gap:8px;}
|
||||
.form-item label{font-size:var(--font-sm);color:var(--text-secondary);}
|
||||
.form-input,.form-select,.form-textarea{border:1px solid var(--border);border-radius:var(--radius-sm);padding:10px 12px;font-size:var(--font-sm);outline:none;background:#fff;}
|
||||
.form-textarea{min-height:96px;resize:none;}
|
||||
.modal__footer{display:flex;justify-content:flex-end;gap:8px;padding:0 24px 20px;}
|
||||
.tip-card{margin-top:16px;background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);padding:16px 20px;}
|
||||
.tip-title{font-size:var(--font-base);font-weight:600;margin-bottom:10px;}
|
||||
.tip-list{display:flex;flex-direction:column;gap:8px;font-size:var(--font-sm);color:var(--text-secondary);}
|
||||
.toast{position:fixed;top:76px;left:50%;transform:translateX(-50%) translateY(-12px);background:#fff;border-left:4px solid var(--primary);box-shadow:var(--shadow-md);padding:10px 18px;border-radius:var(--radius-sm);font-size:var(--font-sm);opacity:0;pointer-events:none;transition:all .25s;z-index:1200;}
|
||||
.toast.show{opacity:1;transform:translateX(-50%) translateY(0);}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header class="top-bar">
|
||||
<div class="top-bar__logo"><svg viewBox="0 0 28 28" fill="none"><rect width="28" height="28" rx="6" fill="#1890FF"/><path d="M7 14h14M14 7v14" stroke="#fff" stroke-width="2.5" stroke-linecap="round"/></svg>营养管理</div>
|
||||
<nav class="top-bar__nav"><a href="trace-query.html">溯源总览</a><a href="arc-employee.html">档案</a><a href="farm-land.html">绿色种采</a><a href="sup-hygiene-check.html">卫生供应</a><a href="prod-qc-daily.html">健康生产</a><a href="din-dict-mgmt.html">营养用餐</a><a href="ano-dirty.html">异常事件</a><a href="edge-server-management.html" class="active">边缘服务器</a></nav>
|
||||
<div class="top-bar__right"><a href="../../index.html" class="btn-back">← 返回首页</a><div class="avatar">管</div><span>系统管理员 ▾</span></div>
|
||||
</header>
|
||||
|
||||
<aside class="sidebar">
|
||||
<div class="sidebar-menu__title">边缘服务器</div>
|
||||
<a href="edge-server-management.html" class="sidebar-item"><span class="mi">🧭</span>边缘服务器管理</a>
|
||||
<a href="edge-report-management.html" class="sidebar-item"><span class="mi">📡</span>数据上报管理</a>
|
||||
<a href="edge-dispatch-management.html" class="sidebar-item active"><span class="mi">📦</span>数据下发管理</a>
|
||||
<a href="edge-alert-center.html" class="sidebar-item"><span class="mi">⚠</span>异常告警中心</a>
|
||||
</aside>
|
||||
|
||||
<main class="main">
|
||||
<section class="page-header">
|
||||
<div><div class="page-header__title">数据下发管理</div><div class="page-header__desc">管理中心服务器向边缘服务器发起的全部数据下发任务,支持新建单次、定时等下发方式,并跟踪每台服务器的接收与应用情况。</div></div>
|
||||
<div class="page-header__actions"><button class="btn btn-default" onclick="showToast('正在导出下发任务')">导出任务</button><button class="btn btn-primary" onclick="openModal()">新建任务</button></div>
|
||||
</section>
|
||||
|
||||
<section class="stats-grid">
|
||||
<div class="stat-card"><div class="stat-card__value">126</div><div class="stat-card__label">当日下发任务</div><div class="stat-card__hint">中心服务器发起</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value" style="color:var(--success)">108</div><div class="stat-card__label">已完成任务</div><div class="stat-card__hint">成功到达目标服务器</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value" style="color:var(--warning)">12</div><div class="stat-card__label">进行中任务</div><div class="stat-card__hint">含定时待执行</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value" style="color:var(--danger)">6</div><div class="stat-card__label">失败任务</div><div class="stat-card__hint">需补发或重试</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value">28</div><div class="stat-card__label">待接收服务器</div><div class="stat-card__hint">来自 4 个任务</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value">97.4%</div><div class="stat-card__label">当日完成率</div><div class="stat-card__hint">较昨日 +0.9%</div></div>
|
||||
</section>
|
||||
|
||||
<section class="filter-bar">
|
||||
<div class="filter-item"><label>下发类型</label><select class="filter-select"><option>全部</option><option>基础资料</option><option>人脸增量</option><option>生产任务</option><option>配置同步</option><option>升级任务</option></select></div>
|
||||
<div class="filter-item"><label>下发方式</label><select class="filter-select"><option>全部</option><option>立即</option><option>单次</option><option>定时</option></select></div>
|
||||
<div class="filter-item"><label>当前状态</label><select class="filter-select"><option>全部</option><option>待执行</option><option>进行中</option><option>已完成</option><option>部分失败</option><option>已失败</option></select></div>
|
||||
<div class="filter-item"><label>目标食堂</label><select class="filter-select"><option>全部</option><option>总部一号食堂</option><option>采气一厂食堂</option><option>炼化中心食堂</option></select></div>
|
||||
<div class="filter-item"><label>计划时间</label><input class="filter-input" type="date"></div>
|
||||
<div class="filter-item"><label>任务编号</label><input class="filter-input" placeholder="请输入任务编号"></div>
|
||||
<button class="btn btn-primary" onclick="showToast('已刷新下发任务列表')">查询</button>
|
||||
<button class="btn btn-default" onclick="showToast('已重置筛选条件')">重置</button>
|
||||
</section>
|
||||
|
||||
<section class="table-card">
|
||||
<div class="table-card__header"><div class="table-card__title">中心服务器下发任务列表</div><button class="btn btn-default" onclick="showToast('已切换为仅看失败任务')">仅看失败</button></div>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead><tr><th>任务编号</th><th>下发类型</th><th>下发方式</th><th>目标范围</th><th>计划执行时间</th><th>发起人</th><th>当前状态</th><th>完成服务器</th><th>失败服务器</th><th>关联告警</th><th>操作</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>FACE-BATCH-0624-04</td><td>人脸增量</td><td><span class="tag tag-blue">单次</span></td><td>采气一厂食堂 1 台</td><td>2026-06-24 08:40:00</td><td>系统自动</td><td><span class="tag tag-orange">进行中</span></td><td>0 / 1</td><td>0</td><td><a class="link" href="edge-alert-center.html">1 条</a></td><td class="td-actions"><a class="link" href="edge-dispatch-detail.html">查看详情</a></td></tr>
|
||||
<tr><td>CFG-PUSH-0624-01</td><td>配置同步</td><td><span class="tag tag-blue">定时</span></td><td>全部食堂 28 台</td><td>每天 06:00</td><td>平台运营</td><td><span class="tag tag-green">已完成</span></td><td>28 / 28</td><td>0</td><td>—</td><td class="td-actions"><a class="link" href="edge-dispatch-detail.html">查看详情</a></td></tr>
|
||||
<tr><td>UPG-20260624-002</td><td>升级任务</td><td><span class="tag tag-blue">单次</span></td><td>炼化中心食堂 2 台</td><td>2026-06-24 11:00:00</td><td>运维李工</td><td><span class="tag tag-red">部分失败</span></td><td>1 / 2</td><td>1</td><td><a class="link" href="edge-alert-center.html">2 条</a></td><td class="td-actions"><a class="link" href="edge-dispatch-detail.html">查看详情</a></td></tr>
|
||||
<tr><td>TASK-20260624-021</td><td>生产任务</td><td><span class="tag tag-blue">立即</span></td><td>总部一号食堂 1 台</td><td>2026-06-24 09:12:00</td><td>营养运营</td><td><span class="tag tag-green">已完成</span></td><td>1 / 1</td><td>0</td><td>—</td><td class="td-actions"><a class="link" href="edge-dispatch-detail.html">查看详情</a></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="tip-card">
|
||||
<div class="tip-title">调度规则提示</div>
|
||||
<div class="tip-list">
|
||||
<div>1. 单次任务适合临时下发,如补发人脸增量、紧急生产任务。</div>
|
||||
<div>2. 定时任务适合每日配置、基础资料、规则包等固定时间同步。</div>
|
||||
<div>3. 对已成功服务器默认不重复覆盖,需强制覆盖时应在任务策略中明确指定。</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<div class="modal-mask" id="taskModal">
|
||||
<div class="modal">
|
||||
<div class="modal__header"><div class="modal__title">新建数据下发任务</div><button class="modal__close" onclick="closeModal()">×</button></div>
|
||||
<div class="modal__body">
|
||||
<div class="form-grid">
|
||||
<div class="form-item"><label>任务类型</label><select class="form-select"><option>基础资料</option><option>人脸增量</option><option>生产任务</option><option>配置同步</option><option>升级任务</option></select></div>
|
||||
<div class="form-item"><label>下发方式</label><select class="form-select"><option>立即</option><option>单次</option><option>定时</option></select></div>
|
||||
<div class="form-item"><label>计划执行时间</label><input class="form-input" type="datetime-local" value="2026-06-24T10:30"></div>
|
||||
<div class="form-item"><label>目标范围</label><select class="form-select"><option>全部食堂</option><option>按食堂选择</option><option>按服务器选择</option></select></div>
|
||||
<div class="form-item"><label>目标食堂</label><select class="form-select"><option>采气一厂食堂</option><option>炼化中心食堂</option><option>总部一号食堂</option></select></div>
|
||||
<div class="form-item"><label>失败策略</label><select class="form-select"><option>失败自动重试 3 次</option><option>失败转人工处理</option><option>仅记录失败不重试</option></select></div>
|
||||
<div class="form-item"><label>关联数据版本</label><input class="form-input" value="CFG-20260624-02"></div>
|
||||
<div class="form-item"><label>通知范围</label><select class="form-select"><option>仅运维</option><option>运维 + 营养运营</option><option>全部相关角色</option></select></div>
|
||||
<div class="form-item" style="grid-column:1 / span 2;"><label>任务说明</label><textarea class="form-textarea">用于午餐前同步人脸增量与配置策略,优先推送到采气一厂后厨服务器。</textarea></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal__footer">
|
||||
<button class="btn btn-default" onclick="closeModal()">取消</button>
|
||||
<button class="btn btn-primary" onclick="createTask()">创建任务</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="toast" id="toast">操作已完成</div>
|
||||
<script>
|
||||
function showToast(text){
|
||||
var toast=document.getElementById('toast');
|
||||
toast.textContent=text;
|
||||
toast.classList.add('show');
|
||||
setTimeout(function(){toast.classList.remove('show');},1800);
|
||||
}
|
||||
function openModal(){
|
||||
document.getElementById('taskModal').classList.add('show');
|
||||
}
|
||||
function closeModal(){
|
||||
document.getElementById('taskModal').classList.remove('show');
|
||||
}
|
||||
function createTask(){
|
||||
closeModal();
|
||||
showToast('已创建下发任务草稿');
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,243 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>营养管理 - 服务器故障详情 | 健康CQ原型</title>
|
||||
<style>
|
||||
*{box-sizing:border-box;margin:0;padding:0;}
|
||||
body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif;background:#F5F7FA;color:#333;min-width:1280px;}
|
||||
:root{--primary:#1890FF;--primary-light:#E6F7FF;--success:#52C41A;--warning:#FAAD14;--danger:#FF4D4F;--text-primary:#333;--text-secondary:#666;--text-placeholder:#999;--border:#E8E8E8;--font-xs:11px;--font-sm:13px;--font-base:15px;--font-lg:17px;--font-xl:20px;--font-xxl:24px;--radius-sm:4px;--radius-md:8px;--radius-lg:12px;--shadow-sm:0 1px 4px rgba(0,0,0,0.08);--shadow-md:0 4px 12px rgba(0,0,0,0.12);}
|
||||
.top-bar{display:flex;align-items:center;height:56px;background:#001529;padding:0 24px;position:fixed;top:0;left:0;right:0;z-index:100;}
|
||||
.top-bar__logo{display:flex;align-items:center;gap:8px;color:#fff;font-size:var(--font-lg);font-weight:600;white-space:nowrap;width:240px;flex-shrink:0;}
|
||||
.top-bar__logo svg{width:28px;height:28px;}
|
||||
.top-bar__nav{display:flex;align-items:center;gap:4px;flex:1;}
|
||||
.top-bar__nav a{color:rgba(255,255,255,0.65);text-decoration:none;padding:6px 14px;border-radius:var(--radius-sm);font-size:var(--font-sm);transition:all .2s;}
|
||||
.top-bar__nav a:hover{color:#fff;background:rgba(255,255,255,0.08);}
|
||||
.top-bar__nav a.active{color:#fff;background:var(--primary);}
|
||||
.top-bar__right{display:flex;align-items:center;gap:16px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);}
|
||||
.btn-back{color:rgba(255,255,255,0.65);text-decoration:none;}
|
||||
.btn-back:hover{color:#fff;}
|
||||
.avatar{width:32px;height:32px;border-radius:50%;background:#1890FF;display:flex;align-items:center;justify-content:center;color:#fff;font-size:var(--font-sm);}
|
||||
.sidebar{position:fixed;top:56px;left:0;bottom:0;width:240px;background:#001529;overflow-y:auto;z-index:90;padding-top:8px;}
|
||||
.sidebar-menu__title{color:rgba(255,255,255,0.35);font-size:var(--font-xs);padding:16px 24px 8px;text-transform:uppercase;letter-spacing:.5px;}
|
||||
.sidebar-item{display:flex;align-items:center;gap:8px;padding:10px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
|
||||
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,0.06);}
|
||||
.sidebar-item.active{color:#fff;background:var(--primary);}
|
||||
.mi{display:inline-block;width:20px;text-align:center;filter:grayscale(1);opacity:.75;margin-right:2px;}
|
||||
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
|
||||
.breadcrumb{font-size:var(--font-sm);color:var(--text-secondary);margin-bottom:12px;}
|
||||
.breadcrumb a{color:var(--primary);text-decoration:none;}
|
||||
.page-header{display:flex;align-items:flex-start;justify-content:space-between;background:#fff;border-radius:var(--radius-md);padding:20px 24px;margin-bottom:16px;box-shadow:var(--shadow-sm);}
|
||||
.page-header__title{font-size:var(--font-xl);font-weight:700;color:var(--text-primary);}
|
||||
.page-header__desc{margin-top:6px;font-size:var(--font-sm);color:var(--text-secondary);}
|
||||
.page-header__actions{display:flex;align-items:center;gap:8px;}
|
||||
.btn{height:36px;padding:0 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;border:none;transition:all .2s;display:inline-flex;align-items:center;gap:6px;white-space:nowrap;}
|
||||
.btn-primary{background:var(--primary);color:#fff;}
|
||||
.btn-primary:hover{background:#40a9ff;}
|
||||
.btn-default{background:#fff;color:var(--text-primary);border:1px solid var(--border);}
|
||||
.btn-default:hover{color:var(--primary);border-color:var(--primary);}
|
||||
.overview-grid{display:grid;grid-template-columns:2fr 1fr;gap:16px;margin-bottom:16px;}
|
||||
.card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
|
||||
.card__header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--border);}
|
||||
.card__title{font-size:var(--font-base);font-weight:600;}
|
||||
.card__body{padding:18px 20px;}
|
||||
.info-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:16px 24px;}
|
||||
.info-item{display:flex;flex-direction:column;gap:4px;}
|
||||
.info-label{font-size:var(--font-xs);color:var(--text-placeholder);}
|
||||
.info-value{font-size:var(--font-sm);color:var(--text-primary);font-weight:500;min-height:22px;}
|
||||
.tag{display:inline-flex;align-items:center;padding:2px 8px;border-radius:10px;font-size:var(--font-xs);width:max-content;}
|
||||
.tag-green{background:#F6FFED;color:var(--success);}
|
||||
.tag-blue{background:var(--primary-light);color:var(--primary);}
|
||||
.tag-orange{background:#FFF7E6;color:var(--warning);}
|
||||
.tag-red{background:#FFF1F0;color:var(--danger);}
|
||||
.tag-gray{background:#F5F5F5;color:#999;}
|
||||
.resource-list{display:flex;flex-direction:column;gap:14px;}
|
||||
.resource-row{display:grid;grid-template-columns:60px 1fr 48px;align-items:center;gap:10px;font-size:var(--font-sm);}
|
||||
.bar{height:8px;background:#F0F2F5;border-radius:4px;overflow:hidden;}
|
||||
.bar span{display:block;height:100%;border-radius:4px;background:var(--primary);}
|
||||
.bar.warning span{background:var(--warning);}
|
||||
.bar.danger span{background:var(--danger);}
|
||||
.tabs{display:flex;align-items:center;background:#fff;border-radius:var(--radius-md) var(--radius-md) 0 0;box-shadow:var(--shadow-sm);overflow:hidden;}
|
||||
.tab{padding:14px 22px;font-size:var(--font-sm);cursor:pointer;border-bottom:2px solid transparent;color:var(--text-secondary);}
|
||||
.tab:hover{color:var(--primary);}
|
||||
.tab.active{color:var(--primary);border-bottom-color:var(--primary);font-weight:600;}
|
||||
.tab-panel{display:none;background:#fff;border-radius:0 0 var(--radius-md) var(--radius-md);box-shadow:var(--shadow-sm);padding:18px 20px;}
|
||||
.tab-panel.active{display:block;}
|
||||
.table-wrap{overflow-x:auto;}
|
||||
.table-wrap table{min-width:1100px;}
|
||||
table{width:100%;border-collapse:collapse;}
|
||||
thead th{background:#FAFAFA;padding:12px 16px;text-align:left;font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);white-space:nowrap;}
|
||||
tbody td{padding:14px 16px;font-size:var(--font-sm);color:var(--text-primary);border-bottom:1px solid var(--border);white-space:nowrap;vertical-align:middle;}
|
||||
tbody tr:hover{background:#F8FBFF;}
|
||||
thead th:last-child,tbody td.td-actions{position:sticky;right:0;background:#fff;box-shadow:-4px 0 8px rgba(0,0,0,0.04);z-index:2;}
|
||||
thead th:last-child{background:#FAFAFA;}
|
||||
tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
.link{color:var(--primary);cursor:pointer;font-size:var(--font-sm);text-decoration:none;}
|
||||
.link:hover{color:#40a9ff;}
|
||||
.timeline{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-bottom:18px;}
|
||||
.mini-stat{border:1px solid var(--border);border-radius:var(--radius-md);padding:14px 16px;background:#FAFCFF;}
|
||||
.mini-stat__value{font-size:var(--font-xl);font-weight:700;}
|
||||
.mini-stat__label{font-size:var(--font-sm);color:var(--text-secondary);margin-top:5px;}
|
||||
.toast{position:fixed;top:76px;left:50%;transform:translateX(-50%) translateY(-12px);background:#fff;border-left:4px solid var(--primary);box-shadow:var(--shadow-md);padding:10px 18px;border-radius:var(--radius-sm);font-size:var(--font-sm);opacity:0;pointer-events:none;transition:all .25s;z-index:1200;}
|
||||
.toast.show{opacity:1;transform:translateX(-50%) translateY(0);}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header class="top-bar">
|
||||
<div class="top-bar__logo">
|
||||
<svg viewBox="0 0 28 28" fill="none"><rect width="28" height="28" rx="6" fill="#1890FF"/><path d="M7 14h14M14 7v14" stroke="#fff" stroke-width="2.5" stroke-linecap="round"/></svg>
|
||||
营养管理
|
||||
</div>
|
||||
<nav class="top-bar__nav">
|
||||
<a href="trace-query.html">溯源总览</a><a href="arc-employee.html">档案</a><a href="farm-land.html">绿色种采</a><a href="sup-hygiene-check.html">卫生供应</a><a href="prod-qc-daily.html">健康生产</a><a href="din-dict-mgmt.html">营养用餐</a><a href="ano-dirty.html">异常事件</a><a href="edge-node-list.html" class="active">边缘服务器</a>
|
||||
</nav>
|
||||
<div class="top-bar__right"><a href="../../index.html" class="btn-back">← 返回首页</a><div class="avatar">管</div><span>系统管理员 ▾</span></div>
|
||||
</header>
|
||||
|
||||
<aside class="sidebar">
|
||||
<div class="sidebar-menu__title">边缘服务器</div>
|
||||
<a href="edge-alarm-center.html" class="sidebar-item"><span class="mi">⚠</span>告警中心</a>
|
||||
<a href="edge-sync-monitor.html" class="sidebar-item"><span class="mi">🔄</span>同步监控</a>
|
||||
<a href="edge-dispatch-compensation.html" class="sidebar-item"><span class="mi">📦</span>下发与补偿</a>
|
||||
<a href="edge-node-list.html" class="sidebar-item active"><span class="mi">🧭</span>边缘服务器列表</a>
|
||||
</aside>
|
||||
|
||||
<main class="main">
|
||||
<div class="breadcrumb"><a href="edge-alarm-center.html">告警中心</a> / <a href="edge-node-list.html">边缘服务器列表</a> / 服务器故障详情</div>
|
||||
<section class="page-header">
|
||||
<div>
|
||||
<div class="page-header__title">采气一厂后厨服务器</div>
|
||||
<div class="page-header__desc">EDGE-CQ-017 · 采气一厂食堂 · 后厨机房 · 当前处于上报故障排查中 · 最近心跳 2026-06-23 14:31:58</div>
|
||||
</div>
|
||||
<div class="page-header__actions">
|
||||
<button class="btn btn-default" onclick="location.href='edge-alarm-center.html'">返回告警中心</button>
|
||||
<button class="btn btn-default" onclick="location.href='edge-sync-monitor.html'">查看同步链路</button>
|
||||
<button class="btn btn-primary" onclick="location.href='edge-dispatch-compensation.html'">发起恢复补偿</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="overview-grid">
|
||||
<div class="card">
|
||||
<div class="card__header"><div class="card__title">基本信息</div><span class="tag tag-orange">健康告警</span></div>
|
||||
<div class="card__body">
|
||||
<div class="info-grid">
|
||||
<div class="info-item"><div class="info-label">服务器名称</div><div class="info-value">采气一厂后厨服务器</div></div>
|
||||
<div class="info-item"><div class="info-label">服务器编号</div><div class="info-value">EDGE-CQ-017</div></div>
|
||||
<div class="info-item"><div class="info-label">服务器 IP</div><div class="info-value">10.18.32.17</div></div>
|
||||
<div class="info-item"><div class="info-label">部署位置</div><div class="info-value">后厨机房</div></div>
|
||||
<div class="info-item"><div class="info-label">服务版本</div><div class="info-value">v2.3.0</div></div>
|
||||
<div class="info-item"><div class="info-label">配置版本</div><div class="info-value">CFG-20260623-04</div></div>
|
||||
<div class="info-item"><div class="info-label">安装时间</div><div class="info-value">2026-05-12 09:20</div></div>
|
||||
<div class="info-item"><div class="info-label">最近启动</div><div class="info-value">2026-06-23 08:11</div></div>
|
||||
<div class="info-item"><div class="info-label">本地数据库</div><div class="info-value"><span class="tag tag-green">正常</span></div></div>
|
||||
<div class="info-item"><div class="info-label">MQ 连接</div><div class="info-value"><span class="tag tag-orange">间歇断连</span></div></div>
|
||||
<div class="info-item"><div class="info-label">未同步数据</div><div class="info-value" style="color:var(--warning)">486 条</div></div>
|
||||
<div class="info-item"><div class="info-label">最后上报成功</div><div class="info-value">2026-06-23 13:42:11</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card__header"><div class="card__title">资源状态</div><span class="tag tag-orange">磁盘预警</span></div>
|
||||
<div class="card__body">
|
||||
<div class="resource-list">
|
||||
<div class="resource-row"><span>CPU</span><div class="bar"><span style="width:46%"></span></div><strong>46%</strong></div>
|
||||
<div class="resource-row"><span>内存</span><div class="bar"><span style="width:68%"></span></div><strong>68%</strong></div>
|
||||
<div class="resource-row"><span>磁盘</span><div class="bar warning"><span style="width:82%"></span></div><strong>82%</strong></div>
|
||||
<div class="resource-row"><span>缓存</span><div class="bar"><span style="width:61%"></span></div><strong>61%</strong></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="tabs">
|
||||
<div class="tab active" onclick="switchTab('base',this)">基本信息</div>
|
||||
<div class="tab" onclick="switchTab('devices',this)">设备列表</div>
|
||||
<div class="tab" onclick="switchTab('sync',this)">同步记录</div>
|
||||
<div class="tab" onclick="switchTab('dispatch',this)">下发记录</div>
|
||||
<div class="tab" onclick="switchTab('files',this)">文件缓存</div>
|
||||
<div class="tab" onclick="switchTab('upgrade',this)">升级记录</div>
|
||||
<div class="tab" onclick="switchTab('alarm',this)">告警记录</div>
|
||||
</section>
|
||||
|
||||
<section id="base" class="tab-panel active">
|
||||
<div class="timeline">
|
||||
<div class="mini-stat"><div class="mini-stat__value">12 / 18</div><div class="mini-stat__label">在线设备 / 绑定设备</div></div>
|
||||
<div class="mini-stat"><div class="mini-stat__value" style="color:var(--warning)">486</div><div class="mini-stat__label">上行待同步数据</div></div>
|
||||
<div class="mini-stat"><div class="mini-stat__value" style="color:var(--warning)">6</div><div class="mini-stat__label">下发失败记录</div></div>
|
||||
<div class="mini-stat"><div class="mini-stat__value" style="color:var(--danger)">3</div><div class="mini-stat__label">当前未恢复告警</div></div>
|
||||
</div>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead><tr><th>检查项</th><th>当前状态</th><th>最近更新时间</th><th>说明</th><th>操作</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>设备接入服务</td><td><span class="tag tag-green">可用</span></td><td>2026-06-23 14:31:58</td><td>营养秤、组配秤、结算终端可正常访问本地 API</td><td class="td-actions"><a class="link">查看日志</a></td></tr>
|
||||
<tr><td>同步引擎</td><td><span class="tag tag-orange">重试中</span></td><td>2026-06-23 14:30:21</td><td>MQ 连接抖动,上行数据存在积压</td><td class="td-actions"><a class="link" href="edge-sync-monitor.html">查看同步</a></td></tr>
|
||||
<tr><td>文件缓存</td><td><span class="tag tag-green">可用</span></td><td>2026-06-23 14:20:12</td><td>升级包、人脸图、过程图片缓存正常</td><td class="td-actions"><a class="link">清理策略</a></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="devices" class="tab-panel">
|
||||
<div class="table-wrap"><table><thead><tr><th>设备名称</th><th>设备编号</th><th>设备类型</th><th>业务场景</th><th>绑定位置</th><th>在线状态</th><th>最近活跃</th><th>当前版本</th><th>最近升级结果</th><th>操作</th></tr></thead><tbody>
|
||||
<tr><td>智能组配秤 01</td><td>CMB-SCALE-001</td><td>净菜组配秤</td><td>净菜组配</td><td>后厨组配区</td><td><span class="tag tag-green">在线</span></td><td>2026-06-23 14:31:42</td><td>v1.8.2</td><td><span class="tag tag-green">成功</span></td><td class="td-actions"><a class="link">设备详情</a> <a class="link">设备日志</a></td></tr>
|
||||
<tr><td>餐线营养秤 02</td><td>LINE-SCALE-002</td><td>餐线营养秤</td><td>上餐监控</td><td>一号餐线</td><td><span class="tag tag-orange">离线</span></td><td>2026-06-23 14:02:18</td><td>v1.7.6</td><td><span class="tag tag-gray">未升级</span></td><td class="td-actions"><a class="link">重新拉取配置</a> <a class="link">下发升级</a></td></tr>
|
||||
<tr><td>人脸识别终端 03</td><td>FACE-TERM-003</td><td>人脸识别设备</td><td>就餐识别</td><td>入口闸机</td><td><span class="tag tag-green">在线</span></td><td>2026-06-23 14:31:55</td><td>v3.2.0</td><td><span class="tag tag-green">成功</span></td><td class="td-actions"><a class="link">设备详情</a> <a class="link">设备日志</a></td></tr>
|
||||
</tbody></table></div>
|
||||
</section>
|
||||
|
||||
<section id="sync" class="tab-panel">
|
||||
<div class="table-wrap"><table><thead><tr><th>方向</th><th>业务类型</th><th>业务编号</th><th>发生/下发时间</th><th>当前状态</th><th>失败次数</th><th>失败原因</th><th>最近处理时间</th><th>操作</th></tr></thead><tbody>
|
||||
<tr><td>上行</td><td>就餐流水</td><td>MEAL-20260623-0178</td><td>2026-06-23 12:11:04</td><td><span class="tag tag-orange">待重试</span></td><td>3</td><td>MQ 连接超时</td><td>2026-06-23 14:28:33</td><td class="td-actions"><a class="link">查看详情</a> <a class="link">手动重试</a></td></tr>
|
||||
<tr><td>下行</td><td>人脸增量</td><td>FACE-BATCH-0623-04</td><td>2026-06-23 10:00:00</td><td><span class="tag tag-red">应用失败</span></td><td>2</td><td>本地特征库写入失败</td><td>2026-06-23 13:57:22</td><td class="td-actions"><a class="link" href="edge-dispatch-compensation.html">发起补偿</a></td></tr>
|
||||
</tbody></table></div>
|
||||
</section>
|
||||
|
||||
<section id="dispatch" class="tab-panel">
|
||||
<div class="table-wrap"><table><thead><tr><th>下发类型</th><th>批次号</th><th>下发时间</th><th>应用状态</th><th>失败原因</th><th>处理完成时间</th><th>操作</th></tr></thead><tbody>
|
||||
<tr><td>基础资料</td><td>BASE-20260623-003</td><td>2026-06-23 09:30:00</td><td><span class="tag tag-green">已应用</span></td><td>—</td><td>2026-06-23 09:31:28</td><td class="td-actions"><a class="link">查看回执</a></td></tr>
|
||||
<tr><td>升级任务</td><td>UPG-20260623-002</td><td>2026-06-23 11:00:00</td><td><span class="tag tag-orange">部分失败</span></td><td>设备离线 2 台</td><td>—</td><td class="td-actions"><a class="link" href="edge-dispatch-compensation.html">补偿处理</a></td></tr>
|
||||
</tbody></table></div>
|
||||
</section>
|
||||
|
||||
<section id="files" class="tab-panel">
|
||||
<div class="table-wrap"><table><thead><tr><th>文件类型</th><th>文件名称</th><th>文件版本</th><th>文件大小</th><th>下载状态</th><th>校验状态</th><th>最近下载时间</th><th>来源任务</th><th>操作</th></tr></thead><tbody>
|
||||
<tr><td>升级包</td><td>line-scale-v1.8.2.bin</td><td>v1.8.2</td><td>86 MB</td><td><span class="tag tag-green">已下载</span></td><td><span class="tag tag-green">通过</span></td><td>2026-06-23 10:42:00</td><td>UPG-20260623-002</td><td class="td-actions"><a class="link">重新校验</a></td></tr>
|
||||
<tr><td>人脸图片</td><td>face-batch-0623-04.zip</td><td>20260623.04</td><td>312 MB</td><td><span class="tag tag-orange">下载中</span></td><td><span class="tag tag-gray">待校验</span></td><td>2026-06-23 14:20:00</td><td>FACE-BATCH-0623-04</td><td class="td-actions"><a class="link">查看任务</a></td></tr>
|
||||
</tbody></table></div>
|
||||
</section>
|
||||
|
||||
<section id="upgrade" class="tab-panel">
|
||||
<div class="table-wrap"><table><thead><tr><th>升级任务编号</th><th>升级对象</th><th>目标版本</th><th>下发时间</th><th>当前状态</th><th>完成时间</th><th>失败原因</th><th>操作</th></tr></thead><tbody>
|
||||
<tr><td>UPG-20260623-002</td><td>餐线营养秤 02</td><td>v1.8.2</td><td>2026-06-23 11:00:00</td><td><span class="tag tag-red">失败</span></td><td>—</td><td>设备离线,未接收升级通知</td><td class="td-actions"><a class="link">重新通知设备</a></td></tr>
|
||||
<tr><td>UPG-20260621-009</td><td>智能组配秤 01</td><td>v1.8.2</td><td>2026-06-21 20:00:00</td><td><span class="tag tag-green">成功</span></td><td>2026-06-21 20:16:28</td><td>—</td><td class="td-actions"><a class="link">查看日志</a></td></tr>
|
||||
</tbody></table></div>
|
||||
</section>
|
||||
|
||||
<section id="alarm" class="tab-panel">
|
||||
<div class="table-wrap"><table><thead><tr><th>告警类型</th><th>告警等级</th><th>首次发生时间</th><th>最近发生时间</th><th>当前状态</th><th>处理人</th><th>处理说明</th><th>操作</th></tr></thead><tbody>
|
||||
<tr><td>同步类 · 上行积压</td><td><span class="tag tag-orange">警告</span></td><td>2026-06-23 13:45:00</td><td>2026-06-23 14:28:00</td><td><span class="tag tag-orange">处理中</span></td><td>运维张工</td><td>正在排查 MQ 连接抖动</td><td class="td-actions"><a class="link" href="edge-alarm-center.html">告警详情</a></td></tr>
|
||||
<tr><td>升级类 · 设备未执行升级</td><td><span class="tag tag-red">严重</span></td><td>2026-06-23 11:10:00</td><td>2026-06-23 14:00:00</td><td><span class="tag tag-orange">待处理</span></td><td>未指派</td><td>餐线营养秤离线</td><td class="td-actions"><a class="link" href="edge-dispatch-compensation.html">发起补偿</a></td></tr>
|
||||
</tbody></table></div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<div class="toast" id="toast">操作已完成</div>
|
||||
<script>
|
||||
function switchTab(id,el){
|
||||
document.querySelectorAll('.tab').forEach(function(tab){tab.classList.remove('active');});
|
||||
document.querySelectorAll('.tab-panel').forEach(function(panel){panel.classList.remove('active');});
|
||||
el.classList.add('active');
|
||||
document.getElementById(id).classList.add('active');
|
||||
}
|
||||
function showToast(text){
|
||||
var toast=document.getElementById('toast');
|
||||
toast.textContent=text;
|
||||
toast.classList.add('show');
|
||||
setTimeout(function(){toast.classList.remove('show');},1800);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>营养管理 - 数据上报管理 | 健康CQ原型</title>
|
||||
<style>
|
||||
*{box-sizing:border-box;margin:0;padding:0;}
|
||||
body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif;background:#F5F7FA;color:#333;min-width:1280px;}
|
||||
:root{--primary:#1890FF;--primary-light:#E6F7FF;--success:#52C41A;--warning:#FAAD14;--danger:#FF4D4F;--text-primary:#333;--text-secondary:#666;--text-placeholder:#999;--border:#E8E8E8;--font-xs:11px;--font-sm:13px;--font-base:15px;--font-lg:17px;--font-xl:20px;--font-xxl:24px;--radius-sm:4px;--radius-md:8px;--radius-lg:12px;--shadow-sm:0 1px 4px rgba(0,0,0,.08);--shadow-md:0 4px 12px rgba(0,0,0,.12);}
|
||||
.top-bar{display:flex;align-items:center;height:56px;background:#001529;padding:0 24px;position:fixed;top:0;left:0;right:0;z-index:100;}
|
||||
.top-bar__logo{display:flex;align-items:center;gap:8px;color:#fff;font-size:var(--font-lg);font-weight:600;white-space:nowrap;width:240px;flex-shrink:0;}
|
||||
.top-bar__logo svg{width:28px;height:28px;}
|
||||
.top-bar__nav{display:flex;align-items:center;gap:4px;flex:1;}
|
||||
.top-bar__nav a{color:rgba(255,255,255,.65);text-decoration:none;padding:6px 14px;border-radius:var(--radius-sm);font-size:var(--font-sm);transition:all .2s;}
|
||||
.top-bar__nav a:hover{color:#fff;background:rgba(255,255,255,.08);}
|
||||
.top-bar__nav a.active{color:#fff;background:var(--primary);}
|
||||
.top-bar__right{display:flex;align-items:center;gap:16px;color:rgba(255,255,255,.65);font-size:var(--font-sm);}
|
||||
.btn-back{color:rgba(255,255,255,.65);text-decoration:none;}
|
||||
.btn-back:hover{color:#fff;}
|
||||
.avatar{width:32px;height:32px;border-radius:50%;background:#1890FF;display:flex;align-items:center;justify-content:center;color:#fff;font-size:var(--font-sm);}
|
||||
.sidebar{position:fixed;top:56px;left:0;bottom:0;width:240px;background:#001529;overflow-y:auto;z-index:90;padding-top:8px;}
|
||||
.sidebar-menu__title{color:rgba(255,255,255,.35);font-size:var(--font-xs);padding:16px 24px 8px;text-transform:uppercase;letter-spacing:.5px;}
|
||||
.sidebar-item{display:flex;align-items:center;gap:8px;padding:10px 24px;color:rgba(255,255,255,.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
|
||||
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,.06);}
|
||||
.sidebar-item.active{color:#fff;background:var(--primary);}
|
||||
.mi{display:inline-block;width:20px;text-align:center;filter:grayscale(1);opacity:.75;margin-right:2px;}
|
||||
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
|
||||
.page-header{display:flex;align-items:center;justify-content:space-between;background:#fff;border-radius:var(--radius-md);padding:18px 24px;margin-bottom:16px;box-shadow:var(--shadow-sm);}
|
||||
.page-header__title{font-size:var(--font-xl);font-weight:700;}
|
||||
.page-header__desc{margin-top:6px;font-size:var(--font-sm);color:var(--text-secondary);}
|
||||
.page-header__meta{font-size:var(--font-sm);color:var(--text-placeholder);}
|
||||
.stats-grid{display:grid;grid-template-columns:repeat(6,1fr);gap:12px;margin-bottom:16px;}
|
||||
.stat-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);padding:16px 18px;}
|
||||
.stat-card__value{font-size:var(--font-xxl);font-weight:700;line-height:1;color:var(--text-primary);}
|
||||
.stat-card__label{font-size:var(--font-sm);color:var(--text-secondary);margin-top:8px;}
|
||||
.stat-card__hint{font-size:var(--font-xs);color:var(--text-placeholder);margin-top:8px;}
|
||||
.filter-bar{background:#fff;border-radius:var(--radius-md);padding:18px 24px;margin-bottom:16px;box-shadow:var(--shadow-sm);display:flex;align-items:center;gap:14px;flex-wrap:wrap;}
|
||||
.filter-item{display:flex;align-items:center;gap:8px;}
|
||||
.filter-item label{font-size:var(--font-sm);color:var(--text-secondary);white-space:nowrap;}
|
||||
.filter-input,.filter-select{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);outline:none;background:#fff;}
|
||||
.filter-input{min-width:170px;}
|
||||
.filter-select{min-width:126px;appearance:auto;}
|
||||
.btn{height:36px;padding:0 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;border:none;display:inline-flex;align-items:center;gap:6px;white-space:nowrap;}
|
||||
.btn-primary{background:var(--primary);color:#fff;}
|
||||
.btn-default{background:#fff;color:var(--text-primary);border:1px solid var(--border);}
|
||||
.btn-default:hover{color:var(--primary);border-color:var(--primary);}
|
||||
.table-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
|
||||
.table-card__header{display:flex;align-items:center;justify-content:space-between;padding:16px 24px;border-bottom:1px solid var(--border);}
|
||||
.table-card__title{font-size:var(--font-base);font-weight:600;}
|
||||
.table-wrap{overflow-x:auto;}
|
||||
.table-wrap table{min-width:1640px;}
|
||||
table{width:100%;border-collapse:collapse;}
|
||||
thead th{background:#FAFAFA;padding:12px 16px;text-align:left;font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);white-space:nowrap;}
|
||||
tbody td{padding:14px 16px;font-size:var(--font-sm);color:var(--text-primary);border-bottom:1px solid var(--border);white-space:nowrap;vertical-align:middle;}
|
||||
tbody tr:hover{background:#F8FBFF;}
|
||||
thead th:last-child,tbody td.td-actions{position:sticky;right:0;background:#fff;box-shadow:-4px 0 8px rgba(0,0,0,.04);z-index:2;}
|
||||
thead th:last-child{background:#FAFAFA;}
|
||||
tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
.tag{display:inline-flex;align-items:center;padding:2px 8px;border-radius:10px;font-size:var(--font-xs);}
|
||||
.tag-green{background:#F6FFED;color:var(--success);}
|
||||
.tag-blue{background:var(--primary-light);color:var(--primary);}
|
||||
.tag-orange{background:#FFF7E6;color:var(--warning);}
|
||||
.tag-red{background:#FFF1F0;color:var(--danger);}
|
||||
.tag-gray{background:#F5F5F5;color:#999;}
|
||||
.link{color:var(--primary);cursor:pointer;font-size:var(--font-sm);text-decoration:none;}
|
||||
.link:hover{color:#40a9ff;}
|
||||
.drawer-mask{position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:1000;display:none;}
|
||||
.drawer-mask.show{display:block;}
|
||||
.drawer{position:absolute;right:0;top:0;bottom:0;width:560px;background:#fff;box-shadow:-6px 0 24px rgba(0,0,0,.12);display:flex;flex-direction:column;}
|
||||
.drawer__header{display:flex;align-items:center;justify-content:space-between;padding:18px 24px;border-bottom:1px solid var(--border);}
|
||||
.drawer__title{font-size:var(--font-lg);font-weight:600;}
|
||||
.drawer__close{border:none;background:none;font-size:22px;cursor:pointer;color:var(--text-placeholder);}
|
||||
.drawer__body{padding:20px 24px;overflow-y:auto;flex:1;}
|
||||
.detail-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px 18px;}
|
||||
.detail-item{display:flex;flex-direction:column;gap:3px;}
|
||||
.detail-label{font-size:var(--font-xs);color:var(--text-placeholder);}
|
||||
.detail-value{font-size:var(--font-sm);color:var(--text-primary);}
|
||||
.section-title{font-size:var(--font-sm);font-weight:600;margin:16px 0 10px;padding-left:8px;border-left:3px solid var(--primary);}
|
||||
.toast{position:fixed;top:76px;left:50%;transform:translateX(-50%) translateY(-12px);background:#fff;border-left:4px solid var(--primary);box-shadow:var(--shadow-md);padding:10px 18px;border-radius:var(--radius-sm);font-size:var(--font-sm);opacity:0;pointer-events:none;transition:all .25s;z-index:1200;}
|
||||
.toast.show{opacity:1;transform:translateX(-50%) translateY(0);}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header class="top-bar">
|
||||
<div class="top-bar__logo"><svg viewBox="0 0 28 28" fill="none"><rect width="28" height="28" rx="6" fill="#1890FF"/><path d="M7 14h14M14 7v14" stroke="#fff" stroke-width="2.5" stroke-linecap="round"/></svg>营养管理</div>
|
||||
<nav class="top-bar__nav"><a href="trace-query.html">溯源总览</a><a href="arc-employee.html">档案</a><a href="farm-land.html">绿色种采</a><a href="sup-hygiene-check.html">卫生供应</a><a href="prod-qc-daily.html">健康生产</a><a href="din-dict-mgmt.html">营养用餐</a><a href="ano-dirty.html">异常事件</a><a href="edge-server-management.html" class="active">边缘服务器</a></nav>
|
||||
<div class="top-bar__right"><a href="../../index.html" class="btn-back">← 返回首页</a><div class="avatar">管</div><span>系统管理员 ▾</span></div>
|
||||
</header>
|
||||
|
||||
<aside class="sidebar">
|
||||
<div class="sidebar-menu__title">边缘服务器</div>
|
||||
<a href="edge-server-management.html" class="sidebar-item"><span class="mi">🧭</span>边缘服务器管理</a>
|
||||
<a href="edge-report-management.html" class="sidebar-item active"><span class="mi">📡</span>数据上报管理</a>
|
||||
<a href="edge-dispatch-management.html" class="sidebar-item"><span class="mi">📦</span>数据下发管理</a>
|
||||
<a href="edge-alert-center.html" class="sidebar-item"><span class="mi">⚠</span>异常告警中心</a>
|
||||
</aside>
|
||||
|
||||
<main class="main">
|
||||
<section class="page-header">
|
||||
<div><div class="page-header__title">数据上报管理</div><div class="page-header__desc">从全局视角查看所有边缘服务器的数据上报日志,定位上报失败、重试、延迟和数据回传中断问题。</div></div>
|
||||
<div class="page-header__meta">最近刷新:2026-06-24 09:26:00</div>
|
||||
</section>
|
||||
|
||||
<section class="stats-grid">
|
||||
<div class="stat-card"><div class="stat-card__value">4,286</div><div class="stat-card__label">当日上报总量</div><div class="stat-card__hint">全部服务器汇总</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value" style="color:var(--success)">4,102</div><div class="stat-card__label">上报成功</div><div class="stat-card__hint">成功率 95.7%</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value" style="color:var(--warning)">132</div><div class="stat-card__label">待重试</div><div class="stat-card__hint">主要集中午餐高峰</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value" style="color:var(--danger)">52</div><div class="stat-card__label">上报失败</div><div class="stat-card__hint">需人工介入 11 条</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value" style="color:var(--warning)">3</div><div class="stat-card__label">异常服务器</div><div class="stat-card__hint">成功率低于阈值</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value">6.2s</div><div class="stat-card__label">平均上报耗时</div><div class="stat-card__hint">较昨日 +1.4s</div></div>
|
||||
</section>
|
||||
|
||||
<section class="filter-bar">
|
||||
<div class="filter-item"><label>所属食堂</label><select class="filter-select"><option>全部</option><option>总部一号食堂</option><option>采气一厂食堂</option><option>炼化中心食堂</option></select></div>
|
||||
<div class="filter-item"><label>边缘服务器</label><select class="filter-select"><option>全部</option><option>采气一厂后厨服务器</option><option>炼化中心餐线服务器</option></select></div>
|
||||
<div class="filter-item"><label>数据类型</label><select class="filter-select"><option>全部</option><option>就餐流水</option><option>称重记录</option><option>设备日志</option><option>监控图片</option></select></div>
|
||||
<div class="filter-item"><label>上报状态</label><select class="filter-select"><option>全部</option><option>已上报</option><option>待重试</option><option>上报失败</option></select></div>
|
||||
<div class="filter-item"><label>发生时间</label><input class="filter-input" type="date"></div>
|
||||
<div class="filter-item"><label>关键字</label><input class="filter-input" placeholder="业务编号 / 设备编号"></div>
|
||||
<button class="btn btn-primary" onclick="showToast('已刷新上报日志')">查询</button>
|
||||
<button class="btn btn-default" onclick="showToast('正在导出上报日志')">导出日志</button>
|
||||
</section>
|
||||
|
||||
<section class="table-card">
|
||||
<div class="table-card__header"><div class="table-card__title">边缘服务器上报日志</div><button class="btn btn-default" onclick="showToast('已筛选高风险上报记录')">仅看异常</button></div>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead><tr><th>食堂</th><th>边缘服务器</th><th>数据类型</th><th>业务编号</th><th>来源设备</th><th>产生时间</th><th>最近上报时间</th><th>上报状态</th><th>中心服务器受理状态</th><th>重试次数</th><th>失败原因</th><th>关联告警</th><th>操作</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>炼化中心食堂</td><td><a class="link" href="edge-server-detail.html">炼化中心餐线服务器</a></td><td>就餐流水</td><td>MEAL-20260624-0781</td><td>结算终端 01</td><td>2026-06-24 08:21:18</td><td>2026-06-24 09:21:20</td><td><span class="tag tag-red">上报失败</span></td><td><span class="tag tag-gray">未受理</span></td><td>8</td><td>服务器离线,等待恢复</td><td><a class="link" href="edge-alert-center.html">ALM-20260624-091</a></td><td class="td-actions"><a class="link" onclick="openDrawer('MEAL-20260624-0781')">查看详情</a></td></tr>
|
||||
<tr><td>采气一厂食堂</td><td><a class="link" href="edge-server-detail.html">采气一厂后厨服务器</a></td><td>称重记录</td><td>WEIGH-20260624-0148</td><td>餐线营养秤 02</td><td>2026-06-24 08:42:03</td><td>2026-06-24 09:18:33</td><td><span class="tag tag-orange">待重试</span></td><td><span class="tag tag-orange">受理超时</span></td><td>3</td><td>MQ 连接超时</td><td><a class="link" href="edge-alert-center.html">ALM-20260624-078</a></td><td class="td-actions"><a class="link" onclick="openDrawer('WEIGH-20260624-0148')">查看详情</a></td></tr>
|
||||
<tr><td>总部一号食堂</td><td><a class="link" href="edge-server-detail.html">总部一号食堂服务器</a></td><td>设备日志</td><td>LOG-20260624-0311</td><td>组配秤 03</td><td>2026-06-24 09:05:48</td><td>2026-06-24 09:06:03</td><td><span class="tag tag-green">已上报</span></td><td><span class="tag tag-green">已受理</span></td><td>0</td><td>—</td><td>—</td><td class="td-actions"><a class="link" onclick="openDrawer('LOG-20260624-0311')">查看详情</a></td></tr>
|
||||
<tr><td>输气站小食堂</td><td><a class="link" href="edge-server-detail.html">输气站小食堂服务器</a></td><td>监控图片</td><td>IMG-20260624-0096</td><td>后厨摄像头 01</td><td>2026-06-24 08:58:27</td><td>2026-06-24 09:00:11</td><td><span class="tag tag-green">已上报</span></td><td><span class="tag tag-blue">受理中</span></td><td>0</td><td>—</td><td>—</td><td class="td-actions"><a class="link" onclick="openDrawer('IMG-20260624-0096')">查看详情</a></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<div class="drawer-mask" id="drawerMask">
|
||||
<div class="drawer">
|
||||
<div class="drawer__header"><div class="drawer__title">上报日志详情</div><button class="drawer__close" onclick="closeDrawer()">×</button></div>
|
||||
<div class="drawer__body">
|
||||
<div class="detail-grid">
|
||||
<div class="detail-item"><div class="detail-label">业务编号</div><div class="detail-value" id="detailBizId">WEIGH-20260624-0148</div></div>
|
||||
<div class="detail-item"><div class="detail-label">边缘服务器</div><div class="detail-value">采气一厂后厨服务器</div></div>
|
||||
<div class="detail-item"><div class="detail-label">数据类型</div><div class="detail-value">称重记录</div></div>
|
||||
<div class="detail-item"><div class="detail-label">来源设备</div><div class="detail-value">餐线营养秤 02</div></div>
|
||||
<div class="detail-item"><div class="detail-label">产生时间</div><div class="detail-value">2026-06-24 08:42:03</div></div>
|
||||
<div class="detail-item"><div class="detail-label">当前状态</div><div class="detail-value"><span class="tag tag-orange">待重试</span></div></div>
|
||||
</div>
|
||||
<div class="section-title">处理轨迹</div>
|
||||
<div class="detail-item"><div class="detail-value">09:18:33 第 3 次重试失败,原因:MQ 连接超时。</div></div>
|
||||
<div class="detail-item"><div class="detail-value">09:05:21 第 2 次重试失败,边缘服务器已切换备用通道。</div></div>
|
||||
<div class="detail-item"><div class="detail-value">08:42:04 首次写入边缘本地库成功,等待异步上传。</div></div>
|
||||
<div class="section-title">处理动作</div>
|
||||
<div style="display:flex;gap:8px;flex-wrap:wrap;">
|
||||
<button class="btn btn-primary" onclick="showToast('已加入手动重试队列')">手动重试</button>
|
||||
<button class="btn btn-default" onclick="location.href='edge-server-detail.html'">服务器详情</button>
|
||||
<button class="btn btn-default" onclick="location.href='edge-alert-center.html'">查看告警</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="toast" id="toast">操作已完成</div>
|
||||
<script>
|
||||
function showToast(text){
|
||||
var toast=document.getElementById('toast');
|
||||
toast.textContent=text;
|
||||
toast.classList.add('show');
|
||||
setTimeout(function(){toast.classList.remove('show');},1800);
|
||||
}
|
||||
function openDrawer(bizId){
|
||||
document.getElementById('detailBizId').textContent=bizId;
|
||||
document.getElementById('drawerMask').classList.add('show');
|
||||
}
|
||||
function closeDrawer(){
|
||||
document.getElementById('drawerMask').classList.remove('show');
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,283 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>营养管理 - 边缘服务器详情 | 健康CQ原型</title>
|
||||
<style>
|
||||
*{box-sizing:border-box;margin:0;padding:0;}
|
||||
body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif;background:#F5F7FA;color:#333;min-width:1280px;}
|
||||
:root{--primary:#1890FF;--primary-light:#E6F7FF;--success:#52C41A;--warning:#FAAD14;--danger:#FF4D4F;--text-primary:#333;--text-secondary:#666;--text-placeholder:#999;--border:#E8E8E8;--font-xs:11px;--font-sm:13px;--font-base:15px;--font-lg:17px;--font-xl:20px;--font-xxl:24px;--radius-sm:4px;--radius-md:8px;--radius-lg:12px;--shadow-sm:0 1px 4px rgba(0,0,0,0.08);--shadow-md:0 4px 12px rgba(0,0,0,0.12);}
|
||||
.top-bar{display:flex;align-items:center;height:56px;background:#001529;padding:0 24px;position:fixed;top:0;left:0;right:0;z-index:100;}
|
||||
.top-bar__logo{display:flex;align-items:center;gap:8px;color:#fff;font-size:var(--font-lg);font-weight:600;white-space:nowrap;width:240px;flex-shrink:0;}
|
||||
.top-bar__logo svg{width:28px;height:28px;}
|
||||
.top-bar__nav{display:flex;align-items:center;gap:4px;flex:1;}
|
||||
.top-bar__nav a{color:rgba(255,255,255,0.65);text-decoration:none;padding:6px 14px;border-radius:var(--radius-sm);font-size:var(--font-sm);transition:all .2s;}
|
||||
.top-bar__nav a:hover{color:#fff;background:rgba(255,255,255,0.08);}
|
||||
.top-bar__nav a.active{color:#fff;background:var(--primary);}
|
||||
.top-bar__right{display:flex;align-items:center;gap:16px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);}
|
||||
.btn-back{color:rgba(255,255,255,0.65);text-decoration:none;}
|
||||
.btn-back:hover{color:#fff;}
|
||||
.avatar{width:32px;height:32px;border-radius:50%;background:#1890FF;display:flex;align-items:center;justify-content:center;color:#fff;font-size:var(--font-sm);}
|
||||
.sidebar{position:fixed;top:56px;left:0;bottom:0;width:240px;background:#001529;overflow-y:auto;z-index:90;padding-top:8px;}
|
||||
.sidebar-menu__title{color:rgba(255,255,255,0.35);font-size:var(--font-xs);padding:16px 24px 8px;text-transform:uppercase;letter-spacing:.5px;}
|
||||
.sidebar-item{display:flex;align-items:center;gap:8px;padding:10px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
|
||||
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,0.06);}
|
||||
.sidebar-item.active{color:#fff;background:var(--primary);}
|
||||
.mi{display:inline-block;width:20px;text-align:center;filter:grayscale(1);opacity:.75;margin-right:2px;}
|
||||
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
|
||||
.breadcrumb{font-size:var(--font-sm);color:var(--text-secondary);margin-bottom:12px;}
|
||||
.breadcrumb a{color:var(--primary);text-decoration:none;}
|
||||
.page-header{display:flex;align-items:flex-start;justify-content:space-between;background:#fff;border-radius:var(--radius-md);padding:20px 24px;margin-bottom:16px;box-shadow:var(--shadow-sm);}
|
||||
.page-header__title{font-size:var(--font-xl);font-weight:700;color:var(--text-primary);}
|
||||
.page-header__desc{margin-top:6px;font-size:var(--font-sm);color:var(--text-secondary);}
|
||||
.page-header__actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap;}
|
||||
.btn{height:36px;padding:0 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;border:none;transition:all .2s;display:inline-flex;align-items:center;gap:6px;white-space:nowrap;}
|
||||
.btn-primary{background:var(--primary);color:#fff;}
|
||||
.btn-primary:hover{background:#40a9ff;}
|
||||
.btn-default{background:#fff;color:var(--text-primary);border:1px solid var(--border);}
|
||||
.btn-default:hover{color:var(--primary);border-color:var(--primary);}
|
||||
.summary-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:16px;}
|
||||
.summary-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);padding:16px 18px;}
|
||||
.summary-card__value{font-size:var(--font-xxl);font-weight:700;line-height:1;}
|
||||
.summary-card__label{font-size:var(--font-sm);color:var(--text-secondary);margin-top:8px;}
|
||||
.summary-card__hint{font-size:var(--font-xs);color:var(--text-placeholder);margin-top:8px;}
|
||||
.overview-grid{display:grid;grid-template-columns:2fr 1fr;gap:16px;margin-bottom:16px;}
|
||||
.card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
|
||||
.card__header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--border);}
|
||||
.card__title{font-size:var(--font-base);font-weight:600;}
|
||||
.card__body{padding:18px 20px;}
|
||||
.info-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:16px 24px;}
|
||||
.info-item{display:flex;flex-direction:column;gap:4px;}
|
||||
.info-label{font-size:var(--font-xs);color:var(--text-placeholder);}
|
||||
.info-value{font-size:var(--font-sm);color:var(--text-primary);font-weight:500;min-height:22px;}
|
||||
.tag{display:inline-flex;align-items:center;padding:2px 8px;border-radius:10px;font-size:var(--font-xs);width:max-content;}
|
||||
.tag-green{background:#F6FFED;color:var(--success);}
|
||||
.tag-blue{background:var(--primary-light);color:var(--primary);}
|
||||
.tag-orange{background:#FFF7E6;color:var(--warning);}
|
||||
.tag-red{background:#FFF1F0;color:var(--danger);}
|
||||
.tag-gray{background:#F5F5F5;color:#999;}
|
||||
.resource-list{display:flex;flex-direction:column;gap:14px;}
|
||||
.resource-row{display:grid;grid-template-columns:60px 1fr 50px;align-items:center;gap:10px;font-size:var(--font-sm);}
|
||||
.bar{height:8px;background:#F0F2F5;border-radius:4px;overflow:hidden;}
|
||||
.bar span{display:block;height:100%;border-radius:4px;background:var(--primary);}
|
||||
.bar.warning span{background:var(--warning);}
|
||||
.bar.danger span{background:var(--danger);}
|
||||
.tabs{display:flex;align-items:center;background:#fff;border-radius:var(--radius-md) var(--radius-md) 0 0;box-shadow:var(--shadow-sm);overflow:hidden;flex-wrap:wrap;}
|
||||
.tab{padding:14px 20px;font-size:var(--font-sm);cursor:pointer;border-bottom:2px solid transparent;color:var(--text-secondary);}
|
||||
.tab:hover{color:var(--primary);}
|
||||
.tab.active{color:var(--primary);border-bottom-color:var(--primary);font-weight:600;}
|
||||
.tab-panel{display:none;background:#fff;border-radius:0 0 var(--radius-md) var(--radius-md);box-shadow:var(--shadow-sm);padding:18px 20px;}
|
||||
.tab-panel.active{display:block;}
|
||||
.table-wrap{overflow-x:auto;}
|
||||
.table-wrap table{min-width:1040px;}
|
||||
table{width:100%;border-collapse:collapse;}
|
||||
thead th{background:#FAFAFA;padding:12px 16px;text-align:left;font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);white-space:nowrap;}
|
||||
tbody td{padding:14px 16px;font-size:var(--font-sm);color:var(--text-primary);border-bottom:1px solid var(--border);white-space:nowrap;vertical-align:middle;}
|
||||
tbody tr:hover{background:#F8FBFF;}
|
||||
thead th:last-child,tbody td.td-actions{position:sticky;right:0;background:#fff;box-shadow:-4px 0 8px rgba(0,0,0,0.04);z-index:2;}
|
||||
thead th:last-child{background:#FAFAFA;}
|
||||
tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
.link{color:var(--primary);cursor:pointer;font-size:var(--font-sm);text-decoration:none;}
|
||||
.link:hover{color:#40a9ff;}
|
||||
.split-grid{display:grid;grid-template-columns:1.4fr .6fr;gap:16px;}
|
||||
.list-stack{display:flex;flex-direction:column;gap:10px;}
|
||||
.list-item{border:1px solid var(--border);border-radius:var(--radius-sm);padding:12px 14px;background:#FAFCFF;}
|
||||
.list-item__title{font-size:var(--font-sm);font-weight:600;}
|
||||
.list-item__desc{font-size:var(--font-xs);color:var(--text-secondary);margin-top:4px;line-height:1.6;}
|
||||
.toast{position:fixed;top:76px;left:50%;transform:translateX(-50%) translateY(-12px);background:#fff;border-left:4px solid var(--primary);box-shadow:var(--shadow-md);padding:10px 18px;border-radius:var(--radius-sm);font-size:var(--font-sm);opacity:0;pointer-events:none;transition:all .25s;z-index:1200;}
|
||||
.toast.show{opacity:1;transform:translateX(-50%) translateY(0);}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header class="top-bar">
|
||||
<div class="top-bar__logo">
|
||||
<svg viewBox="0 0 28 28" fill="none"><rect width="28" height="28" rx="6" fill="#1890FF"/><path d="M7 14h14M14 7v14" stroke="#fff" stroke-width="2.5" stroke-linecap="round"/></svg>
|
||||
营养管理
|
||||
</div>
|
||||
<nav class="top-bar__nav">
|
||||
<a href="trace-query.html">溯源总览</a><a href="arc-employee.html">档案</a><a href="farm-land.html">绿色种采</a><a href="sup-hygiene-check.html">卫生供应</a><a href="prod-qc-daily.html">健康生产</a><a href="din-dict-mgmt.html">营养用餐</a><a href="ano-dirty.html">异常事件</a><a href="edge-server-management.html" class="active">边缘服务器</a>
|
||||
</nav>
|
||||
<div class="top-bar__right"><a href="../../index.html" class="btn-back">← 返回首页</a><div class="avatar">管</div><span>系统管理员 ▾</span></div>
|
||||
</header>
|
||||
|
||||
<aside class="sidebar">
|
||||
<div class="sidebar-menu__title">边缘服务器</div>
|
||||
<a href="edge-server-management.html" class="sidebar-item active"><span class="mi">🧭</span>边缘服务器管理</a>
|
||||
<a href="edge-report-management.html" class="sidebar-item"><span class="mi">📡</span>数据上报管理</a>
|
||||
<a href="edge-dispatch-management.html" class="sidebar-item"><span class="mi">📦</span>数据下发管理</a>
|
||||
<a href="edge-alert-center.html" class="sidebar-item"><span class="mi">⚠</span>异常告警中心</a>
|
||||
</aside>
|
||||
|
||||
<main class="main">
|
||||
<div class="breadcrumb"><a href="edge-server-management.html">边缘服务器管理</a> / 边缘服务器详情</div>
|
||||
|
||||
<section class="page-header">
|
||||
<div>
|
||||
<div class="page-header__title">采气一厂后厨服务器</div>
|
||||
<div class="page-header__desc">EDGE-CQ-017 · 采气一厂食堂 · 后厨机房 · 当前处于告警状态,重点关注上报成功率下降与连接抖动。</div>
|
||||
</div>
|
||||
<div class="page-header__actions">
|
||||
<button class="btn btn-default" onclick="location.href='edge-report-management.html'">查看上报记录</button>
|
||||
<button class="btn btn-default" onclick="location.href='edge-dispatch-management.html'">查看下发任务</button>
|
||||
<button class="btn btn-primary" onclick="location.href='edge-alert-center.html'">查看关联告警</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="summary-grid">
|
||||
<div class="summary-card"><div class="summary-card__value" style="color:var(--warning)">80 / 90</div><div class="summary-card__label">当日上报统计</div><div class="summary-card__hint">成功率 88.9%</div></div>
|
||||
<div class="summary-card"><div class="summary-card__value">3 / 3</div><div class="summary-card__label">当日下发统计</div><div class="summary-card__hint">全部任务已接收</div></div>
|
||||
<div class="summary-card"><div class="summary-card__value" style="color:var(--danger)">3</div><div class="summary-card__label">当日告警</div><div class="summary-card__hint">1 条未恢复</div></div>
|
||||
<div class="summary-card"><div class="summary-card__value">12 / 18</div><div class="summary-card__label">设备在线 / 绑定设备</div><div class="summary-card__hint">餐线设备离线 2 台</div></div>
|
||||
</section>
|
||||
|
||||
<section class="overview-grid">
|
||||
<div class="card">
|
||||
<div class="card__header"><div class="card__title">基本信息</div><span class="tag tag-orange">运行告警中</span></div>
|
||||
<div class="card__body">
|
||||
<div class="info-grid">
|
||||
<div class="info-item"><div class="info-label">服务器名称</div><div class="info-value">采气一厂后厨服务器</div></div>
|
||||
<div class="info-item"><div class="info-label">服务器编号</div><div class="info-value">EDGE-CQ-017</div></div>
|
||||
<div class="info-item"><div class="info-label">所属食堂</div><div class="info-value">采气一厂食堂</div></div>
|
||||
<div class="info-item"><div class="info-label">部署位置</div><div class="info-value">后厨机房</div></div>
|
||||
<div class="info-item"><div class="info-label">服务器 IP</div><div class="info-value">10.18.32.17</div></div>
|
||||
<div class="info-item"><div class="info-label">服务版本</div><div class="info-value">v2.3.0</div></div>
|
||||
<div class="info-item"><div class="info-label">配置版本</div><div class="info-value">CFG-20260624-02</div></div>
|
||||
<div class="info-item"><div class="info-label">安装时间</div><div class="info-value">2026-05-12 09:20</div></div>
|
||||
<div class="info-item"><div class="info-label">最近启动</div><div class="info-value">2026-06-24 06:11</div></div>
|
||||
<div class="info-item"><div class="info-label">最近心跳</div><div class="info-value">2026-06-24 09:17:58</div></div>
|
||||
<div class="info-item"><div class="info-label">本地数据库</div><div class="info-value"><span class="tag tag-green">正常</span></div></div>
|
||||
<div class="info-item"><div class="info-label">消息连接</div><div class="info-value"><span class="tag tag-orange">间歇抖动</span></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card__header"><div class="card__title">资源状态</div><span class="tag tag-orange">磁盘预警</span></div>
|
||||
<div class="card__body">
|
||||
<div class="resource-list">
|
||||
<div class="resource-row"><span>CPU</span><div class="bar"><span style="width:46%"></span></div><strong>46%</strong></div>
|
||||
<div class="resource-row"><span>内存</span><div class="bar"><span style="width:68%"></span></div><strong>68%</strong></div>
|
||||
<div class="resource-row"><span>磁盘</span><div class="bar warning"><span style="width:82%"></span></div><strong>82%</strong></div>
|
||||
<div class="resource-row"><span>缓存</span><div class="bar"><span style="width:61%"></span></div><strong>61%</strong></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="tabs">
|
||||
<div class="tab active" onclick="switchTab('overview',this)">运行概览</div>
|
||||
<div class="tab" onclick="switchTab('devices',this)">设备接入</div>
|
||||
<div class="tab" onclick="switchTab('report',this)">数据上报</div>
|
||||
<div class="tab" onclick="switchTab('dispatch',this)">数据下发</div>
|
||||
<div class="tab" onclick="switchTab('alert',this)">告警记录</div>
|
||||
<div class="tab" onclick="switchTab('log',this)">运行日志</div>
|
||||
</section>
|
||||
|
||||
<section id="overview" class="tab-panel active">
|
||||
<div class="split-grid">
|
||||
<div class="card">
|
||||
<div class="card__header"><div class="card__title">运行检查项</div><a class="link" href="edge-alert-center.html">查看全部告警</a></div>
|
||||
<div class="card__body table-wrap">
|
||||
<table>
|
||||
<thead><tr><th>检查项</th><th>当前状态</th><th>最近更新时间</th><th>说明</th><th>操作</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>设备接入服务</td><td><span class="tag tag-green">可用</span></td><td>2026-06-24 09:17:58</td><td>绑定设备均可访问本地 API</td><td class="td-actions"><a class="link" onclick="showToast('已打开接入诊断视图')">诊断</a></td></tr>
|
||||
<tr><td>上报通道</td><td><span class="tag tag-orange">波动</span></td><td>2026-06-24 09:14:21</td><td>MQ 连接抖动,午餐高峰重试频繁</td><td class="td-actions"><a class="link" href="edge-report-management.html">查看上报</a></td></tr>
|
||||
<tr><td>下发通道</td><td><span class="tag tag-green">正常</span></td><td>2026-06-24 08:50:02</td><td>任务接收成功,设备应用存在个别延迟</td><td class="td-actions"><a class="link" href="edge-dispatch-detail.html">查看任务</a></td></tr>
|
||||
<tr><td>缓存空间</td><td><span class="tag tag-orange">预警</span></td><td>2026-06-24 09:10:40</td><td>升级包与图片缓存占用偏高</td><td class="td-actions"><a class="link" onclick="showToast('已生成缓存清理建议')">清理建议</a></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card__header"><div class="card__title">今日重点</div></div>
|
||||
<div class="card__body">
|
||||
<div class="list-stack">
|
||||
<div class="list-item"><div class="list-item__title">上报成功率下降</div><div class="list-item__desc">09:00 之后就餐流水重试次数升高,需确认食堂网络出口。</div></div>
|
||||
<div class="list-item"><div class="list-item__title">人脸增量任务已接收</div><div class="list-item__desc">中心下发 326 人增量包,当前已接收,待终端全部应用完成。</div></div>
|
||||
<div class="list-item"><div class="list-item__title">磁盘空间接近阈值</div><div class="list-item__desc">建议在晚餐前完成图片缓存清理,避免影响升级包分发。</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="devices" class="tab-panel">
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead><tr><th>设备名称</th><th>设备编号</th><th>设备类型</th><th>业务场景</th><th>绑定位置</th><th>在线状态</th><th>最近活跃</th><th>当前版本</th><th>最近异常</th><th>操作</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>智能组配秤 01</td><td>CMB-SCALE-001</td><td>净菜组配秤</td><td>净菜组配</td><td>后厨组配区</td><td><span class="tag tag-green">在线</span></td><td>2026-06-24 09:17:42</td><td>v1.8.2</td><td>无</td><td class="td-actions"><a class="link" onclick="showToast('设备详情原型待补充')">查看详情</a></td></tr>
|
||||
<tr><td>餐线营养秤 02</td><td>LINE-SCALE-002</td><td>餐线营养秤</td><td>上餐监控</td><td>一号餐线</td><td><span class="tag tag-orange">离线</span></td><td>2026-06-24 08:42:18</td><td>v1.7.6</td><td>离线 35 分钟</td><td class="td-actions"><a class="link" href="edge-alert-center.html">查看告警</a></td></tr>
|
||||
<tr><td>人脸识别终端 03</td><td>FACE-TERM-003</td><td>人脸识别设备</td><td>就餐识别</td><td>入口闸机</td><td><span class="tag tag-green">在线</span></td><td>2026-06-24 09:17:55</td><td>v3.2.0</td><td>增量包待应用</td><td class="td-actions"><a class="link" href="edge-dispatch-detail.html">查看任务</a></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="report" class="tab-panel">
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead><tr><th>数据类型</th><th>业务编号</th><th>来源设备</th><th>产生时间</th><th>上报状态</th><th>失败次数</th><th>最近处理时间</th><th>失败原因</th><th>操作</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>就餐流水</td><td>MEAL-20260624-0178</td><td>结算终端 01</td><td>2026-06-24 08:11:04</td><td><span class="tag tag-orange">待重试</span></td><td>3</td><td>2026-06-24 09:08:33</td><td>MQ 连接超时</td><td class="td-actions"><a class="link" href="edge-report-management.html">查看全量</a></td></tr>
|
||||
<tr><td>称重记录</td><td>WEIGH-20260624-0148</td><td>餐线营养秤 02</td><td>2026-06-24 08:42:03</td><td><span class="tag tag-green">已上报</span></td><td>0</td><td>2026-06-24 08:42:19</td><td>—</td><td class="td-actions"><a class="link" onclick="showToast('已定位到上报轨迹')">查看轨迹</a></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="dispatch" class="tab-panel">
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead><tr><th>任务编号</th><th>下发类型</th><th>下发方式</th><th>发起时间</th><th>接收状态</th><th>应用状态</th><th>完成时间</th><th>失败原因</th><th>操作</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>FACE-BATCH-0624-04</td><td>人脸增量</td><td>单次</td><td>2026-06-24 08:40:00</td><td><span class="tag tag-green">已接收</span></td><td><span class="tag tag-orange">部分待应用</span></td><td>—</td><td>2 台终端尚未回执</td><td class="td-actions"><a class="link" href="edge-dispatch-detail.html">查看详情</a></td></tr>
|
||||
<tr><td>CFG-PUSH-0624-01</td><td>配置同步</td><td>定时</td><td>2026-06-24 06:00:00</td><td><span class="tag tag-green">已接收</span></td><td><span class="tag tag-green">已完成</span></td><td>2026-06-24 06:02:14</td><td>—</td><td class="td-actions"><a class="link" href="edge-dispatch-detail.html">查看详情</a></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="alert" class="tab-panel">
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead><tr><th>告警编号</th><th>告警类型</th><th>告警主题</th><th>等级</th><th>首次发生</th><th>最近发生</th><th>当前状态</th><th>处理人</th><th>操作</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>ALM-20260624-078</td><td>上报失败告警</td><td>就餐流水重试超阈值</td><td><span class="tag tag-orange">警告</span></td><td>2026-06-24 08:45:00</td><td>2026-06-24 09:08:00</td><td><span class="tag tag-blue">处理中</span></td><td>运维张工</td><td class="td-actions"><a class="link" href="edge-alert-center.html">查看详情</a></td></tr>
|
||||
<tr><td>ALM-20260624-066</td><td>设备异常告警</td><td>餐线营养秤离线</td><td><span class="tag tag-red">严重</span></td><td>2026-06-24 08:42:00</td><td>2026-06-24 09:12:00</td><td><span class="tag tag-orange">待处理</span></td><td>未指派</td><td class="td-actions"><a class="link" href="edge-alert-center.html">查看详情</a></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="log" class="tab-panel">
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead><tr><th>时间</th><th>日志级别</th><th>来源服务</th><th>事件说明</th><th>操作</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>2026-06-24 09:14:21</td><td><span class="tag tag-orange">WARN</span></td><td>sync-uploader</td><td>检测到消息通道超时,已进入第 2 次重试</td><td class="td-actions"><a class="link" onclick="showToast('已定位到对应上报失败记录')">关联记录</a></td></tr>
|
||||
<tr><td>2026-06-24 08:50:02</td><td><span class="tag tag-green">INFO</span></td><td>dispatch-consumer</td><td>完成 FACE-BATCH-0624-04 任务接收</td><td class="td-actions"><a class="link" href="edge-dispatch-detail.html">查看任务</a></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<div class="toast" id="toast">操作已完成</div>
|
||||
<script>
|
||||
function switchTab(id,el){
|
||||
document.querySelectorAll('.tab').forEach(function(tab){tab.classList.remove('active');});
|
||||
document.querySelectorAll('.tab-panel').forEach(function(panel){panel.classList.remove('active');});
|
||||
el.classList.add('active');
|
||||
document.getElementById(id).classList.add('active');
|
||||
}
|
||||
function showToast(text){
|
||||
var toast=document.getElementById('toast');
|
||||
toast.textContent=text;
|
||||
toast.classList.add('show');
|
||||
setTimeout(function(){toast.classList.remove('show');},1800);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+103
-55
@@ -1,9 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>营养管理 - 食堂上报风险总览 | 健康CQ原型</title>
|
||||
<title>营养管理 - 边缘服务器管理 | 健康CQ原型</title>
|
||||
<style>
|
||||
*{box-sizing:border-box;margin:0;padding:0;}
|
||||
body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif;background:#F5F7FA;color:#333;min-width:1280px;}
|
||||
@@ -31,7 +31,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
.page-header__desc{margin-top:6px;font-size:var(--font-sm);color:var(--text-secondary);}
|
||||
.page-header__meta{font-size:var(--font-sm);color:var(--text-placeholder);}
|
||||
.stats-row{display:grid;grid-template-columns:repeat(6,1fr);gap:12px;margin-bottom:16px;}
|
||||
.stat-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);padding:16px 18px;cursor:pointer;border:1px solid transparent;transition:all .2s;}
|
||||
.stat-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);padding:16px 18px;border:1px solid transparent;transition:all .2s;cursor:pointer;}
|
||||
.stat-card:hover{border-color:var(--primary);transform:translateY(-1px);}
|
||||
.stat-card__value{font-size:var(--font-xxl);font-weight:700;line-height:1;color:var(--text-primary);}
|
||||
.stat-card__label{font-size:var(--font-sm);color:var(--text-secondary);margin-top:8px;}
|
||||
@@ -39,9 +39,10 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
.filter-bar{background:#fff;border-radius:var(--radius-md);padding:18px 24px;margin-bottom:16px;box-shadow:var(--shadow-sm);display:flex;align-items:center;gap:14px;flex-wrap:wrap;}
|
||||
.filter-item{display:flex;align-items:center;gap:8px;}
|
||||
.filter-item label{font-size:var(--font-sm);color:var(--text-secondary);white-space:nowrap;}
|
||||
.filter-input{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);outline:none;background:#fff;min-width:170px;}
|
||||
.filter-input:focus{border-color:var(--primary);}
|
||||
.filter-select{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 28px 0 12px;font-size:var(--font-sm);outline:none;background:#fff;appearance:auto;min-width:128px;}
|
||||
.filter-input,.filter-select{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);outline:none;background:#fff;}
|
||||
.filter-input{min-width:180px;}
|
||||
.filter-select{min-width:132px;appearance:auto;}
|
||||
.filter-input:focus,.filter-select:focus{border-color:var(--primary);}
|
||||
.btn{height:36px;padding:0 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;border:none;transition:all .2s;display:inline-flex;align-items:center;gap:6px;white-space:nowrap;}
|
||||
.btn-primary{background:var(--primary);color:#fff;}
|
||||
.btn-primary:hover{background:#40a9ff;}
|
||||
@@ -50,9 +51,8 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
.table-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
|
||||
.table-card__header{display:flex;align-items:center;justify-content:space-between;padding:16px 24px;border-bottom:1px solid var(--border);}
|
||||
.table-card__title{font-size:var(--font-base);font-weight:600;}
|
||||
.table-card__actions{display:flex;align-items:center;gap:8px;}
|
||||
.table-card__content{overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;}
|
||||
.table-card__content table{min-width:1560px;}
|
||||
.table-card__content table{min-width:1420px;}
|
||||
table{width:100%;border-collapse:collapse;}
|
||||
thead th{background:#FAFAFA;padding:12px 16px;text-align:left;font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);white-space:nowrap;}
|
||||
tbody td{padding:14px 16px;font-size:var(--font-sm);color:var(--text-primary);border-bottom:1px solid var(--border);white-space:nowrap;vertical-align:middle;}
|
||||
@@ -68,10 +68,10 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
.tag-gray{background:#F5F5F5;color:#999;}
|
||||
.link{color:var(--primary);cursor:pointer;font-size:var(--font-sm);text-decoration:none;}
|
||||
.link:hover{color:#40a9ff;}
|
||||
.node-name{font-weight:600;color:var(--primary);cursor:pointer;}
|
||||
.node-sub{font-size:var(--font-xs);color:var(--text-placeholder);margin-top:3px;}
|
||||
.metric-danger{color:var(--danger);font-weight:600;}
|
||||
.server-name{font-weight:600;color:var(--primary);}
|
||||
.server-sub{font-size:var(--font-xs);color:var(--text-placeholder);margin-top:3px;}
|
||||
.metric-warning{color:var(--warning);font-weight:600;}
|
||||
.metric-danger{color:var(--danger);font-weight:600;}
|
||||
.pagination{display:flex;align-items:center;justify-content:flex-end;padding:16px 24px;gap:8px;border-top:1px solid var(--border);}
|
||||
.pagination span{font-size:var(--font-sm);color:var(--text-secondary);}
|
||||
.page-btn{width:32px;height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);background:#fff;cursor:pointer;font-size:var(--font-sm);}
|
||||
@@ -94,7 +94,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
<a href="prod-qc-daily.html">健康生产</a>
|
||||
<a href="din-dict-mgmt.html">营养用餐</a>
|
||||
<a href="ano-dirty.html">异常事件</a>
|
||||
<a href="edge-node-list.html" class="active">边缘服务器</a>
|
||||
<a href="edge-server-management.html" class="active">边缘服务器</a>
|
||||
</nav>
|
||||
<div class="top-bar__right">
|
||||
<a href="../../index.html" class="btn-back">← 返回首页</a>
|
||||
@@ -105,89 +105,139 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
|
||||
<aside class="sidebar">
|
||||
<div class="sidebar-menu__title">边缘服务器</div>
|
||||
<a href="edge-alarm-center.html" class="sidebar-item"><span class="mi">⚠</span>告警中心</a>
|
||||
<a href="edge-sync-monitor.html" class="sidebar-item"><span class="mi">🔄</span>同步监控</a>
|
||||
<a href="edge-dispatch-compensation.html" class="sidebar-item"><span class="mi">📦</span>下发与补偿</a>
|
||||
<a href="edge-node-list.html" class="sidebar-item active"><span class="mi">🧭</span>边缘服务器列表</a>
|
||||
<a href="edge-server-management.html" class="sidebar-item active"><span class="mi">🧭</span>边缘服务器管理</a>
|
||||
<a href="edge-report-management.html" class="sidebar-item"><span class="mi">📡</span>数据上报管理</a>
|
||||
<a href="edge-dispatch-management.html" class="sidebar-item"><span class="mi">📦</span>数据下发管理</a>
|
||||
<a href="edge-alert-center.html" class="sidebar-item"><span class="mi">⚠</span>异常告警中心</a>
|
||||
</aside>
|
||||
|
||||
<main class="main">
|
||||
<section class="page-header">
|
||||
<div>
|
||||
<div class="page-header__title">食堂上报风险总览</div>
|
||||
<div class="page-header__desc">按食堂维度快速定位服务器离线、上报中断、数据积压和严重故障,作为故障排查总览入口。</div>
|
||||
<div class="page-header__title">边缘服务器管理</div>
|
||||
<div class="page-header__desc">按食堂维度集中查看边缘服务器在线状态、当日上报统计、当日下发统计和当日告警情况,作为服务器运行管理总入口。</div>
|
||||
</div>
|
||||
<div class="page-header__meta">数据更新时间:2026-06-23 14:32:18</div>
|
||||
<div class="page-header__meta">数据更新时间:2026-06-24 09:18:26</div>
|
||||
</section>
|
||||
|
||||
<section class="stats-row">
|
||||
<!-- [交互] 点击统计卡自动带入对应筛选条件 -->
|
||||
<div class="stat-card" onclick="quickFilter('全部服务器')"><div class="stat-card__value">28</div><div class="stat-card__label">接入服务器数</div><div class="stat-card__hint">覆盖 16 个食堂</div></div>
|
||||
<div class="stat-card" onclick="quickFilter('正常上报服务器')"><div class="stat-card__value" style="color:var(--success)">23</div><div class="stat-card__label">正常上报服务器</div><div class="stat-card__hint">最近 3 分钟有心跳且有上报</div></div>
|
||||
<div class="stat-card" onclick="quickFilter('上报中断服务器')"><div class="stat-card__value" style="color:var(--danger)">2</div><div class="stat-card__label">上报中断服务器</div><div class="stat-card__hint">超过 10 分钟未心跳或无上报</div></div>
|
||||
<div class="stat-card" onclick="quickFilter('异常食堂服务器')"><div class="stat-card__value" style="color:var(--warning)">3</div><div class="stat-card__label">异常食堂服务器</div><div class="stat-card__hint">在线但已出现故障告警</div></div>
|
||||
<div class="stat-card" onclick="quickFilter('上报积压服务器')"><div class="stat-card__value" style="color:var(--warning)">5</div><div class="stat-card__label">上报积压服务器</div><div class="stat-card__hint">存在待上报数据积压</div></div>
|
||||
<div class="stat-card" onclick="quickFilter('严重故障服务器')"><div class="stat-card__value" style="color:var(--danger)">4</div><div class="stat-card__label">严重故障服务器</div><div class="stat-card__hint">需运维立即处理</div></div>
|
||||
<div class="stat-card" onclick="quickFilter('在线服务器')"><div class="stat-card__value" style="color:var(--success)">24</div><div class="stat-card__label">在线服务器</div><div class="stat-card__hint">近 3 分钟有心跳</div></div>
|
||||
<div class="stat-card" onclick="quickFilter('离线服务器')"><div class="stat-card__value" style="color:var(--danger)">2</div><div class="stat-card__label">离线服务器</div><div class="stat-card__hint">需运维优先处理</div></div>
|
||||
<div class="stat-card" onclick="quickFilter('高风险服务器')"><div class="stat-card__value" style="color:var(--warning)">4</div><div class="stat-card__label">高风险服务器</div><div class="stat-card__hint">告警或统计异常</div></div>
|
||||
<div class="stat-card" onclick="quickFilter('上报异常服务器')"><div class="stat-card__value" style="color:var(--warning)">3</div><div class="stat-card__label">上报异常服务器</div><div class="stat-card__hint">当日成功率低于 95%</div></div>
|
||||
<div class="stat-card" onclick="quickFilter('当日有告警')"><div class="stat-card__value" style="color:var(--danger)">17</div><div class="stat-card__label">当日告警总数</div><div class="stat-card__hint">含服务器、设备、上报、下发</div></div>
|
||||
</section>
|
||||
|
||||
<section class="filter-bar">
|
||||
<div class="filter-item"><label>所属区域</label><select class="filter-select"><option>全部</option><option>CQ一区</option><option>CQ二区</option><option>CQ三厂</option></select></div>
|
||||
<div class="filter-item"><label>所属食堂</label><select class="filter-select"><option>全部</option><option>总部一号食堂</option><option>采气一厂食堂</option><option>炼化中心食堂</option></select></div>
|
||||
<div class="filter-item"><label>服务器状态</label><select class="filter-select"><option>全部</option><option>在线</option><option>离线</option><option>异常</option></select></div>
|
||||
<div class="filter-item"><label>上报状态</label><select class="filter-select"><option>全部</option><option>正常</option><option>积压</option><option>失败</option><option>中断</option></select></div>
|
||||
<div class="filter-item"><label>是否告警</label><select class="filter-select"><option>全部</option><option>有告警</option><option>无告警</option></select></div>
|
||||
<div class="filter-item"><label>在线状态</label><select class="filter-select"><option>全部</option><option>在线</option><option>离线</option><option>异常</option></select></div>
|
||||
<div class="filter-item"><label>健康状态</label><select class="filter-select"><option>全部</option><option>正常</option><option>告警</option><option>严重</option></select></div>
|
||||
<div class="filter-item"><label>服务版本</label><select class="filter-select"><option>全部</option><option>v2.3.1</option><option>v2.3.0</option><option>v2.2.8</option></select></div>
|
||||
<div class="filter-item"><label>最近心跳</label><input class="filter-input" type="date" style="min-width:150px"></div>
|
||||
<div class="filter-item"><label>关键字</label><input class="filter-input" type="text" placeholder="服务器名称 / 编号 / 食堂"></div>
|
||||
<button class="btn btn-primary" onclick="showToast('已按筛选条件刷新服务器列表')">查询</button>
|
||||
<button class="btn btn-primary" onclick="showToast('已按条件刷新边缘服务器列表')">查询</button>
|
||||
<button class="btn btn-default" onclick="showToast('已重置筛选条件')">重置</button>
|
||||
</section>
|
||||
|
||||
<section class="table-card">
|
||||
<div class="table-card__header">
|
||||
<div class="table-card__title">食堂风险清单</div>
|
||||
<div class="table-card__actions">
|
||||
<button class="btn btn-default" onclick="showToast('正在导出服务器状态')">导出服务器状态</button>
|
||||
</div>
|
||||
<div class="table-card__title">边缘服务器清单</div>
|
||||
<div><button class="btn btn-default" onclick="showToast('正在导出边缘服务器清单')">导出清单</button></div>
|
||||
</div>
|
||||
<div class="table-card__content">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>服务器名称</th><th>所属食堂</th><th>在线状态</th><th>风险状态</th><th>服务版本</th><th>设备在线</th><th>上报积压量</th><th>下发失败数</th><th>最近心跳时间</th><th>最后上报成功</th><th>当前故障告警</th><th>操作</th>
|
||||
<th>服务器名称</th>
|
||||
<th>所属食堂</th>
|
||||
<th>在线状态</th>
|
||||
<th>健康状态</th>
|
||||
<th>服务版本</th>
|
||||
<th>当日上报统计</th>
|
||||
<th>当日下发统计</th>
|
||||
<th>当日告警统计</th>
|
||||
<th>最近心跳时间</th>
|
||||
<th>最后上报成功</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><div class="node-name" onclick="location.href='edge-node-detail.html'">总部一号食堂服务器</div><div class="node-sub">EDGE-CQ-001</div></td>
|
||||
<td>总部一号食堂</td><td><span class="tag tag-green">在线</span></td><td><span class="tag tag-green">正常</span></td><td>v2.3.1</td><td>18 / 20</td><td>0</td><td>0</td><td>2026-06-23 14:32:10</td><td>2026-06-23 14:30:02</td><td>0</td>
|
||||
<td class="td-actions"><a class="link" href="edge-node-detail.html">查看详情</a> <a class="link" href="edge-sync-monitor.html">查看同步</a></td>
|
||||
<td><div class="server-name">总部一号食堂服务器</div><div class="server-sub">EDGE-CQ-001</div></td>
|
||||
<td>总部一号食堂</td>
|
||||
<td><span class="tag tag-green">在线</span></td>
|
||||
<td><span class="tag tag-green">正常</span></td>
|
||||
<td>v2.3.1</td>
|
||||
<td>90 / 90</td>
|
||||
<td>3 / 3</td>
|
||||
<td>0</td>
|
||||
<td>2026-06-24 09:18:10</td>
|
||||
<td>2026-06-24 09:17:43</td>
|
||||
<td class="td-actions"><a class="link" href="edge-server-detail.html">查看详情</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div class="node-name" onclick="location.href='edge-node-detail.html'">采气一厂后厨服务器</div><div class="node-sub">EDGE-CQ-017</div></td>
|
||||
<td>采气一厂食堂</td><td><span class="tag tag-orange">异常</span></td><td><span class="tag tag-orange">告警</span></td><td>v2.3.0</td><td>12 / 18</td><td><a class="link metric-warning" href="edge-sync-monitor.html">486</a></td><td><a class="link metric-warning" href="edge-dispatch-compensation.html">6</a></td><td>2026-06-23 14:31:58</td><td>2026-06-23 13:42:11</td><td><a class="link metric-warning" href="edge-alarm-center.html">3</a></td>
|
||||
<td class="td-actions"><a class="link" href="edge-alarm-center.html">查看告警</a> <a class="link" href="edge-sync-monitor.html">查看同步</a> <a class="link" href="edge-node-detail.html">查看详情</a></td>
|
||||
<td><div class="server-name">采气一厂后厨服务器</div><div class="server-sub">EDGE-CQ-017</div></td>
|
||||
<td>采气一厂食堂</td>
|
||||
<td><span class="tag tag-orange">异常</span></td>
|
||||
<td><span class="tag tag-orange">告警</span></td>
|
||||
<td>v2.3.0</td>
|
||||
<td><span class="metric-warning">80 / 90</span></td>
|
||||
<td>3 / 3</td>
|
||||
<td><span class="metric-warning">3</span></td>
|
||||
<td>2026-06-24 09:17:58</td>
|
||||
<td>2026-06-24 08:41:11</td>
|
||||
<td class="td-actions"><a class="link" href="edge-server-detail.html">查看详情</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div class="node-name" onclick="location.href='edge-node-detail.html'">炼化中心餐线服务器</div><div class="node-sub">EDGE-CQ-024</div></td>
|
||||
<td>炼化中心食堂</td><td><span class="tag tag-red">离线</span></td><td><span class="tag tag-red">严重</span></td><td>v2.2.8</td><td>0 / 16</td><td><a class="link metric-danger" href="edge-sync-monitor.html">1,284</a></td><td><a class="link metric-danger" href="edge-dispatch-compensation.html">21</a></td><td>2026-06-23 13:54:09</td><td>2026-06-23 12:40:18</td><td><a class="link metric-danger" href="edge-alarm-center.html">8</a></td>
|
||||
<td class="td-actions"><a class="link" href="edge-alarm-center.html">查看告警</a> <a class="link" href="edge-sync-monitor.html">查看同步</a> <a class="link" href="edge-node-detail.html">查看详情</a></td>
|
||||
<td><div class="server-name">炼化中心餐线服务器</div><div class="server-sub">EDGE-CQ-024</div></td>
|
||||
<td>炼化中心食堂</td>
|
||||
<td><span class="tag tag-red">离线</span></td>
|
||||
<td><span class="tag tag-red">严重</span></td>
|
||||
<td>v2.2.8</td>
|
||||
<td><span class="metric-danger">56 / 90</span></td>
|
||||
<td><span class="metric-danger">1 / 4</span></td>
|
||||
<td><span class="metric-danger">8</span></td>
|
||||
<td>2026-06-24 08:54:09</td>
|
||||
<td>2026-06-24 08:12:18</td>
|
||||
<td class="td-actions"><a class="link" href="edge-server-detail.html">查看详情</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div class="node-name" onclick="location.href='edge-node-detail.html'">输气站小食堂服务器</div><div class="node-sub">EDGE-CQ-032</div></td>
|
||||
<td>输气站小食堂</td><td><span class="tag tag-green">在线</span></td><td><span class="tag tag-orange">告警</span></td><td>v2.3.1</td><td>7 / 8</td><td><a class="link metric-warning" href="edge-sync-monitor.html">94</a></td><td>0</td><td>2026-06-23 14:32:02</td><td>2026-06-23 14:10:33</td><td><a class="link metric-warning" href="edge-alarm-center.html">1</a></td>
|
||||
<td class="td-actions"><a class="link" href="edge-alarm-center.html">查看告警</a> <a class="link" href="edge-sync-monitor.html">查看同步</a> <a class="link" href="edge-dispatch-compensation.html">恢复补偿</a></td>
|
||||
<td><div class="server-name">输气站小食堂服务器</div><div class="server-sub">EDGE-CQ-032</div></td>
|
||||
<td>输气站小食堂</td>
|
||||
<td><span class="tag tag-green">在线</span></td>
|
||||
<td><span class="tag tag-orange">告警</span></td>
|
||||
<td>v2.3.1</td>
|
||||
<td><span class="metric-warning">72 / 80</span></td>
|
||||
<td>2 / 2</td>
|
||||
<td>1</td>
|
||||
<td>2026-06-24 09:18:02</td>
|
||||
<td>2026-06-24 08:50:33</td>
|
||||
<td class="td-actions"><a class="link" href="edge-server-detail.html">查看详情</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div class="node-name" onclick="location.href='edge-node-detail.html'">培训中心食堂服务器</div><div class="node-sub">EDGE-CQ-041</div></td>
|
||||
<td>培训中心食堂</td><td><span class="tag tag-green">在线</span></td><td><span class="tag tag-green">正常</span></td><td>v2.3.1</td><td>10 / 10</td><td>0</td><td>0</td><td>2026-06-23 14:32:14</td><td>2026-06-23 14:28:41</td><td>0</td>
|
||||
<td class="td-actions"><a class="link" href="edge-node-detail.html">查看详情</a> <a class="link" href="edge-sync-monitor.html">查看同步</a></td>
|
||||
<td><div class="server-name">培训中心食堂服务器</div><div class="server-sub">EDGE-CQ-041</div></td>
|
||||
<td>培训中心食堂</td>
|
||||
<td><span class="tag tag-green">在线</span></td>
|
||||
<td><span class="tag tag-green">正常</span></td>
|
||||
<td>v2.3.1</td>
|
||||
<td>84 / 84</td>
|
||||
<td>2 / 2</td>
|
||||
<td>0</td>
|
||||
<td>2026-06-24 09:18:14</td>
|
||||
<td>2026-06-24 09:14:41</td>
|
||||
<td class="td-actions"><a class="link" href="edge-server-detail.html">查看详情</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="pagination">
|
||||
<span>显示 5 条,共 28 条</span><button class="page-btn">‹</button><button class="page-btn active">1</button><button class="page-btn">2</button><button class="page-btn">3</button><button class="page-btn">›</button>
|
||||
<span>显示 5 条,共 28 条</span>
|
||||
<button class="page-btn">‹</button>
|
||||
<button class="page-btn active">1</button>
|
||||
<button class="page-btn">2</button>
|
||||
<button class="page-btn">3</button>
|
||||
<button class="page-btn">›</button>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
@@ -200,11 +250,9 @@ function showToast(text){
|
||||
toast.classList.add('show');
|
||||
setTimeout(function(){toast.classList.remove('show');},1800);
|
||||
}
|
||||
function quickFilter(type){showToast('已筛选:'+type);}
|
||||
function confirmAction(text){
|
||||
if(window.confirm('确认执行该操作?')){showToast(text);}
|
||||
function quickFilter(type){
|
||||
showToast('已切换到“'+type+'”视角');
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,210 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>营养管理 - 食堂上报同步监控 | 健康CQ原型</title>
|
||||
<style>
|
||||
*{box-sizing:border-box;margin:0;padding:0;}
|
||||
body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif;background:#F5F7FA;color:#333;min-width:1280px;}
|
||||
:root{--primary:#1890FF;--primary-light:#E6F7FF;--success:#52C41A;--warning:#FAAD14;--danger:#FF4D4F;--text-primary:#333;--text-secondary:#666;--text-placeholder:#999;--border:#E8E8E8;--font-xs:11px;--font-sm:13px;--font-base:15px;--font-lg:17px;--font-xl:20px;--font-xxl:24px;--radius-sm:4px;--radius-md:8px;--shadow-sm:0 1px 4px rgba(0,0,0,.08);--shadow-md:0 4px 12px rgba(0,0,0,.12);}
|
||||
.top-bar{display:flex;align-items:center;height:56px;background:#001529;padding:0 24px;position:fixed;top:0;left:0;right:0;z-index:100;}
|
||||
.top-bar__logo{display:flex;align-items:center;gap:8px;color:#fff;font-size:var(--font-lg);font-weight:600;white-space:nowrap;width:240px;flex-shrink:0;}
|
||||
.top-bar__logo svg{width:28px;height:28px;}
|
||||
.top-bar__nav{display:flex;align-items:center;gap:4px;flex:1;}
|
||||
.top-bar__nav a{color:rgba(255,255,255,.65);text-decoration:none;padding:6px 14px;border-radius:var(--radius-sm);font-size:var(--font-sm);transition:all .2s;}
|
||||
.top-bar__nav a:hover{color:#fff;background:rgba(255,255,255,.08);}
|
||||
.top-bar__nav a.active{color:#fff;background:var(--primary);}
|
||||
.top-bar__right{display:flex;align-items:center;gap:16px;color:rgba(255,255,255,.65);font-size:var(--font-sm);}
|
||||
.btn-back{color:rgba(255,255,255,.65);text-decoration:none;}
|
||||
.btn-back:hover{color:#fff;}
|
||||
.avatar{width:32px;height:32px;border-radius:50%;background:#1890FF;display:flex;align-items:center;justify-content:center;color:#fff;font-size:var(--font-sm);}
|
||||
.sidebar{position:fixed;top:56px;left:0;bottom:0;width:240px;background:#001529;overflow-y:auto;z-index:90;padding-top:8px;}
|
||||
.sidebar-menu__title{color:rgba(255,255,255,.35);font-size:var(--font-xs);padding:16px 24px 8px;text-transform:uppercase;letter-spacing:.5px;}
|
||||
.sidebar-item{display:flex;align-items:center;gap:8px;padding:10px 24px;color:rgba(255,255,255,.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
|
||||
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,.06);}
|
||||
.sidebar-item.active{color:#fff;background:var(--primary);}
|
||||
.mi{display:inline-block;width:20px;text-align:center;filter:grayscale(1);opacity:.75;margin-right:2px;}
|
||||
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
|
||||
.page-header{display:flex;align-items:center;justify-content:space-between;background:#fff;border-radius:var(--radius-md);padding:18px 24px;margin-bottom:16px;box-shadow:var(--shadow-sm);}
|
||||
.page-header__title{font-size:var(--font-xl);font-weight:700;}
|
||||
.page-header__desc{margin-top:6px;font-size:var(--font-sm);color:var(--text-secondary);}
|
||||
.page-header__meta{font-size:var(--font-sm);color:var(--text-placeholder);}
|
||||
.stats-grid{display:grid;grid-template-columns:repeat(6,1fr);gap:12px;margin-bottom:16px;}
|
||||
.stat-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);padding:16px 18px;}
|
||||
.stat-card__value{font-size:var(--font-xxl);font-weight:700;line-height:1;color:var(--text-primary);}
|
||||
.stat-card__label{font-size:var(--font-sm);color:var(--text-secondary);margin-top:8px;}
|
||||
.stat-card__trend{font-size:var(--font-xs);color:var(--text-placeholder);margin-top:8px;}
|
||||
.layout-grid{display:grid;grid-template-columns:2fr 1fr;gap:16px;margin-bottom:16px;}
|
||||
.card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
|
||||
.card__header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--border);}
|
||||
.card__title{font-size:var(--font-base);font-weight:600;}
|
||||
.card__body{padding:18px 20px;}
|
||||
.trend-chart{height:220px;display:flex;align-items:flex-end;gap:12px;border-bottom:1px solid var(--border);padding:18px 8px 0;}
|
||||
.trend-bar{flex:1;background:var(--primary-light);border-radius:4px 4px 0 0;position:relative;min-width:20px;}
|
||||
.trend-bar::after{content:attr(data-label);position:absolute;left:50%;bottom:-24px;transform:translateX(-50%);font-size:var(--font-xs);color:var(--text-placeholder);}
|
||||
.trend-bar.warning{background:#FFF7E6;}
|
||||
.trend-bar.danger{background:#FFF1F0;}
|
||||
.node-risk-list{display:flex;flex-direction:column;gap:12px;}
|
||||
.node-risk-item{display:grid;grid-template-columns:1fr auto;gap:12px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:12px;}
|
||||
.node-risk-item__title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);}
|
||||
.node-risk-item__desc{font-size:var(--font-xs);color:var(--text-secondary);margin-top:4px;}
|
||||
.tabs{display:flex;background:#fff;border-radius:var(--radius-md) var(--radius-md) 0 0;box-shadow:var(--shadow-sm);overflow:hidden;}
|
||||
.tab{padding:14px 22px;font-size:var(--font-sm);cursor:pointer;border-bottom:2px solid transparent;color:var(--text-secondary);}
|
||||
.tab.active{color:var(--primary);border-bottom-color:var(--primary);font-weight:600;}
|
||||
.tab-panel{display:none;background:#fff;border-radius:0 0 var(--radius-md) var(--radius-md);box-shadow:var(--shadow-sm);padding:18px 20px;}
|
||||
.tab-panel.active{display:block;}
|
||||
.filter-bar{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-bottom:16px;}
|
||||
.filter-item{display:flex;align-items:center;gap:8px;}
|
||||
.filter-item label{font-size:var(--font-sm);color:var(--text-secondary);white-space:nowrap;}
|
||||
.filter-input{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);outline:none;min-width:160px;}
|
||||
.filter-select{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);outline:none;min-width:120px;background:#fff;}
|
||||
.btn{height:36px;padding:0 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;border:none;display:inline-flex;align-items:center;gap:6px;}
|
||||
.btn-primary{background:var(--primary);color:#fff;}
|
||||
.btn-default{background:#fff;color:var(--text-primary);border:1px solid var(--border);}
|
||||
.btn-default:hover{color:var(--primary);border-color:var(--primary);}
|
||||
.table-wrap{overflow-x:auto;}
|
||||
.table-wrap table{min-width:1320px;}
|
||||
table{width:100%;border-collapse:collapse;}
|
||||
thead th{background:#FAFAFA;padding:12px 16px;text-align:left;font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);white-space:nowrap;}
|
||||
tbody td{padding:14px 16px;font-size:var(--font-sm);color:var(--text-primary);border-bottom:1px solid var(--border);white-space:nowrap;vertical-align:middle;}
|
||||
tbody tr:hover{background:#F8FBFF;}
|
||||
thead th:last-child,tbody td.td-actions{position:sticky;right:0;background:#fff;box-shadow:-4px 0 8px rgba(0,0,0,.04);z-index:2;}
|
||||
thead th:last-child{background:#FAFAFA;}
|
||||
tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
.tag{display:inline-flex;align-items:center;padding:2px 8px;border-radius:10px;font-size:var(--font-xs);}
|
||||
.tag-green{background:#F6FFED;color:var(--success);}
|
||||
.tag-blue{background:var(--primary-light);color:var(--primary);}
|
||||
.tag-orange{background:#FFF7E6;color:var(--warning);}
|
||||
.tag-red{background:#FFF1F0;color:var(--danger);}
|
||||
.tag-gray{background:#F5F5F5;color:#999;}
|
||||
.link{color:var(--primary);cursor:pointer;font-size:var(--font-sm);text-decoration:none;}
|
||||
.link:hover{color:#40a9ff;}
|
||||
.toast{position:fixed;top:76px;left:50%;transform:translateX(-50%) translateY(-12px);background:#fff;border-left:4px solid var(--primary);box-shadow:var(--shadow-md);padding:10px 18px;border-radius:var(--radius-sm);font-size:var(--font-sm);opacity:0;pointer-events:none;transition:all .25s;z-index:1200;}
|
||||
.toast.show{opacity:1;transform:translateX(-50%) translateY(0);}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header class="top-bar">
|
||||
<div class="top-bar__logo"><svg viewBox="0 0 28 28" fill="none"><rect width="28" height="28" rx="6" fill="#1890FF"/><path d="M7 14h14M14 7v14" stroke="#fff" stroke-width="2.5" stroke-linecap="round"/></svg>营养管理</div>
|
||||
<nav class="top-bar__nav"><a href="trace-query.html">溯源总览</a><a href="arc-employee.html">档案</a><a href="farm-land.html">绿色种采</a><a href="sup-hygiene-check.html">卫生供应</a><a href="prod-qc-daily.html">健康生产</a><a href="din-dict-mgmt.html">营养用餐</a><a href="ano-dirty.html">异常事件</a><a href="edge-node-list.html" class="active">边缘服务器</a></nav>
|
||||
<div class="top-bar__right"><a href="../../index.html" class="btn-back">← 返回首页</a><div class="avatar">管</div><span>系统管理员 ▾</span></div>
|
||||
</header>
|
||||
|
||||
<aside class="sidebar">
|
||||
<div class="sidebar-menu__title">边缘服务器</div>
|
||||
<a href="edge-alarm-center.html" class="sidebar-item"><span class="mi">⚠</span>告警中心</a>
|
||||
<a href="edge-sync-monitor.html" class="sidebar-item active"><span class="mi">🔄</span>同步监控</a>
|
||||
<a href="edge-dispatch-compensation.html" class="sidebar-item"><span class="mi">📦</span>下发与补偿</a>
|
||||
<a href="edge-node-list.html" class="sidebar-item"><span class="mi">🧭</span>边缘服务器列表</a>
|
||||
</aside>
|
||||
|
||||
<main class="main">
|
||||
<section class="page-header">
|
||||
<div><div class="page-header__title">食堂上报同步监控</div><div class="page-header__desc">聚焦食堂现场数据上报链路,查看上报中断、延迟、积压和恢复处理情况。</div></div>
|
||||
<div class="page-header__meta">最近刷新:2026-06-23 14:35:00</div>
|
||||
</section>
|
||||
|
||||
<section class="stats-grid">
|
||||
<div class="stat-card"><div class="stat-card__value">23 / 2</div><div class="stat-card__label">正常上报 / 中断服务器</div><div class="stat-card__trend">5 分钟心跳与上报口径</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value" style="color:var(--warning)">5</div><div class="stat-card__label">上报异常食堂</div><div class="stat-card__trend">较昨日 +2</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value" style="color:var(--warning)">1,864</div><div class="stat-card__label">待上报数据条数</div><div class="stat-card__trend">主要来自就餐流水</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value" style="color:var(--danger)">38</div><div class="stat-card__label">恢复下发失败</div><div class="stat-card__trend">含人脸/升级包</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value" style="color:var(--danger)">4</div><div class="stat-card__label">积压超阈值服务器</div><div class="stat-card__trend">积压超过 30 分钟</div></div>
|
||||
<div class="stat-card"><div class="stat-card__value">96.8%</div><div class="stat-card__label">24小时上报成功率</div><div class="stat-card__trend">午间链路波动明显</div></div>
|
||||
</section>
|
||||
|
||||
<section class="layout-grid">
|
||||
<div class="card">
|
||||
<div class="card__header"><div class="card__title">最近 24 小时上报失败趋势</div><span class="tag tag-orange">按小时统计</span></div>
|
||||
<div class="card__body">
|
||||
<div class="trend-chart">
|
||||
<div class="trend-bar" style="height:30%" data-label="00"></div>
|
||||
<div class="trend-bar" style="height:42%" data-label="04"></div>
|
||||
<div class="trend-bar warning" style="height:58%" data-label="08"></div>
|
||||
<div class="trend-bar warning" style="height:72%" data-label="12"></div>
|
||||
<div class="trend-bar danger" style="height:88%" data-label="14"></div>
|
||||
<div class="trend-bar warning" style="height:60%" data-label="16"></div>
|
||||
<div class="trend-bar" style="height:36%" data-label="20"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card__header"><div class="card__title">上报高风险服务器</div><a class="link" href="edge-alarm-center.html">查看故障</a></div>
|
||||
<div class="card__body">
|
||||
<div class="node-risk-list">
|
||||
<div class="node-risk-item"><div><div class="node-risk-item__title">炼化中心餐线服务器</div><div class="node-risk-item__desc">上行积压 1,284 条,下发失败 21 条</div></div><span class="tag tag-red">严重</span></div>
|
||||
<div class="node-risk-item"><div><div class="node-risk-item__title">采气一厂后厨服务器</div><div class="node-risk-item__desc">MQ 间歇断连,失败重试中</div></div><span class="tag tag-orange">告警</span></div>
|
||||
<div class="node-risk-item"><div><div class="node-risk-item__title">输气站小食堂服务器</div><div class="node-risk-item__desc">上行待同步 94 条</div></div><span class="tag tag-orange">积压</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="tabs">
|
||||
<div class="tab active" onclick="switchTab('up',this)">食堂上报记录</div>
|
||||
<div class="tab" onclick="switchTab('down',this)">恢复下发记录</div>
|
||||
</section>
|
||||
|
||||
<section id="up" class="tab-panel active">
|
||||
<div class="filter-bar">
|
||||
<div class="filter-item"><label>食堂</label><select class="filter-select"><option>全部</option><option>采气一厂食堂</option><option>炼化中心食堂</option></select></div>
|
||||
<div class="filter-item"><label>服务器</label><select class="filter-select"><option>全部</option><option>采气一厂后厨服务器</option><option>炼化中心餐线服务器</option></select></div>
|
||||
<div class="filter-item"><label>业务类型</label><select class="filter-select"><option>全部</option><option>就餐流水</option><option>称重记录</option><option>设备日志</option></select></div>
|
||||
<div class="filter-item"><label>同步状态</label><select class="filter-select"><option>全部</option><option>待同步</option><option>同步失败</option><option>已同步</option></select></div>
|
||||
<div class="filter-item"><label>发生时间</label><input class="filter-input" type="date"></div>
|
||||
<button class="btn btn-primary" onclick="showToast('已刷新食堂上报记录')">查询</button>
|
||||
<button class="btn btn-default" onclick="showToast('正在导出食堂上报记录')">导出记录</button>
|
||||
</div>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead><tr><th>食堂</th><th>服务器</th><th>业务类型</th><th>业务编号</th><th>上报产生时间</th><th>上报状态</th><th>失败次数</th><th>最近处理时间</th><th>失败原因</th><th>操作</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>炼化中心食堂</td><td>炼化中心餐线服务器</td><td>就餐流水</td><td>MEAL-20260623-0781</td><td>2026-06-23 12:21:18</td><td><span class="tag tag-red">同步失败</span></td><td>8</td><td>2026-06-23 14:31:20</td><td>服务器离线,等待恢复</td><td class="td-actions"><a class="link" href="edge-alarm-center.html">查看故障</a> <a class="link" href="edge-dispatch-compensation.html">恢复补偿</a></td></tr>
|
||||
<tr><td>采气一厂食堂</td><td>采气一厂后厨服务器</td><td>称重记录</td><td>WEIGH-20260623-0148</td><td>2026-06-23 11:42:03</td><td><span class="tag tag-orange">待重试</span></td><td>3</td><td>2026-06-23 14:28:33</td><td>MQ 连接超时</td><td class="td-actions"><a class="link" href="edge-alarm-center.html">查看故障</a> <a class="link" onclick="showToast('已加入重试队列')">手动重试</a></td></tr>
|
||||
<tr><td>总部一号食堂</td><td>总部一号食堂服务器</td><td>设备日志</td><td>LOG-20260623-0311</td><td>2026-06-23 13:05:48</td><td><span class="tag tag-green">已同步</span></td><td>0</td><td>2026-06-23 13:06:03</td><td>—</td><td class="td-actions"><a class="link" href="edge-node-detail.html">服务器详情</a></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="down" class="tab-panel">
|
||||
<div class="filter-bar">
|
||||
<div class="filter-item"><label>食堂</label><select class="filter-select"><option>全部</option><option>采气一厂食堂</option><option>炼化中心食堂</option></select></div>
|
||||
<div class="filter-item"><label>服务器</label><select class="filter-select"><option>全部</option><option>采气一厂后厨服务器</option><option>炼化中心餐线服务器</option></select></div>
|
||||
<div class="filter-item"><label>下发类型</label><select class="filter-select"><option>全部</option><option>基础资料</option><option>人脸批次</option><option>生产任务</option><option>升级包</option></select></div>
|
||||
<div class="filter-item"><label>应用状态</label><select class="filter-select"><option>全部</option><option>已应用</option><option>应用失败</option><option>待处理</option></select></div>
|
||||
<div class="filter-item"><label>下发时间</label><input class="filter-input" type="date"></div>
|
||||
<button class="btn btn-primary" onclick="showToast('已刷新恢复下发记录')">查询</button>
|
||||
</div>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead><tr><th>食堂</th><th>服务器</th><th>下发类型</th><th>批次号</th><th>下发时间</th><th>当前状态</th><th>完成时间</th><th>失败原因</th><th>操作</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>采气一厂食堂</td><td>采气一厂后厨服务器</td><td>人脸批次</td><td>FACE-BATCH-0623-04</td><td>2026-06-23 10:00:00</td><td><span class="tag tag-red">应用失败</span></td><td>—</td><td>本地特征库写入失败</td><td class="td-actions"><a class="link" href="edge-dispatch-compensation.html">恢复补偿</a> <a class="link" href="edge-alarm-center.html">查看故障</a></td></tr>
|
||||
<tr><td>炼化中心食堂</td><td>炼化中心餐线服务器</td><td>升级包</td><td>UPG-20260623-002</td><td>2026-06-23 11:00:00</td><td><span class="tag tag-orange">部分失败</span></td><td>—</td><td>2 台设备未执行升级</td><td class="td-actions"><a class="link" href="edge-dispatch-compensation.html">恢复补偿</a> <a class="link" href="edge-alarm-center.html">查看故障</a></td></tr>
|
||||
<tr><td>总部一号食堂</td><td>总部一号食堂服务器</td><td>基础资料</td><td>BASE-20260623-003</td><td>2026-06-23 09:30:00</td><td><span class="tag tag-green">已应用</span></td><td>2026-06-23 09:31:28</td><td>—</td><td class="td-actions"><a class="link" href="edge-node-detail.html">服务器详情</a></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<div class="toast" id="toast">操作已完成</div>
|
||||
<script>
|
||||
function switchTab(id,el){
|
||||
document.querySelectorAll('.tab').forEach(function(tab){tab.classList.remove('active');});
|
||||
document.querySelectorAll('.tab-panel').forEach(function(panel){panel.classList.remove('active');});
|
||||
el.classList.add('active');
|
||||
document.getElementById(id).classList.add('active');
|
||||
}
|
||||
function showToast(text){
|
||||
var toast=document.getElementById('toast');
|
||||
toast.textContent=text;
|
||||
toast.classList.add('show');
|
||||
setTimeout(function(){toast.classList.remove('show');},1800);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user