[F9/F10] 子系统管理 + 告警正交 5 态列表(UI 骨架 + mock)

两任务并行完成,因同动 router/iot.ts 与 locales/page.json 合并 commit。

F9 子系统管理 + 设备批量绑定:
- apps/web-antd/src/api/iot/subsystem/index.ts
- apps/web-antd/src/views/iot/subsystem/{list,form,devices}.vue
- apps/web-antd/src/views/iot/subsystem/__tests__/subsystem-utils.test.ts (18 用例)
- Known Pitfalls: 评审 A6 Redis stats 降级 / A7 403 拦截器 / 批量 100 台 / code regex snake_case
- 后端 B10/B11 API 契约: /iot/subsystem/{page,get,create,update,delete,simple-list,device-count}
  + /iot/device/{batchBindSubsystem,bindSubsystem} + /iot/device/page 加 subsystemId 过滤

F10 告警记录 (评审 C1 正交 5 态):
- apps/web-antd/src/views/iot/alarm/record/{list,detail}.vue
- apps/web-antd/src/views/iot/alarm/record/{alarm-state,api}.ts
- apps/web-antd/src/views/iot/alarm/record/components/{AlarmStateTag,AlarmOperations}.vue
- apps/web-antd/src/views/iot/alarm/record/__tests__/AlarmStateTag.spec.ts (12 用例)
- 5 态: 活跃·未确认(red) / 活跃·已确认(orange) / 已清除·未确认(gold醒目) / 已清除·已确认(green) / 已归档(default)
- Known Pitfalls: C1 5 态必展示 / archived 优先判断 / 30s 轮询 / 严重度颜色映射
- 后端 B12 API 契约: /iot/alarm-record/{page,get,ack,unack,clear,archive,batch-*,history,remark}

共同:
- apps/web-antd/src/router/routes/modules/iot.ts 追加 subsystem + alarm 路由
- locales/langs/{zh-CN,en-US}/page.json 追加 iot.subsystem.* + iot.alarm.*

note: 路由用顶级路径;项目采用动态菜单机制,菜单注册需后端 menu 表配置(运维侧工作),
      前端路由可通过 URL 直接访问。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
lzh
2026-04-23 22:39:47 +08:00
parent 8613641d1d
commit ba459aa1d7
15 changed files with 2443 additions and 0 deletions

View File

