Files
iot-device-management-frontend/apps/web-antd/src/api/iot/subsystem/index.ts
lzh ba459aa1d7 [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>
2026-04-23 22:39:47 +08:00

137 lines
4.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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/bindSubsystemsubsystemId=null 表示解绑)
*/
export function unbindDeviceSubsystem(deviceId: number) {
return requestClient.put<boolean>('/iot/device/bindSubsystem', {
deviceId,
subsystemId: null,
} satisfies IotSubsystemApi.BindSubsystemReqVO);
}