# RTU Web 前端功能详细清单

> 基于原工程 packed_fs.c 完整分析
> 分析日期: 2026-07-10
> 重构目标: 海外项目级工业品质 Web 管理界面

---

## 一、架构总览

| 项目 | 原实现 | 重构方案 |
|------|--------|---------|
| 类型 | Hash-based SPA | 同架构 (零依赖, 纯 HTML+CSS+JS) |
| 路由 | `window.location.hash` | 同 |
| 通信 | WebSocket (ws://0.0.0.0:8080/ws) | 同 |
| 图标 | Emoji | → SVG 图标 (Lucide Icons) |
| 样式 | 自定义 CSS | → 暗色工业主题 + CSS 变量系统 |
| 静态服务 | Mongoose packed_fs | → `mg_http_serve_dir` 从磁盘服务 |
| 文件数 | 8 个嵌入文件 | → 单文件 SPA (index.html 内嵌全部 CSS+JS) |

---

## 二、页面清单 (12 页)

### 2.1 登录页 (`#login`)
- 账号/密码输入框
- 登录按钮 → 设置 `localStorage.rtu_logged_in`
- 所有非登录页须先验证登录状态
- 已登录时访问 login → 自动跳转 dashboard

### 2.2 仪表盘 (`#dashboard`)
- **信号导航卡片**: 5 张卡片 (out / in / yk / ao / param), 显示每种信号已注册数量
- **批量 XML 导入/导出**:
  - 导出: 将所有信号导出为一个 XML 文件 (按 type 分组)
  - 导入: 选择 XML 文件, 按 type 批量执行 add 请求
- **实时连接状态**: WebSocket 连接指示器 (绿色/红色圆点)

### 2.3 开出信号 (`#out`)
- 信号注册表 (表格): saddr / desc / type / value
- **添加**: 弹出表单 (saddr, desc, data_type 下拉选择)
- **编辑**: 行内修改按钮 → 弹出
- **删除**: 单选 + 批量勾选删除
- 实时值刷新 (来自 WebSocket 推送)

### 2.4 开入信号 (`#in`)
- 链接信号表: saddr / desc / link_saddr / type / value
- **添加**: saddr, desc, link_saddr (链接目标)
- **删除**: 单选 + 批量
- 只读显示 (值来自链接的 out 信号)

### 2.5 遥控 (`#yk`)
- 遥控信号表: saddr / desc / type / ctrl_type / value
- **添加**: saddr, desc, type, ctrl_type (直控=0 / 选控=1)
- **控制操作**: set 按钮 → 输入值 → 发送 set 命令
- **删除**: 单选 + 批量

### 2.6 参数 (`#ao`)
- 参数信号表: saddr / desc / type / ctrl_type / value / min / max / step / unit
- **添加**: 含元数据字段 (min/max/step/unit)
- **修改**: set 按钮
- 支持下拉选择值 (val 数组)
- **删除**: 单选 + 批量

### 2.7 定值 (`#param`)
- 定值信号表: saddr / desc / type / ctrl_type
- **展开行**: 点击展开, 显示多定值区子表
- 每区: 区号 / 值 / 修改按钮
- **添加**: 含 ctrl_type

### 2.8 数据中心 (`#datacenter`)
- 信号类型标签栏切换 (out/in/yk/ao/param)
- 表格: saddr / desc / type / value / 操作
- 所有信号类型的统一浏览视图
- 支持 set 操作

### 2.9 PLC 调试 (`#plc_debug`)
- 显示所有 out 信号
- saddr / desc / value 表
- 实时刷新 (来自 WebSocket 推送)
- 用于调试 PLC 逻辑输出

### 2.10 PLC 逻辑配置 (`#plc_config`)
- **Canvas 图形编辑器** (原工程最复杂功能):
  - 拖拽节点 (Gate/Input/Output)
  - 连线编辑
  - 双击节点编辑属性
  - XML 保存/加载
- 重构方案: 保留功能, 重构 UI

### 2.11 实时监控 (`#monitor`)
- WebSocket 消息实时环形缓冲区
- 15 分钟 TTL / 500 条上限
- 消息方向: ↑ (发送) / ↓ (接收)
- 实时滚动显示
- JSON 格式化预览

### 2.12 命令终端 (`#terminal`)
- 仿真终端样式 (黑底绿字)
- 命令输入框
- stdout 实时输出 (WebSocket Binary 帧)
- 命令列表标签 (get_cmds)
- 历史命令支持

---

## 三、WebSocket 协议

### 客户端 → 服务端

| 消息类型 | JSON | 说明 |
|---------|------|------|
| 信号 CRUD | `{saddr, signal_type, curd, signal_data, setting_zone}` | add/del/set |
| 命令执行 | `{type:"cmd", data:"command args"}` | 远程命令 |
| 获取命令列表 | `{type:"get_cmds"}` | 用于终端 Tab 补全 |

### 服务端 → 客户端

| 消息类型 | JSON | 说明 |
|---------|------|------|
| 信号推送 | `{out:[...], in:[...], yk:[...], ao:[...], param:[...]}` | 增量 (仅变化) |
| 命令输出 | Binary frame (stdout) | 命令 stdout |
| 命令列表 | `{type:"cmd_list", cmds:[{name,desc},...]}` | 命令补全数据 |
| 异步输出 | `{type:"async_output", module, data, success, msg}` | 异步命令结果 |

### 推送消息结构

```json
{
  "out":   [{"saddr":"x", "desc":"x", "type":"int",   "val":"123"}],
  "in":    [{"saddr":"x", "desc":"x", "type":"float", "val":"3.14"}],
  "yk":    [{"saddr":"x", "desc":"x", "type":"int",   "val":"0", "ctrl_type":1}],
  "ao":    [{"saddr":"x", "desc":"x", "type":"float", "val":"5.0", "ctrl_type":0}],
  "param": [{"saddr":"x", "desc":"x", "type":"int",   "ctrl_type":0,
             "setting_zone_list":[{"id":"0","val":"100"}, ...]}]
}
```

---

## 四、导航结构

```
┌──────────┐
│  Logo    │  RTU Web Manager
│  Status  │  ● Connected / ○ Disconnected
├──────────┤
│ 📊 Dashboard    │
│ 📡 Out 信号     │
│ 🔗 In 信号      │
│ ⚡ YK 遥控      │
│ 🎚️ AO 参数      │
│ ⚙️ Param 定值   │
│ 📋 Datacenter   │
│ 🔌 PLC Debug    │
│ 🧩 PLC Config   │
│ 📈 Monitor      │
│ 💻 Terminal     │
└──────────┘
```

左侧固定导航栏 (220px), 右侧主内容区
响应式: 移动端隐藏侧栏

---

## 五、重构设计参数

| 参数 | 选择 |
|------|------|
| 风格 | 暗色工业主题 (Dark Industrial Dashboard) |
| 主色调 | Cyan/Teal accent (#00E5FF / #1DE9B6) |
| 背景 | #0F1923 (deep navy) / #1A2332 (card bg) |
| 字体 | Inter (标题) + JetBrains Mono (数据/终端) |
| 图标 | Lucide SVG inline (通过 CDN) |
| 圆角 | 8px (卡片), 6px (按钮), 4px (输入框) |
| 间距 | 8px 基础网格 |
| 动画 | 150-300ms ease-out transitions |
| 图表 | Canvas 手写轻量图表 (实时监控页) |

---

## 六、文件结构 (重构后)

```
src/system/libweb_server/
├── inc/
│   └── web_server.h          # 头文件 (无需修改)
└── src/
    ├── web_server.cpp         # +static file serving (mg_http_serve_dir)
    └── ws_method.cpp          # 已有, 无需修改

web/                           # 新建: 前端静态文件目录
└── index.html                 # 单文件 SPA (全部 CSS+JS 内嵌)
    ├── <style> ... </style>
    ├── <script>
    │   ├── WsClient           # WebSocket 通信
    │   ├── MonitorRing        # 实时监控环形缓冲
    │   ├── App                # 主应用 (路由/页面管理)
    │   ├── SignalStore        # 信号数据缓存
    │   ├── Pages.*            # 各页面渲染函数
    │   └── UI.*               # 通用 UI 组件 (Modal/Toast/Table/Toolbar)
    └── </script>
```

---

## 七、实现优先级

| 优先级 | 页面 | 理由 |
|--------|------|------|
| P0 | index.html 壳 + 登录 + 仪表盘 + 导航 | 基础框架 |
| P0 | WsClient + SignalStore | 通信基础 |
| P1 | Out / In 信号管理 | 核心 CRUD |
| P1 | YK / AO / Param 管理 | 完整信号类型 |
| P2 | Datacenter 统一浏览 | 数据汇总 |
| P2 | Monitor 实时监控 | 调试利器 |
| P2 | Terminal 命令终端 | 远程管理 |
| P3 | PLC Debug / PLC Config | 高级功能, Canvas 编辑器较复杂 |


---

## 八、重构实现 vs 原规格详细对比分析

> 分析日期: 2026-07-10
> 对比维度: 架构 / 页面 / 功能 / 样式 / 协议 / 后端对齐

---

### 8.1 架构对比

```mermaid
graph TB
    subgraph 原规格FEATURE_INVENTORY
        A1["单文件 SPA<br/>index.html 内嵌全部 CSS+JS"]
        A2["CDN 加载 Lucide Icons"]
        A3["单文件内分模块<br/>WsClient/MonitorRing/App/SignalStore/Pages.*/UI.*"]
    end
    subgraph 当前实现web/
        B1["多文件 SPA<br/>index.html + 15 JS + 1 CSS"]
        B2["内联 SVG Icons<br/>Icon._map 18个Lucide图标"]
        B3["独立文件模块<br/>ws-client/signal-store/router/ui/i18n<br/>+ 8个pages/*.js"]
    end
    A1 -->|"❌ 不一致"| B1
    A2 -->|"⚠️ 变体实现"| B2
    A3 -->|"✅ 架构对齐"| B3
```

| 维度 | 原规格 (FEATURE_INVENTORY) | 当前实现 (web/) | 状态 |
|------|--------------------------|-----------------|:---:|
| **文件架构** | 单文件 SPA (index.html 内嵌) | 多文件 (1 HTML + 1 CSS + 13 JS) | ❌ 偏离 |
| **图标方案** | CDN 加载 Lucide Icons | 内联 SVG 字符串 (ui.js `Icon._map`) | ⚠️ 变体 |
| **路由方案** | `App` 对象管理路由 | `Router` 全局对象 (router.js) | ✅ 对齐 |
| **通信层** | `WsClient` 对象 | `WsClient` 全局对象 (ws-client.js) | ✅ 对齐 |
| **数据层** | `SignalStore` 对象 | `SignalStore` 全局对象 (signal-store.js) | ✅ 对齐 |
| **样式方案** | 单文件内嵌 `<style>` | 独立 `css/style.css` | ❌ 偏离 |
| **i18n** | 未提及 | `I18n` 全局对象 (i18n.js, 95 个 key, zh/en) | 🆕 新增 |

---

### 8.2 页面功能逐页对比

#### 8.2.1 登录页 (`#login`)

| 功能点 | 原规格 | 当前实现 | 状态 |
|--------|--------|---------|:---:|
| 用户名/密码输入 | ✅ | ✅ `#login-user` / `#login-pass` | ✅ |
| 登录验证 | 未明确 | 硬编码 `admin/admin` | ⚠️ |
| localStorage 持久化 | ✅ `rtu_logged_in` | ✅ `rtu_logged_in='1'` | ✅ |
| 登录守卫 | ✅ 非登录页强制跳转 | ✅ `Router.dispatch()` 守卫 | ✅ |
| 已登录自动跳转 | ✅ login→dashboard | ✅ login→dashboard | ✅ |
| Enter 键登录 | 未提及 | ✅ `keydown` 监听 | 🆕 |
| 自动聚焦 | 未提及 | ✅ `#login-user` autofocus | 🆕 |
| 暗色主题 | 未提及 | ✅ `.login-wrap` 暗色背景 | 🆕 |

#### 8.2.2 仪表盘 (`#dashboard`)

| 功能点 | 原规格 | 当前实现 | 状态 |
|--------|--------|---------|:---:|
| 5 张信号计数卡片 | ✅ out/in/yk/ao/param | ✅ `.dash-grid` 5 cards | ✅ |
| 卡片点击跳转 | 未明确 | ✅ 点击跳转对应 hash | 🆕 |
| 卡片颜色区分 | 未明确 | ✅ 5 种不同色 (accent/accent2/warning/success/#a78bfa) | 🆕 |
| 实时连接状态 | ✅ WS 连接指示器 | ✅ `#dash-status` + `WsClient.onStatus()` | ✅ |
| XML 批量导出 | ✅ 按 type 分组导出 | ✅ `DashActions.exportAll()` → Blob 下载 | ✅ |
| XML 批量导入 | ✅ 选择文件→批量 add | ✅ `<input type=file>` → 解析 XML → 逐条 add | ✅ |
| 快速操作按钮 | 未提及 | ✅ 打开终端 + 打开监控 + 导出 + 导入 | 🆕 |
| 每卡显示描述 | 未提及 | ✅ `dash.{type}.desc` 多行描述 | 🆕 |
| 信号数量实时更新 | 未提及 | ✅ 绑定 `SignalStore.data` | 🆕 |

#### 8.2.3 开出信号 (`#out`)

| 功能点 | 原规格 | 当前实现 | 状态 |
|--------|--------|---------|:---:|
| 表格列 | saddr/desc/type/value | SAddr/Desc/Type/Val/Action | ✅ |
| 添加信号 | ✅ 弹出表单 (saddr/desc/data_type) | ✅ `SignalActions.add('out')` Modal | ✅ |
| 编辑信号 | ✅ 行内修改按钮→弹出 | ❌ **无编辑功能**，仅有 Set 值 | ❌ 缺失 |
| 删除信号 | ✅ 单选+批量勾选 | ✅ 单选 + 批量 checkbox | ✅ |
| 实时值刷新 | ✅ WS 推送 | ✅ `cell-val.changed` 动画 | ✅ |
| 值变化动画 | 未提及 | ✅ `flash-val` 0.6s 绿色闪烁 | 🆕 |
| 数据类型下拉 | data_type 选择 | U8/U16/U32/F32/STR | ✅ |
| 设值操作 | 未明确 | ✅ `SignalActions.set()` → 输入新值 | 🆕 |

**⚠️ 缺失: 编辑信号属性 (修改 saddr/desc/type)。当前前端只有"添加"和"删除"，没有"修改"已有信号属性的功能。**

#### 8.2.4 开入信号 (`#in`)

| 功能点 | 原规格 | 当前实现 | 状态 |
|--------|--------|---------|:---:|
| 表格列 | saddr/desc/link_saddr/type/value | SAddr/Desc/Link/Type/Val/Action | ✅ |
| link_saddr 字段 | ✅ 链接目标 | ✅ `#mf-link` 输入 | ✅ |
| 添加信号 | ✅ saddr/desc/link_saddr | ✅ `SignalActions.add('in')` | ✅ |
| 删除信号 | ✅ 单选+批量 | ✅ 单选 + 批量 | ✅ |
| 只读显示 | ✅ 值来自链接 out | ⚠️ 表格显示值但无链接跳转 | ⚠️ 弱 |
| Set 操作 | ❌ in 只读 | ❌ 无 Set 按钮 | ✅ |

#### 8.2.5 遥控 (`#yk`)

| 功能点 | 原规格 | 当前实现 | 状态 |
|--------|--------|---------|:---:|
| 表格列 | saddr/desc/type/ctrl_type/value | SAddr/Desc/Type/Ctrl/Val/Action | ✅ |
| ctrl_type 显示 | ✅ 直控=0/选控=1 | ✅ `ctrl_direct`/`ctrl_sbo` 翻译 | ✅ |
| 控制操作 | ✅ set→输入值→发送 | ✅ `SignalActions.set('yk',...)` | ✅ |
| 添加信号 | ✅ saddr/desc/type/ctrl_type | ✅ `SignalActions.add('yk')` | ✅ |
| 删除信号 | ✅ 单选+批量 | ✅ 单选 (无批量 checkbox! ) | ⚠️ |
| **批量删除** | 原规格有 | ❌ YK 页面无 checkbox | ❌ **缺失** |

**⚠️ 差异: YK/AO 页面缺少批量选择/删除 checkbox，而原规格明确写了"单选 + 批量勾选删除"。**

#### 8.2.6 模拟输出 (`#ao`)

| 功能点 | 原规格 | 当前实现 | 状态 |
|--------|--------|---------|:---:|
| 基础功能 | 同 YK | ✅ 同 YK (共用 renderSignalPage) | ✅ |
| **元数据字段** | min/max/step/unit | ❌ **完全没有** | ❌ **严重缺失** |
| **下拉选择值** | val 数组下拉 | ❌ **完全没有** | ❌ **严重缺失** |
| 批量删除 | 原规格有 | ❌ 无 checkbox | ❌ **缺失** |

**⚠️ 严重缺失: AO 页面在原规格中有 min/max/step/unit 元数据字段和 val 下拉选择，当前实现完全缺失。当前 AO = YK 的复制品，没有任何 AO 特有功能。**

#### 8.2.7 定值 (`#param`)

| 功能点 | 原规格 | 当前实现 | 状态 |
|--------|--------|---------|:---:|
| **展开行设计** | 点击展开 → 多区子表 | 主表 Zone 列平铺所有区 | ❌ **不同设计** |
| 多定值区显示 | 区号/值/修改按钮 | Zone 列内逐行: `#id` + 值 + Set 按钮 | ⚠️ 功能等效 |
| ~~每区独立 Set~~ | 展开行内有 Set | ✅ `ParamActions.set(saddr, zoneId)` | ✅ |
| 添加信号 | ✅ 含 ctrl_type | ✅ `SignalActions.add('param')` | ✅ |
| 删除信号 | ✅ (原规格未提批量) | ✅ 单选删除 | ✅ |
| 批量删除 | 未提及 | ❌ 无 checkbox | — |

**⚠️ 设计差异: 原规格使用"点击展开行→子表"的层级 UI，当前使用平铺 Zone 列的扁平 UI。功能等效但交互不同。**

#### 8.2.8 数据中心 (`#datacenter`)

| 功能点 | 原规格 | 当前实现 | 状态 |
|--------|--------|---------|:---:|
| 类型标签切换 | ✅ out/in/yk/ao/param | ✅ 5 个按钮切换 | ✅ |
| 表格列 | saddr/desc/type/value/操作 | SAddr/Desc/Type/Val/Action | ✅ |
| Set 操作 | ✅ | ✅ out/yk/ao 可 Set (in/param 不可) | ✅ |
| **从 RTU 查询** | 未明确 | ✅ `DcCmd.refreshAll()` → `datacenter all` | 🆕 |
| **文本解析** | 无 | ✅ `_parse()` 固定宽度文本表格解析 | 🆕 |
| **自动刷新** | 无 | ✅ 10 秒间隔自动刷新 | 🆕 |
| 信号数量显示 | 未提及 | ✅ 类型按钮上显示数量 | 🆕 |

#### 8.2.9 PLC 调试 (`#plc_debug`)

| 功能点 | 原规格 | 当前实现 | 状态 |
|--------|--------|---------|:---:|
| 显示 out 信号 | ✅ saddr/desc/value 表 | ❌ **完全不同** | ❌ **重构** |
| 三列布局 | ❌ 原规格是单表 | ✅ LED列/信号列/Button列 | 🆕 新设计 |
| LED 指示灯 | ❌ | ✅ 可绑定信号的 LED 圆点 | 🆕 |
| 翻转按钮 | ❌ | ✅ 点击翻转值→WS set | 🆕 |
| 信号选择器 | ❌ | ✅ `showSignalPicker()` Modal | 🆕 |
| 批量绑定 | ❌ | ✅ 勾选信号→一键→LED/BTN | 🆕 |

**⚠️ 原规格 PLC Debug 是"显示所有 out 信号的表"，当前实现是"三列交互式调试面板"。功能完全不同，新版本更强。**

#### 8.2.10 PLC 配置 (`#plc_config`)

| 功能点 | 原规格 | 当前实现 | 状态 |
|--------|--------|---------|:---:|
| Canvas 图形编辑器 | ✅ 拖拽/连线/属性编辑 | ✅ 完整实现 | ✅ |
| 节点类型 | 未列出 | ✅ 11 种 (INPUT/OUTPUT/OR/AND/NOT/T62P/T62D/RISING/SR/RS/FALLING) | 🆕 |
| 网格吸附 | 未提及 | ✅ 20px grid | 🆕 |
| 缩放/平移 | 未提及 | ✅ zoom 0.5-2.0 + pan | 🆕 |
| XML 保存 | ✅ | ✅ `{type:'set_file'}` → RTU | ⚠️ |
| XML 加载 | ✅ | ✅ `{type:'cmd',data:'cat ...'}` → 解析 | ⚠️ |
| XML 只读预览 | 未提及 | ✅ `#plc-xml-view` 实时预览 | 🆕 |
| 连线拖拽 | ✅ | ✅ 引脚拖出虚线→目标引脚 | ✅ |
| 节点属性编辑 | ✅ 双击编辑 | ✅ 双击 Modal (saddr/desc/delayMs) | ✅ |
| 删除节点 | 未提及 | ✅ 选中+删除按钮+关联线自动删 | 🆕 |
| 缩放栏 | 未提及 | ✅ zoom in/out/fit + 百分比 | 🆕 |
| **后端 set_file** | ♾️ | ⚠️ **前端用 set_file 但后端未实现!** | ❌ **BUG** |

**❌ 严重 BUG: `PlcConfig.save()` 发送 `{type:'set_file', path, content}`，但后端 `ws_method.cpp` 中没有 `get_file`/`set_file` 的 handler！save 功能形同虚设。load 功能通过 `cat` 命令间接实现，可行但不优雅。**

#### 8.2.11 实时监控 (`#monitor`)

| 功能点 | 原规格 | 当前实现 | 状态 |
|--------|--------|---------|:---:|
| 环形缓冲区 | ✅ 500 条上限 | ✅ `MonitorRing.ring` max=500 | ✅ |
| TTL 过期 | ✅ 15 分钟 TTL | ❌ **无 TTL** | ❌ **缺失** |
| 消息方向 | ✅ ↑ 发送 / ↓ 接收 | ✅ `.monitor-dir.up`/`.monitor-dir.down` | ✅ |
| 实时滚动 | ✅ | ✅ reverse 排序，最新在顶部 | ✅ |
| **JSON 格式化预览** | ✅ | ❌ **仅截断 200 字符原文** | ❌ **缺失** |
| 暂停/恢复 | 未提及 | ✅ `paused` + `togglePause()` | 🆕 |
| 统计面板 | 未提及 | ✅ 总计/发送/接收 计数 | 🆕 |
| 清除按钮 | 未提及 | ✅ `clear()` | 🆕 |
| 消息截断 | 未提及 | ✅ 200 字符截断 | ⚠️ |

**⚠️ 缺失: (1) 15 分钟 TTL 过期机制; (2) JSON 格式化预览 (当前是 truncate 200 字符的原始文本)。**

#### 8.2.12 命令终端 (`#terminal`)

| 功能点 | 原规格 | 当前实现 | 状态 |
|--------|--------|---------|:---:|
| 仿真终端样式 | ✅ 黑底绿字 | ✅ 暗色终端风格 | ✅ |
| 命令输入 | ✅ | ✅ `#term-input` + Enter | ✅ |
| stdout 实时输出 | ✅ Binary 帧 | ✅ `term-out` 追加 binary 文本 | ✅ |
| 命令列表标签 | ✅ get_cmds | ✅ `#cmd-chips` 芯片样式 | ✅ |
| **历史命令** | ✅ | ❌ **无历史命令** | ❌ **缺失** |
| help 命令 | 未提及 | ✅ 内置 help 文本 | 🆕 |
| clear 命令 | 未提及 | ✅ 清空 `#term-out` | 🆕 |
| 命令芯片点击 | 未提及 | ✅ 点击填充到输入框 | 🆕 |

**⚠️ 缺失: 历史命令支持 (上下箭头浏览历史)。原规格明确提到"历史命令支持"。**

---

### 8.3 样式/主题对比

| 参数 | 原规格 | 当前实现 | 状态 |
|------|--------|---------|:---:|
| **风格** | 暗色工业主题 | 暗色工业主题 | ✅ |
| **主色调** | Cyan/Teal `#00E5FF` / `#1DE9B6` | Teal `#00d4aa` / Blue `#00a8e8` | ⚠️ 色值不同 |
| **背景** | `#0F1923` (deep navy) | `#0a0f16` (更深) | ⚠️ 色值不同 |
| **卡片背景** | `#1A2332` | `#111922` | ⚠️ 色值不同 |
| **字体** | Inter + JetBrains Mono | Inter + JetBrains Mono | ✅ |
| **图标** | Lucide CDN | 内联 SVG (18 个图标) | ⚠️ 变体 |
| **圆角** | 8px / 6px / 4px | 12px / 8px / 4px | ⚠️ 差异 |
| **间距** | 8px 基础网格 | 未显式定义网格 | ⚠️ |
| **动画** | 150-300ms ease-out | 180ms ease-out | ✅ |
| **CSS 变量** | 未提及 | ✅ 30 个设计令牌 | 🆕 |
| **响应式** | ✅ 移动端隐藏侧栏 | ✅ `@media max-width:768px` | ✅ |
| **图表** | Canvas 手写轻量图表 | ❌ 未实现 | ❌ |
| **滚动条样式** | 未提及 | ✅ `::-webkit-scrollbar*` 定制 | 🆕 |

---

### 8.4 WebSocket 协议后端对齐

```mermaid
graph LR
    subgraph 前端发送
        F1["{type:'cmd'}"]
        F2["{type:'get_cmds'}"]
        F3["{type:'set_file'}"]
        F4["信号 CRUD add/del/set"]
    end
    subgraph 后端处理ws_method.cpp
        B1["✅ ws_recv → ws_cmd_exec"]
        B2["✅ ws_recv → cmd_manager_get_commands"]
        B3["❌ 无 handler! get_file/set_file 未实现"]
        B4["✅ ws_recv → ws_sig_add/del/set"]
    end
    F1 --> B1
    F2 --> B2
    F3 -->|"❌ 断裂"| B3
    F4 --> B4
```

| 消息 | 前端发送 | 后端接收 | 状态 |
|------|---------|---------|:---:|
| `{type:"cmd"}` | ✅ Terminal/Datacenter/PlcConfig.load | ✅ `ws_recv()` → `ws_cmd_exec()` | ✅ |
| `{type:"get_cmds"}` | ✅ WsClient.onopen | ✅ `ws_recv()` → `cmd_manager_get_commands()` | ✅ |
| `{type:"set_file"}` | ✅ PlcConfig.save() | ❌ **无 handler** | ❌ **断裂** |
| `{type:"get_file"}` | ❌ 未使用 | ❌ **无 handler** | ❌ **断裂** |
| `{saddr, signal_type, curd:"add"}` | ✅ SignalActions.add | ✅ `ws_sig_add_*` | ✅ |
| `{saddr, signal_type, curd:"del"}` | ✅ SignalActions.del/batchDel | ✅ `ws_sig_del` | ✅ |
| `{saddr, signal_type, curd:"set"}` | ✅ SignalActions.set/ParamActions.set | ✅ `ws_sig_set` | ✅ |

| 推送 | 后端发送 | 前端接收 | 状态 |
|------|---------|---------|:---:|
| 信号增量推送 | ✅ `ws_push_task()` 100ms | ✅ `SignalStore.update()` | ✅ |
| 命令输出 Binary | ✅ `ws_send_binary()` | ✅ `term-out` + `MonitorRing` | ✅ |
| 命令列表 | ✅ `cmd_list` | ✅ `#cmd-chips` 芯片 | ✅ |
| 异步输出 | ✅ `async_cmd_printf()` | ⚠️ 仅 `index.html` 内联处理器中 toast | ⚠️ 弱 |

**❌ 关键缺陷: `set_file`/`get_file` 在前端被使用但后端未实现。`PlcConfig.save()` 无法正常工作。**

---

### 8.5 缺失功能汇总

```mermaid
graph TD
    subgraph 严重缺失P0
        M1["❌ set_file/get_file 后端未实现<br/>→ PLC Config 保存无效"]
        M2["❌ AO 页面缺少元数据<br/>min/max/step/unit + val下拉"]
    end
    subgraph 中等缺失P1
        M3["❌ 终端缺少历史命令<br/>上下箭头浏览"]
        M4["❌ Monitor 缺少 JSON格式化<br/>+ 15分钟 TTL"]
        M5["❌ YK/AO 缺少批量删除<br/>checkbox"]
        M6["❌ 信号编辑功能缺失<br/>修改 saddr/desc/type"]
    end
    subgraph 轻微缺失P2
        M7["⚠️ Monitor JSON预览 → 截断200字符"]
        M8["⚠️ Canvas 图表（原规格提及）"]
        M9["⚠️ 异步输出前端处理较弱"]
        M10["⚠️ in 信号无链接跳转"]
    end
    subgraph 后端未实现但头文件声明
        M11["♾️ get_file / set_file<br/>ws_method.cpp 头注释声明但未实现"]
    end
```

#### P0 — 阻塞级

| # | 缺失项 | 影响范围 | 根因 |
|---|--------|---------|------|
| 1 | **`set_file`/`get_file` 后端未实现** | PlcConfig.save() 完全无效 | `ws_method.cpp` 头注释声明但代码中无 handler |
| 2 | **AO 页面元数据字段** | AO 信号管理功能不完整 | 前端未实现 min/max/step/unit/val 下拉 |

#### P1 — 重要级

| # | 缺失项 | 影响范围 |
|---|--------|---------|
| 3 | 终端历史命令 | 用户体验差，无法快速重复执行 |
| 4 | Monitor JSON 格式化 + TTL | 可读性差，缓冲区无限增长 |
| 5 | YK/AO 批量删除 | 管理效率低 |
| 6 | 信号属性编辑 | 只能删了重建，无法修改 |

#### P2 — 改进级

| # | 缺失项 | 说明 |
|---|--------|------|
| 7 | 异步输出前端处理 | 目前仅在 index.html 内联 toast，应接入 Monitor |
| 8 | Canvas 图表 | 原规格提到但一直未实现 |
| 9 | in 信号链接跳转 | 点击 link_saddr 应跳转到对应 out 信号 |
| 10 | param 展开行 UI | 原规格的层级展开设计比当前平铺更优雅 |

---

### 8.6 新增功能 (超出原规格)

| # | 新增功能 | 位置 |
|---|---------|------|
| 1 | **I18n 国际化** (zh/en, 95 key) | `i18n.js` |
| 2 | **CSS 设计令牌系统** (30 个变量) | `style.css` |
| 3 | **Dashboard 快速操作按钮** | `dashboard.js` |
| 4 | **Dashboard 每卡描述文本** | `dashboard.js` |
| 5 | **信号值变化动画** (`flash-val`) | `style.css` |
| 6 | **Datacenter 自动刷新** (10s) | `datacenter.js` + `index.html` |
| 7 | **Datacenter RTU 命令解析器** | `datacenter.js DcCmd._parse()` |
| 8 | **PLC Debug 三列交互面板** (原规格为简单表) | `plc.js PlcDebug` |
| 9 | **PLC Config 缩放/平移/网格** | `plc.js PlcConfig` |
| 10 | **PLC Config XML 实时预览** | `plc.js refreshXmlView()` |
| 11 | **Monitor 暂停/统计/清除** | `monitor.js` |
| 12 | **Terminal 命令芯片** | `terminal.js` |
| 13 | **Toast 通知系统** (success/error/info) | `ui.js` |
| 14 | **Modal 对话框系统** | `ui.js` |
| 15 | **SVG 图标库** (18 个 Lucide 风格图标) | `ui.js Icon._map` |
| 16 | **通用信号表格渲染器** (`renderSignalPage`) | `ui.js` |
| 17 | **导航分组标题** (Overview/Signal/Tools) | `router.js NAV` |
| 18 | **语言切换器** | `index.html #lang-select` + `i18n.js` |
| 19 | **实时时钟** | `index.html #nav-time` |
| 20 | **移动端响应式** | `style.css @media` + `toggleNav()` |

---

### 8.7 代码质量对比

| 维度 | 原规格目标 | 当前实现 | 评价 |
|------|-----------|---------|:---:|
| 模块化 | 单文件内分对象 | 多文件独立模块 | ✅ 更好 |
| 可维护性 | 单文件 2000+ 行 | 13 文件，每文件 50-400 行 | ✅ 更好 |
| 国际化 | 无 | 95 key, zh/en | ✅ 更好 |
| 代码注释 | 未提及 | JSDoc 风格文件头 | ✅ |
| 全局命名空间 | 对象挂 window | 全局对象 (WsClient/SignalStore/Router...) | ⚠️ 一般 |
| 重复代码 | 低（单文件共享） | 中等（signals.js 4 个页面模板重复） | ⚠️ 一般 |
| 错误处理 | 未提及 | Toast 通知 + try-catch | ✅ |
| 后端对齐 | 100% | ~85%（缺 set_file/get_file） | ⚠️ |

---

### 8.8 按模块分析总结

```
┌─────────────────────────────────────────────────────────┐
│                  前端模块完成度矩阵                        │
├────────────┬──────────┬──────────┬──────────┬───────────┤
│  模块      │ 基础CRUD │ 高级功能 │ UI/UX    │ 后端对齐  │
├────────────┼──────────┼──────────┼──────────┼───────────┤
│ login      │   100%   │   100%   │   100%   │    N/A    │
│ dashboard  │   100%   │   100%   │   100%   │   100%    │
│ out        │   100%   │    90%   │   100%   │   100%    │
│ in         │   100%   │    80%   │   100%   │   100%    │
│ yk         │    90%   │    80%   │    90%   │   100%    │
│ ao         │    60%   │    30%   │    90%   │   100%    │
│ param      │   100%   │    90%   │    90%   │   100%    │
│ datacenter │   100%   │   100%   │   100%   │   100%    │
│ plc_debug  │   150%   │   150%   │   100%   │   100%    │
│ plc_config │   100%   │   120%   │   100%   │    50%    │
│ monitor    │   100%   │    70%   │    90%   │   100%    │
│ terminal   │   100%   │    80%   │   100%   │   100%    │
├────────────┼──────────┼──────────┼──────────┼───────────┤
│ 总完成度   │    96%   │    86%   │    97%   │    96%    │
└────────────┴──────────┴──────────┴──────────┴───────────┘
```

**最低完成度模块**: AO (60% 基础 / 30% 高级) — 缺少 min/max/step/unit 元数据字段和 val 下拉选择。
**后端对齐最差**: plc_config (50%) — set_file/get_file 后端未实现。

---

### 8.9 优先级修复建议

| 优先级 | 任务 | 预估工时 |
|:---:|------|:---:|
| **P0** | 后端实现 `set_file`/`get_file` handler | 2h |
| **P0** | AO 页面补全元数据字段 + val 下拉 | 2h |
| **P1** | 终端添加历史命令 (sessionStorage) | 1h |
| **P1** | Monitor 添加 JSON 格式化预览 | 1h |
| **P1** | YK/AO 页面添加批量删除 checkbox | 0.5h |
| **P1** | 信号编辑功能 (修改 saddr/desc/type) | 2h |
| **P2** | Monitor 15min TTL 过期 | 0.5h |
| **P2** | in 信号链接跳转 | 0.5h |
| **P2** | Canvas 图表 (实时监控页) | 3h |
| **P2** | 异步输出接入 Monitor | 0.5h |
