From 9a9263f7a18eafddc0d9a19cc95402225f59eca4 Mon Sep 17 00:00:00 2001 From: lzh Date: Tue, 3 Feb 2026 15:37:52 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20ops=E6=8E=A5=E5=8F=A3ts=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web-antd/src/api/ops/area/index.ts | 174 ++++++++++++++++++ apps/web-antd/src/api/ops/cleaning/index.ts | 117 +++++++++--- .../src/api/ops/order-center/index.ts | 20 +- .../web-antd/src/router/routes/modules/ops.ts | 10 + 4 files changed, 283 insertions(+), 38 deletions(-) create mode 100644 apps/web-antd/src/api/ops/area/index.ts diff --git a/apps/web-antd/src/api/ops/area/index.ts b/apps/web-antd/src/api/ops/area/index.ts new file mode 100644 index 000000000..cd4b14c82 --- /dev/null +++ b/apps/web-antd/src/api/ops/area/index.ts @@ -0,0 +1,174 @@ +import { requestClient } from '#/api/request'; + +/** 区域类型 */ +export type AreaType = 'PARK' | 'BUILDING' | 'FLOOR' | 'FUNCTION'; + +/** 功能类型 */ +export type FunctionType = + | 'MALE_TOILET' + | 'FEMALE_TOILET' + | 'PUBLIC' + | 'ELEVATOR' + | string; + +/** 区域等级 */ +export type AreaLevel = 'HIGH' | 'MEDIUM' | 'LOW'; + +/** 关联类型 */ +export type RelationType = 'TRAFFIC_COUNTER' | 'BEACON' | 'BADGE' | string; + +export namespace OpsAreaApi { + /** 业务区域 */ + export interface BusArea { + id?: number; + parentId?: number; + parentPath?: string; + areaName: string; + areaCode?: string; + areaType?: AreaType; + functionType?: FunctionType; + floorNo?: number; + cleaningFrequency?: number; + standardDuration?: number; + areaLevel?: AreaLevel; + isActive?: boolean; + sort?: number; + createTime?: string; + updateTime?: string; + children?: BusArea[]; + } + + /** 区域树查询参数 */ + export interface AreaTreeQuery { + areaType?: AreaType; + isActive?: boolean; + name?: string; + pageNo?: number; + pageSize?: number; + } + + /** 创建区域请求 */ + export interface CreateAreaReq { + areaName: string; + parentId?: number; + areaCode?: string; + areaType?: AreaType; + functionType?: FunctionType; + floorNo?: number; + cleaningFrequency?: number; + standardDuration?: number; + areaLevel?: AreaLevel; + isActive?: boolean; + sort?: number; + } + + /** 更新区域请求 */ + export interface UpdateAreaReq { + id: number; + areaName?: string; + parentId?: number; + areaCode?: string; + areaType?: AreaType; + functionType?: FunctionType; + floorNo?: number; + cleaningFrequency?: number; + standardDuration?: number; + areaLevel?: AreaLevel; + isActive?: boolean; + sort?: number; + } + + /** 区域-设备关联 */ + export interface AreaDeviceRelation { + id?: number; + areaId: number; + areaName?: string; + deviceId: number; + deviceKey?: string; + deviceName?: string; + nickname?: string; + productId?: number; + productKey?: string; + productName?: string; + relationType: RelationType; + configData?: Record; + enabled?: boolean; + createTime?: string; + updateTime?: string; + } + + /** 绑定设备请求 */ + export interface BindDeviceReq { + areaId: number; + deviceId: number; + relationType: RelationType; + configData?: Record; + } + + /** 更新关联请求 */ + export interface UpdateRelationReq { + id: number; + configData?: Record; + enabled?: boolean; + } +} + +// ========== 业务区域 API ========== + +/** 获取区域树(平铺列表,由前端自行组装) */ +export function getAreaTree(params?: OpsAreaApi.AreaTreeQuery) { + return requestClient.get('/ops/area/tree', { + params, + }); +} + +/** 获取区域详情 */ +export function getArea(id: number) { + return requestClient.get(`/ops/area/get`, { + params: { id }, + }); +} + +/** 新增区域 */ +export function createArea(data: OpsAreaApi.CreateAreaReq) { + return requestClient.post('/ops/area/create', data); +} + +/** 更新区域 */ +export function updateArea(data: OpsAreaApi.UpdateAreaReq) { + return requestClient.put('/ops/area/update', data); +} + +/** 删除区域 */ +export function deleteArea(id: number) { + return requestClient.delete(`/ops/area/delete`, { + params: { id }, + }); +} + +// ========== 区域设备关联 API ========== + +/** 获取区域下已绑定设备列表 */ +export function getDeviceRelationList(areaId: number) { + return requestClient.get( + '/ops/area/device-relation/list', + { params: { areaId } }, + ); +} + +/** 绑定设备到区域 */ +export function bindDevice(data: OpsAreaApi.BindDeviceReq) { + return requestClient.post('/ops/area/device-relation/bind', data); +} + +/** 更新设备关联配置 */ +export function updateDeviceRelation(data: OpsAreaApi.UpdateRelationReq) { + return requestClient.put('/ops/area/device-relation/update', data); +} + +/** 解除设备绑定 */ +export function removeDeviceRelation(id: number) { + return requestClient.delete('/ops/area/device-relation/remove', { + params: { id }, + }); +} diff --git a/apps/web-antd/src/api/ops/cleaning/index.ts b/apps/web-antd/src/api/ops/cleaning/index.ts index 472c2f947..faf812ad0 100644 --- a/apps/web-antd/src/api/ops/cleaning/index.ts +++ b/apps/web-antd/src/api/ops/cleaning/index.ts @@ -1,8 +1,8 @@ import { requestClient } from '#/api/request'; export namespace OpsCleaningApi { - /** 保洁员状态枚举 */ - export enum CleanerStatus { + /** 工牌状态枚举 */ + export enum BadgeStatus { BUSY = 'BUSY', // 忙碌 IDLE = 'IDLE', // 空闲 OFFLINE = 'OFFLINE', // 离线 @@ -23,17 +23,16 @@ export namespace OpsCleaningApi { /** 工牌通知请求 */ export interface DeviceNotifyReq { - cleanerId: number; // 保洁员ID + badgeId: number; // 工牌设备ID type: NotifyType; // 通知类型 content?: string; // 语音内容(仅语音通知需要) } - /** 保洁员状态信息 */ - export interface CleanerStatusItem { - userId: number; // 用户ID - userName: string; // 用户名称 - avatar?: string; // 头像 - status: CleanerStatus; // 状态 + /** 工牌状态信息 */ + export interface BadgeStatusItem { + deviceId: number; // 设备ID + deviceKey: string; // 设备编码 + status: BadgeStatus; // 状态 currentAreaId?: number; // 当前区域ID currentAreaName?: string; // 当前区域名称 batteryLevel: number; // 电量(0-100) @@ -42,15 +41,15 @@ export namespace OpsCleaningApi { todayWorkMinutes?: number; // 今日工作时长(分钟) } - /** 保洁员状态列表查询参数 */ - export interface CleanerListQuery { + /** 工牌状态列表查询参数 */ + export interface BadgeListQuery { areaId?: number; // 区域ID(可选) - status?: CleanerStatus; // 状态筛选(可选) + status?: BadgeStatus; // 状态筛选(可选) } - /** 保洁员状态列表响应 */ - export interface CleanerListResp { - list: CleanerStatusItem[]; + /** 工牌状态列表响应 */ + export interface BadgeListResp { + list: BadgeStatusItem[]; } /** 工单时间轴节点 */ @@ -70,19 +69,70 @@ export namespace OpsCleaningApi { timeline: TimelineItem[]; } - /** 保洁员工牌实时状态 */ + /** 工牌实时状态 */ export interface BadgeRealtimeStatus { - cleanerId: number; // 保洁员ID deviceId: number; // 设备ID deviceKey: string; // 设备Key - status: CleanerStatus; // 设备状态 - batteryLevel: number; // 电量 + status: BadgeStatus | string; // 设备状态(兼容大小写) + batteryLevel: number | null; // 电量(可能为空) lastHeartbeatTime: string; // 最后心跳时间 - rssi?: number; // 信号强度 + rssi?: number | null; // 信号强度(可能为空) isInArea: boolean; // 是否在区域内 areaId?: number; // 当前区域ID - areaName?: string; // 当前区域名称 + areaName?: string | null; // 当前区域名称(可能为空) } + + /** 业务日志类型 */ + export type BusinessLogType = 'alert' | 'device' | 'operation' | 'system'; + + /** 业务日志项 */ + export interface BusinessLog { + id: number; + type: BusinessLogType; // 日志类型 + title: string; // 标题 + content: string; // 内容 + operator: string; // 操作人 + time: string; // 时间 + status?: string; // 关联的状态阶段 + extra?: Record; // 额外信息 + } + + /** 业务日志响应 */ + export interface BusinessLogsResp { + orderId: number; + logs: BusinessLog[]; + } +} + +// ==================== IoT 设备接口(直接调用 IoT 模块) ==================== + +/** IoT 设备状态响应 */ +export interface IotDeviceStatus { + deviceId: number; + deviceCode: string; + status: number; // 0=未激活, 1=在线, 2=离线 + statusChangeTime: string; +} + +/** 获取 IoT 设备状态 */ +export function getIotDeviceStatus(deviceId: number) { + return requestClient.get(`/iot/device/status/get-status`, { + params: { deviceId } + }); +} + +/** 批量获取 IoT 设备状态 */ +export function batchGetIotDeviceStatus(deviceIds: number[]) { + return requestClient.get(`/iot/device/status/batch-get-status`, { + params: { deviceIds } + }); +} + +/** 获取 IoT 设备最新属性(含电量、RSSI等) */ +export function getIotDeviceProperties(deviceId: number) { + return requestClient.get>(`/iot/device/property/get-latest`, { + params: { deviceId } + }); } // ==================== 保洁专属接口 ==================== @@ -97,32 +147,39 @@ export function sendDeviceNotify(data: OpsCleaningApi.DeviceNotifyReq) { return requestClient.post('/ops/clean/device/notify', data); } -/** 查询保洁员实时状态列表 */ -export function getCleanerStatusList(params?: OpsCleaningApi.CleanerListQuery) { - return requestClient.get( - '/ops/clean/cleaner/list', +/** 查询工牌实时状态列表 */ +export function getBadgeStatusList(params?: OpsCleaningApi.BadgeListQuery) { + return requestClient.get( + '/ops/clean/badge/list', { params }, ); } /** 获取工单时间轴 */ -export function getOrderTimeline(orderId: number) { +export function getOrderTimeline(orderId: number | string) { return requestClient.get( `/ops/clean/order/timeline/${orderId}`, ); } -/** 获取保洁员工牌实时状态 */ -export function getBadgeRealtimeStatus(cleanerId: number) { +/** 获取工牌实时状态 */ +export function getBadgeRealtimeStatus(badgeId: number) { return requestClient.get( - `/ops/clean/badge/realtime/${cleanerId}`, + `/ops/clean/badge/realtime/${badgeId}`, ); } -/** 手动完成工单(兜底操作) */ +/** 手动完成工单(兜底操作) */ export function manualCompleteOrder(orderId: number, remark?: string) { return requestClient.post('/ops/clean/order/manual-complete', { orderId, remark, }); } + +/** 获取工单业务日志 */ +export function getOrderBusinessLogs(orderId: number | string) { + return requestClient.get( + `/ops/order/business-logs/${orderId}`, + ); +} diff --git a/apps/web-antd/src/api/ops/order-center/index.ts b/apps/web-antd/src/api/ops/order-center/index.ts index a072275f5..84719f538 100644 --- a/apps/web-antd/src/api/ops/order-center/index.ts +++ b/apps/web-antd/src/api/ops/order-center/index.ts @@ -39,8 +39,10 @@ export namespace OpsOrderCenterApi { /** 触发来源枚举 */ export enum TriggerSource { IOT_BEACON = 'IOT_BEACON', // 蓝牙信标 + IOT_TRAFFIC = 'IOT_TRAFFIC', // 客流阈值(IoT设备) + TRAFFIC = 'TRAFFIC', // 客流阈值 + PEOPLE_FLOW = 'PEOPLE_FLOW', // 客流阈值(旧版) MANUAL = 'MANUAL', // 手动创建 - PEOPLE_FLOW = 'PEOPLE_FLOW', // 客流阈值 } /** 工单查询参数 */ @@ -76,8 +78,9 @@ export namespace OpsOrderCenterApi { areaId?: number; // 区域ID location?: string; // 位置描述 assigneeId?: number; // 执行人ID - assigneeName?: string; // 执行人姓名 - createTime: string; // 创建时间 + assigneeName?: string | null; // 执行人姓名(可能为空) + createTime: string | number; // 创建时间(支持时间戳或字符串) + sourceType?: TriggerSource; // 触发来源 extInfo?: CleaningExtInfo; // 保洁扩展信息 } @@ -88,8 +91,9 @@ export namespace OpsOrderCenterApi { triggerDeviceId?: number; // 触发设备ID triggerDeviceKey?: string; // 触发设备Key triggerRule?: string; // 触发规则 - startTime?: string; // 开始时间(到岗时间) - endTime?: string; // 结束时间 + startTime?: string | number | null; // 开始时间(到岗时间,支持时间戳或字符串) + endTime?: string | number | null; // 结束时间(支持时间戳或字符串) + updateTime?: string | number; // 更新时间(支持时间戳或字符串) remark?: string; // 备注 } @@ -100,12 +104,12 @@ export namespace OpsOrderCenterApi { }; } - /** 快速统计数据 */ + /** 快速统计��据 */ export interface QuickStats { pendingCount: number; // 待分配数量 inProgressCount: number; // 进行中数量 completedTodayCount: number; // 今日完成数量 - onlineCleanerCount: number; // 在线保洁员数量 + onlineBadgeCount: number; // 在线工牌数量 } /** 分配工单请求 */ @@ -146,7 +150,7 @@ export function getOrderPage(params: OpsOrderCenterApi.OrderPageQuery) { } /** 查询工单详情 */ -export function getOrderDetail(id: number) { +export function getOrderDetail(id: number | string) { return requestClient.get( `/ops/order-center/detail/${id}`, ); diff --git a/apps/web-antd/src/router/routes/modules/ops.ts b/apps/web-antd/src/router/routes/modules/ops.ts index 335af62a4..b724ae5d8 100644 --- a/apps/web-antd/src/router/routes/modules/ops.ts +++ b/apps/web-antd/src/router/routes/modules/ops.ts @@ -11,6 +11,16 @@ const routes: RouteRecordRaw[] = [ hideInMenu: true, }, children: [ + // 业务区域管理 + { + path: 'area', + name: 'OpsBusArea', + meta: { + title: '业务区域管理', + activePath: '/ops/config/area', + }, + component: () => import('#/views/ops/area/index.vue'), + }, // 保洁工单详情 { path: 'cleaning/work-order/detail/:id',