From b9f45c8fdcf93d2fb94ecf1a388c1408f844329c Mon Sep 17 00:00:00 2001 From: lzh Date: Sun, 15 Mar 2026 16:54:38 +0800 Subject: [PATCH] =?UTF-8?q?feat(@vben/web-antd):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=8C=BA=E5=9F=9F=E5=AE=89=E4=BF=9D=E9=85=8D=E7=BD=AE=E6=A8=A1?= =?UTF-8?q?=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增区域安保 API 接口定义 - 新增区域安保配置页面,支持区域视图和人员视图 - 包含人员绑定弹窗和人员卡片组件 Co-Authored-By: Claude Opus 4.6 --- .../src/api/ops/area-security/index.ts | 60 ++ .../src/views/ops/area-security/data.ts | 79 +++ .../src/views/ops/area-security/index.vue | 43 ++ .../ops/area-security/modules/area-view.vue | 356 ++++++++++ .../modules/bind-staff-modal.vue | 102 +++ .../ops/area-security/modules/staff-card.vue | 179 ++++++ .../ops/area-security/modules/staff-view.vue | 606 ++++++++++++++++++ 7 files changed, 1425 insertions(+) create mode 100644 apps/web-antd/src/api/ops/area-security/index.ts create mode 100644 apps/web-antd/src/views/ops/area-security/data.ts create mode 100644 apps/web-antd/src/views/ops/area-security/index.vue create mode 100644 apps/web-antd/src/views/ops/area-security/modules/area-view.vue create mode 100644 apps/web-antd/src/views/ops/area-security/modules/bind-staff-modal.vue create mode 100644 apps/web-antd/src/views/ops/area-security/modules/staff-card.vue create mode 100644 apps/web-antd/src/views/ops/area-security/modules/staff-view.vue diff --git a/apps/web-antd/src/api/ops/area-security/index.ts b/apps/web-antd/src/api/ops/area-security/index.ts new file mode 100644 index 000000000..c2e05eda5 --- /dev/null +++ b/apps/web-antd/src/api/ops/area-security/index.ts @@ -0,0 +1,60 @@ +import { requestClient } from '#/api/request'; + +export namespace AreaSecurityApi { + /** 区域-安保人员绑定记录(对应后端 OpsAreaSecurityUserRespVO) */ + export interface AreaSecurityUser { + id: number; + areaId: number; + userId: number; + userName: string; + teamId?: number; + enabled: boolean; + sort: number; + createTime?: string; + } + + /** 绑定安保人员请求(对应后端 OpsAreaSecurityUserBindReqVO) */ + export interface BindReq { + areaId: number; + userId: number; + userName?: string; + teamId?: number; + sort?: number; + } + + /** 更新绑定请求(对应后端 OpsAreaSecurityUserUpdateReqVO) */ + export interface UpdateReq { + id: number; + enabled?: boolean; + sort?: number; + teamId?: number; + } +} + +// ========== 区域安保人员绑定 API ========== +// 后端路径前缀: /ops/security/area-user + +/** 获取某区域已绑定的安保人员列表 */ +export function getAreaSecurityUserList(areaId: number) { + return requestClient.get( + '/ops/security/area-user/list', + { params: { areaId } }, + ); +} + +/** 绑定安保人员到区域 */ +export function bindAreaSecurityUser(data: AreaSecurityApi.BindReq) { + return requestClient.post('/ops/security/area-user/bind', data); +} + +/** 更新绑定信息(启用/停用、排序、团队) */ +export function updateAreaSecurityUser(data: AreaSecurityApi.UpdateReq) { + return requestClient.put('/ops/security/area-user/update', data); +} + +/** 解绑安保人员(按绑定记录 ID) */ +export function unbindAreaSecurityUser(id: number) { + return requestClient.delete('/ops/security/area-user/unbind', { + params: { id }, + }); +} diff --git a/apps/web-antd/src/views/ops/area-security/data.ts b/apps/web-antd/src/views/ops/area-security/data.ts new file mode 100644 index 000000000..b9c588fa9 --- /dev/null +++ b/apps/web-antd/src/views/ops/area-security/data.ts @@ -0,0 +1,79 @@ +import type { OpsAreaApi } from '#/api/ops/area'; +import type { AreaSecurityApi } from '#/api/ops/area-security'; + +/** + * 前端扩展类型:在后端返回的直接绑定基础上,增加"继承"标识。 + * 后端只存直接绑定;继承关系由前端根据区域树向上查找计算。 + */ +export type BindType = 'DIRECT' | 'INHERITED'; + +export interface AreaSecurityUserDisplay + extends AreaSecurityApi.AreaSecurityUser { + /** 绑定类型 —— 直接绑定 or 从父区域继承 */ + bindType: BindType; + /** 继承来源区域 ID(仅 INHERITED) */ + sourceAreaId?: number; + /** 继承来源区域名称(仅 INHERITED) */ + sourceAreaName?: string; +} + +/** 绑定类型标签配置 */ +export const BIND_TYPE_MAP: Record = + { + DIRECT: { label: '直接绑定', color: 'blue' }, + INHERITED: { label: '继承', color: 'default' }, + }; + +/** + * 计算某区域的完整安保人员列表(直接 + 继承)。 + * + * 逻辑:从当前区域向上遍历所有祖先区域, + * 每个祖先区域的绑定人员标记为 INHERITED,当前区域的标记为 DIRECT。 + * 同一个 userId 出现多次时,DIRECT 优先(不重复展示)。 + * + * @param currentAreaId 当前选中区域 ID + * @param areaFlatList 所有区域平铺列表 + * @param fetchBindings 获取某区域绑定列表的函数 + */ +export async function computeFullSecurityList( + currentAreaId: number, + areaFlatList: OpsAreaApi.BusArea[], + fetchBindings: ( + areaId: number, + ) => Promise, +): Promise { + const areaMap = new Map(areaFlatList.map((a) => [a.id, a])); + + // 收集祖先链(从当前区域向上) + const ancestorChain: OpsAreaApi.BusArea[] = []; + let current = areaMap.get(currentAreaId); + while (current) { + ancestorChain.push(current); + current = current.parentId ? areaMap.get(current.parentId) : undefined; + } + + // 逐层获取绑定并标记类型 + const seen = new Set(); // userId 去重 + const result: AreaSecurityUserDisplay[] = []; + + for (const area of ancestorChain) { + if (!area.id) continue; + const bindings = await fetchBindings(area.id); + const isDirect = area.id === currentAreaId; + + for (const binding of bindings) { + if (!binding.enabled) continue; + if (seen.has(binding.userId)) continue; + seen.add(binding.userId); + + result.push({ + ...binding, + bindType: isDirect ? 'DIRECT' : 'INHERITED', + sourceAreaId: isDirect ? undefined : area.id, + sourceAreaName: isDirect ? undefined : area.areaName, + }); + } + } + + return result; +} diff --git a/apps/web-antd/src/views/ops/area-security/index.vue b/apps/web-antd/src/views/ops/area-security/index.vue new file mode 100644 index 000000000..c5edb878d --- /dev/null +++ b/apps/web-antd/src/views/ops/area-security/index.vue @@ -0,0 +1,43 @@ + + + + + diff --git a/apps/web-antd/src/views/ops/area-security/modules/area-view.vue b/apps/web-antd/src/views/ops/area-security/modules/area-view.vue new file mode 100644 index 000000000..036a64011 --- /dev/null +++ b/apps/web-antd/src/views/ops/area-security/modules/area-view.vue @@ -0,0 +1,356 @@ + + + + + diff --git a/apps/web-antd/src/views/ops/area-security/modules/bind-staff-modal.vue b/apps/web-antd/src/views/ops/area-security/modules/bind-staff-modal.vue new file mode 100644 index 000000000..bb5087cde --- /dev/null +++ b/apps/web-antd/src/views/ops/area-security/modules/bind-staff-modal.vue @@ -0,0 +1,102 @@ + + + diff --git a/apps/web-antd/src/views/ops/area-security/modules/staff-card.vue b/apps/web-antd/src/views/ops/area-security/modules/staff-card.vue new file mode 100644 index 000000000..9ce133359 --- /dev/null +++ b/apps/web-antd/src/views/ops/area-security/modules/staff-card.vue @@ -0,0 +1,179 @@ + + + + + diff --git a/apps/web-antd/src/views/ops/area-security/modules/staff-view.vue b/apps/web-antd/src/views/ops/area-security/modules/staff-view.vue new file mode 100644 index 000000000..efaee179a --- /dev/null +++ b/apps/web-antd/src/views/ops/area-security/modules/staff-view.vue @@ -0,0 +1,606 @@ + + + + +