From 84ec762d09a6840e71d494602f69b726dc1cd12b Mon Sep 17 00:00:00 2001 From: 16337 <1633794139@qq.com> Date: Mon, 23 Mar 2026 16:49:27 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=9F=E8=83=BD=EF=BC=9A=E6=91=84=E5=83=8F?= =?UTF-8?q?=E5=A4=B4=E9=A1=B5=E9=9D=A2=E5=A2=9E=E5=8A=A0=E5=8C=BA=E5=9F=9F?= =?UTF-8?q?=E9=80=89=E6=8B=A9=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 编辑弹窗新增「所属区域」下拉框,支持搜索,数据来自 IoT 平台 2. 表格新增「区域」列显示已绑定的区域名称 3. API 新增 getAreaList 函数查询 vsp-service 区域列表接口 4. Vite 代理新增 /api/area → vsp-service:8000 5. Camera 类型新增 areaId 字段 --- apps/web-antd/src/api/aiot/device/index.ts | 13 +++++ .../src/views/aiot/device/camera/index.vue | 53 ++++++++++++++++++- apps/web-antd/vite.config.mts | 5 ++ 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/apps/web-antd/src/api/aiot/device/index.ts b/apps/web-antd/src/api/aiot/device/index.ts index 2a4cab599..e81295230 100644 --- a/apps/web-antd/src/api/aiot/device/index.ts +++ b/apps/web-antd/src/api/aiot/device/index.ts @@ -45,6 +45,7 @@ export namespace AiotDeviceApi { mediaServerId?: string; streamKey?: string; createTime?: string; + areaId?: number; // 所属区域ID } /** ROI 区域 */ @@ -299,3 +300,15 @@ export async function getAlertImageUrl(imagePath: string): Promise { `&access-token=${encodeURIComponent(token)}` ); } + +// ==================== 区域列表 ==================== + +/** 获取区域列表(从 IoT 平台代理查询) */ +export async function getAreaList(): Promise< + { id: number; areaName: string; parentId?: number }[] +> { + const resp = await fetch(`${apiURL}/api/area/list`); + const json = await resp.json(); + if (json.code === 0) return json.data || []; + return []; +} diff --git a/apps/web-antd/src/views/aiot/device/camera/index.vue b/apps/web-antd/src/views/aiot/device/camera/index.vue index 433129a5f..d3449b746 100644 --- a/apps/web-antd/src/views/aiot/device/camera/index.vue +++ b/apps/web-antd/src/views/aiot/device/camera/index.vue @@ -30,6 +30,7 @@ import { import { deleteCamera, + getAreaList, getCameraList, getMediaServerList, getRoiByCameraId, @@ -59,6 +60,7 @@ const columns = [ { title: '摄像头名称', dataIndex: 'cameraName', width: 150 }, { title: '拉流地址', dataIndex: 'srcUrl', ellipsis: true }, { title: '边缘设备', dataIndex: 'edgeDeviceId', width: 100 }, + { title: '区域', key: 'areaName', width: 100 }, { title: '状态', key: 'status', width: 60, align: 'center' as const }, { title: 'ROI', key: 'roiCount', width: 80, align: 'center' as const }, { title: '操作', key: 'actions', width: 240, fixed: 'right' as const }, @@ -70,6 +72,7 @@ const editModalOpen = ref(false); const editModalTitle = ref('添加摄像头'); const saving = ref(false); const mediaServerOptions = ref<{ label: string; value: string }[]>([]); +const areaOptions = ref<{ label: string; value: number }[]>([]); const editForm = reactive>({ id: undefined, type: 'default', @@ -86,6 +89,7 @@ const editForm = reactive>({ enableDisableNoneReader: true, relatesMediaServerId: '', ffmpegCmdKey: '', + areaId: undefined as number | undefined, }); // 从已有摄像头中提取应用名选项 @@ -108,6 +112,15 @@ const appOptions = computed(() => { .sort((a, b) => b.count - a.count); }); +// 区域名称映射(area_id → area_name) +const areaNameMap = computed(() => { + const map: Record = {}; + areaOptions.value.forEach((a) => { + map[a.value] = a.label; + }); + return map; +}); + // ==================== 数据加载 ==================== async function loadData() { @@ -173,6 +186,18 @@ async function loadMediaServers() { } } +async function loadAreaOptions() { + try { + const list = await getAreaList(); + areaOptions.value = list.map((a: any) => ({ + label: a.areaName || a.name || `区域${a.id}`, + value: a.id, + })); + } catch { + areaOptions.value = []; + } +} + // ==================== 新增 / 编辑 ==================== function resetForm() { @@ -192,6 +217,7 @@ function resetForm() { enableDisableNoneReader: true, relatesMediaServerId: '', ffmpegCmdKey: '', + areaId: undefined, }); } @@ -234,6 +260,7 @@ function handleAdd() { editModalTitle.value = '添加摄像头'; editModalOpen.value = true; loadMediaServers(); + loadAreaOptions(); // 自动填充流ID autoFillStreamId(); } @@ -256,10 +283,12 @@ function handleEdit(row: AiotDeviceApi.Camera) { enableDisableNoneReader: row.enableDisableNoneReader ?? true, relatesMediaServerId: row.relatesMediaServerId || '', ffmpegCmdKey: row.ffmpegCmdKey || '', + areaId: row.areaId || undefined, }); editModalTitle.value = '编辑摄像头'; editModalOpen.value = true; loadMediaServers(); + loadAreaOptions(); } async function handleSave() { @@ -368,6 +397,7 @@ watch( onMounted(() => { loadData(); + loadAreaOptions(); }); @@ -421,7 +451,13 @@ onMounted(() => { size="middle" >