From bc2f1e89c9718d750ef87f9af0fd1b72659fde66 Mon Sep 17 00:00:00 2001 From: 16337 <1633794139@qq.com> Date: Fri, 6 Feb 2026 16:40:08 +0800 Subject: [PATCH 01/99] =?UTF-8?q?fix:=20=E6=81=A2=E5=A4=8D=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E4=B8=BB=E9=A2=98=E8=89=B2=E7=B3=BB=EF=BC=8C=E5=85=B3?= =?UTF-8?q?=E9=97=AD=E7=A7=9F=E6=88=B7=E5=92=8C=20API=20=E5=8A=A0=E5=AF=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除自定义暖黄色主题,恢复芋道默认蓝色色系 - 关闭租户功能(VITE_APP_TENANT_ENABLE=false) - 关闭 API 加密(VITE_APP_API_ENCRYPT_ENABLE=false) - 以上配置适配当前 FastAPI 后端(不支持租户和加密) Co-Authored-By: Claude Opus 4.6 --- apps/web-antd/.env | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/web-antd/.env b/apps/web-antd/.env index a0bfde11e..106374cf5 100644 --- a/apps/web-antd/.env +++ b/apps/web-antd/.env @@ -11,7 +11,7 @@ VITE_APP_STORE_SECURE_KEY=please-replace-me-with-your-own-key VITE_NITRO_MOCK=false # 租户开关 -VITE_APP_TENANT_ENABLE=true +VITE_APP_TENANT_ENABLE=false # 验证码的开关 VITE_APP_CAPTCHA_ENABLE=false @@ -26,7 +26,7 @@ VITE_APP_BAIDU_CODE = e98f2eab6ceb8688bc6d8fc5332ff093 VITE_GOVIEW_URL='http://127.0.0.1:3000' # API 加解密 -VITE_APP_API_ENCRYPT_ENABLE = true +VITE_APP_API_ENCRYPT_ENABLE = false VITE_APP_API_ENCRYPT_HEADER = X-Api-Encrypt VITE_APP_API_ENCRYPT_ALGORITHM = AES VITE_APP_API_ENCRYPT_REQUEST_KEY = 52549111389893486934626385991395 From 159a82aaa966f4c6c0be549b4c87f59637f6e16e Mon Sep 17 00:00:00 2001 From: 16337 <1633794139@qq.com> Date: Fri, 6 Feb 2026 16:40:26 +0800 Subject: [PATCH 02/99] =?UTF-8?q?feat(aiot):=20=E6=90=AD=E5=BB=BA=20aiot?= =?UTF-8?q?=20=E5=89=8D=E7=AB=AF=E6=A8=A1=E5=9D=97=E8=B7=AF=E7=94=B1?= =?UTF-8?q?=E5=92=8C=20API=20=E5=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 router/routes/modules/aiot.ts:6 个页面路由 告警列表、摄像头汇总、摄像头管理、ROI配置、实时视频、边缘节点 - 新增 api/aiot/alarm/:告警 API(分页、详情、处理、删除、统计、汇总) - 新增 api/aiot/edge/:边缘设备 API(分页、详情、统计) - 新增 api/aiot/device/:摄像头和 ROI API(调用 WVP 后端) - 新增 api/aiot/video/:视频播放 API(playStart/playStop) - 新增 api/aiot/request.ts:WVP 专用请求客户端(跳过芋道响应拦截器) Co-Authored-By: Claude Opus 4.6 --- apps/web-antd/src/api/aiot/alarm/index.ts | 118 ++++++++++++++++++ apps/web-antd/src/api/aiot/device/index.ts | 99 +++++++++++++++ apps/web-antd/src/api/aiot/edge/index.ts | 55 ++++++++ apps/web-antd/src/api/aiot/request.ts | 35 ++++++ apps/web-antd/src/api/aiot/video/index.ts | 43 +++++++ .../src/router/routes/modules/aiot.ts | 71 +++++++++++ 6 files changed, 421 insertions(+) create mode 100644 apps/web-antd/src/api/aiot/alarm/index.ts create mode 100644 apps/web-antd/src/api/aiot/device/index.ts create mode 100644 apps/web-antd/src/api/aiot/edge/index.ts create mode 100644 apps/web-antd/src/api/aiot/request.ts create mode 100644 apps/web-antd/src/api/aiot/video/index.ts create mode 100644 apps/web-antd/src/router/routes/modules/aiot.ts diff --git a/apps/web-antd/src/api/aiot/alarm/index.ts b/apps/web-antd/src/api/aiot/alarm/index.ts new file mode 100644 index 000000000..40126c09d --- /dev/null +++ b/apps/web-antd/src/api/aiot/alarm/index.ts @@ -0,0 +1,118 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace AiotAlarmApi { + /** AI 告警 VO */ + export interface Alert { + id?: number; + alertNo?: string; + cameraId?: string; + cameraName?: string; + roiId?: string; + bindId?: string; + deviceId?: string; + alertType?: string; + alertTypeName?: string; + algorithm?: string; + confidence?: number; + durationMinutes?: number; + triggerTime?: string; + message?: string; + bbox?: string; + snapshotUrl?: string; + ossUrl?: string; + status?: string; + statusName?: string; + level?: string; + handleRemark?: string; + handledBy?: string; + handledAt?: string; + workOrderId?: number; + aiAnalysis?: Record; + createdAt?: string; + updatedAt?: string; + logInfo?: AlertLogInfo; + } + + /** 告警日志信息 */ + export interface AlertLogInfo { + receiveTime?: string; + handleTime?: string; + handledBy?: string; + handleRemark?: string; + confidence?: number; + durationMinutes?: number; + bbox?: string; + aiAnalysis?: Record; + } + + /** 摄像头告警汇总 */ + export interface CameraAlertSummary { + cameraId?: string; + cameraName?: string; + totalCount?: number; + pendingCount?: number; + lastAlertTime?: string; + lastAlertType?: string; + lastAlertTypeName?: string; + } + + /** 告警统计 */ + export interface AlertStatistics { + total?: number; + todayCount?: number; + pendingCount?: number; + handledCount?: number; + byType?: Record; + byStatus?: Record; + byLevel?: Record; + } +} + +// ==================== 告警管理 API ==================== + +/** 分页查询告警列表 */ +export function getAlertPage(params: PageParam) { + return requestClient.get>( + '/aiot/alarm/alert/page', + { params }, + ); +} + +/** 获取告警详情 */ +export function getAlert(id: number) { + return requestClient.get( + `/aiot/alarm/alert/get?id=${id}`, + ); +} + +/** 处理告警 */ +export function handleAlert(id: number, status: string, remark?: string) { + return requestClient.put('/aiot/alarm/alert/handle', null, { + params: { id, status, remark }, + }); +} + +/** 删除告警 */ +export function deleteAlert(id: number) { + return requestClient.delete(`/aiot/alarm/alert/delete?id=${id}`); +} + +/** 获取告警统计 */ +export function getAlertStatistics(startTime?: string, endTime?: string) { + return requestClient.get( + '/aiot/alarm/alert/statistics', + { params: { startTime, endTime } }, + ); +} + +// ==================== 摄像头告警汇总 API ==================== + +/** 以摄像头维度获取告警汇总 */ +export function getCameraAlertSummary(params: PageParam) { + return requestClient.get>( + '/aiot/alarm/camera-summary/page', + { params }, + ); +} diff --git a/apps/web-antd/src/api/aiot/device/index.ts b/apps/web-antd/src/api/aiot/device/index.ts new file mode 100644 index 000000000..bcffe8f33 --- /dev/null +++ b/apps/web-antd/src/api/aiot/device/index.ts @@ -0,0 +1,99 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { wvpRequestClient } from '#/api/aiot/request'; + +export namespace AiotDeviceApi { + /** ROI 区域 */ + export interface Roi { + id?: number; + channelId?: string; + channelName?: string; + name?: string; + type?: string; + points?: string; + enabled?: boolean; + createdAt?: string; + updatedAt?: string; + } + + /** ROI 算法绑定 */ + export interface RoiAlgoBind { + id?: number; + roiId?: number; + algorithmId?: number; + algorithmName?: string; + enabled?: boolean; + config?: Record; + } + + /** 算法 */ + export interface Algorithm { + id?: number; + name?: string; + code?: string; + description?: string; + enabled?: boolean; + } + + /** 摄像头通道 */ + export interface Channel { + channelId?: string; + name?: string; + manufacturer?: string; + status?: string; + ptztypeText?: string; + longitudeWgs84?: number; + latitudeWgs84?: number; + } +} + +// ==================== ROI 区域管理 API ==================== + +/** 获取 ROI 列表 */ +export function getRoiPage(params: PageParam) { + return wvpRequestClient.get>( + '/aiot/device/roi/list', + { params }, + ); +} + +/** 保存 ROI */ +export function saveRoi(data: AiotDeviceApi.Roi) { + return wvpRequestClient.post('/aiot/device/roi/save', data); +} + +/** 删除 ROI */ +export function deleteRoi(id: number) { + return wvpRequestClient.delete(`/aiot/device/roi/delete?id=${id}`); +} + +// ==================== 算法绑定 API ==================== + +/** 绑定算法到 ROI */ +export function bindAlgo(data: { roiId: number; algorithmId: number }) { + return wvpRequestClient.post('/aiot/device/roi/bindAlgo', data); +} + +/** 解绑算法 */ +export function unbindAlgo(bindId: number) { + return wvpRequestClient.delete( + `/aiot/device/roi/unbindAlgo?bindId=${bindId}`, + ); +} + +/** 获取算法列表 */ +export function getAlgorithmList() { + return wvpRequestClient.get( + '/aiot/device/algorithm/list', + ); +} + +// ==================== 摄像头通道 API ==================== + +/** 获取摄像头通道列表 */ +export function getChannelPage(params: PageParam) { + return wvpRequestClient.get>( + '/aiot/device/channel/list', + { params }, + ); +} diff --git a/apps/web-antd/src/api/aiot/edge/index.ts b/apps/web-antd/src/api/aiot/edge/index.ts new file mode 100644 index 000000000..39cf9843c --- /dev/null +++ b/apps/web-antd/src/api/aiot/edge/index.ts @@ -0,0 +1,55 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace AiotEdgeApi { + /** 边缘设备 VO */ + export interface Device { + id?: number; + deviceId?: string; + deviceName?: string; + status?: string; + statusName?: string; + lastHeartbeat?: string; + uptimeSeconds?: number; + framesProcessed?: number; + alertsGenerated?: number; + ipAddress?: string; + streamCount?: number; + configVersion?: string; + extraInfo?: Record; + updatedAt?: string; + } + + /** 设备统计 */ + export interface DeviceStatistics { + total?: number; + online?: number; + offline?: number; + error?: number; + } +} + +// ==================== 边缘设备 API ==================== + +/** 分页查询边缘设备列表 */ +export function getDevicePage(params: PageParam) { + return requestClient.get>( + '/aiot/edge/device/page', + { params }, + ); +} + +/** 获取设备详情 */ +export function getDevice(id: string) { + return requestClient.get( + `/aiot/edge/device/get?id=${id}`, + ); +} + +/** 获取设备统计 */ +export function getDeviceStatistics() { + return requestClient.get( + '/aiot/edge/device/statistics', + ); +} diff --git a/apps/web-antd/src/api/aiot/request.ts b/apps/web-antd/src/api/aiot/request.ts new file mode 100644 index 000000000..638d33689 --- /dev/null +++ b/apps/web-antd/src/api/aiot/request.ts @@ -0,0 +1,35 @@ +/** + * WVP 视频平台专用请求客户端 + * + * WVP 返回原始 JSON(非芋道的 {code:0, data:...} 格式), + * 需要跳过芋道默认的响应拦截器。 + */ +import { useAppConfig } from '@vben/hooks'; +import { preferences } from '@vben/preferences'; +import { RequestClient } from '@vben/request'; +import { useAccessStore } from '@vben/stores'; + +const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD); + +function createWvpRequestClient(baseURL: string) { + const client = new RequestClient({ + baseURL, + }); + + // 请求头:携带 token 用于 Vite 代理鉴权透传 + client.addRequestInterceptor({ + fulfilled: async (config) => { + const accessStore = useAccessStore(); + const token = accessStore.accessToken; + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + config.headers['Accept-Language'] = preferences.app.locale; + return config; + }, + }); + + return client; +} + +export const wvpRequestClient = createWvpRequestClient(apiURL); diff --git a/apps/web-antd/src/api/aiot/video/index.ts b/apps/web-antd/src/api/aiot/video/index.ts new file mode 100644 index 000000000..4fdd62dae --- /dev/null +++ b/apps/web-antd/src/api/aiot/video/index.ts @@ -0,0 +1,43 @@ +import { wvpRequestClient } from '#/api/aiot/request'; + +export namespace AiotVideoApi { + /** 流信息 */ + export interface StreamInfo { + app?: string; + stream?: string; + ip?: string; + flv?: string; + ws_flv?: string; + rtmp?: string; + hls?: string; + rtsp?: string; + mediaServerId?: string; + tracks?: StreamTrack[]; + } + + export interface StreamTrack { + codec_id?: number; + codec_id_name?: string; + ready?: boolean; + type?: number; + width?: number; + height?: number; + fps?: number; + } +} + +// ==================== 视频播放 API ==================== + +/** 开始播放 */ +export function playStart(deviceId: string, channelId: string) { + return wvpRequestClient.get( + `/aiot/video/play/start/${deviceId}/${channelId}`, + ); +} + +/** 停止播放 */ +export function playStop(deviceId: string, channelId: string) { + return wvpRequestClient.get( + `/aiot/video/play/stop/${deviceId}/${channelId}`, + ); +} diff --git a/apps/web-antd/src/router/routes/modules/aiot.ts b/apps/web-antd/src/router/routes/modules/aiot.ts new file mode 100644 index 000000000..c61ea6746 --- /dev/null +++ b/apps/web-antd/src/router/routes/modules/aiot.ts @@ -0,0 +1,71 @@ +import type { RouteRecordRaw } from 'vue-router'; + +const routes: RouteRecordRaw[] = [ + { + path: '/aiot', + name: 'AIoT', + meta: { + title: 'AIoT 智能平台', + icon: 'ant-design:robot-outlined', + keepAlive: true, + }, + children: [ + { + path: 'alarm/list', + name: 'AiotAlarmList', + component: () => import('#/views/aiot/alarm/list/index.vue'), + meta: { + title: '告警列表', + icon: 'ant-design:alert-outlined', + }, + }, + { + path: 'alarm/summary', + name: 'AiotAlarmSummary', + component: () => import('#/views/aiot/alarm/summary/index.vue'), + meta: { + title: '摄像头告警汇总', + icon: 'ant-design:video-camera-outlined', + }, + }, + { + path: 'device/camera', + name: 'AiotDeviceCamera', + component: () => import('#/views/aiot/device/camera/index.vue'), + meta: { + title: '摄像头管理', + icon: 'ant-design:camera-outlined', + }, + }, + { + path: 'device/roi', + name: 'AiotDeviceRoi', + component: () => import('#/views/aiot/device/roi/index.vue'), + meta: { + title: 'ROI 区域配置', + icon: 'ant-design:aim-outlined', + }, + }, + { + path: 'video/live', + name: 'AiotVideoLive', + component: () => import('#/views/aiot/video/live/index.vue'), + meta: { + title: '实时视频', + icon: 'ant-design:play-circle-outlined', + }, + }, + { + path: 'edge/node', + name: 'AiotEdgeNode', + component: () => import('#/views/aiot/edge/node/index.vue'), + meta: { + title: '边缘节点管理', + icon: 'ant-design:cluster-outlined', + }, + }, + ], + }, +]; + +export default routes; From c601395a03dafecc9007c0a175cd09609e2a5812 Mon Sep 17 00:00:00 2001 From: 16337 <1633794139@qq.com> Date: Fri, 6 Feb 2026 16:40:43 +0800 Subject: [PATCH 03/99] =?UTF-8?q?feat(aiot):=20=E6=B7=BB=E5=8A=A0=20aiot?= =?UTF-8?q?=20=E5=85=A8=E9=83=A8=E4=B8=9A=E5=8A=A1=E8=A7=86=E5=9B=BE?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - alarm/list:告警列表(搜索、详情弹窗、处理/忽略操作) - alarm/summary:摄像头告警汇总(跳转到对应摄像头告警列表) - device/camera:摄像头通道管理(跳转配置 ROI) - device/roi:ROI 区域配置列表(删除操作) - video/live:实时视频播放(输入设备/通道 ID 播放) - edge/node:边缘节点管理(状态徽标、运行时长、帧数统计) Co-Authored-By: Claude Opus 4.6 --- .../src/views/aiot/alarm/list/data.ts | 180 +++++++++ .../src/views/aiot/alarm/list/index.vue | 368 ++++++++++++++++++ .../src/views/aiot/alarm/summary/index.vue | 83 ++++ .../src/views/aiot/device/camera/index.vue | 110 ++++++ .../src/views/aiot/device/roi/data.ts | 40 ++ .../src/views/aiot/device/roi/index.vue | 99 +++++ .../web-antd/src/views/aiot/edge/node/data.ts | 77 ++++ .../src/views/aiot/edge/node/index.vue | 92 +++++ .../src/views/aiot/video/live/index.vue | 112 ++++++ 9 files changed, 1161 insertions(+) create mode 100644 apps/web-antd/src/views/aiot/alarm/list/data.ts create mode 100644 apps/web-antd/src/views/aiot/alarm/list/index.vue create mode 100644 apps/web-antd/src/views/aiot/alarm/summary/index.vue create mode 100644 apps/web-antd/src/views/aiot/device/camera/index.vue create mode 100644 apps/web-antd/src/views/aiot/device/roi/data.ts create mode 100644 apps/web-antd/src/views/aiot/device/roi/index.vue create mode 100644 apps/web-antd/src/views/aiot/edge/node/data.ts create mode 100644 apps/web-antd/src/views/aiot/edge/node/index.vue create mode 100644 apps/web-antd/src/views/aiot/video/live/index.vue diff --git a/apps/web-antd/src/views/aiot/alarm/list/data.ts b/apps/web-antd/src/views/aiot/alarm/list/data.ts new file mode 100644 index 000000000..47535808e --- /dev/null +++ b/apps/web-antd/src/views/aiot/alarm/list/data.ts @@ -0,0 +1,180 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 告警类型选项 */ +export const ALERT_TYPE_OPTIONS = [ + { label: '离岗检测', value: 'leave_post' }, + { label: '周界入侵', value: 'intrusion' }, +]; + +/** 告警状态选项 */ +export const ALERT_STATUS_OPTIONS = [ + { label: '待处理', value: 'pending' }, + { label: '处理中', value: 'processing' }, + { label: '已处理', value: 'handled' }, + { label: '已忽略', value: 'ignored' }, +]; + +/** 告警级别选项 */ +export const ALERT_LEVEL_OPTIONS = [ + { label: '低', value: 'low' }, + { label: '中', value: 'medium' }, + { label: '高', value: 'high' }, +]; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'cameraId', + label: '摄像头', + component: 'Input', + componentProps: { + placeholder: '请输入摄像头ID', + allowClear: true, + }, + }, + { + fieldName: 'alertType', + label: '告警类型', + component: 'Select', + componentProps: { + options: ALERT_TYPE_OPTIONS, + placeholder: '请选择告警类型', + allowClear: true, + }, + }, + { + fieldName: 'status', + label: '处理状态', + component: 'Select', + componentProps: { + options: ALERT_STATUS_OPTIONS, + placeholder: '请选择处理状态', + allowClear: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'alertNo', + title: '告警编号', + minWidth: 160, + }, + { + field: 'cameraId', + title: '摄像头', + minWidth: 120, + slots: { default: 'camera' }, + }, + { + field: 'alertType', + title: '告警类型', + minWidth: 100, + slots: { default: 'alertType' }, + }, + { + field: 'confidence', + title: '置信度', + minWidth: 80, + slots: { default: 'confidence' }, + }, + { + field: 'durationMinutes', + title: '持续时长', + minWidth: 100, + slots: { default: 'duration' }, + }, + { + field: 'status', + title: '状态', + minWidth: 90, + slots: { default: 'status' }, + }, + { + field: 'level', + title: '级别', + minWidth: 80, + slots: { default: 'level' }, + }, + { + field: 'triggerTime', + title: '触发时间', + minWidth: 170, + formatter: 'formatDateTime', + }, + { + field: 'createdAt', + title: '创建时间', + minWidth: 170, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 150, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 摄像头告警汇总列表字段 */ +export function useCameraSummaryColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'cameraId', + title: '摄像头ID', + minWidth: 150, + }, + { + field: 'cameraName', + title: '摄像头名称', + minWidth: 150, + }, + { + field: 'totalCount', + title: '告警总数', + minWidth: 100, + sortable: true, + }, + { + field: 'pendingCount', + title: '待处理', + minWidth: 100, + slots: { default: 'pendingCount' }, + }, + { + field: 'lastAlertTypeName', + title: '最近告警类型', + minWidth: 120, + }, + { + field: 'lastAlertTime', + title: '最近告警时间', + minWidth: 170, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 120, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-antd/src/views/aiot/alarm/list/index.vue b/apps/web-antd/src/views/aiot/alarm/list/index.vue new file mode 100644 index 000000000..394ba1e91 --- /dev/null +++ b/apps/web-antd/src/views/aiot/alarm/list/index.vue @@ -0,0 +1,368 @@ + + + diff --git a/apps/web-antd/src/views/aiot/alarm/summary/index.vue b/apps/web-antd/src/views/aiot/alarm/summary/index.vue new file mode 100644 index 000000000..263ed7865 --- /dev/null +++ b/apps/web-antd/src/views/aiot/alarm/summary/index.vue @@ -0,0 +1,83 @@ + + + diff --git a/apps/web-antd/src/views/aiot/device/camera/index.vue b/apps/web-antd/src/views/aiot/device/camera/index.vue new file mode 100644 index 000000000..a4f7e66c5 --- /dev/null +++ b/apps/web-antd/src/views/aiot/device/camera/index.vue @@ -0,0 +1,110 @@ + + + diff --git a/apps/web-antd/src/views/aiot/device/roi/data.ts b/apps/web-antd/src/views/aiot/device/roi/data.ts new file mode 100644 index 000000000..67dcee6d5 --- /dev/null +++ b/apps/web-antd/src/views/aiot/device/roi/data.ts @@ -0,0 +1,40 @@ +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +/** ROI 列表字段 */ +export function useRoiGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'name', + title: 'ROI 名称', + minWidth: 150, + }, + { + field: 'channelName', + title: '摄像头', + minWidth: 150, + }, + { + field: 'type', + title: '类型', + minWidth: 100, + }, + { + field: 'enabled', + title: '启用状态', + minWidth: 90, + slots: { default: 'enabled' }, + }, + { + field: 'createdAt', + title: '创建时间', + minWidth: 170, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 120, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-antd/src/views/aiot/device/roi/index.vue b/apps/web-antd/src/views/aiot/device/roi/index.vue new file mode 100644 index 000000000..b31993e8f --- /dev/null +++ b/apps/web-antd/src/views/aiot/device/roi/index.vue @@ -0,0 +1,99 @@ + + + diff --git a/apps/web-antd/src/views/aiot/edge/node/data.ts b/apps/web-antd/src/views/aiot/edge/node/data.ts new file mode 100644 index 000000000..a9c352b58 --- /dev/null +++ b/apps/web-antd/src/views/aiot/edge/node/data.ts @@ -0,0 +1,77 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +/** 设备状态选项 */ +export const DEVICE_STATUS_OPTIONS = [ + { label: '在线', value: 'online' }, + { label: '离线', value: 'offline' }, + { label: '异常', value: 'error' }, +]; + +/** 边缘设备搜索表单 */ +export function useEdgeGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'status', + label: '设备状态', + component: 'Select', + componentProps: { + options: DEVICE_STATUS_OPTIONS, + placeholder: '请选择设备状态', + allowClear: true, + }, + }, + ]; +} + +/** 边缘设备列表字段 */ +export function useEdgeGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'deviceId', + title: '设备ID', + minWidth: 160, + }, + { + field: 'deviceName', + title: '设备名称', + minWidth: 150, + }, + { + field: 'status', + title: '状态', + minWidth: 90, + slots: { default: 'status' }, + }, + { + field: 'lastHeartbeat', + title: '最后心跳', + minWidth: 170, + formatter: 'formatDateTime', + }, + { + field: 'uptimeSeconds', + title: '运行时长', + minWidth: 100, + slots: { default: 'uptime' }, + }, + { + field: 'framesProcessed', + title: '处理帧数', + minWidth: 100, + slots: { default: 'frames' }, + }, + { + field: 'alertsGenerated', + title: '告警数', + minWidth: 90, + slots: { default: 'alerts' }, + }, + { + field: 'updatedAt', + title: '更新时间', + minWidth: 170, + formatter: 'formatDateTime', + }, + ]; +} diff --git a/apps/web-antd/src/views/aiot/edge/node/index.vue b/apps/web-antd/src/views/aiot/edge/node/index.vue new file mode 100644 index 000000000..7eed61c47 --- /dev/null +++ b/apps/web-antd/src/views/aiot/edge/node/index.vue @@ -0,0 +1,92 @@ + + + diff --git a/apps/web-antd/src/views/aiot/video/live/index.vue b/apps/web-antd/src/views/aiot/video/live/index.vue new file mode 100644 index 000000000..bd0139d7f --- /dev/null +++ b/apps/web-antd/src/views/aiot/video/live/index.vue @@ -0,0 +1,112 @@ + + + From 936dff610cc52fd2389adcaf6f9e5d83ef150b52 Mon Sep 17 00:00:00 2001 From: 16337 <1633794139@qq.com> Date: Fri, 6 Feb 2026 16:41:00 +0800 Subject: [PATCH 04/99] =?UTF-8?q?feat(aiot):=20=E6=9B=B4=E6=96=B0=20Vite?= =?UTF-8?q?=20=E4=BB=A3=E7=90=86=E8=A7=84=E5=88=99=EF=BC=8C=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=E6=97=A7=20ai-alert=20=E8=B7=AF=E7=94=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 4 条 aiot 代理规则: /admin-api/aiot/alarm → 告警服务 :8000(直通) /admin-api/aiot/edge → 告警服务 :8000(直通) /admin-api/aiot/device → WVP :18080(rewrite 为 /api/ai) /admin-api/aiot/video → WVP :18080(rewrite 为 /api) - 移除旧的 /admin-api/ai-alert 和 /admin-api/video 代理规则 - 删除旧的 ai-alert 路由、视图、API 文件 Co-Authored-By: Claude Opus 4.6 --- apps/web-antd/vite.config.mts | 54 ++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/apps/web-antd/vite.config.mts b/apps/web-antd/vite.config.mts index a75425fb4..1e3fb349b 100644 --- a/apps/web-antd/vite.config.mts +++ b/apps/web-antd/vite.config.mts @@ -7,10 +7,62 @@ export default defineConfig(async () => { server: { allowedHosts: true, proxy: { + // ==================== AIoT 统一路由 ==================== + // aiot/alarm, aiot/edge -> 告警服务 :8000(直通) + '/admin-api/aiot/alarm': { + changeOrigin: true, + target: 'http://127.0.0.1:8000', + }, + '/admin-api/aiot/edge': { + changeOrigin: true, + target: 'http://127.0.0.1:8000', + }, + // aiot/device -> WVP :18080(rewrite: /admin-api/aiot/device -> /api/ai) + '/admin-api/aiot/device': { + changeOrigin: true, + target: 'http://127.0.0.1:18080', + rewrite: (path: string) => + path.replace('/admin-api/aiot/device', '/api/ai'), + }, + // aiot/video -> WVP :18080(rewrite: /admin-api/aiot/video -> /api) + '/admin-api/aiot/video': { + changeOrigin: true, + target: 'http://127.0.0.1:18080', + rewrite: (path: string) => + path.replace('/admin-api/aiot/video', '/api'), + }, + // ==================== 系统基础路由 ==================== + // 认证相关接口 -> 告警平台(测试阶段提供模拟认证) + '/admin-api/system/auth': { + changeOrigin: true, + target: 'http://127.0.0.1:8000', + ws: true, + }, + // 租户相关接口 -> 告警平台(测试阶段返回默认值) + '/admin-api/system/tenant': { + changeOrigin: true, + target: 'http://127.0.0.1:8000', + }, + // 验证码接口 -> 告警平台(测试阶段返回禁用状态) + '/admin-api/system/captcha': { + changeOrigin: true, + target: 'http://127.0.0.1:8000', + }, + // 字典数据接口 -> 告警平台(测试阶段返回空) + '/admin-api/system/dict-data': { + changeOrigin: true, + target: 'http://127.0.0.1:8000', + }, + // 消息通知接口 -> 告警平台(测试阶段返回空) + '/admin-api/system/notify-message': { + changeOrigin: true, + target: 'http://127.0.0.1:8000', + }, + // 其他接口 -> 芋道主平台(未来对接) + // 测试阶段:这些接口会 404,但不影响核心功能 '/admin-api': { changeOrigin: true, rewrite: (path) => path.replace(/^\/admin-api/, ''), - // mock代理目标地址 target: 'http://localhost:48080/admin-api', ws: true, }, From c28606df4eaefa4cbcc3e1274bee0d4e6c87501f Mon Sep 17 00:00:00 2001 From: 16337 <1633794139@qq.com> Date: Sun, 8 Feb 2026 23:23:56 +0800 Subject: [PATCH 05/99] =?UTF-8?q?feat(aiot-device):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E6=91=84=E5=83=8F=E5=A4=B4=E4=BB=A3=E7=90=86=20Vite=20?= =?UTF-8?q?=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WVP 的摄像头拉流代理接口位于 /api/proxy 路径下, 与 ROI/算法接口的 /api/ai 前缀不同,需单独匹配。 新增 /admin-api/aiot/device/proxy → /api/proxy 代理规则。 Co-Authored-By: Claude Opus 4.6 --- apps/web-antd/vite.config.mts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/web-antd/vite.config.mts b/apps/web-antd/vite.config.mts index 1e3fb349b..5ba2648b6 100644 --- a/apps/web-antd/vite.config.mts +++ b/apps/web-antd/vite.config.mts @@ -17,6 +17,14 @@ export default defineConfig(async () => { changeOrigin: true, target: 'http://127.0.0.1:8000', }, + // aiot/device/proxy -> WVP :18080(rewrite: /admin-api/aiot/device/proxy -> /api/proxy) + // 摄像头拉流代理接口在 /api/proxy 下,需单独匹配 + '/admin-api/aiot/device/proxy': { + changeOrigin: true, + target: 'http://127.0.0.1:18080', + rewrite: (path: string) => + path.replace('/admin-api/aiot/device/proxy', '/api/proxy'), + }, // aiot/device -> WVP :18080(rewrite: /admin-api/aiot/device -> /api/ai) '/admin-api/aiot/device': { changeOrigin: true, From fc56ea0f759cf818bb808ab1958f187463b67cd9 Mon Sep 17 00:00:00 2001 From: 16337 <1633794139@qq.com> Date: Sun, 8 Feb 2026 23:24:12 +0800 Subject: [PATCH 06/99] =?UTF-8?q?feat(aiot-device):=20=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=E6=A8=A1=E5=9D=97=20API=20=E5=B1=82=EF=BC=8C?= =?UTF-8?q?=E5=AF=B9=E6=8E=A5=20WVP=20=E5=AE=9E=E9=99=85=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 从 WVP 项目迁移全部设备管理 API: - 摄像头管理:列表查询、开始/停止拉流 - ROI 区域:增删改查、按摄像头查询、截图 - 算法绑定:绑定/解绑/更新参数 - 配置推送:推送到边缘端、导出配置 - 移除未使用的 PageParam 导入 Co-Authored-By: Claude Opus 4.6 --- apps/web-antd/src/api/aiot/device/index.ts | 171 ++++++++++++++------- 1 file changed, 119 insertions(+), 52 deletions(-) diff --git a/apps/web-antd/src/api/aiot/device/index.ts b/apps/web-antd/src/api/aiot/device/index.ts index bcffe8f33..6dd952945 100644 --- a/apps/web-antd/src/api/aiot/device/index.ts +++ b/apps/web-antd/src/api/aiot/device/index.ts @@ -1,99 +1,166 @@ -import type { PageParam, PageResult } from '@vben/request'; +import { useAppConfig } from '@vben/hooks'; import { wvpRequestClient } from '#/api/aiot/request'; +const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD); + export namespace AiotDeviceApi { /** ROI 区域 */ export interface Roi { id?: number; - channelId?: string; - channelName?: string; + roiId?: string; + cameraId?: string; + deviceId?: string; name?: string; - type?: string; - points?: string; - enabled?: boolean; - createdAt?: string; - updatedAt?: string; + roiType?: string; // rectangle | polygon + coordinates?: string; + color?: string; + priority?: number; + enabled?: number; // 0 | 1 + description?: string; + algorithms?: RoiAlgoBinding[]; } - /** ROI 算法绑定 */ - export interface RoiAlgoBind { - id?: number; - roiId?: number; - algorithmId?: number; - algorithmName?: string; - enabled?: boolean; - config?: Record; + /** ROI 算法绑定(详情里的嵌套结构) */ + export interface RoiAlgoBinding { + bind: AlgoBind; + algorithm?: Algorithm; + } + + /** 算法绑定记录 */ + export interface AlgoBind { + bindId?: string; + roiId?: string; + algoCode?: string; + enabled?: number; + params?: string; } /** 算法 */ export interface Algorithm { id?: number; - name?: string; - code?: string; + algoCode?: string; + algoName?: string; description?: string; - enabled?: boolean; + isActive?: boolean; + paramSchema?: string; } - /** 摄像头通道 */ - export interface Channel { - channelId?: string; - name?: string; - manufacturer?: string; - status?: string; - ptztypeText?: string; - longitudeWgs84?: number; - latitudeWgs84?: number; + /** 摄像头(拉流代理) */ + export interface Camera { + id?: number; + app?: string; + stream?: string; + srcUrl?: string; + pulling?: boolean; + mediaServerId?: string; } } +// ==================== 摄像头管理 API ==================== + +/** 获取摄像头列表 */ +export function getCameraList(params: { + page: number; + count: number; + query?: string; + pulling?: boolean; +}) { + return wvpRequestClient.get('/aiot/device/proxy/list', { params }); +} + +/** 开始拉流 */ +export function startCamera(id: number) { + return wvpRequestClient.get('/aiot/device/proxy/start', { params: { id } }); +} + +/** 停止拉流 */ +export function stopCamera(id: number) { + return wvpRequestClient.get('/aiot/device/proxy/stop', { params: { id } }); +} + // ==================== ROI 区域管理 API ==================== -/** 获取 ROI 列表 */ -export function getRoiPage(params: PageParam) { - return wvpRequestClient.get>( - '/aiot/device/roi/list', - { params }, - ); +/** 获取 ROI 列表(分页) */ +export function getRoiList(params: { + page: number; + count: number; + cameraId?: string; + deviceId?: string; + query?: string; +}) { + return wvpRequestClient.get('/aiot/device/roi/list', { params }); +} + +/** 获取 ROI 详情 */ +export function getRoiDetail(id: number) { + return wvpRequestClient.get(`/aiot/device/roi/${id}`); +} + +/** 获取某摄像头的所有 ROI */ +export function getRoiByCameraId(cameraId: string) { + return wvpRequestClient.get('/aiot/device/roi/channel', { + params: { cameraId }, + }); } /** 保存 ROI */ -export function saveRoi(data: AiotDeviceApi.Roi) { +export function saveRoi(data: Partial) { return wvpRequestClient.post('/aiot/device/roi/save', data); } /** 删除 ROI */ -export function deleteRoi(id: number) { - return wvpRequestClient.delete(`/aiot/device/roi/delete?id=${id}`); +export function deleteRoi(roiId: string) { + return wvpRequestClient.delete(`/aiot/device/roi/delete/${roiId}`); +} + +/** 获取截图 URL */ +export function getSnapUrl(app: string, stream: string) { + return `${apiURL}/aiot/device/roi/snap?app=${encodeURIComponent(app)}&stream=${encodeURIComponent(stream)}&t=${Date.now()}`; } // ==================== 算法绑定 API ==================== /** 绑定算法到 ROI */ -export function bindAlgo(data: { roiId: number; algorithmId: number }) { +export function bindAlgo(data: { roiId: string; algoCode: string }) { return wvpRequestClient.post('/aiot/device/roi/bindAlgo', data); } /** 解绑算法 */ -export function unbindAlgo(bindId: number) { - return wvpRequestClient.delete( - `/aiot/device/roi/unbindAlgo?bindId=${bindId}`, - ); +export function unbindAlgo(bindId: string) { + return wvpRequestClient.delete('/aiot/device/roi/unbindAlgo', { + params: { bindId }, + }); } +/** 更新算法参数 */ +export function updateAlgoParams(data: { + bindId: string; + params?: string; + enabled?: number; +}) { + return wvpRequestClient.post('/aiot/device/roi/updateAlgoParams', data); +} + +// ==================== 算法管理 API ==================== + /** 获取算法列表 */ export function getAlgorithmList() { - return wvpRequestClient.get( - '/aiot/device/algorithm/list', - ); + return wvpRequestClient.get('/aiot/device/algorithm/list'); } -// ==================== 摄像头通道 API ==================== +// ==================== 配置推送 API ==================== -/** 获取摄像头通道列表 */ -export function getChannelPage(params: PageParam) { - return wvpRequestClient.get>( - '/aiot/device/channel/list', - { params }, - ); +/** 推送配置到边缘端 */ +export function pushConfig(cameraId: string) { + return wvpRequestClient.post('/aiot/device/config/push', null, { + params: { cameraId }, + }); +} + +/** 导出配置 */ +export function exportConfig(cameraId: string) { + return wvpRequestClient.get('/aiot/device/config/export', { + params: { cameraId }, + }); } From b72839622e68656e75e4f8b1b45d24313498e04d Mon Sep 17 00:00:00 2001 From: 16337 <1633794139@qq.com> Date: Sun, 8 Feb 2026 23:24:27 +0800 Subject: [PATCH 07/99] =?UTF-8?q?feat(aiot-device):=20=E9=87=8D=E5=86=99?= =?UTF-8?q?=E6=91=84=E5=83=8F=E5=A4=B4=E7=AE=A1=E7=90=86=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=EF=BC=8C=E8=BF=81=E7=A7=BB=20WVP=20=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 从 WVP 的 cameraConfig/index.vue 迁移至 Vue3 + Ant Design: - 搜索过滤:支持名称/地址搜索和拉流状态筛选 - 摄像头列表:显示应用名、流ID、拉流地址、状态 - ROI 数量徽标:异步加载每个摄像头的 ROI 数量 - 操作按钮:ROI 配置(跳转)、拉流/停止、导出配置 - 配置导出:JSON 文件下载 Co-Authored-By: Claude Opus 4.6 --- .../src/views/aiot/device/camera/index.vue | 286 ++++++++++++------ 1 file changed, 201 insertions(+), 85 deletions(-) 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 a4f7e66c5..cd930010a 100644 --- a/apps/web-antd/src/views/aiot/device/camera/index.vue +++ b/apps/web-antd/src/views/aiot/device/camera/index.vue @@ -1,110 +1,226 @@ From e2bde76f20a8dfc1c93567d5d90a9df5ed044ace Mon Sep 17 00:00:00 2001 From: 16337 <1633794139@qq.com> Date: Sun, 8 Feb 2026 23:24:48 +0800 Subject: [PATCH 08/99] =?UTF-8?q?feat(aiot-device):=20=E9=87=8D=E5=86=99?= =?UTF-8?q?=20ROI=20=E9=85=8D=E7=BD=AE=E9=A1=B5=E9=9D=A2=EF=BC=8C=E8=BF=81?= =?UTF-8?q?=E7=A7=BB=20WVP=20=E7=94=BB=E5=B8=83=E5=BC=8F=E4=BA=A4=E4=BA=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 从 WVP 的 roiConfig 整套组件迁移至 Vue3 + Ant Design: ROI 配置主页面 (roi/index.vue): - 无参数时显示摄像头选择器,有参数时直接进入配置 - 左侧画布面板 + 右侧 ROI 列表/属性/算法侧边栏 - 画矩形(拖拽)、画多边形(点击+双击完成) - ROI 属性编辑:名称、颜色、优先级、描述 - 推送配置到边缘端 RoiCanvas 组件 (roi/components/RoiCanvas.vue): - Canvas 叠加在摄像头截图上,归一化坐标 (0-1) - 矩形拖拽绘制、多边形逐点绘制 - ROI 选中高亮、点击选择、右键删除 - 窗口 resize 自适应 RoiAlgorithmBind 组件 (roi/components/RoiAlgorithmBind.vue): - 算法列表加载、绑定/解绑操作 - 启用/禁用开关、参数配置入口 AlgorithmParamEditor 组件 (roi/components/AlgorithmParamEditor.vue): - 根据 JSON Schema 动态生成表单 - 支持 int(数字输入)、list(标签列表)、string(文本输入) - 参数保存到后端 删除旧的 data.ts(VxeGrid 列定义,已不再使用) Co-Authored-By: Claude Opus 4.6 --- .../roi/components/AlgorithmParamEditor.vue | 165 +++++ .../roi/components/RoiAlgorithmBind.vue | 229 +++++++ .../aiot/device/roi/components/RoiCanvas.vue | 354 +++++++++++ .../src/views/aiot/device/roi/data.ts | 40 -- .../src/views/aiot/device/roi/index.vue | 567 +++++++++++++++--- 5 files changed, 1243 insertions(+), 112 deletions(-) create mode 100644 apps/web-antd/src/views/aiot/device/roi/components/AlgorithmParamEditor.vue create mode 100644 apps/web-antd/src/views/aiot/device/roi/components/RoiAlgorithmBind.vue create mode 100644 apps/web-antd/src/views/aiot/device/roi/components/RoiCanvas.vue delete mode 100644 apps/web-antd/src/views/aiot/device/roi/data.ts diff --git a/apps/web-antd/src/views/aiot/device/roi/components/AlgorithmParamEditor.vue b/apps/web-antd/src/views/aiot/device/roi/components/AlgorithmParamEditor.vue new file mode 100644 index 000000000..42deca53f --- /dev/null +++ b/apps/web-antd/src/views/aiot/device/roi/components/AlgorithmParamEditor.vue @@ -0,0 +1,165 @@ + + + diff --git a/apps/web-antd/src/views/aiot/device/roi/components/RoiAlgorithmBind.vue b/apps/web-antd/src/views/aiot/device/roi/components/RoiAlgorithmBind.vue new file mode 100644 index 000000000..7f984bedd --- /dev/null +++ b/apps/web-antd/src/views/aiot/device/roi/components/RoiAlgorithmBind.vue @@ -0,0 +1,229 @@ + + + diff --git a/apps/web-antd/src/views/aiot/device/roi/components/RoiCanvas.vue b/apps/web-antd/src/views/aiot/device/roi/components/RoiCanvas.vue new file mode 100644 index 000000000..d4080fee1 --- /dev/null +++ b/apps/web-antd/src/views/aiot/device/roi/components/RoiCanvas.vue @@ -0,0 +1,354 @@ + + + + + diff --git a/apps/web-antd/src/views/aiot/device/roi/data.ts b/apps/web-antd/src/views/aiot/device/roi/data.ts deleted file mode 100644 index 67dcee6d5..000000000 --- a/apps/web-antd/src/views/aiot/device/roi/data.ts +++ /dev/null @@ -1,40 +0,0 @@ -import type { VxeTableGridOptions } from '#/adapter/vxe-table'; - -/** ROI 列表字段 */ -export function useRoiGridColumns(): VxeTableGridOptions['columns'] { - return [ - { - field: 'name', - title: 'ROI 名称', - minWidth: 150, - }, - { - field: 'channelName', - title: '摄像头', - minWidth: 150, - }, - { - field: 'type', - title: '类型', - minWidth: 100, - }, - { - field: 'enabled', - title: '启用状态', - minWidth: 90, - slots: { default: 'enabled' }, - }, - { - field: 'createdAt', - title: '创建时间', - minWidth: 170, - formatter: 'formatDateTime', - }, - { - title: '操作', - width: 120, - fixed: 'right', - slots: { default: 'actions' }, - }, - ]; -} diff --git a/apps/web-antd/src/views/aiot/device/roi/index.vue b/apps/web-antd/src/views/aiot/device/roi/index.vue index b31993e8f..f9dfa30b9 100644 --- a/apps/web-antd/src/views/aiot/device/roi/index.vue +++ b/apps/web-antd/src/views/aiot/device/roi/index.vue @@ -1,99 +1,522 @@ + + From e59eb5fe658203b8794d419e947ce71f9c8fd808 Mon Sep 17 00:00:00 2001 From: 16337 <1633794139@qq.com> Date: Mon, 9 Feb 2026 09:33:34 +0800 Subject: [PATCH 09/99] =?UTF-8?q?fix(aiot):=20=E5=88=A0=E9=99=A4=E9=9D=99?= =?UTF-8?q?=E6=80=81=E8=B7=AF=E7=94=B1=E6=96=87=E4=BB=B6=EF=BC=8C=E6=B6=88?= =?UTF-8?q?=E9=99=A4=E9=87=8D=E5=A4=8D=E8=8F=9C=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit accessMode 为 backend 模式时菜单由后端接口返回, 静态路由文件 aiot.ts 导致出现两个重复的 AIoT 菜单。 Co-Authored-By: Claude Opus 4.6 --- .../src/router/routes/modules/aiot.ts | 71 ------------------- 1 file changed, 71 deletions(-) delete mode 100644 apps/web-antd/src/router/routes/modules/aiot.ts diff --git a/apps/web-antd/src/router/routes/modules/aiot.ts b/apps/web-antd/src/router/routes/modules/aiot.ts deleted file mode 100644 index c61ea6746..000000000 --- a/apps/web-antd/src/router/routes/modules/aiot.ts +++ /dev/null @@ -1,71 +0,0 @@ -import type { RouteRecordRaw } from 'vue-router'; - -const routes: RouteRecordRaw[] = [ - { - path: '/aiot', - name: 'AIoT', - meta: { - title: 'AIoT 智能平台', - icon: 'ant-design:robot-outlined', - keepAlive: true, - }, - children: [ - { - path: 'alarm/list', - name: 'AiotAlarmList', - component: () => import('#/views/aiot/alarm/list/index.vue'), - meta: { - title: '告警列表', - icon: 'ant-design:alert-outlined', - }, - }, - { - path: 'alarm/summary', - name: 'AiotAlarmSummary', - component: () => import('#/views/aiot/alarm/summary/index.vue'), - meta: { - title: '摄像头告警汇总', - icon: 'ant-design:video-camera-outlined', - }, - }, - { - path: 'device/camera', - name: 'AiotDeviceCamera', - component: () => import('#/views/aiot/device/camera/index.vue'), - meta: { - title: '摄像头管理', - icon: 'ant-design:camera-outlined', - }, - }, - { - path: 'device/roi', - name: 'AiotDeviceRoi', - component: () => import('#/views/aiot/device/roi/index.vue'), - meta: { - title: 'ROI 区域配置', - icon: 'ant-design:aim-outlined', - }, - }, - { - path: 'video/live', - name: 'AiotVideoLive', - component: () => import('#/views/aiot/video/live/index.vue'), - meta: { - title: '实时视频', - icon: 'ant-design:play-circle-outlined', - }, - }, - { - path: 'edge/node', - name: 'AiotEdgeNode', - component: () => import('#/views/aiot/edge/node/index.vue'), - meta: { - title: '边缘节点管理', - icon: 'ant-design:cluster-outlined', - }, - }, - ], - }, -]; - -export default routes; From f7bfde01356c6f50817c447a43e5766f496f9acd Mon Sep 17 00:00:00 2001 From: 16337 <1633794139@qq.com> Date: Mon, 9 Feb 2026 09:51:37 +0800 Subject: [PATCH 10/99] =?UTF-8?q?feat(aiot-device):=20=E6=91=84=E5=83=8F?= =?UTF-8?q?=E5=A4=B4=E7=AE=A1=E7=90=86=E5=A2=9E=E5=8A=A0=E5=A2=9E=E5=88=A0?= =?UTF-8?q?=E6=94=B9=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Camera 接口扩展完整字段(type、timeout、rtspType、enable 等) - 新增 saveCamera、deleteCamera、getMediaServerList API - 摄像头管理页面增加添加/编辑弹窗(表单含代理类型、拉流地址、RTSP方式等) - 增加删除确认对话框 - Vite 代理增加媒体服务器 API 转发规则 Co-Authored-By: Claude Opus 4.6 --- apps/web-antd/src/api/aiot/device/index.ts | 26 ++ .../src/views/aiot/device/camera/index.vue | 276 ++++++++++++++++-- apps/web-antd/vite.config.mts | 7 + 3 files changed, 286 insertions(+), 23 deletions(-) diff --git a/apps/web-antd/src/api/aiot/device/index.ts b/apps/web-antd/src/api/aiot/device/index.ts index 6dd952945..f82f868ea 100644 --- a/apps/web-antd/src/api/aiot/device/index.ts +++ b/apps/web-antd/src/api/aiot/device/index.ts @@ -49,9 +49,18 @@ export namespace AiotDeviceApi { /** 摄像头(拉流代理) */ export interface Camera { id?: number; + type?: string; // default | ffmpeg app?: string; stream?: string; srcUrl?: string; + timeout?: number; + rtspType?: string; // 0=TCP, 1=UDP, 2=Multicast + enable?: boolean; + enableAudio?: boolean; + enableMp4?: boolean; + enableDisableNoneReader?: boolean; + relatesMediaServerId?: string; + ffmpegCmdKey?: string; pulling?: boolean; mediaServerId?: string; } @@ -79,6 +88,23 @@ export function stopCamera(id: number) { return wvpRequestClient.get('/aiot/device/proxy/stop', { params: { id } }); } +/** 保存摄像头(新增/编辑) */ +export function saveCamera(data: Partial) { + return wvpRequestClient.post('/aiot/device/proxy/save', data); +} + +/** 删除摄像头 */ +export function deleteCamera(id: number) { + return wvpRequestClient.delete('/aiot/device/proxy/delete', { + params: { id }, + }); +} + +/** 获取在线媒体服务器列表 */ +export function getMediaServerList() { + return wvpRequestClient.get('/aiot/device/server/online/list'); +} + // ==================== ROI 区域管理 API ==================== /** 获取 ROI 列表(分页) */ 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 cd930010a..230ff7bad 100644 --- a/apps/web-antd/src/views/aiot/device/camera/index.vue +++ b/apps/web-antd/src/views/aiot/device/camera/index.vue @@ -1,7 +1,7 @@ - - From 777e52986e32167ed66e39da284959ee55891034 Mon Sep 17 00:00:00 2001 From: 16337 <1633794139@qq.com> Date: Tue, 10 Feb 2026 15:22:31 +0800 Subject: [PATCH 16/99] =?UTF-8?q?fix(aiot):=20=E4=BF=AE=E5=A4=8D=E6=91=84?= =?UTF-8?q?=E5=83=8F=E5=A4=B4=E4=BF=9D=E5=AD=98=E5=A4=B1=E8=B4=A5=20+=20?= =?UTF-8?q?=E6=A0=87=E9=A2=98=E6=94=B9=E4=B8=BA=E6=91=84=E5=83=8F=E5=A4=B4?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - saveCamera 拆分为 addCamera/updateCamera 匹配WVP实际接口 - .env 标题改为"摄像头管理系统" - 告警列表字段适配新三表结构 - preferences 配置调整 Co-Authored-By: Claude Opus 4.6 --- apps/web-antd/.env | 2 +- apps/web-antd/src/api/aiot/device/index.ts | 12 +++++++++++- apps/web-antd/src/views/aiot/alarm/list/data.ts | 6 +++--- apps/web-antd/src/views/aiot/alarm/list/index.vue | 2 +- packages/@core/preferences/src/config.ts | 2 +- 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/apps/web-antd/.env b/apps/web-antd/.env index 106374cf5..dca9fe050 100644 --- a/apps/web-antd/.env +++ b/apps/web-antd/.env @@ -1,5 +1,5 @@ # 应用标题 -VITE_APP_TITLE=芋道管理系统 +VITE_APP_TITLE=摄像头管理系统 # 应用命名空间,用于缓存、store等功能的前缀,确保隔离 VITE_APP_NAMESPACE=yudao-vben-antd diff --git a/apps/web-antd/src/api/aiot/device/index.ts b/apps/web-antd/src/api/aiot/device/index.ts index db4d240e3..0d352279e 100644 --- a/apps/web-antd/src/api/aiot/device/index.ts +++ b/apps/web-antd/src/api/aiot/device/index.ts @@ -107,9 +107,19 @@ export function getCameraList(params: { ); } +/** 新增摄像头 */ +export function addCamera(data: Partial) { + return wvpRequestClient.post('/aiot/device/proxy/add', data); +} + +/** 编辑摄像头 */ +export function updateCamera(data: Partial) { + return wvpRequestClient.post('/aiot/device/proxy/update', data); +} + /** 保存摄像头(新增/编辑,有 id 为编辑) */ export function saveCamera(data: Partial) { - return wvpRequestClient.post('/aiot/device/proxy/save', data); + return data.id ? updateCamera(data) : addCamera(data); } /** 删除摄像头 */ diff --git a/apps/web-antd/src/views/aiot/alarm/list/data.ts b/apps/web-antd/src/views/aiot/alarm/list/data.ts index 47535808e..f37708579 100644 --- a/apps/web-antd/src/views/aiot/alarm/list/data.ts +++ b/apps/web-antd/src/views/aiot/alarm/list/data.ts @@ -75,7 +75,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] { { field: 'alertNo', title: '告警编号', - minWidth: 160, + width: 210, }, { field: 'cameraId', @@ -98,7 +98,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] { { field: 'durationMinutes', title: '持续时长', - minWidth: 100, + width: 125, slots: { default: 'duration' }, }, { @@ -127,7 +127,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] { }, { title: '操作', - width: 150, + width: 180, fixed: 'right', slots: { default: 'actions' }, }, diff --git a/apps/web-antd/src/views/aiot/alarm/list/index.vue b/apps/web-antd/src/views/aiot/alarm/list/index.vue index 394ba1e91..5e7076ca8 100644 --- a/apps/web-antd/src/views/aiot/alarm/list/index.vue +++ b/apps/web-antd/src/views/aiot/alarm/list/index.vue @@ -176,7 +176,7 @@ const [Grid, gridApi] = useVbenVxeGrid({ - - -
diff --git a/apps/web-antd/src/views/aiot/device/roi/components/RoiAlgorithmBind.vue b/apps/web-antd/src/views/aiot/device/roi/components/RoiAlgorithmBind.vue index 703cf0d35..8d011b802 100644 --- a/apps/web-antd/src/views/aiot/device/roi/components/RoiAlgorithmBind.vue +++ b/apps/web-antd/src/views/aiot/device/roi/components/RoiAlgorithmBind.vue @@ -124,6 +124,15 @@ async function onToggleEnabled(bind: AiotDeviceApi.AlgoBind, val: string | numbe function onParamSaved() { emit('changed'); } + +// 获取算法的固定帧率 +function getAlgoFrameRate(algoCode: string): string { + const frameRates: Record = { + leave_post: '3帧/秒', + intrusion: '1帧/秒', + }; + return frameRates[algoCode] || '5帧/秒'; +}