@@ -41,6 +41,53 @@
}
},
"iot": {
"subsystem": {
"title": "Subsystem",
"listTitle": "Subsystem List",
"name": "Name",
"namePlaceholder": "Enter subsystem name",
"nameRequired": "Name is required",
"nameTooLong": "Name must not exceed 128 characters",
"code": "Code",
"codePlaceholder": "e.g. security, clean",
"codeRequired": "Code is required",
"codeTooLong": "Code must not exceed 64 characters",
"codeFormat": "Code must start with a lowercase letter and contain only lowercase letters, digits, or underscores",
"codeHelp": "Format: starts with a letter, allows digits and underscores, e.g. clean_01",
"status": "Status",
"statusEnabled": "Enabled",
"statusDisabled": "Disabled",
"statusPlaceholder": "Select status",
"icon": "Icon",
"iconPlaceholder": "Icon URL or iconify name",
"description": "Description",
"descriptionPlaceholder": "Enter description",
"deviceCount": "Devices",
"searchPlaceholder": "Search name / code",
"viewDevices": "View Devices",
"deleteDisabledTip": "Cannot delete: subsystem has {0} device(s)",
"deviceListTitle": "Subsystem Devices",
"deviceName": "Device Name",
"serialNumber": "Serial Number",
"deviceState": "Online State",
"deviceOnline": "Online",
"deviceOffline": "Offline",
"deviceSearchPlaceholder": "Search device name",
"statTotal": "Total Devices",
"statOnline": "Online",
"statAlarm": "Alarming",
"statsNotice": "Stats are provided by Redis cache and may have a short delay (⚠️ A6)",
"batchBind": "Batch Bind Devices",
"batchBindHint": "Max 100 devices per batch",
"bindSuccess": "{0} device(s) bound successfully",
"selectDeviceTip": "Please select devices first",
"unbind": "Remove",
"unbindConfirm": "Remove device \"{0}\" from this subsystem?",
"unbinding": "Removing device...",
"unbindSuccess": "Device removed",
"transferCandidate": "Candidate Devices",
"transferSelected": "Selected Devices"
},
"dag": {
"toolbar": {
"zoomIn": "Zoom In",
@@ -120,6 +167,86 @@
"desc": "Send notification (SMS/Email/Webhook)"
}
}
},
"alarm": {
"state": {
"archived": "Archived",
"activeUnacked": "Active·Unacked",
"activeAcked": "Active·Acked",
"clearedUnacked": "Cleared·Unacked",
"clearedAcked": "Cleared·Acked",
"unknown": "Unknown"
},
"severity": {
"critical": "Critical",
"major": "Major",
"minor": "Minor",
"warning": "Warning",
"info": "Info",
"unknown": "Unknown"
},
"list": {
"title": "Alarm Records"
},
"field": {
"alarmName": "Alarm Name",
"severity": "Severity",
"state": "State",
"deviceName": "Device",
"productName": "Product",
"triggerCount": "Trigger Count",
"startTs": "First Triggered",
"endTs": "Last Triggered",
"clearTs": "Cleared At",
"ackTs": "Acked At"
},
"filter": {
"all": "All",
"ackState": "Ack State",
"clearState": "Clear State",
"archived": "Archive",
"unacked": "Unacked",
"acked": "Acked",
"active": "Active",
"cleared": "Cleared",
"excludeArchived": "Exclude Archived",
"allRecords": "All Records",
"onlyArchived": "Archived Only"
},
"op": {
"ack": "Ack",
"unack": "Unack",
"clear": "Clear",
"archive": "Archive",
"noOps": "No Operations",
"confirmTitle": "Confirm",
"ackConfirm": "Acknowledge this alarm?",
"unackConfirm": "Unacknowledge this alarm?",
"clearConfirm": "Clear this alarm?",
"archiveConfirm": "Archive this alarm? No further operations after archiving.",
"batchAck": "Batch Ack",
"batchClear": "Batch Clear",
"batchArchive": "Batch Archive",
"batchAckConfirm": "Acknowledge {count} selected alarm(s)?",
"batchClearConfirm": "Clear {count} selected alarm(s)?",
"batchArchiveConfirm": "Archive {count} selected alarm(s)? No further operations after archiving.",
"selectFirst": "Please select records first",
"ackSuccess": "Acknowledged",
"unackSuccess": "Unacknowledged",
"clearSuccess": "Cleared",
"archiveSuccess": "Archived"
},
"detail": {
"title": "Alarm Detail",
"basicInfo": "Basic Info",
"propagationPath": "Propagation Path",
"details": "Alarm Details",
"remark": "Process Remark",
"remarkPlaceholder": "Enter remark",
"remarkSaved": "Remark saved",
"history": "History",
"noHistory": "No history records"
}
}
}
}

View File

