[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:
136
apps/web-antd/src/api/iot/subsystem/index.ts
Normal file
136
apps/web-antd/src/api/iot/subsystem/index.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
import type { PageParam, PageResult } from '@vben/request';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace IotSubsystemApi {
|
||||
/** 子系统 */
|
||||
export interface Subsystem {
|
||||
id?: number; // 子系统编号
|
||||
name: string; // 子系统名称
|
||||
code: string; // 子系统编码(小写+下划线,如 security / energy / clean)
|
||||
description?: string; // 子系统描述
|
||||
icon?: string; // 子系统图标 URL 或 iconify 名称
|
||||
status: number; // 状态(0=禁用 1=启用)
|
||||
sort?: number; // 排序
|
||||
projectId?: number; // 所属项目 ID(预留字段,当前版本不启用)
|
||||
deviceCount?: number; // 设备数量(来自 /iot/subsystem/device-count 聚合,非实时 count)
|
||||
createTime?: Date; // 创建时间
|
||||
updateTime?: Date; // 更新时间
|
||||
}
|
||||
|
||||
/** 子系统分页查询参数 */
|
||||
export interface SubsystemPageParam extends PageParam {
|
||||
keyword?: string; // 关键字(名称/编码模糊搜索)
|
||||
status?: number; // 状态过滤
|
||||
}
|
||||
|
||||
/** 子系统精简信息(下拉列表) */
|
||||
export interface SubsystemSimple {
|
||||
id: number;
|
||||
name: string;
|
||||
code: string;
|
||||
}
|
||||
|
||||
/** 设备统计 map 中单条数据 */
|
||||
export interface SubsystemDeviceCount {
|
||||
subsystemId: number;
|
||||
total: number;
|
||||
online: number;
|
||||
alarm: number;
|
||||
}
|
||||
|
||||
/** 批量绑定子系统请求体 */
|
||||
export interface BatchBindSubsystemReqVO {
|
||||
deviceIds: number[]; // 设备编号列表(每批 ≤ 100)
|
||||
subsystemId: number; // 目标子系统 ID
|
||||
}
|
||||
|
||||
/** 单设备绑定/解绑子系统请求体 */
|
||||
export interface BindSubsystemReqVO {
|
||||
deviceId: number;
|
||||
subsystemId: null | number; // null = 解绑(移除归属)
|
||||
}
|
||||
}
|
||||
|
||||
// ─── 子系统 CRUD ────────────────────────────────────────────────────────────
|
||||
|
||||
/** 创建子系统 */
|
||||
export function createSubsystem(data: IotSubsystemApi.Subsystem) {
|
||||
return requestClient.post<number>('/iot/subsystem/create', data);
|
||||
}
|
||||
|
||||
/** 更新子系统 */
|
||||
export function updateSubsystem(data: IotSubsystemApi.Subsystem) {
|
||||
return requestClient.put<boolean>('/iot/subsystem/update', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除子系统
|
||||
* 后端会校验子系统下是否仍有设备,有则返回业务异常(前端 deleteCount 检查后禁用按钮)
|
||||
*/
|
||||
export function deleteSubsystem(id: number) {
|
||||
return requestClient.delete<boolean>(`/iot/subsystem/delete?id=${id}`);
|
||||
}
|
||||
|
||||
/** 获取子系统详情 */
|
||||
export function getSubsystem(id: number) {
|
||||
return requestClient.get<IotSubsystemApi.Subsystem>(
|
||||
`/iot/subsystem/get?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/** 子系统分页查询 */
|
||||
export function getSubsystemPage(params: IotSubsystemApi.SubsystemPageParam) {
|
||||
return requestClient.get<PageResult<IotSubsystemApi.Subsystem>>(
|
||||
'/iot/subsystem/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 子系统精简列表(下拉)
|
||||
* 注意:⚠️ [评审 A7] 此接口已加权限,403 由请求拦截器统一处理
|
||||
*/
|
||||
export function getSubsystemSimpleList() {
|
||||
return requestClient.get<IotSubsystemApi.SubsystemSimple[]>(
|
||||
'/iot/subsystem/simple-list',
|
||||
);
|
||||
}
|
||||
|
||||
// ─── 设备统计(B10 需提供)────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* 各子系统设备统计(Redis 聚合)
|
||||
* ⚠️ [评审 A6] 统计数据来自 Redis 缓存,不做实时 count
|
||||
* B10 需要实现:GET /iot/subsystem/device-count
|
||||
* 返回格式:{ [subsystemId]: { total, online, alarm } }
|
||||
*/
|
||||
export function getSubsystemDeviceCount() {
|
||||
return requestClient.get<
|
||||
Record<string, IotSubsystemApi.SubsystemDeviceCount>
|
||||
>('/iot/subsystem/device-count');
|
||||
}
|
||||
|
||||
// ─── 设备归属操作(B11 需提供)───────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* 批量绑定设备到子系统
|
||||
* ⚠️ 每批 ≤ 100 台,调用方需自行分批
|
||||
* B11 需要实现:PUT /iot/device/batchBindSubsystem
|
||||
*/
|
||||
export function batchBindSubsystem(
|
||||
data: IotSubsystemApi.BatchBindSubsystemReqVO,
|
||||
) {
|
||||
return requestClient.put<boolean>('/iot/device/batchBindSubsystem', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 单设备解绑子系统(移除归属)
|
||||
* B11 需要实现:PUT /iot/device/bindSubsystem(subsystemId=null 表示解绑)
|
||||
*/
|
||||
export function unbindDeviceSubsystem(deviceId: number) {
|
||||
return requestClient.put<boolean>('/iot/device/bindSubsystem', {
|
||||
deviceId,
|
||||
subsystemId: null,
|
||||
} satisfies IotSubsystemApi.BindSubsystemReqVO);
|
||||
}
|
||||
Reference in New Issue
Block a user