重构: aiot 模块重命名为 video,WVP 凭据移至环境变量
路径重命名:
- api/aiot/{alarm,device,edge,request} → api/video/{alarm,device,edge,request}
- views/aiot/{alarm,device,edge} → views/video/{alarm,device,edge}
- vite.config.mts 代理路径 /admin-api/aiot/* → /admin-api/video/*
video/request.ts 改造:
- WVP 用户名/密码 MD5 改读 import.meta.env,不再写死在源码里
- force 截图失败时补一条 console.debug,便于回溯 COS 图片加载异常
video/alarm/index.ts 顺带清理:
- 移除无调用方的重复 API getRecentAlerts(与 getAlertPage 重叠)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@ import type { PageParam, PageResult } from '@vben/request';
|
|||||||
|
|
||||||
import { requestClient } from '#/api/request';
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
export namespace AiotAlarmApi {
|
export namespace VideoAlarmApi {
|
||||||
/** AI 告警 VO */
|
/** AI 告警 VO */
|
||||||
export interface Alert {
|
export interface Alert {
|
||||||
id?: number | string;
|
id?: number | string;
|
||||||
@@ -113,16 +113,16 @@ export namespace AiotAlarmApi {
|
|||||||
|
|
||||||
/** 分页查询告警列表 */
|
/** 分页查询告警列表 */
|
||||||
export function getAlertPage(params: PageParam) {
|
export function getAlertPage(params: PageParam) {
|
||||||
return requestClient.get<PageResult<AiotAlarmApi.Alert>>(
|
return requestClient.get<PageResult<VideoAlarmApi.Alert>>(
|
||||||
'/aiot/alarm/alert/page',
|
'/video/alarm/alert/page',
|
||||||
{ params },
|
{ params },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取告警详情 */
|
/** 获取告警详情 */
|
||||||
export function getAlert(id: number | string) {
|
export function getAlert(id: number | string) {
|
||||||
return requestClient.get<AiotAlarmApi.Alert>(
|
return requestClient.get<VideoAlarmApi.Alert>(
|
||||||
`/aiot/alarm/alert/get?id=${id}`,
|
`/video/alarm/alert/get?id=${id}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,69 +132,62 @@ export function handleAlert(
|
|||||||
status: string,
|
status: string,
|
||||||
remark?: string,
|
remark?: string,
|
||||||
) {
|
) {
|
||||||
return requestClient.put('/aiot/alarm/alert/handle', null, {
|
return requestClient.put('/video/alarm/alert/handle', null, {
|
||||||
params: { id, status, remark },
|
params: { id, status, remark },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 删除告警 */
|
/** 删除告警 */
|
||||||
export function deleteAlert(id: number | string) {
|
export function deleteAlert(id: number | string) {
|
||||||
return requestClient.delete(`/aiot/alarm/alert/delete?id=${id}`);
|
return requestClient.delete(`/video/alarm/alert/delete?id=${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取看板聚合数据(单次请求) */
|
/** 获取看板聚合数据(单次请求) */
|
||||||
export function getAlertDashboard(trendDays: number = 7) {
|
export function getAlertDashboard(trendDays: number = 7) {
|
||||||
return requestClient.get<AiotAlarmApi.DashboardData>(
|
return requestClient.get<VideoAlarmApi.DashboardData>(
|
||||||
'/aiot/alarm/alert/dashboard',
|
'/video/alarm/alert/dashboard',
|
||||||
{ params: { trendDays } },
|
{ params: { trendDays } },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取告警统计 */
|
/** 获取告警统计 */
|
||||||
export function getAlertStatistics(startTime?: string, endTime?: string) {
|
export function getAlertStatistics(startTime?: string, endTime?: string) {
|
||||||
return requestClient.get<AiotAlarmApi.AlertStatistics>(
|
return requestClient.get<VideoAlarmApi.AlertStatistics>(
|
||||||
'/aiot/alarm/alert/statistics',
|
'/video/alarm/alert/statistics',
|
||||||
{ params: { startTime, endTime } },
|
{ params: { startTime, endTime } },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取告警趋势 */
|
/** 获取告警趋势 */
|
||||||
export function getAlertTrend(days: number = 7) {
|
export function getAlertTrend(days: number = 7) {
|
||||||
return requestClient.get<AiotAlarmApi.TrendItem[]>(
|
return requestClient.get<VideoAlarmApi.TrendItem[]>(
|
||||||
'/aiot/alarm/alert/trend',
|
'/video/alarm/alert/trend',
|
||||||
{ params: { days } },
|
{ params: { days } },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取设备告警排行 */
|
/** 获取设备告警排行 */
|
||||||
export function getAlertDeviceTop(limit: number = 10, days: number = 7) {
|
export function getAlertDeviceTop(limit: number = 10, days: number = 7) {
|
||||||
return requestClient.get<AiotAlarmApi.DeviceTopItem[]>(
|
return requestClient.get<VideoAlarmApi.DeviceTopItem[]>(
|
||||||
'/aiot/alarm/alert/device-top',
|
'/video/alarm/alert/device-top',
|
||||||
{ params: { limit, days } },
|
{ params: { limit, days } },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取24小时告警分布 */
|
/** 获取24小时告警分布 */
|
||||||
export function getAlertHourDistribution(days: number = 7) {
|
export function getAlertHourDistribution(days: number = 7) {
|
||||||
return requestClient.get<AiotAlarmApi.HourDistItem[]>(
|
return requestClient.get<VideoAlarmApi.HourDistItem[]>(
|
||||||
'/aiot/alarm/alert/hour-distribution',
|
'/video/alarm/alert/hour-distribution',
|
||||||
{ params: { days } },
|
{ params: { days } },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取最近告警列表 */
|
|
||||||
export function getRecentAlerts(pageSize: number = 10) {
|
|
||||||
return requestClient.get<any>('/aiot/alarm/alert/page', {
|
|
||||||
params: { pageNo: 1, pageSize },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== 摄像头告警汇总 API ====================
|
// ==================== 摄像头告警汇总 API ====================
|
||||||
|
|
||||||
/** 以摄像头维度获取告警汇总 */
|
/** 以摄像头维度获取告警汇总 */
|
||||||
export function getCameraAlertSummary(params: PageParam) {
|
export function getCameraAlertSummary(params: PageParam) {
|
||||||
return requestClient.get<PageResult<AiotAlarmApi.CameraAlertSummary>>(
|
return requestClient.get<PageResult<VideoAlarmApi.CameraAlertSummary>>(
|
||||||
'/aiot/alarm/device-summary/page',
|
'/video/alarm/device-summary/page',
|
||||||
{ params },
|
{ params },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,22 +1,22 @@
|
|||||||
/**
|
/**
|
||||||
* AIoT 设备管理 API
|
* Video 模块 - 设备管理 API
|
||||||
*
|
*
|
||||||
* 所有请求通过 wvpRequestClient 发送到 WVP 视频平台后端,
|
* 所有请求通过 wvpRequestClient 发送到 WVP 视频平台后端,
|
||||||
* 由 Vite 代理按路径分发并 rewrite:
|
* 由 Vite 代理按路径分发并 rewrite:
|
||||||
* /aiot/device/proxy/* → WVP /api/proxy/*
|
* /video/device/proxy/* → WVP /api/proxy/*
|
||||||
* /aiot/device/user/* → WVP /api/user/*
|
* /video/device/user/* → WVP /api/user/*
|
||||||
* /aiot/device/server/* → WVP /api/server/media_server/*
|
* /video/device/server/* → WVP /api/server/media_server/*
|
||||||
* /aiot/device/* → WVP /api/ai/*
|
* /video/device/* → WVP /api/ai/*
|
||||||
*/
|
*/
|
||||||
import { useAppConfig } from '@vben/hooks';
|
import { useAppConfig } from '@vben/hooks';
|
||||||
|
|
||||||
import { getWvpToken, wvpRequestClient } from '#/api/aiot/request';
|
import { getWvpToken, wvpRequestClient } from '#/api/video/request';
|
||||||
|
|
||||||
const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
|
const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
|
||||||
|
|
||||||
// ==================== 类型定义 ====================
|
// ==================== 类型定义 ====================
|
||||||
|
|
||||||
export namespace AiotDeviceApi {
|
export namespace VideoDeviceApi {
|
||||||
/** 分页响应结构 */
|
/** 分页响应结构 */
|
||||||
export interface PageResult<T> {
|
export interface PageResult<T> {
|
||||||
list: T[];
|
list: T[];
|
||||||
@@ -105,48 +105,48 @@ export function getCameraList(params: {
|
|||||||
query?: string;
|
query?: string;
|
||||||
pulling?: boolean;
|
pulling?: boolean;
|
||||||
}) {
|
}) {
|
||||||
return wvpRequestClient.get<AiotDeviceApi.PageResult<AiotDeviceApi.Camera>>(
|
return wvpRequestClient.get<VideoDeviceApi.PageResult<VideoDeviceApi.Camera>>(
|
||||||
'/aiot/device/proxy/list',
|
'/video/device/proxy/list',
|
||||||
{ params },
|
{ params },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 新增摄像头 */
|
/** 新增摄像头 */
|
||||||
export function addCamera(data: Partial<AiotDeviceApi.Camera>) {
|
export function addCamera(data: Partial<VideoDeviceApi.Camera>) {
|
||||||
return wvpRequestClient.post('/aiot/device/proxy/add', data);
|
return wvpRequestClient.post('/video/device/proxy/add', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 编辑摄像头 */
|
/** 编辑摄像头 */
|
||||||
export function updateCamera(data: Partial<AiotDeviceApi.Camera>) {
|
export function updateCamera(data: Partial<VideoDeviceApi.Camera>) {
|
||||||
return wvpRequestClient.post('/aiot/device/proxy/update', data);
|
return wvpRequestClient.post('/video/device/proxy/update', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 保存摄像头(新增/编辑,有 id 为编辑) */
|
/** 保存摄像头(新增/编辑,有 id 为编辑) */
|
||||||
export function saveCamera(data: Partial<AiotDeviceApi.Camera>) {
|
export function saveCamera(data: Partial<VideoDeviceApi.Camera>) {
|
||||||
return data.id ? updateCamera(data) : addCamera(data);
|
return data.id ? updateCamera(data) : addCamera(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 删除摄像头 */
|
/** 删除摄像头 */
|
||||||
export function deleteCamera(id: number) {
|
export function deleteCamera(id: number) {
|
||||||
return wvpRequestClient.delete('/aiot/device/proxy/delete', {
|
return wvpRequestClient.delete('/video/device/proxy/delete', {
|
||||||
params: { id },
|
params: { id },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 开始拉流 */
|
/** 开始拉流 */
|
||||||
export function startCamera(id: number) {
|
export function startCamera(id: number) {
|
||||||
return wvpRequestClient.get('/aiot/device/proxy/start', { params: { id } });
|
return wvpRequestClient.get('/video/device/proxy/start', { params: { id } });
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 停止拉流 */
|
/** 停止拉流 */
|
||||||
export function stopCamera(id: number) {
|
export function stopCamera(id: number) {
|
||||||
return wvpRequestClient.get('/aiot/device/proxy/stop', { params: { id } });
|
return wvpRequestClient.get('/video/device/proxy/stop', { params: { id } });
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 在线媒体服务器列表 */
|
/** 在线媒体服务器列表 */
|
||||||
export function getMediaServerList() {
|
export function getMediaServerList() {
|
||||||
return wvpRequestClient.get<AiotDeviceApi.MediaServer[]>(
|
return wvpRequestClient.get<VideoDeviceApi.MediaServer[]>(
|
||||||
'/aiot/device/server/online/list',
|
'/video/device/server/online/list',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,7 +154,7 @@ export function getMediaServerList() {
|
|||||||
export function getCameraOptions() {
|
export function getCameraOptions() {
|
||||||
return wvpRequestClient.get<
|
return wvpRequestClient.get<
|
||||||
{ cameraCode: string; cameraName: string }[]
|
{ cameraCode: string; cameraName: string }[]
|
||||||
>('/aiot/device/camera/options');
|
>('/video/device/camera/options');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== ROI 区域管理 ====================
|
// ==================== ROI 区域管理 ====================
|
||||||
@@ -167,33 +167,33 @@ export function getRoiList(params: {
|
|||||||
deviceId?: string;
|
deviceId?: string;
|
||||||
query?: string;
|
query?: string;
|
||||||
}) {
|
}) {
|
||||||
return wvpRequestClient.get<AiotDeviceApi.PageResult<AiotDeviceApi.Roi>>(
|
return wvpRequestClient.get<VideoDeviceApi.PageResult<VideoDeviceApi.Roi>>(
|
||||||
'/aiot/device/roi/list',
|
'/video/device/roi/list',
|
||||||
{ params },
|
{ params },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ROI 详情(含算法绑定) */
|
/** ROI 详情(含算法绑定) */
|
||||||
export function getRoiDetail(id: number) {
|
export function getRoiDetail(id: number) {
|
||||||
return wvpRequestClient.get<AiotDeviceApi.Roi>(`/aiot/device/roi/${id}`);
|
return wvpRequestClient.get<VideoDeviceApi.Roi>(`/video/device/roi/${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 某摄像头的所有 ROI */
|
/** 某摄像头的所有 ROI */
|
||||||
export function getRoiByCameraId(cameraId: string) {
|
export function getRoiByCameraId(cameraId: string) {
|
||||||
return wvpRequestClient.get<AiotDeviceApi.Roi[]>(
|
return wvpRequestClient.get<VideoDeviceApi.Roi[]>(
|
||||||
'/aiot/device/roi/channel',
|
'/video/device/roi/channel',
|
||||||
{ params: { cameraId } },
|
{ params: { cameraId } },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 保存 ROI(新增/编辑) */
|
/** 保存 ROI(新增/编辑) */
|
||||||
export function saveRoi(data: Partial<AiotDeviceApi.Roi>) {
|
export function saveRoi(data: Partial<VideoDeviceApi.Roi>) {
|
||||||
return wvpRequestClient.post('/aiot/device/roi/save', data);
|
return wvpRequestClient.post('/video/device/roi/save', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 删除 ROI */
|
/** 删除 ROI */
|
||||||
export function deleteRoi(roiId: string) {
|
export function deleteRoi(roiId: string) {
|
||||||
return wvpRequestClient.delete(`/aiot/device/roi/delete/${roiId}`);
|
return wvpRequestClient.delete(`/video/device/roi/delete/${roiId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -207,15 +207,19 @@ export async function getSnapUrl(cameraCode: string, force = false): Promise<str
|
|||||||
if (force) {
|
if (force) {
|
||||||
// 强制刷新:先触发边缘端截图(等待完成)
|
// 强制刷新:先触发边缘端截图(等待完成)
|
||||||
try {
|
try {
|
||||||
await wvpRequestClient.get('/aiot/device/roi/snap', {
|
await wvpRequestClient.get('/video/device/roi/snap', {
|
||||||
params: { cameraCode, force: true },
|
params: { cameraCode, force: true },
|
||||||
});
|
});
|
||||||
} catch {
|
} catch (err) {
|
||||||
// 截图请求可能超时,但 COS 上可能已有图片,继续返回代理 URL
|
// 截图请求可能超时,但 COS 上可能已有图片,继续返回代理 URL
|
||||||
|
// 留一条日志便于图片加载 401/404 时回溯根因
|
||||||
|
if (import.meta.env.DEV) {
|
||||||
|
console.debug('[WVP] force snap 触发失败,继续返回已有代理 URL', err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 加时间戳破浏览器缓存
|
// 加时间戳破浏览器缓存
|
||||||
return (
|
return (
|
||||||
`${apiURL}/aiot/device/roi/snap/image` +
|
`${apiURL}/video/device/roi/snap/image` +
|
||||||
`?cameraCode=${encodeURIComponent(cameraCode)}` +
|
`?cameraCode=${encodeURIComponent(cameraCode)}` +
|
||||||
`&access-token=${encodeURIComponent(token)}` +
|
`&access-token=${encodeURIComponent(token)}` +
|
||||||
`&t=${Date.now()}`
|
`&t=${Date.now()}`
|
||||||
@@ -223,7 +227,7 @@ export async function getSnapUrl(cameraCode: string, force = false): Promise<str
|
|||||||
}
|
}
|
||||||
// 非 force:使用代理端点,不加时间戳,浏览器自动缓存
|
// 非 force:使用代理端点,不加时间戳,浏览器自动缓存
|
||||||
return (
|
return (
|
||||||
`${apiURL}/aiot/device/roi/snap/image` +
|
`${apiURL}/video/device/roi/snap/image` +
|
||||||
`?cameraCode=${encodeURIComponent(cameraCode)}` +
|
`?cameraCode=${encodeURIComponent(cameraCode)}` +
|
||||||
`&access-token=${encodeURIComponent(token)}`
|
`&access-token=${encodeURIComponent(token)}`
|
||||||
);
|
);
|
||||||
@@ -233,8 +237,8 @@ export async function getSnapUrl(cameraCode: string, force = false): Promise<str
|
|||||||
|
|
||||||
/** 算法列表 */
|
/** 算法列表 */
|
||||||
export function getAlgorithmList(deviceId?: string) {
|
export function getAlgorithmList(deviceId?: string) {
|
||||||
return wvpRequestClient.get<AiotDeviceApi.Algorithm[]>(
|
return wvpRequestClient.get<VideoDeviceApi.Algorithm[]>(
|
||||||
'/aiot/device/algorithm/list',
|
'/video/device/algorithm/list',
|
||||||
{ params: deviceId ? { deviceId } : {} },
|
{ params: deviceId ? { deviceId } : {} },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -242,7 +246,7 @@ export function getAlgorithmList(deviceId?: string) {
|
|||||||
/** 保存算法全局参数 */
|
/** 保存算法全局参数 */
|
||||||
export function saveAlgoGlobalParams(algoCode: string, globalParams: string) {
|
export function saveAlgoGlobalParams(algoCode: string, globalParams: string) {
|
||||||
return wvpRequestClient.post(
|
return wvpRequestClient.post(
|
||||||
`/aiot/device/algorithm/global-params/${algoCode}`,
|
`/video/device/algorithm/global-params/${algoCode}`,
|
||||||
{ globalParams },
|
{ globalParams },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -250,7 +254,7 @@ export function saveAlgoGlobalParams(algoCode: string, globalParams: string) {
|
|||||||
/** 批量更新设备算法参数 */
|
/** 批量更新设备算法参数 */
|
||||||
export function updateDeviceAlgoParams(deviceId: string, algoCode: string, params: string) {
|
export function updateDeviceAlgoParams(deviceId: string, algoCode: string, params: string) {
|
||||||
return wvpRequestClient.post(
|
return wvpRequestClient.post(
|
||||||
`/aiot/device/algorithm/device-binds/${deviceId}/${algoCode}`,
|
`/video/device/algorithm/device-binds/${deviceId}/${algoCode}`,
|
||||||
{ params },
|
{ params },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -259,12 +263,12 @@ export function updateDeviceAlgoParams(deviceId: string, algoCode: string, param
|
|||||||
|
|
||||||
/** 绑定算法到 ROI */
|
/** 绑定算法到 ROI */
|
||||||
export function bindAlgo(data: { roiId: string; algoCode: string }) {
|
export function bindAlgo(data: { roiId: string; algoCode: string }) {
|
||||||
return wvpRequestClient.post('/aiot/device/roi/bindAlgo', data);
|
return wvpRequestClient.post('/video/device/roi/bindAlgo', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 解绑算法 */
|
/** 解绑算法 */
|
||||||
export function unbindAlgo(bindId: string) {
|
export function unbindAlgo(bindId: string) {
|
||||||
return wvpRequestClient.delete('/aiot/device/roi/unbindAlgo', {
|
return wvpRequestClient.delete('/video/device/roi/unbindAlgo', {
|
||||||
params: { bindId },
|
params: { bindId },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -275,14 +279,14 @@ export function updateAlgoParams(data: {
|
|||||||
params?: string;
|
params?: string;
|
||||||
enabled?: number;
|
enabled?: number;
|
||||||
}) {
|
}) {
|
||||||
return wvpRequestClient.post('/aiot/device/roi/updateAlgoParams', data);
|
return wvpRequestClient.post('/video/device/roi/updateAlgoParams', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 配置推送 ====================
|
// ==================== 配置推送 ====================
|
||||||
|
|
||||||
/** 推送配置到边缘端 */
|
/** 推送配置到边缘端 */
|
||||||
export function pushConfig(cameraId: string) {
|
export function pushConfig(cameraId: string) {
|
||||||
return wvpRequestClient.post('/aiot/device/config/push', null, {
|
return wvpRequestClient.post('/video/device/config/push', null, {
|
||||||
params: { cameraId },
|
params: { cameraId },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -290,14 +294,14 @@ export function pushConfig(cameraId: string) {
|
|||||||
/** 一次性推送全部配置到本地Edge */
|
/** 一次性推送全部配置到本地Edge */
|
||||||
export function pushAllConfig() {
|
export function pushAllConfig() {
|
||||||
return wvpRequestClient.post<Record<string, any>>(
|
return wvpRequestClient.post<Record<string, any>>(
|
||||||
'/aiot/device/config/push-all',
|
'/video/device/config/push-all',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 导出摄像头配置 JSON */
|
/** 导出摄像头配置 JSON */
|
||||||
export function exportConfig(cameraId: string) {
|
export function exportConfig(cameraId: string) {
|
||||||
return wvpRequestClient.get<Record<string, any>>(
|
return wvpRequestClient.get<Record<string, any>>(
|
||||||
'/aiot/device/config/export',
|
'/video/device/config/export',
|
||||||
{ params: { cameraId } },
|
{ params: { cameraId } },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -312,7 +316,7 @@ export async function getAlertImageUrl(imagePath: string): Promise<string> {
|
|||||||
if (!imagePath) return '';
|
if (!imagePath) return '';
|
||||||
const token = await getWvpToken();
|
const token = await getWvpToken();
|
||||||
return (
|
return (
|
||||||
`${apiURL}/aiot/device/alert/image` +
|
`${apiURL}/video/device/alert/image` +
|
||||||
`?imagePath=${encodeURIComponent(imagePath)}` +
|
`?imagePath=${encodeURIComponent(imagePath)}` +
|
||||||
`&access-token=${encodeURIComponent(token)}`
|
`&access-token=${encodeURIComponent(token)}`
|
||||||
);
|
);
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { PageParam, PageResult } from '@vben/request';
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
|
||||||
import { wvpRequestClient } from '#/api/aiot/request';
|
import { wvpRequestClient } from '#/api/video/request';
|
||||||
|
|
||||||
export namespace AiotEdgeApi {
|
export namespace VideoEdgeApi {
|
||||||
/** 边缘设备 VO */
|
/** 边缘设备 VO */
|
||||||
export interface Device {
|
export interface Device {
|
||||||
id?: number;
|
id?: number;
|
||||||
@@ -32,33 +32,33 @@ export namespace AiotEdgeApi {
|
|||||||
|
|
||||||
// ==================== 边缘设备 API ====================
|
// ==================== 边缘设备 API ====================
|
||||||
// 数据源:WVP 数据库 wvp_ai_edge_device 表
|
// 数据源:WVP 数据库 wvp_ai_edge_device 表
|
||||||
// 路径经 vite proxy: /admin-api/aiot/device/* → rewrite → /api/ai/* → WVP:18080
|
// 路径经 vite proxy: /admin-api/video/device/* → rewrite → /api/ai/* → WVP:18080
|
||||||
|
|
||||||
/** 获取全部边缘设备列表 */
|
/** 获取全部边缘设备列表 */
|
||||||
export function getDeviceList() {
|
export function getDeviceList() {
|
||||||
return wvpRequestClient.get<AiotEdgeApi.Device[]>(
|
return wvpRequestClient.get<VideoEdgeApi.Device[]>(
|
||||||
'/aiot/device/device/list',
|
'/video/device/device/list',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 分页查询边缘设备列表 */
|
/** 分页查询边缘设备列表 */
|
||||||
export function getDevicePage(params: PageParam) {
|
export function getDevicePage(params: PageParam) {
|
||||||
return wvpRequestClient.get<PageResult<AiotEdgeApi.Device>>(
|
return wvpRequestClient.get<PageResult<VideoEdgeApi.Device>>(
|
||||||
'/aiot/device/device/page',
|
'/video/device/device/page',
|
||||||
{ params },
|
{ params },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取设备详情 */
|
/** 获取设备详情 */
|
||||||
export function getDevice(deviceId: string) {
|
export function getDevice(deviceId: string) {
|
||||||
return wvpRequestClient.get<AiotEdgeApi.Device>('/aiot/device/device/get', {
|
return wvpRequestClient.get<VideoEdgeApi.Device>('/video/device/device/get', {
|
||||||
params: { deviceId },
|
params: { deviceId },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取设备统计 */
|
/** 获取设备统计 */
|
||||||
export function getDeviceStatistics() {
|
export function getDeviceStatistics() {
|
||||||
return wvpRequestClient.get<AiotEdgeApi.DeviceStatistics>(
|
return wvpRequestClient.get<VideoEdgeApi.DeviceStatistics>(
|
||||||
'/aiot/device/device/statistics',
|
'/video/device/device/statistics',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -13,9 +13,17 @@ const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
|
|||||||
|
|
||||||
// ==================== WVP Token 管理 ====================
|
// ==================== WVP Token 管理 ====================
|
||||||
|
|
||||||
/** WVP 默认账号(开发环境使用,生产环境应通过环境变量配置) */
|
/**
|
||||||
const WVP_USERNAME = 'admin';
|
* WVP 默认账号
|
||||||
const WVP_PASSWORD_MD5 = '21232f297a57a5a743894a0e4a801fc3'; // admin 的 MD5
|
*
|
||||||
|
* 注意:前端任何 WVP 账号/密码都会打进 bundle,对懂行的人等于明文。
|
||||||
|
* 长期方案应由后端代理 WVP 鉴权,不要把账号分发到客户端。
|
||||||
|
* 作为过渡,至少走环境变量让生产可覆盖。
|
||||||
|
*/
|
||||||
|
const WVP_USERNAME = import.meta.env.VITE_WVP_USERNAME || 'admin';
|
||||||
|
const WVP_PASSWORD_MD5 =
|
||||||
|
import.meta.env.VITE_WVP_PASSWORD_MD5 ||
|
||||||
|
'21232f297a57a5a743894a0e4a801fc3'; // 'admin' 的 MD5,仅作兜底
|
||||||
|
|
||||||
let wvpAccessToken: null | string = null;
|
let wvpAccessToken: null | string = null;
|
||||||
let tokenPromise: null | Promise<string> = null;
|
let tokenPromise: null | Promise<string> = null;
|
||||||
@@ -23,7 +31,7 @@ let tokenPromise: null | Promise<string> = null;
|
|||||||
/** 登录 WVP 获取 access-token */
|
/** 登录 WVP 获取 access-token */
|
||||||
async function loginToWvp(): Promise<string> {
|
async function loginToWvp(): Promise<string> {
|
||||||
const url =
|
const url =
|
||||||
`${apiURL}/aiot/device/user/login` +
|
`${apiURL}/video/device/user/login` +
|
||||||
`?username=${encodeURIComponent(WVP_USERNAME)}` +
|
`?username=${encodeURIComponent(WVP_USERNAME)}` +
|
||||||
`&password=${encodeURIComponent(WVP_PASSWORD_MD5)}`;
|
`&password=${encodeURIComponent(WVP_PASSWORD_MD5)}`;
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
|||||||
component: 'ApiSelect',
|
component: 'ApiSelect',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
api: async () => {
|
api: async () => {
|
||||||
const { getCameraOptions } = await import('#/api/aiot/device');
|
const { getCameraOptions } = await import('#/api/video/device');
|
||||||
const list = await getCameraOptions();
|
const list = await getCameraOptions();
|
||||||
return list.map((item: { cameraCode: string; cameraName: string }) => ({
|
return list.map((item: { cameraCode: string; cameraName: string }) => ({
|
||||||
label: item.cameraName,
|
label: item.cameraName,
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
import type { AiotAlarmApi } from '#/api/aiot/alarm';
|
import type { VideoAlarmApi } from '#/api/video/alarm';
|
||||||
|
|
||||||
import { h, ref } from 'vue';
|
import { h, ref } from 'vue';
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
getAlert,
|
getAlert,
|
||||||
getAlertPage,
|
getAlertPage,
|
||||||
handleAlert,
|
handleAlert,
|
||||||
} from '#/api/aiot/alarm';
|
} from '#/api/video/alarm';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ALERT_LEVEL_OPTIONS,
|
ALERT_LEVEL_OPTIONS,
|
||||||
@@ -23,7 +23,7 @@ import {
|
|||||||
useGridFormSchema,
|
useGridFormSchema,
|
||||||
} from './data';
|
} from './data';
|
||||||
|
|
||||||
defineOptions({ name: 'AiotAlarmList' });
|
defineOptions({ name: 'VideoAlarmList' });
|
||||||
|
|
||||||
/** 格式化持续时长(毫秒 → 可读文本) */
|
/** 格式化持续时长(毫秒 → 可读文本) */
|
||||||
function formatDuration(ms: number | null | undefined): string {
|
function formatDuration(ms: number | null | undefined): string {
|
||||||
@@ -43,7 +43,7 @@ function formatDuration(ms: number | null | undefined): string {
|
|||||||
return minutes > 0 ? `${hours} 小时 ${minutes} 分` : `${hours} 小时`;
|
return minutes > 0 ? `${hours} 小时 ${minutes} 分` : `${hours} 小时`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentAlert = ref<AiotAlarmApi.Alert | null>(null);
|
const currentAlert = ref<VideoAlarmApi.Alert | null>(null);
|
||||||
const detailVisible = ref(false);
|
const detailVisible = ref(false);
|
||||||
|
|
||||||
/** 刷新表格 */
|
/** 刷新表格 */
|
||||||
@@ -108,7 +108,7 @@ function getLevelColor(level?: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 查看告警详情 */
|
/** 查看告警详情 */
|
||||||
async function handleView(row: AiotAlarmApi.Alert) {
|
async function handleView(row: VideoAlarmApi.Alert) {
|
||||||
try {
|
try {
|
||||||
const alertId = row.alarmId || row.id;
|
const alertId = row.alarmId || row.id;
|
||||||
if (!alertId) {
|
if (!alertId) {
|
||||||
@@ -126,11 +126,11 @@ async function handleView(row: AiotAlarmApi.Alert) {
|
|||||||
|
|
||||||
/** 处理告警 — "处理"弹窗(支持备注) */
|
/** 处理告警 — "处理"弹窗(支持备注) */
|
||||||
const handleModalVisible = ref(false);
|
const handleModalVisible = ref(false);
|
||||||
const handleModalRow = ref<AiotAlarmApi.Alert | null>(null);
|
const handleModalRow = ref<VideoAlarmApi.Alert | null>(null);
|
||||||
const handleRemark = ref('');
|
const handleRemark = ref('');
|
||||||
const handleSubmitting = ref(false);
|
const handleSubmitting = ref(false);
|
||||||
|
|
||||||
function openHandleModal(row: AiotAlarmApi.Alert) {
|
function openHandleModal(row: VideoAlarmApi.Alert) {
|
||||||
handleModalRow.value = row;
|
handleModalRow.value = row;
|
||||||
handleRemark.value = '';
|
handleRemark.value = '';
|
||||||
handleModalVisible.value = true;
|
handleModalVisible.value = true;
|
||||||
@@ -159,7 +159,7 @@ async function submitHandle() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 处理告警 — "误报"确认弹窗 */
|
/** 处理告警 — "误报"确认弹窗 */
|
||||||
function handleFalseAlarm(row: AiotAlarmApi.Alert) {
|
function handleFalseAlarm(row: VideoAlarmApi.Alert) {
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: '标记误报',
|
title: '标记误报',
|
||||||
content: h('div', [
|
content: h('div', [
|
||||||
@@ -225,7 +225,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
refresh: true,
|
refresh: true,
|
||||||
search: true,
|
search: true,
|
||||||
},
|
},
|
||||||
} as VxeTableGridOptions<AiotAlarmApi.Alert>,
|
} as VxeTableGridOptions<VideoAlarmApi.Alert>,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { AiotAlarmApi } from '#/api/aiot/alarm';
|
import type { VideoAlarmApi } from '#/api/video/alarm';
|
||||||
|
|
||||||
const TYPE_NAMES: Record<string, string> = {
|
const TYPE_NAMES: Record<string, string> = {
|
||||||
leave_post: '离岗检测',
|
leave_post: '离岗检测',
|
||||||
@@ -29,7 +29,7 @@ const LEVEL_COLORS: Record<number, string> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/** 告警趋势折线图(每种类型独立一条线,都从0开始) */
|
/** 告警趋势折线图(每种类型独立一条线,都从0开始) */
|
||||||
export function getTrendChartOptions(data: AiotAlarmApi.TrendItem[]): any {
|
export function getTrendChartOptions(data: VideoAlarmApi.TrendItem[]): any {
|
||||||
const dates = data.map((d) => d.date.slice(5)); // MM-DD
|
const dates = data.map((d) => d.date.slice(5)); // MM-DD
|
||||||
const types = Object.keys(TYPE_NAMES);
|
const types = Object.keys(TYPE_NAMES);
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ export function getTypePieChartOptions(
|
|||||||
|
|
||||||
/** 设备告警 Top10 横向条形图 */
|
/** 设备告警 Top10 横向条形图 */
|
||||||
export function getDeviceTopChartOptions(
|
export function getDeviceTopChartOptions(
|
||||||
data: AiotAlarmApi.DeviceTopItem[],
|
data: VideoAlarmApi.DeviceTopItem[],
|
||||||
): any {
|
): any {
|
||||||
const sorted = [...data].reverse(); // 最多的在上面
|
const sorted = [...data].reverse(); // 最多的在上面
|
||||||
return {
|
return {
|
||||||
@@ -172,7 +172,7 @@ export function getLevelBarChartOptions(
|
|||||||
|
|
||||||
/** 24小时时段分布柱状图 */
|
/** 24小时时段分布柱状图 */
|
||||||
export function getHourDistChartOptions(
|
export function getHourDistChartOptions(
|
||||||
data: AiotAlarmApi.HourDistItem[],
|
data: VideoAlarmApi.HourDistItem[],
|
||||||
): any {
|
): any {
|
||||||
const counts = data.map((d) => d.count);
|
const counts = data.map((d) => d.count);
|
||||||
const maxCount = Math.max(...counts, 1);
|
const maxCount = Math.max(...counts, 1);
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { AiotAlarmApi } from '#/api/aiot/alarm';
|
import type { VideoAlarmApi } from '#/api/video/alarm';
|
||||||
|
|
||||||
import { nextTick, onMounted, onUnmounted, ref } from 'vue';
|
import { nextTick, onMounted, onUnmounted, ref } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
@@ -25,7 +25,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
getAlertDashboard,
|
getAlertDashboard,
|
||||||
getAlertTrend,
|
getAlertTrend,
|
||||||
} from '#/api/aiot/alarm';
|
} from '#/api/video/alarm';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getDeviceTopChartOptions,
|
getDeviceTopChartOptions,
|
||||||
@@ -35,17 +35,17 @@ import {
|
|||||||
getTypePieChartOptions,
|
getTypePieChartOptions,
|
||||||
} from './chart-options';
|
} from './chart-options';
|
||||||
|
|
||||||
defineOptions({ name: 'AiotAlarmDashboard' });
|
defineOptions({ name: 'VideoAlarmDashboard' });
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const loading = ref(true);
|
const loading = ref(true);
|
||||||
|
|
||||||
// 数据
|
// 数据
|
||||||
const stats = ref<AiotAlarmApi.AlertStatistics>({});
|
const stats = ref<VideoAlarmApi.AlertStatistics>({});
|
||||||
const trendData = ref<AiotAlarmApi.TrendItem[]>([]);
|
const trendData = ref<VideoAlarmApi.TrendItem[]>([]);
|
||||||
const deviceTopData = ref<AiotAlarmApi.DeviceTopItem[]>([]);
|
const deviceTopData = ref<VideoAlarmApi.DeviceTopItem[]>([]);
|
||||||
const hourDistData = ref<AiotAlarmApi.HourDistItem[]>([]);
|
const hourDistData = ref<VideoAlarmApi.HourDistItem[]>([]);
|
||||||
const recentAlerts = ref<AiotAlarmApi.Alert[]>([]);
|
const recentAlerts = ref<VideoAlarmApi.Alert[]>([]);
|
||||||
|
|
||||||
// 趋势天数切换
|
// 趋势天数切换
|
||||||
const trendDays = ref(7);
|
const trendDays = ref(7);
|
||||||
@@ -162,7 +162,7 @@ function formatTime(time?: string) {
|
|||||||
|
|
||||||
/** 跳转到告警列表 */
|
/** 跳转到告警列表 */
|
||||||
function goToAlertList() {
|
function goToAlertList() {
|
||||||
router.push('/aiot/alarm/list');
|
router.push('/video/alarm/list');
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { AiotDeviceApi } from '#/api/aiot/device';
|
import type { VideoDeviceApi } from '#/api/video/device';
|
||||||
import type { AiotEdgeApi } from '#/api/aiot/edge';
|
import type { VideoEdgeApi } from '#/api/video/edge';
|
||||||
|
|
||||||
import { computed, onMounted, ref } from 'vue';
|
import { computed, onMounted, ref } from 'vue';
|
||||||
|
|
||||||
@@ -20,8 +20,8 @@ import {
|
|||||||
getAlgorithmList,
|
getAlgorithmList,
|
||||||
saveAlgoGlobalParams,
|
saveAlgoGlobalParams,
|
||||||
updateDeviceAlgoParams,
|
updateDeviceAlgoParams,
|
||||||
} from '#/api/aiot/device';
|
} from '#/api/video/device';
|
||||||
import { getDeviceList } from '#/api/aiot/edge';
|
import { getDeviceList } from '#/api/video/edge';
|
||||||
|
|
||||||
// 参数名中英文映射(与 AlgorithmParamEditor 保持一致)
|
// 参数名中英文映射(与 AlgorithmParamEditor 保持一致)
|
||||||
const paramNameMap: Record<string, string> = {
|
const paramNameMap: Record<string, string> = {
|
||||||
@@ -62,7 +62,7 @@ const paramDescMap: Record<string, string> = {
|
|||||||
|
|
||||||
// ==================== 设备选择器 ====================
|
// ==================== 设备选择器 ====================
|
||||||
const selectedDeviceId = ref<string>('');
|
const selectedDeviceId = ref<string>('');
|
||||||
const deviceList = ref<AiotEdgeApi.Device[]>([]);
|
const deviceList = ref<VideoEdgeApi.Device[]>([]);
|
||||||
|
|
||||||
const isDeviceMode = computed(() => selectedDeviceId.value !== '');
|
const isDeviceMode = computed(() => selectedDeviceId.value !== '');
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ const saveButtonText = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// ==================== 算法数据 ====================
|
// ==================== 算法数据 ====================
|
||||||
const algorithms = ref<AiotDeviceApi.Algorithm[]>([]);
|
const algorithms = ref<VideoDeviceApi.Algorithm[]>([]);
|
||||||
const activeTab = ref<string>('');
|
const activeTab = ref<string>('');
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const saving = ref(false);
|
const saving = ref(false);
|
||||||
@@ -189,7 +189,7 @@ async function loadAlgorithms() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function initFormData(algo: AiotDeviceApi.Algorithm) {
|
function initFormData(algo: VideoDeviceApi.Algorithm) {
|
||||||
const code = algo.algoCode || '';
|
const code = algo.algoCode || '';
|
||||||
let schema: Record<string, any> = {};
|
let schema: Record<string, any> = {};
|
||||||
let globalParams: Record<string, any> = {};
|
let globalParams: Record<string, any> = {};
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
* 功能:摄像头列表展示、搜索筛选、新增/编辑/删除、拉流控制、ROI 配置跳转、配置导出
|
* 功能:摄像头列表展示、搜索筛选、新增/编辑/删除、拉流控制、ROI 配置跳转、配置导出
|
||||||
* 后端:WVP 视频平台 StreamProxy API
|
* 后端:WVP 视频平台 StreamProxy API
|
||||||
*/
|
*/
|
||||||
import type { AiotDeviceApi } from '#/api/aiot/device';
|
import type { VideoDeviceApi } from '#/api/video/device';
|
||||||
|
|
||||||
import { computed, onMounted, reactive, ref, watch } from 'vue';
|
import { computed, onMounted, reactive, ref, watch } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
@@ -36,9 +36,9 @@ import {
|
|||||||
getSnapUrl,
|
getSnapUrl,
|
||||||
pushAllConfig,
|
pushAllConfig,
|
||||||
saveCamera,
|
saveCamera,
|
||||||
} from '#/api/aiot/device';
|
} from '#/api/video/device';
|
||||||
|
|
||||||
defineOptions({ name: 'AiotDeviceCamera' });
|
defineOptions({ name: 'VideoDeviceCamera' });
|
||||||
|
|
||||||
const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
|
const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ const router = useRouter();
|
|||||||
// ==================== 列表状态 ====================
|
// ==================== 列表状态 ====================
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const cameraList = ref<AiotDeviceApi.Camera[]>([]);
|
const cameraList = ref<VideoDeviceApi.Camera[]>([]);
|
||||||
const roiCounts = ref<Record<string, number>>({});
|
const roiCounts = ref<Record<string, number>>({});
|
||||||
const cameraStatus = ref<Record<string, boolean | null>>({});
|
const cameraStatus = ref<Record<string, boolean | null>>({});
|
||||||
const page = ref(1);
|
const page = ref(1);
|
||||||
@@ -70,7 +70,7 @@ const editModalOpen = ref(false);
|
|||||||
const editModalTitle = ref('添加摄像头');
|
const editModalTitle = ref('添加摄像头');
|
||||||
const saving = ref(false);
|
const saving = ref(false);
|
||||||
const mediaServerOptions = ref<{ label: string; value: string }[]>([]);
|
const mediaServerOptions = ref<{ label: string; value: string }[]>([]);
|
||||||
const editForm = reactive<Partial<AiotDeviceApi.Camera>>({
|
const editForm = reactive<Partial<VideoDeviceApi.Camera>>({
|
||||||
id: undefined,
|
id: undefined,
|
||||||
type: 'default',
|
type: 'default',
|
||||||
cameraName: '',
|
cameraName: '',
|
||||||
@@ -148,7 +148,7 @@ async function loadCameraStatus() {
|
|||||||
if (!cameraCode) continue;
|
if (!cameraCode) continue;
|
||||||
// 使用 fetch HEAD 请求检测截图是否可用(/snap/image 已免认证)
|
// 使用 fetch HEAD 请求检测截图是否可用(/snap/image 已免认证)
|
||||||
try {
|
try {
|
||||||
const url = `${apiURL}/aiot/device/roi/snap/image?cameraCode=${encodeURIComponent(cameraCode)}`;
|
const url = `${apiURL}/video/device/roi/snap/image?cameraCode=${encodeURIComponent(cameraCode)}`;
|
||||||
const res = await fetch(url, { method: 'HEAD' });
|
const res = await fetch(url, { method: 'HEAD' });
|
||||||
cameraStatus.value = { ...cameraStatus.value, [cameraCode]: res.ok };
|
cameraStatus.value = { ...cameraStatus.value, [cameraCode]: res.ok };
|
||||||
} catch {
|
} catch {
|
||||||
@@ -238,7 +238,7 @@ function handleAdd() {
|
|||||||
autoFillStreamId();
|
autoFillStreamId();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleEdit(row: AiotDeviceApi.Camera) {
|
function handleEdit(row: VideoDeviceApi.Camera) {
|
||||||
Object.assign(editForm, {
|
Object.assign(editForm, {
|
||||||
id: row.id,
|
id: row.id,
|
||||||
type: row.type || 'default',
|
type: row.type || 'default',
|
||||||
@@ -301,7 +301,7 @@ async function handleSave() {
|
|||||||
|
|
||||||
// ==================== 删除 ====================
|
// ==================== 删除 ====================
|
||||||
|
|
||||||
function handleDelete(row: AiotDeviceApi.Camera) {
|
function handleDelete(row: VideoDeviceApi.Camera) {
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: '删除确认',
|
title: '删除确认',
|
||||||
content: `确定删除摄像头 ${row.cameraName || row.cameraCode || row.stream} ?`,
|
content: `确定删除摄像头 ${row.cameraName || row.cameraCode || row.stream} ?`,
|
||||||
@@ -320,9 +320,9 @@ function handleDelete(row: AiotDeviceApi.Camera) {
|
|||||||
|
|
||||||
// ==================== ROI 配置跳转 ====================
|
// ==================== ROI 配置跳转 ====================
|
||||||
|
|
||||||
function handleRoiConfig(row: AiotDeviceApi.Camera) {
|
function handleRoiConfig(row: VideoDeviceApi.Camera) {
|
||||||
router.push({
|
router.push({
|
||||||
path: '/aiot/device/roi',
|
path: '/video/device/roi',
|
||||||
query: {
|
query: {
|
||||||
cameraCode: row.cameraCode,
|
cameraCode: row.cameraCode,
|
||||||
srcUrl: row.srcUrl,
|
srcUrl: row.srcUrl,
|
||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
Tag,
|
Tag,
|
||||||
} from 'ant-design-vue';
|
} from 'ant-design-vue';
|
||||||
|
|
||||||
import { updateAlgoParams } from '#/api/aiot/device';
|
import { updateAlgoParams } from '#/api/video/device';
|
||||||
|
|
||||||
import WorkingHoursEditor from './WorkingHoursEditor.vue';
|
import WorkingHoursEditor from './WorkingHoursEditor.vue';
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { AiotDeviceApi } from '#/api/aiot/device';
|
import type { VideoDeviceApi } from '#/api/video/device';
|
||||||
|
|
||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
getAlgorithmList,
|
getAlgorithmList,
|
||||||
unbindAlgo,
|
unbindAlgo,
|
||||||
updateAlgoParams,
|
updateAlgoParams,
|
||||||
} from '#/api/aiot/device';
|
} from '#/api/video/device';
|
||||||
|
|
||||||
import AlgorithmParamEditor from './AlgorithmParamEditor.vue';
|
import AlgorithmParamEditor from './AlgorithmParamEditor.vue';
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ const DEFAULT_ALARM_LEVELS: Record<string, number> = {
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
roiId: string;
|
roiId: string;
|
||||||
bindings: AiotDeviceApi.RoiAlgoBinding[];
|
bindings: VideoDeviceApi.RoiAlgoBinding[];
|
||||||
snapOk: boolean;
|
snapOk: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ const emit = defineEmits<{
|
|||||||
|
|
||||||
const showAddDialog = ref(false);
|
const showAddDialog = ref(false);
|
||||||
const selectedAlgoCode = ref<string | undefined>(undefined);
|
const selectedAlgoCode = ref<string | undefined>(undefined);
|
||||||
const availableAlgorithms = ref<AiotDeviceApi.Algorithm[]>([]);
|
const availableAlgorithms = ref<VideoDeviceApi.Algorithm[]>([]);
|
||||||
const paramEditorOpen = ref(false);
|
const paramEditorOpen = ref(false);
|
||||||
const currentParamSchema = ref('{}');
|
const currentParamSchema = ref('{}');
|
||||||
const currentParams = ref('{}');
|
const currentParams = ref('{}');
|
||||||
@@ -123,7 +123,7 @@ function handleUnbind(bindId: string) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function openParamEditor(item: AiotDeviceApi.RoiAlgoBinding) {
|
function openParamEditor(item: VideoDeviceApi.RoiAlgoBinding) {
|
||||||
currentBindId.value = item.bind.bindId || '';
|
currentBindId.value = item.bind.bindId || '';
|
||||||
currentParams.value = item.bind.params || '{}';
|
currentParams.value = item.bind.params || '{}';
|
||||||
currentParamSchema.value = item.algorithm?.paramSchema || '{}';
|
currentParamSchema.value = item.algorithm?.paramSchema || '{}';
|
||||||
@@ -137,7 +137,7 @@ function openParamEditor(item: AiotDeviceApi.RoiAlgoBinding) {
|
|||||||
paramEditorOpen.value = true;
|
paramEditorOpen.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onToggleEnabled(bind: AiotDeviceApi.AlgoBind, val: string | number | boolean) {
|
async function onToggleEnabled(bind: VideoDeviceApi.AlgoBind, val: string | number | boolean) {
|
||||||
bind.enabled = val ? 1 : 0;
|
bind.enabled = val ? 1 : 0;
|
||||||
try {
|
try {
|
||||||
await updateAlgoParams({
|
await updateAlgoParams({
|
||||||
@@ -165,7 +165,7 @@ function getAlgoFrameRate(algoCode: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 从 params JSON 中读取告警等级 */
|
/** 从 params JSON 中读取告警等级 */
|
||||||
function getAlarmLevel(item: AiotDeviceApi.RoiAlgoBinding): number {
|
function getAlarmLevel(item: VideoDeviceApi.RoiAlgoBinding): number {
|
||||||
try {
|
try {
|
||||||
const params = JSON.parse(item.bind.params || '{}');
|
const params = JSON.parse(item.bind.params || '{}');
|
||||||
if (params.alarm_level !== undefined) return params.alarm_level;
|
if (params.alarm_level !== undefined) return params.alarm_level;
|
||||||
@@ -174,7 +174,7 @@ function getAlarmLevel(item: AiotDeviceApi.RoiAlgoBinding): number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 修改告警等级 */
|
/** 修改告警等级 */
|
||||||
async function onAlarmLevelChange(item: AiotDeviceApi.RoiAlgoBinding, level: number) {
|
async function onAlarmLevelChange(item: VideoDeviceApi.RoiAlgoBinding, level: number) {
|
||||||
try {
|
try {
|
||||||
let params: Record<string, any> = {};
|
let params: Record<string, any> = {};
|
||||||
try {
|
try {
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { AiotDeviceApi } from '#/api/aiot/device';
|
import type { VideoDeviceApi } from '#/api/video/device';
|
||||||
|
|
||||||
import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
|
import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
rois: AiotDeviceApi.Roi[];
|
rois: VideoDeviceApi.Roi[];
|
||||||
drawMode: string | null;
|
drawMode: string | null;
|
||||||
selectedRoiId: string | null;
|
selectedRoiId: string | null;
|
||||||
snapUrl: string;
|
snapUrl: string;
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
* 算法绑定管理、配置推送到边缘端
|
* 算法绑定管理、配置推送到边缘端
|
||||||
* 后端:WVP 视频平台 AiRoi / AiConfig API
|
* 后端:WVP 视频平台 AiRoi / AiConfig API
|
||||||
*/
|
*/
|
||||||
import type { AiotDeviceApi } from '#/api/aiot/device';
|
import type { VideoDeviceApi } from '#/api/video/device';
|
||||||
|
|
||||||
import { computed, onMounted, ref } from 'vue';
|
import { computed, onMounted, ref } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
@@ -34,13 +34,13 @@ import {
|
|||||||
getSnapUrl,
|
getSnapUrl,
|
||||||
pushConfig,
|
pushConfig,
|
||||||
saveRoi,
|
saveRoi,
|
||||||
} from '#/api/aiot/device';
|
} from '#/api/video/device';
|
||||||
import { wvpRequestClient } from '#/api/aiot/request';
|
import { wvpRequestClient } from '#/api/video/request';
|
||||||
|
|
||||||
import RoiAlgorithmBind from './components/RoiAlgorithmBind.vue';
|
import RoiAlgorithmBind from './components/RoiAlgorithmBind.vue';
|
||||||
import RoiCanvas from './components/RoiCanvas.vue';
|
import RoiCanvas from './components/RoiCanvas.vue';
|
||||||
|
|
||||||
defineOptions({ name: 'AiotDeviceRoi' });
|
defineOptions({ name: 'VideoDeviceRoi' });
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -48,11 +48,11 @@ const router = useRouter();
|
|||||||
// ==================== 摄像头选择 ====================
|
// ==================== 摄像头选择 ====================
|
||||||
|
|
||||||
const cameraCode = ref('');
|
const cameraCode = ref('');
|
||||||
const currentCamera = ref<AiotDeviceApi.Camera | null>(null);
|
const currentCamera = ref<VideoDeviceApi.Camera | null>(null);
|
||||||
|
|
||||||
const showCameraSelector = ref(false);
|
const showCameraSelector = ref(false);
|
||||||
const cameraOptions = ref<
|
const cameraOptions = ref<
|
||||||
{ camera: AiotDeviceApi.Camera; label: string; value: string }[]
|
{ camera: VideoDeviceApi.Camera; label: string; value: string }[]
|
||||||
>([]);
|
>([]);
|
||||||
const selectedCamera = ref<string | undefined>(undefined);
|
const selectedCamera = ref<string | undefined>(undefined);
|
||||||
const cameraLoading = ref(false);
|
const cameraLoading = ref(false);
|
||||||
@@ -60,9 +60,9 @@ const cameraLoading = ref(false);
|
|||||||
// ==================== ROI 状态 ====================
|
// ==================== ROI 状态 ====================
|
||||||
|
|
||||||
const drawMode = ref<null | string>(null);
|
const drawMode = ref<null | string>(null);
|
||||||
const roiList = ref<AiotDeviceApi.Roi[]>([]);
|
const roiList = ref<VideoDeviceApi.Roi[]>([]);
|
||||||
const selectedRoiId = ref<null | string>(null);
|
const selectedRoiId = ref<null | string>(null);
|
||||||
const selectedRoiBindings = ref<AiotDeviceApi.RoiAlgoBinding[]>([]);
|
const selectedRoiBindings = ref<VideoDeviceApi.RoiAlgoBinding[]>([]);
|
||||||
const snapUrl = ref('');
|
const snapUrl = ref('');
|
||||||
const snapOk = ref(false);
|
const snapOk = ref(false);
|
||||||
const panelVisible = ref(false);
|
const panelVisible = ref(false);
|
||||||
@@ -100,7 +100,7 @@ async function loadCurrentCamera() {
|
|||||||
try {
|
try {
|
||||||
const res = await getCameraList({ page: 1, count: 200 });
|
const res = await getCameraList({ page: 1, count: 200 });
|
||||||
const list = res.list || [];
|
const list = res.list || [];
|
||||||
const camera = list.find((c: AiotDeviceApi.Camera) => c.cameraCode === cameraCode.value);
|
const camera = list.find((c: VideoDeviceApi.Camera) => c.cameraCode === cameraCode.value);
|
||||||
if (camera) {
|
if (camera) {
|
||||||
currentCamera.value = camera;
|
currentCamera.value = camera;
|
||||||
}
|
}
|
||||||
@@ -114,7 +114,7 @@ async function loadCameraOptions() {
|
|||||||
try {
|
try {
|
||||||
const res = await getCameraList({ page: 1, count: 200 });
|
const res = await getCameraList({ page: 1, count: 200 });
|
||||||
const list = res.list || [];
|
const list = res.list || [];
|
||||||
cameraOptions.value = list.map((cam: AiotDeviceApi.Camera) => ({
|
cameraOptions.value = list.map((cam: VideoDeviceApi.Camera) => ({
|
||||||
value: cam.cameraCode || '',
|
value: cam.cameraCode || '',
|
||||||
label: `${cam.cameraName || cam.app || cam.stream}${cam.srcUrl ? ` (${cam.srcUrl})` : ''}`,
|
label: `${cam.cameraName || cam.app || cam.stream}${cam.srcUrl ? ` (${cam.srcUrl})` : ''}`,
|
||||||
camera: cam,
|
camera: cam,
|
||||||
@@ -138,18 +138,18 @@ async function onCameraSelected(val: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function goBack() {
|
function goBack() {
|
||||||
router.push('/aiot/device/camera');
|
router.push('/video/device/camera');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== ROI 类型标签 ====================
|
// ==================== ROI 类型标签 ====================
|
||||||
|
|
||||||
function getRoiTagColor(roi: AiotDeviceApi.Roi) {
|
function getRoiTagColor(roi: VideoDeviceApi.Roi) {
|
||||||
if (roi.roiType === 'fullscreen') return 'orange';
|
if (roi.roiType === 'fullscreen') return 'orange';
|
||||||
if (roi.roiType === 'rectangle') return 'blue';
|
if (roi.roiType === 'rectangle') return 'blue';
|
||||||
return 'green';
|
return 'green';
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRoiTagLabel(roi: AiotDeviceApi.Roi) {
|
function getRoiTagLabel(roi: VideoDeviceApi.Roi) {
|
||||||
if (roi.roiType === 'fullscreen') return '全图';
|
if (roi.roiType === 'fullscreen') return '全图';
|
||||||
if (roi.roiType === 'rectangle') return '矩形';
|
if (roi.roiType === 'rectangle') return '矩形';
|
||||||
return '自定义';
|
return '自定义';
|
||||||
@@ -209,7 +209,7 @@ function addFullscreen() {
|
|||||||
title: '新建全图选区',
|
title: '新建全图选区',
|
||||||
content: '将创建覆盖整张图片的选区',
|
content: '将创建覆盖整张图片的选区',
|
||||||
async onOk() {
|
async onOk() {
|
||||||
const newRoi: Partial<AiotDeviceApi.Roi> = {
|
const newRoi: Partial<VideoDeviceApi.Roi> = {
|
||||||
cameraId: cameraCode.value,
|
cameraId: cameraCode.value,
|
||||||
name: `全图-${roiList.value.length + 1}`,
|
name: `全图-${roiList.value.length + 1}`,
|
||||||
roiType: 'fullscreen',
|
roiType: 'fullscreen',
|
||||||
@@ -262,7 +262,7 @@ function onDrawCancelled() {
|
|||||||
async function onRoiDrawn(data: { coordinates: string; roi_type: string }) {
|
async function onRoiDrawn(data: { coordinates: string; roi_type: string }) {
|
||||||
drawMode.value = null;
|
drawMode.value = null;
|
||||||
const roiName = `ROI-${roiList.value.length + 1}`;
|
const roiName = `ROI-${roiList.value.length + 1}`;
|
||||||
const newRoi: Partial<AiotDeviceApi.Roi> = {
|
const newRoi: Partial<VideoDeviceApi.Roi> = {
|
||||||
cameraId: cameraCode.value,
|
cameraId: cameraCode.value,
|
||||||
name: roiName,
|
name: roiName,
|
||||||
roiType: data.roi_type,
|
roiType: data.roi_type,
|
||||||
@@ -292,7 +292,7 @@ function onRoiSelected(roiId: null | string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectRoi(roi: AiotDeviceApi.Roi) {
|
function selectRoi(roi: VideoDeviceApi.Roi) {
|
||||||
selectedRoiId.value = roi.roiId || null;
|
selectedRoiId.value = roi.roiId || null;
|
||||||
loadRoiDetail();
|
loadRoiDetail();
|
||||||
}
|
}
|
||||||
@@ -305,7 +305,7 @@ function closePanel() {
|
|||||||
|
|
||||||
// ==================== ROI 编辑 ====================
|
// ==================== ROI 编辑 ====================
|
||||||
|
|
||||||
async function updateRoiData(roi: AiotDeviceApi.Roi) {
|
async function updateRoiData(roi: VideoDeviceApi.Roi) {
|
||||||
try {
|
try {
|
||||||
await saveRoi(roi);
|
await saveRoi(roi);
|
||||||
loadRois();
|
loadRois();
|
||||||
@@ -326,7 +326,7 @@ function onRoiDeleted(roiId: string) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDeleteRoi(roi: AiotDeviceApi.Roi) {
|
function handleDeleteRoi(roi: VideoDeviceApi.Roi) {
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: '提示',
|
title: '提示',
|
||||||
content: '确定删除该ROI?关联的算法绑定也将删除。',
|
content: '确定删除该ROI?关联的算法绑定也将删除。',
|
||||||
@@ -15,15 +15,15 @@ import {
|
|||||||
Tag,
|
Tag,
|
||||||
} from 'ant-design-vue';
|
} from 'ant-design-vue';
|
||||||
|
|
||||||
import { getDeviceList } from '#/api/aiot/edge';
|
import { getDeviceList } from '#/api/video/edge';
|
||||||
import type { AiotEdgeApi } from '#/api/aiot/edge';
|
import type { VideoEdgeApi } from '#/api/video/edge';
|
||||||
|
|
||||||
import { formatUptime, STATUS_CONFIG } from './data';
|
import { formatUptime, STATUS_CONFIG } from './data';
|
||||||
|
|
||||||
defineOptions({ name: 'AiotEdgeNode' });
|
defineOptions({ name: 'VideoEdgeNode' });
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const devices = ref<AiotEdgeApi.Device[]>([]);
|
const devices = ref<VideoEdgeApi.Device[]>([]);
|
||||||
|
|
||||||
async function fetchDevices() {
|
async function fetchDevices() {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
@@ -7,14 +7,14 @@ export default defineConfig(async () => {
|
|||||||
server: {
|
server: {
|
||||||
allowedHosts: true,
|
allowedHosts: true,
|
||||||
proxy: {
|
proxy: {
|
||||||
// ==================== AIoT 统一路由 ====================
|
// ==================== Video 模块统一路由 ====================
|
||||||
|
|
||||||
// aiot/alarm, aiot/edge -> 告警服务 :8000(直通)
|
// video/alarm, video/edge -> 告警服务 :8000(直通)
|
||||||
'/admin-api/aiot/alarm': {
|
'/admin-api/video/alarm': {
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
target: 'http://127.0.0.1:8000',
|
target: 'http://127.0.0.1:8000',
|
||||||
},
|
},
|
||||||
'/admin-api/aiot/edge': {
|
'/admin-api/video/edge': {
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
target: 'http://127.0.0.1:8000',
|
target: 'http://127.0.0.1:8000',
|
||||||
},
|
},
|
||||||
@@ -29,44 +29,44 @@ export default defineConfig(async () => {
|
|||||||
target: 'http://127.0.0.1:8000',
|
target: 'http://127.0.0.1:8000',
|
||||||
},
|
},
|
||||||
// COS 存储相关接口 -> 告警服务 :8000
|
// COS 存储相关接口 -> 告警服务 :8000
|
||||||
'/admin-api/aiot/storage': {
|
'/admin-api/video/storage': {
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
target: 'http://127.0.0.1:8000',
|
target: 'http://127.0.0.1:8000',
|
||||||
},
|
},
|
||||||
|
|
||||||
// aiot/device/* -> WVP :18080(按子路径分别 rewrite)
|
// video/device/* -> WVP :18080(按子路径分别 rewrite)
|
||||||
// 注意:更具体的路径必须写在通配路径前面
|
// 注意:更具体的路径必须写在通配路径前面
|
||||||
|
|
||||||
// 摄像头拉流代理: /admin-api/aiot/device/proxy -> /api/proxy
|
// 摄像头拉流代理: /admin-api/video/device/proxy -> /api/proxy
|
||||||
'/admin-api/aiot/device/proxy': {
|
'/admin-api/video/device/proxy': {
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
target: 'http://127.0.0.1:18080',
|
target: 'http://127.0.0.1:18080',
|
||||||
rewrite: (path: string) =>
|
rewrite: (path: string) =>
|
||||||
path.replace('/admin-api/aiot/device/proxy', '/api/proxy'),
|
path.replace('/admin-api/video/device/proxy', '/api/proxy'),
|
||||||
},
|
},
|
||||||
// WVP 用户认证: /admin-api/aiot/device/user -> /api/user
|
// WVP 用户认证: /admin-api/video/device/user -> /api/user
|
||||||
'/admin-api/aiot/device/user': {
|
'/admin-api/video/device/user': {
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
target: 'http://127.0.0.1:18080',
|
target: 'http://127.0.0.1:18080',
|
||||||
rewrite: (path: string) =>
|
rewrite: (path: string) =>
|
||||||
path.replace('/admin-api/aiot/device/user', '/api/user'),
|
path.replace('/admin-api/video/device/user', '/api/user'),
|
||||||
},
|
},
|
||||||
// 媒体服务器: /admin-api/aiot/device/server -> /api/server/media_server
|
// 媒体服务器: /admin-api/video/device/server -> /api/server/media_server
|
||||||
'/admin-api/aiot/device/server': {
|
'/admin-api/video/device/server': {
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
target: 'http://127.0.0.1:18080',
|
target: 'http://127.0.0.1:18080',
|
||||||
rewrite: (path: string) =>
|
rewrite: (path: string) =>
|
||||||
path.replace(
|
path.replace(
|
||||||
'/admin-api/aiot/device/server',
|
'/admin-api/video/device/server',
|
||||||
'/api/server/media_server',
|
'/api/server/media_server',
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
// ROI/算法/配置等: /admin-api/aiot/device -> /api/ai(通配,放最后)
|
// ROI/算法/配置等: /admin-api/video/device -> /api/ai(通配,放最后)
|
||||||
'/admin-api/aiot/device': {
|
'/admin-api/video/device': {
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
target: 'http://127.0.0.1:18080',
|
target: 'http://127.0.0.1:18080',
|
||||||
rewrite: (path: string) =>
|
rewrite: (path: string) =>
|
||||||
path.replace('/admin-api/aiot/device', '/api/ai'),
|
path.replace('/admin-api/video/device', '/api/ai'),
|
||||||
},
|
},
|
||||||
|
|
||||||
// ==================== 芋道主平台 ====================
|
// ==================== 芋道主平台 ====================
|
||||||
|
|||||||
Reference in New Issue
Block a user