@@ -41,6 +41,53 @@
}
},
"iot": {
"subsystem": {
"title": "子系统",
"listTitle": "子系统列表",
"name": "子系统名称",
"namePlaceholder": "请输入子系统名称",
"nameRequired": "子系统名称不能为空",
"nameTooLong": "子系统名称长度不能超过 128 个字符",
"code": "子系统编码",
"codePlaceholder": "请输入编码,如 security、clean",
"codeRequired": "子系统编码不能为空",
"codeTooLong": "子系统编码长度不能超过 64 个字符",
"codeFormat": "编码格式错误:仅允许小写字母/数字/下划线,且以字母开头",
"codeHelp": "格式:小写字母开头,允许数字和下划线,如 clean_01",
"status": "状态",
"statusEnabled": "启用",
"statusDisabled": "禁用",
"statusPlaceholder": "请选择状态",
"icon": "图标",
"iconPlaceholder": "图标 URL 或 iconify 名称",
"description": "描述",
"descriptionPlaceholder": "请输入子系统描述",
"deviceCount": "设备数",
"searchPlaceholder": "搜索名称 / 编码",
"viewDevices": "查看设备",
"deleteDisabledTip": "子系统下有 {0} 台设备,无法删除",
"deviceListTitle": "子系统设备列表",
"deviceName": "设备名称",
"serialNumber": "序列号",
"deviceState": "在线状态",
"deviceOnline": "在线",
"deviceOffline": "离线",
"deviceSearchPlaceholder": "搜索设备名称",
"statTotal": "设备总数",
"statOnline": "在线设备",
"statAlarm": "告警设备",
"statsNotice": "统计数据由 Redis 缓存提供,可能有短暂延迟(⚠️ A6",
"batchBind": "批量绑定设备",
"batchBindHint": "每批最多支持 100 台设备同时绑定",
"bindSuccess": "已成功绑定 {0} 台设备",
"selectDeviceTip": "请先选择要绑定的设备",
"unbind": "移除",
"unbindConfirm": "确认将设备「{0}」从当前子系统移除?",
"unbinding": "正在移除设备...",
"unbindSuccess": "设备已移除",
"transferCandidate": "候选设备",
"transferSelected": "已选设备"
},
"dag": {
"toolbar": {
"zoomIn": "放大",
@@ -120,6 +167,86 @@
"desc": "发送消息通知(短信/邮件/webhook"
}
}
},
"alarm": {
"state": {
"archived": "已归档",
"activeUnacked": "活跃·未确认",
"activeAcked": "活跃·已确认",
"clearedUnacked": "已清除·未确认",
"clearedAcked": "已清除·已确认",
"unknown": "未知"
},
"severity": {
"critical": "紧急",
"major": "重要",
"minor": "次要",
"warning": "警告",
"info": "信息",
"unknown": "未知"
},
"list": {
"title": "告警记录"
},
"field": {
"alarmName": "告警名称",
"severity": "严重度",
"state": "状态",
"deviceName": "设备",
"productName": "产品",
"triggerCount": "触发次数",
"startTs": "首次触发",
"endTs": "最近触发",
"clearTs": "清除时间",
"ackTs": "确认时间"
},
"filter": {
"all": "全部",
"ackState": "确认状态",
"clearState": "清除状态",
"archived": "归档",
"unacked": "未确认",
"acked": "已确认",
"active": "活跃",
"cleared": "已清除",
"excludeArchived": "排除归档",
"allRecords": "全部记录",
"onlyArchived": "只看归档"
},
"op": {
"ack": "确认",
"unack": "撤销确认",
"clear": "清除",
"archive": "归档",
"noOps": "无可操作",
"confirmTitle": "操作确认",
"ackConfirm": "确认该告警?",
"unackConfirm": "撤销确认该告警?",
"clearConfirm": "清除该告警?",
"archiveConfirm": "归档该告警?归档后不可操作。",
"batchAck": "批量确认",
"batchClear": "批量清除",
"batchArchive": "批量归档",
"batchAckConfirm": "确认选中的 {count} 条告警?",
"batchClearConfirm": "清除选中的 {count} 条告警?",
"batchArchiveConfirm": "归档选中的 {count} 条告警?归档后不可操作。",
"selectFirst": "请先选择记录",
"ackSuccess": "确认成功",
"unackSuccess": "撤销确认成功",
"clearSuccess": "清除成功",
"archiveSuccess": "归档成功"
},
"detail": {
"title": "告警详情",
"basicInfo": "基本信息",
"propagationPath": "传播路径",
"details": "告警详情",
"remark": "处理备注",
"remarkPlaceholder": "请输入处理备注",
"remarkSaved": "备注已保存",
"history": "历史变化",
"noHistory": "暂无历史记录"
}
}
}
}