Compare commits

...

20 Commits

Author SHA1 Message Date
yangsy
ff35fda046 feat: 调用新的设备告警日志导出接口 2025-12-19 14:19:50 +08:00
yangsy
b238af2c77 feat: 调整服务器运行时间 label 文案 2025-12-19 12:50:07 +08:00
yangsy
5b989fab0f feat: DeviceHardwareCard组件添加自定义标签属性 2025-12-19 12:46:27 +08:00
yangsy
24a7881b94 fix: 视频平台日志页面补全遗漏的操作类型字段 2025-12-19 12:44:39 +08:00
yangsy
ee019c44a0 style 2025-12-19 11:14:51 +08:00
yangsy
fbe79c4dfc chore: vite代理配置 2025-12-19 11:14:51 +08:00
yangsy
eb0ee841cf feat: 细化设备树自动定位的触发条件
- 添加 `hasFromPage` 属性,辅助区分选择设备的来源是用户操作还是路由参数
2025-12-19 11:14:51 +08:00
yangsy
d0b065d6ba refactor: 路由参数 from 改为fromPage 2025-12-18 21:35:34 +08:00
yangsy
fee9c5ef6c feat: 渲染全线设备树时自动定位到所选设备 2025-12-18 20:49:15 +08:00
yangsy
d565fd6a5f fix: 修复由动画属性导致设备树在特定场景下无法自行滚动及展开节点失效的问题
- 当选中的设备所属车站在设备树的视口外时,定位操作无效,且节点无法点击展开,解决方案是在定位逻辑中切换Tree组件的animated状态
2025-12-18 20:47:00 +08:00
yangsy
5b47734c3b fix: 设备树仅在非车站模式下显示收起和定位按钮 2025-12-18 14:30:49 +08:00
yangsy
3b13b93cec refactor: 抽离登录校验查询 2025-12-18 10:46:39 +08:00
yangsy
742d561c33 fix: 修复设备更新面板中错误的表单校验逻辑 2025-12-18 10:46:39 +08:00
yangsy
ec4dd8917f refactor: 简化设备树节点双击和点击事件的逻辑并添加注释 2025-12-18 10:46:39 +08:00
yangsy
03d5fb3fcd feat: 设备树添加管理功能
- 新增设备导入、导出、删除功能及相关API
- 封装设备管理逻辑,拆分设备选择与设备管理逻辑
- 添加右键菜单支持设备管理操作
2025-12-17 15:38:08 +08:00
yangsy
073a29a83a refactor: 优化登录逻辑并添加注释 2025-12-17 15:38:08 +08:00
yangsy
495dc001a1 refactor: 优化请求封装
- 优化Result接口定义
- 新增响应数据解析逻辑
- 优化错误解析逻辑
2025-12-17 15:38:08 +08:00
yangsy
52ba3add3f feat: 新增设备导入导出API 2025-12-15 21:23:03 +08:00
yangsy
426d92a5f9 fix: 在导入和删除IndexedDB数据时停止轮询并启用离线开发模式以保证数据一致性 2025-12-15 12:49:21 +08:00
yangsy
91a2fcb743 fix: 兼容后端返回不完整的设备类型 2025-12-15 12:46:02 +08:00
67 changed files with 1213 additions and 501 deletions

View File

@@ -1,6 +1,6 @@
<script setup lang="ts">
import { useVersionCheckQuery } from './composables';
import { GlobalFeedback } from '@/components';
import { useVersionCheckQuery } from '@/composables';
import { useSettingStore } from '@/stores';
import { VueQueryDevtools } from '@tanstack/vue-query-devtools';
import { dateZhCN, NConfigProvider, NDialogProvider, NLoadingBarProvider, NMessageProvider, NNotificationProvider, zhCN } from 'naive-ui';

View File

@@ -1,11 +1,14 @@
import type { Nullable } from '@/types';
import type { NdmAlarmHost } from './alarm';
import type { NdmSecurityBox, NdmSwitch } from './other';
import type { NdmNvr } from './storage';
import type { NdmAlarmHost, NdmAlarmHostPageQuery } from './alarm';
import type { NdmSecurityBox, NdmSecurityBoxPageQuery, NdmSwitch, NdmSwitchPageQuery } from './other';
import type { NdmNvr, NdmNvrPageQuery } from './storage';
import type {
NdmCamera,
NdmCameraPageQuery,
NdmDecoder,
NdmDecoderPageQuery,
NdmKeyboard,
NdmKeyboardPageQuery,
NdmMediaServer,
NdmMediaServerPageQuery,
NdmMediaServerResultVO,
@@ -19,6 +22,16 @@ import type {
} from './video';
export type NdmDevice = NdmAlarmHost | NdmCamera | NdmDecoder | NdmKeyboard | NdmMediaServer | NdmNvr | NdmSecurityBox | NdmSwitch | NdmVideoServer;
export type NdmDevicePageQuery =
| NdmAlarmHostPageQuery
| NdmCameraPageQuery
| NdmDecoderPageQuery
| NdmKeyboardPageQuery
| NdmMediaServerPageQuery
| NdmNvrPageQuery
| NdmSecurityBoxPageQuery
| NdmSwitchPageQuery
| NdmVideoServerPageQuery;
export type NdmDeviceResultVO = Nullable<NdmDevice>;

View File

@@ -0,0 +1,13 @@
export interface ImportMsg {
wrongLines: WrongLine[];
wrongNum: number;
updateNum: number;
insertNum: number;
unchangedNum: number;
total: number;
}
export interface WrongLine {
rowNum: number;
msg: string;
}

View File

@@ -0,0 +1 @@
export * from './import-msg';

View File

@@ -1,3 +1,4 @@
export * from './base';
export * from './biz';
export * from './common';
export * from './system';

View File

@@ -1,6 +1,7 @@
import {
ndmClient,
userClient,
type ImportMsg,
type NdmAlarmHostPageQuery,
type NdmAlarmHostResultVO,
type NdmAlarmHostSaveVO,
@@ -9,6 +10,7 @@ import {
type PageResult,
type Station,
} from '@/apis';
import { unwrapResponse } from '@/utils';
export const pageAlarmHostApi = async (pageQuery: PageParams<NdmAlarmHostPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
@@ -16,9 +18,7 @@ export const pageAlarmHostApi = async (pageQuery: PageParams<NdmAlarmHostPageQue
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmAlarmHost/page`;
const resp = await client.post<PageResult<NdmAlarmHostResultVO>>(endpoint, pageQuery, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -28,9 +28,7 @@ export const detailAlarmHostApi = async (id: string, options?: { stationCode?: S
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmAlarmHost/detail`;
const resp = await client.get<NdmAlarmHostResultVO>(endpoint, { params: { id }, signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -40,9 +38,7 @@ export const saveAlarmHostApi = async (saveVO: NdmAlarmHostSaveVO, options?: { s
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmAlarmHost`;
const resp = await client.post<NdmAlarmHostResultVO>(endpoint, saveVO, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -52,9 +48,7 @@ export const updateAlarmHostApi = async (updateVO: NdmAlarmHostUpdateVO, options
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmAlarmHost`;
const resp = await client.put<NdmAlarmHostResultVO>(endpoint, updateVO, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -64,8 +58,28 @@ export const deleteAlarmHostApi = async (ids: string[], options?: { stationCode?
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmAlarmHost`;
const resp = await client.delete<boolean>(endpoint, ids, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
export const exportAlarmHostApi = async (pageQuery: PageParams<NdmAlarmHostPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
const client = stationCode ? ndmClient : userClient;
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmAlarmHost/defaultExportByTemplate`;
const resp = await client.post<Blob>(endpoint, pageQuery, { responseType: 'blob', retRaw: true, signal });
const data = unwrapResponse(resp);
return data;
};
export const importAlarmHostApi = async (file: File, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
const client = stationCode ? ndmClient : userClient;
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmAlarmHost/importReturnMsg`;
const formData = new FormData();
formData.append('file', file);
const resp = await client.post<ImportMsg>(endpoint, formData, { signal, upload: true });
const data = unwrapResponse(resp);
return data;
};

View File

@@ -1,4 +1,5 @@
import { ndmClient, userClient, type StationDevices } from '@/apis';
import { initStationDevices, ndmClient, userClient, type StationDevices } from '@/apis';
import { unwrapResponse } from '@/utils';
export const getAllDevicesApi = async (options?: { stationCode?: string; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
@@ -6,8 +7,10 @@ export const getAllDevicesApi = async (options?: { stationCode?: string; signal?
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmDevices/all`;
const resp = await client.get<StationDevices>(endpoint, { retRaw: true, signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
return data;
const data = unwrapResponse(resp);
// 由于各线路后端版本不一致,接口返回的设备类型可能不够完整,需要做一次合并
return {
...initStationDevices(),
...data,
};
};

View File

@@ -0,0 +1,32 @@
import {
deleteAlarmHostApi,
deleteCameraApi,
deleteDecoderApi,
deleteKeyboardApi,
deleteMediaServerApi,
deleteNvrApi,
deleteSecurityBoxApi,
deleteSwitchApi,
deleteVideoServerApi,
type Station,
} from '@/apis';
import { DEVICE_TYPE_LITERALS, type DeviceType } from '@/enums';
export const deleteDeviceApi = async (deviceType: DeviceType, id: string, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const apiRecord = {
[DEVICE_TYPE_LITERALS.ndmAlarmHost]: deleteAlarmHostApi,
[DEVICE_TYPE_LITERALS.ndmCamera]: deleteCameraApi,
[DEVICE_TYPE_LITERALS.ndmDecoder]: deleteDecoderApi,
[DEVICE_TYPE_LITERALS.ndmKeyboard]: deleteKeyboardApi,
[DEVICE_TYPE_LITERALS.ndmMediaServer]: deleteMediaServerApi,
[DEVICE_TYPE_LITERALS.ndmNvr]: deleteNvrApi,
[DEVICE_TYPE_LITERALS.ndmSecurityBox]: deleteSecurityBoxApi,
[DEVICE_TYPE_LITERALS.ndmSwitch]: deleteSwitchApi,
[DEVICE_TYPE_LITERALS.ndmVideoServer]: deleteVideoServerApi,
};
const deleteApi = apiRecord[deviceType];
if (!deleteApi) throw new Error('接口不存在');
return deleteApi([id], options);
};

View File

@@ -0,0 +1,34 @@
import {
exportAlarmHostApi,
exportCameraApi,
exportDecoderApi,
exportKeyboardApi,
exportMediaServerApi,
exportNvrApi,
exportSecurityBoxApi,
exportSwitchApi,
exportVideoServerApi,
type NdmDevicePageQuery,
type PageParams,
type Station,
} from '@/apis';
import { DEVICE_TYPE_LITERALS, type DeviceType } from '@/enums';
export const exportDeviceApi = async (deviceType: DeviceType, pageQuery: PageParams<NdmDevicePageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const apiRecord = {
[DEVICE_TYPE_LITERALS.ndmAlarmHost]: exportAlarmHostApi,
[DEVICE_TYPE_LITERALS.ndmCamera]: exportCameraApi,
[DEVICE_TYPE_LITERALS.ndmDecoder]: exportDecoderApi,
[DEVICE_TYPE_LITERALS.ndmKeyboard]: exportKeyboardApi,
[DEVICE_TYPE_LITERALS.ndmMediaServer]: exportMediaServerApi,
[DEVICE_TYPE_LITERALS.ndmNvr]: exportNvrApi,
[DEVICE_TYPE_LITERALS.ndmSecurityBox]: exportSecurityBoxApi,
[DEVICE_TYPE_LITERALS.ndmSwitch]: exportSwitchApi,
[DEVICE_TYPE_LITERALS.ndmVideoServer]: exportVideoServerApi,
};
const exportApi = apiRecord[deviceType];
if (!exportApi) throw new Error('接口不存在');
return exportApi(pageQuery, options);
};

View File

@@ -0,0 +1,32 @@
import {
importAlarmHostApi,
importCameraApi,
importDecoderApi,
importKeyboardApi,
importMediaServerApi,
importNvrApi,
importSecurityBoxApi,
importSwitchApi,
importVideoServerApi,
type Station,
} from '@/apis';
import { DEVICE_TYPE_LITERALS, type DeviceType } from '@/enums';
export const importDeviceApi = async (deviceType: DeviceType, file: File, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const apiRecord = {
[DEVICE_TYPE_LITERALS.ndmAlarmHost]: importAlarmHostApi,
[DEVICE_TYPE_LITERALS.ndmCamera]: importCameraApi,
[DEVICE_TYPE_LITERALS.ndmDecoder]: importDecoderApi,
[DEVICE_TYPE_LITERALS.ndmKeyboard]: importKeyboardApi,
[DEVICE_TYPE_LITERALS.ndmMediaServer]: importMediaServerApi,
[DEVICE_TYPE_LITERALS.ndmNvr]: importNvrApi,
[DEVICE_TYPE_LITERALS.ndmSecurityBox]: importSecurityBoxApi,
[DEVICE_TYPE_LITERALS.ndmSwitch]: importSwitchApi,
[DEVICE_TYPE_LITERALS.ndmVideoServer]: importVideoServerApi,
};
const importApi = apiRecord[deviceType];
if (!importApi) throw new Error('接口不存在');
return importApi(file, options);
};

View File

@@ -1,2 +1,5 @@
export * from './delete-device';
export * from './detail-device';
export * from './export-device';
export * from './import-device';
export * from './probe-device';

View File

@@ -1,4 +1,5 @@
import { ndmClient, userClient, type Station } from '@/apis';
import { unwrapResponse } from '@/utils';
export const resetMonitorScheduleApi = async (options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
@@ -6,6 +7,5 @@ export const resetMonitorScheduleApi = async (options?: { stationCode?: Station[
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmConstant/anyTenant/resetMonitorSchedule`;
const resp = await client.get<void>(endpoint, { signal });
const [err] = resp;
if (err) throw err;
unwrapResponse(resp);
};

View File

@@ -1,11 +1,10 @@
import { userClient, type VerifyServer } from '@/apis';
import { unwrapResponse } from '@/utils';
export const batchVerifyApi = async (options?: { signal?: AbortSignal }) => {
const { signal } = options ?? {};
const endpoint = `/api/ndm/ndmKeepAlive/batchVerify`;
const resp = await userClient.post<VerifyServer[]>(endpoint, {}, { retRaw: true, timeout: 5000, signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};

View File

@@ -1,4 +1,5 @@
import { ndmClient, userClient, type IcmpEntity, type Station } from '@/apis';
import { unwrapResponse } from '@/utils';
export const exportIcmpApi = async (status?: string, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
@@ -13,9 +14,7 @@ export const exportIcmpApi = async (status?: string, options?: { stationCode?: S
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
signal,
});
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -36,9 +35,7 @@ export const exportIcmpByStationApi = async (stationCodes: Station['code'][], st
signal,
},
);
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -48,8 +45,6 @@ export const icmpEntityByDeviceId = async (deviceId: string, options?: { station
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmIcmpExport/icmpEntityByDeviceId`;
const resp = await client.get<IcmpEntity[]>(endpoint, { params: { deviceId }, signal, retRaw: true });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};

View File

@@ -1,11 +1,11 @@
import { ndmClient, userClient, type Station } from '@/apis';
import { unwrapResponse } from '@/utils';
export const verifyApi = async (options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
const client = stationCode ? ndmClient : userClient;
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmKeepAlive/verify`;
const resp = await client.post<void>(endpoint, {}, { timeout: 5000, signal });
const [err] = resp;
if (err) throw err;
const resp = await client.post<void>(endpoint, {}, { retRaw: true, timeout: 5000, signal });
unwrapResponse(resp);
};

View File

@@ -1,4 +1,5 @@
import { ndmClient, userClient, type NdmCallLogPageQuery, type NdmCallLogResultVO, type PageParams, type PageResult, type Station } from '@/apis';
import { unwrapResponse } from '@/utils';
export const pageCallLogApi = async (pageQuery: PageParams<NdmCallLogPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
@@ -6,9 +7,7 @@ export const pageCallLogApi = async (pageQuery: PageParams<NdmCallLogPageQuery>,
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmCallLog/page`;
const resp = await client.post<PageResult<NdmCallLogResultVO>>(endpoint, pageQuery, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -18,8 +17,6 @@ export const exportCallLogApi = async (pageQuery: PageParams<NdmCallLogPageQuery
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmCallLog/defaultExportByTemplate`;
const resp = await client.post<Blob>(endpoint, pageQuery, { responseType: 'blob', retRaw: true, signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};

View File

@@ -1,4 +1,5 @@
import { ndmClient, userClient, type NdmDeviceAlarmLogPageQuery, type NdmDeviceAlarmLogResultVO, type NdmDeviceAlarmLogUpdateVO, type PageParams, type PageResult, type Station } from '@/apis';
import { unwrapResponse } from '@/utils';
export const pageDeviceAlarmLogApi = async (pageQuery: PageParams<NdmDeviceAlarmLogPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
@@ -6,9 +7,7 @@ export const pageDeviceAlarmLogApi = async (pageQuery: PageParams<NdmDeviceAlarm
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmDeviceAlarmLog/page`;
const resp = await client.post<PageResult<NdmDeviceAlarmLogResultVO>>(endpoint, pageQuery, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -18,9 +17,7 @@ export const updateDeviceAlarmLogApi = async (updateVO: NdmDeviceAlarmLogUpdateV
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmDeviceAlarmLog`;
const resp = await client.put<NdmDeviceAlarmLogResultVO>(endpoint, updateVO, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -28,10 +25,8 @@ export const exportDeviceAlarmLogApi = async (pageQuery: PageParams<NdmDeviceAla
const { stationCode, signal } = options ?? {};
const client = stationCode ? ndmClient : userClient;
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmDeviceAlarmLog/defaultExportByTemplate`;
const endpoint = `${prefix}/api/ndm/ndmDeviceAlarmLog/exportByTemplateV2`;
const resp = await client.post<BlobPart>(endpoint, pageQuery, { responseType: 'blob', retRaw: true, signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};

View File

@@ -1,4 +1,5 @@
import { ndmClient, userClient, type NdmIcmpLogPageQuery, type NdmIcmpLogResultVO, type PageParams, type PageResult, type Station } from '@/apis';
import { unwrapResponse } from '@/utils';
export const pageIcmpLogApi = async (pageQuery: PageParams<NdmIcmpLogPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
@@ -6,8 +7,6 @@ export const pageIcmpLogApi = async (pageQuery: PageParams<NdmIcmpLogPageQuery>,
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmIcmpLog/page`;
const resp = await client.post<PageResult<NdmIcmpLogResultVO>>(endpoint, pageQuery, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};

View File

@@ -1,4 +1,5 @@
import { ndmClient, userClient, type ClientChannel, type NdmNvrResultVO, type NdmRecordCheck } from '@/apis';
import { unwrapResponse } from '@/utils';
import dayjs from 'dayjs';
export const getChannelListApi = async (ndmNvr: NdmNvrResultVO, options?: { stationCode?: string; signal?: AbortSignal }) => {
@@ -7,9 +8,7 @@ export const getChannelListApi = async (ndmNvr: NdmNvrResultVO, options?: { stat
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmRecordCheck/getChannelList`;
const resp = await client.post<ClientChannel[]>(endpoint, { code: ndmNvr.gbCode, time: '' }, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -24,9 +23,7 @@ export const getRecordCheckApi = async (ndmNvr: NdmNvrResultVO, lastDays: number
const end = endDateTime.format('YYYY-MM-DD');
const parentId = ndmNvr.gbCode;
const resp = await client.post<NdmRecordCheck[]>(endpoint, { start, end, parentId, gbCodeList }, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -36,9 +33,7 @@ export const reloadRecordCheckApi = async (channel: ClientChannel, dayOffset: nu
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmRecordCheck/reloadRecordCheckByGbId`;
const resp = await client.post<boolean>(endpoint, { ...channel, dayOffset }, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};

View File

@@ -1,4 +1,5 @@
import { ndmClient, userClient, type NdmSnmpLogPageQuery, type NdmSnmpLogResultVO, type PageParams, type PageResult, type Station } from '@/apis';
import { unwrapResponse } from '@/utils';
export const pageSnmpLogApi = async (pageQuery: PageParams<NdmSnmpLogPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
@@ -6,8 +7,6 @@ export const pageSnmpLogApi = async (pageQuery: PageParams<NdmSnmpLogPageQuery>,
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmSnmpLog/page`;
const resp = await client.post<PageResult<NdmSnmpLogResultVO>>(endpoint, pageQuery, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};

View File

@@ -1,4 +1,5 @@
import { ndmClient, userClient, type NdmVimpLogPageQuery, type NdmVimpLogResultVO, type PageParams, type PageResult, type Station } from '@/apis';
import { unwrapResponse } from '@/utils';
export const pageVimpLogApi = async (pageQuery: PageParams<NdmVimpLogPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
@@ -6,9 +7,7 @@ export const pageVimpLogApi = async (pageQuery: PageParams<NdmVimpLogPageQuery>,
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmVimpLog/page`;
const resp = await client.post<PageResult<NdmVimpLogResultVO>>(endpoint, pageQuery, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -18,8 +17,6 @@ export const exportVimpLogApi = async (pageQuery: PageParams<NdmVimpLogPageQuery
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmVimpLog/defaultExportByTemplate`;
const resp = await client.post<Blob>(endpoint, pageQuery, { responseType: 'blob', retRaw: true, signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};

View File

@@ -1,6 +1,7 @@
import {
ndmClient,
userClient,
type ImportMsg,
type NdmSecurityBoxPageQuery,
type NdmSecurityBoxResultVO,
type NdmSecurityBoxSaveVO,
@@ -9,6 +10,7 @@ import {
type PageResult,
type Station,
} from '@/apis';
import { unwrapResponse } from '@/utils';
export const pageSecurityBoxApi = async (pageQuery: PageParams<NdmSecurityBoxPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
@@ -16,9 +18,7 @@ export const pageSecurityBoxApi = async (pageQuery: PageParams<NdmSecurityBoxPag
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmSecurityBox/page`;
const resp = await client.post<PageResult<NdmSecurityBoxResultVO>>(endpoint, pageQuery, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -28,9 +28,7 @@ export const detailSecurityBoxApi = async (id: string, options?: { stationCode?:
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmSecurityBox/detail`;
const resp = await client.get<NdmSecurityBoxResultVO>(endpoint, { params: { id }, signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -40,9 +38,7 @@ export const saveSecurityBoxApi = async (saveVO: NdmSecurityBoxSaveVO, options?:
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmSecurityBox`;
const resp = await client.post<NdmSecurityBoxResultVO>(endpoint, saveVO, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -52,9 +48,7 @@ export const updateSecurityBoxApi = async (updateVO: NdmSecurityBoxUpdateVO, opt
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmSecurityBox`;
const resp = await client.put<NdmSecurityBoxResultVO>(endpoint, updateVO, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -64,9 +58,29 @@ export const deleteSecurityBoxApi = async (ids: string[], options?: { stationCod
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmSecurityBox`;
const resp = await client.delete<boolean>(endpoint, ids, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
export const exportSecurityBoxApi = async (pageQuery: PageParams<NdmSecurityBoxPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
const client = stationCode ? ndmClient : userClient;
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmSecurityBox/defaultExportByTemplate`;
const resp = await client.post<Blob>(endpoint, pageQuery, { responseType: 'blob', retRaw: true, signal });
const data = unwrapResponse(resp);
return data;
};
export const importSecurityBoxApi = async (file: File, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
const client = stationCode ? ndmClient : userClient;
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmSecurityBox/importReturnMsg`;
const formData = new FormData();
formData.append('file', file);
const resp = await client.post<ImportMsg>(endpoint, formData, { signal, upload: true });
const data = unwrapResponse(resp);
return data;
};
@@ -76,8 +90,7 @@ export const probeSecurityBoxApi = async (ids: string[], options?: { stationCode
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmSecurityBox/probeByIds`;
const resp = await client.post<void>(endpoint, ids, { signal });
const [err] = resp;
if (err) throw err;
unwrapResponse(resp);
};
export const turnCitcuitStatusApi = async (ipAddress: string, circuitIndex: number, status: number, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
@@ -86,9 +99,7 @@ export const turnCitcuitStatusApi = async (ipAddress: string, circuitIndex: numb
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmSecurityBox/turnStatus`;
const resp = await client.post<boolean>(endpoint, { community: 'public', ipAddress, circuit: `${circuitIndex}`, status }, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -98,8 +109,6 @@ export const rebootSecurityBoxApi = async (ipAddress: string, options?: { statio
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmSecurityBox/reboot`;
const resp = await client.post<boolean>(endpoint, { community: 'public', ipAddress }, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};

View File

@@ -1,4 +1,5 @@
import { ndmClient, userClient, type MediaServerStatus, type Station } from '@/apis';
import { unwrapResponse } from '@/utils';
export const isMediaServerAliveApi = async (options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
@@ -6,9 +7,7 @@ export const isMediaServerAliveApi = async (options?: { stationCode?: Station['c
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmServiceAvailable/mediaServer/isAlive`;
const resp = await client.get<MediaServerStatus[]>(endpoint, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -18,8 +17,6 @@ export const isSipServerAliveApi = async (options?: { stationCode?: Station['cod
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmServiceAvailable/sipServer/isAlive`;
const resp = await client.get<boolean>(endpoint, { signal });
const [err, data] = resp;
if (err) throw err;
if (data === null) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};

View File

@@ -1,4 +1,16 @@
import { ndmClient, type NdmSwitchPageQuery, type NdmSwitchResultVO, type NdmSwitchSaveVO, type NdmSwitchUpdateVO, type PageParams, type PageResult, type Station } from '@/apis';
import {
ndmClient,
userClient,
type ImportMsg,
type NdmSwitchPageQuery,
type NdmSwitchResultVO,
type NdmSwitchSaveVO,
type NdmSwitchUpdateVO,
type PageParams,
type PageResult,
type Station,
} from '@/apis';
import { unwrapResponse } from '@/utils';
export const pageSwitchApi = async (pageQuery: PageParams<NdmSwitchPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
@@ -6,9 +18,7 @@ export const pageSwitchApi = async (pageQuery: PageParams<NdmSwitchPageQuery>, o
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmSwitch/page`;
const resp = await client.post<PageResult<NdmSwitchResultVO>>(endpoint, pageQuery, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -18,9 +28,7 @@ export const detailSwitchApi = async (id: string, options?: { stationCode?: Stat
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmSwitch/detail`;
const resp = await client.get<NdmSwitchResultVO>(endpoint, { params: { id }, signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -30,9 +38,7 @@ export const saveSwitchApi = async (saveVO: NdmSwitchSaveVO, options?: { station
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmSwitch`;
const resp = await client.post<NdmSwitchResultVO>(endpoint, saveVO, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -42,9 +48,7 @@ export const updateSwitchApi = async (updateVO: NdmSwitchUpdateVO, options?: { s
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmSwitch`;
const resp = await client.put<NdmSwitchResultVO>(endpoint, updateVO, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -54,9 +58,29 @@ export const deleteSwitchApi = async (ids: string[], options?: { stationCode?: S
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmSwitch`;
const resp = await client.delete<boolean>(endpoint, ids, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
export const exportSwitchApi = async (pageQuery: PageParams<NdmSwitchPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
const client = stationCode ? ndmClient : ndmClient;
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmSwitch/defaultExportByTemplate`;
const resp = await client.post<Blob>(endpoint, pageQuery, { responseType: 'blob', retRaw: true, signal });
const data = unwrapResponse(resp);
return data;
};
export const importSwitchApi = async (file: File, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
const client = stationCode ? ndmClient : userClient;
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmSwitch/importReturnMsg`;
const formData = new FormData();
formData.append('file', file);
const resp = await client.post<ImportMsg>(endpoint, formData, { signal, upload: true });
const data = unwrapResponse(resp);
return data;
};
@@ -66,6 +90,5 @@ export const probeSwitchApi = async (ids: string[], options?: { stationCode?: St
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmSwitch/probeByIds`;
const resp = await client.post<void>(endpoint, ids, { signal });
const [err] = resp;
if (err) throw err;
unwrapResponse(resp);
};

View File

@@ -1,4 +1,5 @@
import { ndmClient, userClient, type NdmNvrPageQuery, type NdmNvrResultVO, type NdmNvrSaveVO, type NdmNvrUpdateVO, type PageParams, type PageResult, type Station } from '@/apis';
import { ndmClient, userClient, type ImportMsg, type NdmNvrPageQuery, type NdmNvrResultVO, type NdmNvrSaveVO, type NdmNvrUpdateVO, type PageParams, type PageResult, type Station } from '@/apis';
import { unwrapResponse } from '@/utils';
export const pageNvrPageApi = async (pageQuery: PageParams<NdmNvrPageQuery>, options?: { stationCode: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
@@ -6,9 +7,7 @@ export const pageNvrPageApi = async (pageQuery: PageParams<NdmNvrPageQuery>, opt
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmNvr/page`;
const resp = await client.post<PageResult<NdmNvrResultVO>>(endpoint, pageQuery, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -18,9 +17,7 @@ export const detailNvrApi = async (id: string, options?: { stationCode?: Station
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmNvr/detail`;
const resp = await client.get<NdmNvrResultVO>(endpoint, { params: { id }, signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -30,9 +27,7 @@ export const saveNvrApi = async (saveVO: NdmNvrSaveVO, options?: { stationCode?:
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmNvr`;
const resp = await client.post<NdmNvrResultVO>(endpoint, saveVO, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -42,9 +37,7 @@ export const updateNvrApi = async (updateVO: NdmNvrUpdateVO, options?: { station
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmNvr`;
const resp = await client.put<NdmNvrResultVO>(endpoint, updateVO, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -54,9 +47,29 @@ export const deleteNvrApi = async (ids: string[], options?: { stationCode?: Stat
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmNvr`;
const resp = await client.delete<boolean>(endpoint, ids, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
export const exportNvrApi = async (pageQuery: PageParams<NdmNvrPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
const client = stationCode ? ndmClient : userClient;
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmNvr/defaultExportByTemplate`;
const resp = await client.post<Blob>(endpoint, pageQuery, { responseType: 'blob', retRaw: true, signal });
const data = unwrapResponse(resp);
return data;
};
export const importNvrApi = async (file: File, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
const client = stationCode ? ndmClient : userClient;
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmNvr/importReturnMsg`;
const formData = new FormData();
formData.append('file', file);
const resp = await client.post<ImportMsg>(endpoint, formData, { signal, upload: true });
const data = unwrapResponse(resp);
return data;
};
@@ -66,8 +79,7 @@ export const probeNvrApi = async (ids: string[], options?: { stationCode?: Stati
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmNvr/probeByIds`;
const resp = await client.post<void>(endpoint, ids, { signal });
const [err] = resp;
if (err) throw err;
unwrapResponse(resp);
};
export const syncNvrChannelsApi = async (options?: { stationCode?: string; signal?: AbortSignal }) => {
@@ -76,6 +88,5 @@ export const syncNvrChannelsApi = async (options?: { stationCode?: string; signa
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmNvr/syncNvrChannels`;
const resp = await client.get<void>(endpoint, { signal });
const [err] = resp;
if (err) throw err;
unwrapResponse(resp);
};

View File

@@ -1,4 +1,5 @@
import { ndmClient, userClient, type NdmCameraIgnorePageQuery, type NdmCameraIgnoreResultVO, type NdmCameraIgnoreSaveVO, type NdmCameraIgnoreUpdateVO, type PageParams, type PageResult } from '@/apis';
import { unwrapResponse } from '@/utils';
export const pageCameraIgnoreApi = async (pageQuery: PageParams<NdmCameraIgnorePageQuery>, options?: { stationCode?: string; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
@@ -6,9 +7,7 @@ export const pageCameraIgnoreApi = async (pageQuery: PageParams<NdmCameraIgnoreP
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmCameraIgnore/page`;
const resp = await client.post<PageResult<NdmCameraIgnoreResultVO>>(endpoint, pageQuery, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -18,9 +17,7 @@ export const detailCameraIgnoreApi = async (id: string, options?: { stationCode?
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmCameraIgnore/detail`;
const resp = await client.get<NdmCameraIgnoreResultVO>(endpoint, { params: { id }, signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -30,9 +27,7 @@ export const saveCameraIgnoreApi = async (saveVO: NdmCameraIgnoreSaveVO, options
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmCameraIgnore`;
const resp = await client.post<NdmCameraIgnoreResultVO>(endpoint, saveVO, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -42,9 +37,7 @@ export const updateCameraIgnoreApi = async (updateVO: NdmCameraIgnoreUpdateVO, o
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmCameraIgnore`;
const resp = await client.put<NdmCameraIgnoreResultVO>(endpoint, updateVO, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -54,8 +47,6 @@ export const deleteCameraIgnoreApi = async (ids: string[], options?: { stationCo
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmCameraIgnore`;
const resp = await client.delete<boolean>(endpoint, ids, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};

View File

@@ -1,6 +1,7 @@
import {
ndmClient,
userClient,
type ImportMsg,
type NdmCameraPageQuery,
type NdmCameraResultVO,
type NdmCameraSaveVO,
@@ -10,6 +11,7 @@ import {
type SnapResult,
type Station,
} from '@/apis';
import { unwrapResponse } from '@/utils';
export const pageCameraApi = async (pageQuery: PageParams<NdmCameraPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
@@ -17,9 +19,7 @@ export const pageCameraApi = async (pageQuery: PageParams<NdmCameraPageQuery>, o
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmCamera/page`;
const resp = await client.post<PageResult<NdmCameraResultVO>>(endpoint, pageQuery, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -29,9 +29,7 @@ export const detailCameraApi = async (id: string, options?: { stationCode?: Stat
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmCamera/detail`;
const resp = await client.get<NdmCameraResultVO>(endpoint, { params: { id }, signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -41,9 +39,7 @@ export const saveCameraApi = async (saveVO: NdmCameraSaveVO, options?: { station
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmCamera`;
const resp = await client.post<NdmCameraResultVO>(endpoint, saveVO, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -53,9 +49,7 @@ export const updateCameraApi = async (updateVO: NdmCameraUpdateVO, options?: { s
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmCamera`;
const resp = await client.put<NdmCameraResultVO>(endpoint, updateVO, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -65,9 +59,29 @@ export const deleteCameraApi = async (ids: string[], options?: { stationCode?: S
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmCamera`;
const resp = await client.delete<boolean>(endpoint, ids, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
export const exportCameraApi = async (pageQuery: PageParams<NdmCameraPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
const client = stationCode ? ndmClient : userClient;
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmCamera/defaultExportByTemplate`;
const resp = await client.post<Blob>(endpoint, pageQuery, { responseType: 'blob', retRaw: true, signal });
const data = unwrapResponse(resp);
return data;
};
export const importCameraApi = async (file: File, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
const client = stationCode ? ndmClient : userClient;
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmCamera/importReturnMsg`;
const formData = new FormData();
formData.append('file', file);
const resp = await client.post<ImportMsg>(endpoint, formData, { signal, upload: true });
const data = unwrapResponse(resp);
return data;
};
@@ -75,9 +89,7 @@ export const getCameraSnapApi = async (deviceId: string, options?: { signal?: Ab
const { signal } = options ?? {};
const endpoint = `/api/ndm/ndmCamera/getSnapByDeviceId`;
const resp = await ndmClient.get<SnapResult>(endpoint, { params: { deviceId }, signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -87,8 +99,6 @@ export const syncCameraApi = async (options?: { stationCode?: string; signal?: A
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmCamera/syncCamera`;
const resp = await client.get<boolean>(endpoint, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};

View File

@@ -1,4 +1,16 @@
import { ndmClient, userClient, type NdmDecoderPageQuery, type NdmDecoderResultVO, type NdmDecoderSaveVO, type NdmDecoderUpdateVO, type PageParams, type PageResult, type Station } from '@/apis';
import {
ndmClient,
userClient,
type ImportMsg,
type NdmDecoderPageQuery,
type NdmDecoderResultVO,
type NdmDecoderSaveVO,
type NdmDecoderUpdateVO,
type PageParams,
type PageResult,
type Station,
} from '@/apis';
import { unwrapResponse } from '@/utils';
export const pageDecoderApi = async (pageQuery: PageParams<NdmDecoderPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
@@ -6,9 +18,7 @@ export const pageDecoderApi = async (pageQuery: PageParams<NdmDecoderPageQuery>,
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmDecoder/page`;
const resp = await client.post<PageResult<NdmDecoderResultVO>>(endpoint, pageQuery, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -18,9 +28,7 @@ export const detailDecoderApi = async (id: string, options?: { stationCode?: Sta
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmDecoder/detail`;
const resp = await client.get<NdmDecoderResultVO>(endpoint, { params: { id }, signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -30,9 +38,7 @@ export const saveDecoderApi = async (saveVO: NdmDecoderSaveVO, options?: { stati
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmDecoder`;
const resp = await client.post<NdmDecoderResultVO>(endpoint, saveVO, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -42,9 +48,7 @@ export const updateDecoderApi = async (updateVO: NdmDecoderUpdateVO, options?: {
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmDecoder`;
const resp = await client.put<NdmDecoderResultVO>(endpoint, updateVO, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -54,9 +58,29 @@ export const deleteDecoderApi = async (ids: string[], options?: { stationCode?:
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmDecoder`;
const resp = await client.delete<boolean>(endpoint, ids, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
export const exportDecoderApi = async (pageQuery: PageParams<NdmDecoderPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
const client = stationCode ? ndmClient : userClient;
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmDecoder/defaultExportByTemplate`;
const resp = await client.post<Blob>(endpoint, pageQuery, { responseType: 'blob', retRaw: true, signal });
const data = unwrapResponse(resp);
return data;
};
export const importDecoderApi = async (file: File, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
const client = stationCode ? ndmClient : userClient;
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmDecoder/importReturnMsg`;
const formData = new FormData();
formData.append('file', file);
const resp = await client.post<ImportMsg>(endpoint, formData, { signal, upload: true });
const data = unwrapResponse(resp);
return data;
};
@@ -66,6 +90,5 @@ export const probeDecoderApi = async (ids: string[], options?: { stationCode?: S
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmDecoder/probeByIds`;
const resp = await client.post<void>(endpoint, ids, { signal });
const [err] = resp;
if (err) throw err;
unwrapResponse(resp);
};

View File

@@ -1,4 +1,16 @@
import { ndmClient, userClient, type NdmKeyboardPageQuery, type NdmKeyboardResultVO, type NdmKeyboardSaveVO, type NdmKeyboardUpdateVO, type PageParams, type PageResult, type Station } from '@/apis';
import {
ndmClient,
userClient,
type ImportMsg,
type NdmKeyboardPageQuery,
type NdmKeyboardResultVO,
type NdmKeyboardSaveVO,
type NdmKeyboardUpdateVO,
type PageParams,
type PageResult,
type Station,
} from '@/apis';
import { unwrapResponse } from '@/utils';
export const pageKeyboardApi = async (pageQuery: PageParams<NdmKeyboardPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
@@ -6,9 +18,7 @@ export const pageKeyboardApi = async (pageQuery: PageParams<NdmKeyboardPageQuery
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmKeyboard/page`;
const resp = await client.post<PageResult<NdmKeyboardResultVO>>(endpoint, pageQuery, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -18,9 +28,7 @@ export const detailKeyboardApi = async (id: string, options?: { stationCode?: St
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmKeyboard/detail`;
const resp = await client.get<NdmKeyboardResultVO>(endpoint, { params: { id }, signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -30,9 +38,7 @@ export const saveKeyboardApi = async (saveVO: NdmKeyboardSaveVO, options?: { sta
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmKeyboard`;
const resp = await client.post<NdmKeyboardResultVO>(endpoint, saveVO, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -42,9 +48,7 @@ export const updateKeyboardApi = async (updateVO: NdmKeyboardUpdateVO, options?:
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmKeyboard`;
const resp = await client.put<NdmKeyboardResultVO>(endpoint, updateVO, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -54,8 +58,28 @@ export const deleteKeyboardApi = async (ids: string[], options?: { stationCode?:
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmKeyboard`;
const resp = await client.delete<boolean>(endpoint, ids, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
export const exportKeyboardApi = async (pageQuery: PageParams<NdmKeyboardPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
const client = stationCode ? ndmClient : userClient;
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmKeyboard/defaultExportByTemplate`;
const resp = await client.post<Blob>(endpoint, pageQuery, { responseType: 'blob', retRaw: true, signal });
const data = unwrapResponse(resp);
return data;
};
export const importKeyboardApi = async (file: File, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
const client = stationCode ? ndmClient : userClient;
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmKeyboard/importReturnMsg`;
const formData = new FormData();
formData.append('file', file);
const resp = await client.post<ImportMsg>(endpoint, formData, { signal, upload: true });
const data = unwrapResponse(resp);
return data;
};

View File

@@ -1,6 +1,7 @@
import {
ndmClient,
userClient,
type ImportMsg,
type NdmMediaServerPageQuery,
type NdmMediaServerResultVO,
type NdmMediaServerSaveVO,
@@ -9,6 +10,7 @@ import {
type PageResult,
type Station,
} from '@/apis';
import { unwrapResponse } from '@/utils';
export const postNdmMediaServerPage = async (pageQuery: PageParams<NdmMediaServerPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
@@ -16,9 +18,7 @@ export const postNdmMediaServerPage = async (pageQuery: PageParams<NdmMediaServe
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmMediaServer/page`;
const resp = await client.post<PageResult<NdmMediaServerResultVO>>(endpoint, pageQuery, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -28,9 +28,7 @@ export const detailMediaServerApi = async (id: string, options?: { stationCode?:
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmMediaServer/detail`;
const resp = await client.get<NdmMediaServerResultVO>(endpoint, { params: { id }, signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -40,9 +38,7 @@ export const saveMediaServerApi = async (saveVO: NdmMediaServerSaveVO, options?:
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmMediaServer`;
const resp = await client.post<NdmMediaServerResultVO>(endpoint, saveVO, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -52,9 +48,7 @@ export const updateMediaServerApi = async (updateVO: NdmMediaServerUpdateVO, opt
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmMediaServer`;
const resp = await client.put<NdmMediaServerResultVO>(endpoint, updateVO, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -64,9 +58,29 @@ export const deleteMediaServerApi = async (ids: string[], options?: { stationCod
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmMediaServer`;
const resp = await client.delete<boolean>(endpoint, ids, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
export const exportMediaServerApi = async (pageQuery: PageParams<NdmMediaServerPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
const client = stationCode ? ndmClient : userClient;
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmMediaServer/defaultExportByTemplate`;
const resp = await client.post<Blob>(endpoint, pageQuery, { responseType: 'blob', retRaw: true, signal });
const data = unwrapResponse(resp);
return data;
};
export const importMediaServerApi = async (file: File, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
const client = stationCode ? ndmClient : userClient;
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmMediaServer/importReturnMsg`;
const formData = new FormData();
formData.append('file', file);
const resp = await client.post<ImportMsg>(endpoint, formData, { signal, upload: true });
const data = unwrapResponse(resp);
return data;
};
@@ -76,6 +90,5 @@ export const probeMediaServerApi = async (ids: string[], options?: { stationCode
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmMediaServer/probeByIds`;
const resp = await client.post<void>(endpoint, ids, { signal });
const [err] = resp;
if (err) throw err;
unwrapResponse(resp);
};

View File

@@ -1,6 +1,7 @@
import {
ndmClient,
userClient,
type ImportMsg,
type NdmVideoServerPageQuery,
type NdmVideoServerResultVO,
type NdmVideoServerSaveVO,
@@ -9,6 +10,7 @@ import {
type PageResult,
type Station,
} from '@/apis';
import { unwrapResponse } from '@/utils';
export const pageVideoServerApi = async (pageQuery: PageParams<NdmVideoServerPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
@@ -16,9 +18,7 @@ export const pageVideoServerApi = async (pageQuery: PageParams<NdmVideoServerPag
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmVideoServer/page`;
const resp = await client.post<PageResult<NdmVideoServerResultVO>>(endpoint, pageQuery, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -28,9 +28,7 @@ export const detailVideoServerApi = async (id: string, options?: { stationCode?:
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmVideoServer/detail`;
const resp = await client.get<NdmVideoServerResultVO>(endpoint, { params: { id }, signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -40,9 +38,7 @@ export const saveVideoServerApi = async (saveVO: NdmVideoServerSaveVO, options?:
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmVideoServer`;
const resp = await client.post<NdmVideoServerResultVO>(endpoint, saveVO, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -52,9 +48,7 @@ export const updateVideoServerApi = async (id: string, updateVO: NdmVideoServerU
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmVideoServer`;
const resp = await client.put<NdmVideoServerResultVO>(endpoint, updateVO, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -64,9 +58,29 @@ export const deleteVideoServerApi = async (ids: string[], options?: { stationCod
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmVideoServer`;
const resp = await client.delete<boolean>(endpoint, ids, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
export const exportVideoServerApi = async (pageQuery: PageParams<NdmVideoServerPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
const client = stationCode ? ndmClient : userClient;
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmVideoServer/defaultExportByTemplate`;
const resp = await client.post<Blob>(endpoint, pageQuery, { responseType: 'blob', retRaw: true, signal });
const data = unwrapResponse(resp);
return data;
};
export const importVideoServerApi = async (file: File, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
const client = stationCode ? ndmClient : userClient;
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmVideoServer/importReturnMsg`;
const formData = new FormData();
formData.append('file', file);
const resp = await client.post<ImportMsg>(endpoint, formData, { signal, upload: true });
const data = unwrapResponse(resp);
return data;
};
@@ -76,6 +90,5 @@ export const probeVideoServerApi = async (ids: string[], options?: { stationCode
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/ndm/ndmVideoServer/probeByIds`;
const resp = await client.post<void>(endpoint, ids, { signal });
const [err] = resp;
if (err) throw err;
unwrapResponse(resp);
};

View File

@@ -1,5 +1,6 @@
import { ndmClient, userClient, type DefParameterPageQuery, type DefParameterResultVO, type DefParameterUpdateVO, type PageParams, type PageResult, type Station } from '@/apis';
import type { Result } from '@/types';
import { unwrapResponse } from '@/utils';
export const pageDefParameterApi = async (pageQuery: PageParams<DefParameterPageQuery>, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
const { stationCode, signal } = options ?? {};
@@ -7,9 +8,7 @@ export const pageDefParameterApi = async (pageQuery: PageParams<DefParameterPage
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/system/defParameter/page`;
const resp = await client.post<PageResult<DefParameterResultVO>>(endpoint, pageQuery, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};
@@ -19,8 +18,6 @@ export const updateDefParameterApi = async (updateVO: DefParameterUpdateVO, opti
const prefix = stationCode ? `/${stationCode}` : '';
const endpoint = `${prefix}/api/system/defParameter`;
const resp = await client.put<Result<DefParameterResultVO>>(endpoint, updateVO, { signal });
const [err, data] = resp;
if (err) throw err;
if (!data) throw new Error(`${data}`);
const data = unwrapResponse(resp);
return data;
};

View File

@@ -8,12 +8,16 @@ const props = defineProps<{
memUsage?: string;
diskUsage?: string;
runningTime?: string;
cpuUsageLabel?: string;
memUsageLabel?: string;
diskUsageLabel?: string;
runningTimeLabel?: string;
}>();
const { cpuUsage, memUsage, diskUsage, runningTime } = toRefs(props);
const { cpuUsage, memUsage, diskUsage, runningTime, cpuUsageLabel, memUsageLabel, diskUsageLabel, runningTimeLabel } = toRefs(props);
const showCard = computed(() => {
return Object.values(props).some((value) => !!value);
return Object.values({ cpuUsage, memUsage, diskUsage, runningTime }).some((value) => !!value);
});
const cpuPercent = computed(() => {
@@ -51,22 +55,22 @@ const getProgressStatus = (percent: number): ProgressStatus => {
<NFlex vertical>
<NFlex v-if="cpuUsage" style="width: 100%" align="center" :wrap="false">
<NIcon :component="FireOutlined" />
<span style="word-break: keep-all">CPU</span>
<span style="word-break: keep-all">{{ cpuUsageLabel || 'CPU' }}</span>
<NProgress :percentage="cpuPercent" :status="getProgressStatus(cpuPercent)">{{ cpuPercent }}%</NProgress>
</NFlex>
<NFlex v-if="memUsage" style="width: 100%" align="center" :wrap="false">
<NIcon :component="CodeOutlined" />
<span style="word-break: keep-all">内存</span>
<span style="word-break: keep-all">{{ memUsageLabel || '内存' }}</span>
<NProgress :percentage="memPercent" :status="getProgressStatus(memPercent)">{{ memPercent }}%</NProgress>
</NFlex>
<NFlex v-if="diskUsage" style="width: 100%" align="center" :wrap="false">
<NIcon :component="SaveOutlined" />
<span style="word-break: keep-all">磁盘</span>
<span style="word-break: keep-all">{{ diskUsageLabel || '磁盘' }}</span>
<NProgress :percentage="diskPercent" :status="getProgressStatus(diskPercent)">{{ diskPercent }}%</NProgress>
</NFlex>
<NFlex v-if="runningTime" style="width: 100%" align="center" :wrap="false">
<NIcon :component="ClockCircleOutlined" />
<span>系统运行时间</span>
<span>{{ runningTimeLabel || '运行时间' }}</span>
<span>{{ formattedRunningTime }}</span>
</NFlex>
</NFlex>

View File

@@ -21,10 +21,10 @@ const { debugModeEnabled } = storeToRefs(settingStore);
const { ndmDevice, station } = toRefs(props);
const showPageHeader = computed(() => {
return !!route.query['from'];
return !!route.query['fromPage'];
});
const onBack = () => {
router.push({ path: `${route.query['from']}` });
router.push({ path: `${route.query['fromPage']}` });
};
const activeTabName = ref('当前诊断');

View File

@@ -55,6 +55,9 @@ const { mutateAsync: validateDeviceIdDuplicated } = useMutation({
const deviceIdPattern = /^\d{4}01\d{4}$/;
if (!deviceIdPattern.test(deviceId)) throw new Error('设备ID不符合规范');
// 如果没有修改设备ID则不做ID唯一性校验
if (deviceId === ndmDevice.value.deviceId) return;
validatorAbortController.value.abort();
validatorAbortController.value = new AbortController();
@@ -69,8 +72,7 @@ const { mutateAsync: validateDeviceIdDuplicated } = useMutation({
const { mutate: updateDevice, isPending: loading } = useMutation({
mutationFn: async () => {
await formInst.value?.validate().catch(() => {
window.$message.error('表单验失败');
return;
throw new Error('表单验失败');
});
abortController.value.abort();
@@ -85,6 +87,7 @@ const { mutate: updateDevice, isPending: loading } = useMutation({
onSuccess: (newDevice) => {
localDevice.value = { ...newDevice };
deviceStore.patchDevice(station.value.code, { ...newDevice });
window.$message.success('更新成功');
},
onError: (error) => {
if (isCancel(error)) return;

View File

@@ -21,10 +21,10 @@ const { debugModeEnabled } = storeToRefs(settingStore);
const { ndmDevice, station } = toRefs(props);
const showPageHeader = computed(() => {
return !!route.query['from'];
return !!route.query['fromPage'];
});
const onBack = () => {
router.push({ path: `${route.query['from']}` });
router.push({ path: `${route.query['fromPage']}` });
};
const activeTabName = ref('当前诊断');

View File

@@ -54,6 +54,9 @@ const { mutateAsync: validateDeviceIdDuplicated } = useMutation({
const deviceIdPattern = /^\d{4}06\d{4}$/;
if (!deviceIdPattern.test(deviceId)) throw new Error('设备ID不符合规范');
// 如果没有修改设备ID则不做ID唯一性校验
if (deviceId === ndmDevice.value.deviceId) return;
validatorAbortController.value.abort();
validatorAbortController.value = new AbortController();
@@ -68,8 +71,7 @@ const { mutateAsync: validateDeviceIdDuplicated } = useMutation({
const { mutate: updateDevice, isPending: loading } = useMutation({
mutationFn: async () => {
await formInst.value?.validate().catch(() => {
window.$message.error('表单验失败');
return;
throw new Error('表单验失败');
});
abortController.value.abort();
@@ -84,6 +86,7 @@ const { mutate: updateDevice, isPending: loading } = useMutation({
onSuccess: (newDevice) => {
localDevice.value = { ...newDevice };
deviceStore.patchDevice(station.value.code, { ...newDevice });
window.$message.success('更新成功');
},
onError: (error) => {
if (isCancel(error)) return;

View File

@@ -21,10 +21,10 @@ const { debugModeEnabled } = storeToRefs(settingStore);
const { ndmDevice, station } = toRefs(props);
const showPageHeader = computed(() => {
return !!route.query['from'];
return !!route.query['fromPage'];
});
const onBack = () => {
router.push({ path: `${route.query['from']}` });
router.push({ path: `${route.query['fromPage']}` });
};
const activeTabName = ref('当前诊断');

View File

@@ -54,6 +54,9 @@ const { mutateAsync: validateDeviceIdDuplicated } = useMutation({
const deviceIdPattern = /^\d{4}07\d{4}$/;
if (!deviceIdPattern.test(deviceId)) throw new Error('设备ID不符合规范');
// 如果没有修改设备ID则不做ID唯一性校验
if (deviceId === ndmDevice.value.deviceId) return;
validatorAbortController.value.abort();
validatorAbortController.value = new AbortController();
@@ -68,8 +71,7 @@ const { mutateAsync: validateDeviceIdDuplicated } = useMutation({
const { mutate: updateDevice, isPending: loading } = useMutation({
mutationFn: async () => {
await formInst.value?.validate().catch(() => {
window.$message.error('表单验失败');
return;
throw new Error('表单验失败');
});
abortController.value.abort();
@@ -84,6 +86,7 @@ const { mutate: updateDevice, isPending: loading } = useMutation({
onSuccess: (newDevice) => {
localDevice.value = { ...newDevice };
deviceStore.patchDevice(station.value.code, { ...newDevice });
window.$message.success('更新成功');
},
onError: (error) => {
if (isCancel(error)) return;

View File

@@ -21,10 +21,10 @@ const { debugModeEnabled } = storeToRefs(settingStore);
const { ndmDevice, station } = toRefs(props);
const showPageHeader = computed(() => {
return !!route.query['from'];
return !!route.query['fromPage'];
});
const onBack = () => {
router.push({ path: `${route.query['from']}` });
router.push({ path: `${route.query['fromPage']}` });
};
const activeTabName = ref('当前诊断');

View File

@@ -54,6 +54,9 @@ const { mutateAsync: validateDeviceIdDuplicated } = useMutation({
const deviceIdPattern = /^\d{4}08\d{4}$/;
if (!deviceIdPattern.test(deviceId)) throw new Error('设备ID不符合规范');
// 如果没有修改设备ID则不做ID唯一性校验
if (deviceId === ndmDevice.value.deviceId) return;
validatorAbortController.value.abort();
validatorAbortController.value = new AbortController();
@@ -68,8 +71,7 @@ const { mutateAsync: validateDeviceIdDuplicated } = useMutation({
const { mutate: updateDevice, isPending: loading } = useMutation({
mutationFn: async () => {
await formInst.value?.validate().catch(() => {
window.$message.error('表单验失败');
return;
throw new Error('表单验失败');
});
abortController.value.abort();
@@ -84,6 +86,7 @@ const { mutate: updateDevice, isPending: loading } = useMutation({
onSuccess: (newDevice) => {
localDevice.value = { ...newDevice };
deviceStore.patchDevice(station.value.code, { ...newDevice });
window.$message.success('更新成功');
},
onError: (error) => {
if (isCancel(error)) return;

View File

@@ -21,10 +21,10 @@ const { debugModeEnabled } = storeToRefs(settingStore);
const { ndmDevice, station } = toRefs(props);
const showPageHeader = computed(() => {
return !!route.query['from'];
return !!route.query['fromPage'];
});
const onBack = () => {
router.push({ path: `${route.query['from']}` });
router.push({ path: `${route.query['fromPage']}` });
};
const activeTabName = ref('当前诊断');

View File

@@ -54,6 +54,9 @@ const { mutateAsync: validateDeviceIdDuplicated } = useMutation({
const deviceIdPattern = /^\d{4}05\d{4}$/;
if (!deviceIdPattern.test(deviceId)) throw new Error('设备ID不符合规范');
// 如果没有修改设备ID则不做ID唯一性校验
if (deviceId === ndmDevice.value.deviceId) return;
validatorAbortController.value.abort();
validatorAbortController.value = new AbortController();
@@ -68,8 +71,7 @@ const { mutateAsync: validateDeviceIdDuplicated } = useMutation({
const { mutate: updateDevice, isPending } = useMutation({
mutationFn: async () => {
await formInst.value?.validate().catch(() => {
window.$message.error('表单验失败');
return;
throw new Error('表单验失败');
});
abortController.value.abort();
@@ -84,6 +86,7 @@ const { mutate: updateDevice, isPending } = useMutation({
onSuccess: (newDevice) => {
localDevice.value = { ...newDevice };
deviceStore.patchDevice(station.value.code, { ...newDevice });
window.$message.success('更新成功');
},
onError: (error) => {
if (isCancel(error)) return;

View File

@@ -21,10 +21,10 @@ const { debugModeEnabled } = storeToRefs(settingStore);
const { ndmDevice, station } = toRefs(props);
const showPageHeader = computed(() => {
return !!route.query['from'];
return !!route.query['fromPage'];
});
const onBack = () => {
router.push({ path: `${route.query['from']}` });
router.push({ path: `${route.query['fromPage']}` });
};
const activeTabName = ref('当前诊断');

View File

@@ -54,6 +54,9 @@ const { mutateAsync: validateDeviceIdDuplicated } = useMutation({
const deviceIdPattern = /^\d{4}03\d{4}$/;
if (!deviceIdPattern.test(deviceId)) throw new Error('设备ID不符合规范');
// 如果没有修改设备ID则不做ID唯一性校验
if (deviceId === ndmDevice.value.deviceId) return;
validatorAbortController.value.abort();
validatorAbortController.value = new AbortController();
@@ -68,8 +71,7 @@ const { mutateAsync: validateDeviceIdDuplicated } = useMutation({
const { mutate: updateDevice, isPending } = useMutation({
mutationFn: async () => {
await formInst.value?.validate().catch(() => {
window.$message.error('表单验失败');
return;
throw new Error('表单验失败');
});
abortController.value.abort();
@@ -84,6 +86,7 @@ const { mutate: updateDevice, isPending } = useMutation({
onSuccess: (newDevice) => {
localDevice.value = { ...newDevice };
deviceStore.patchDevice(station.value.code, { ...newDevice });
window.$message.success('更新成功');
},
onError: (error) => {
if (isCancel(error)) return;

View File

@@ -21,10 +21,10 @@ const { debugModeEnabled } = storeToRefs(settingStore);
const { ndmDevice, station } = toRefs(props);
const showPageHeader = computed(() => {
return !!route.query['from'];
return !!route.query['fromPage'];
});
const onBack = () => {
router.push({ path: `${route.query['from']}` });
router.push({ path: `${route.query['fromPage']}` });
};
const activeTabName = ref('当前诊断');

View File

@@ -28,7 +28,7 @@ const runningTime = computed(() => lastDiagInfo.value?.commInfo?.系统运行时
<template>
<NFlex vertical>
<DeviceHeaderCard :ndm-device="ndmDevice" :station="station" />
<DeviceHardwareCard :cpu-usage="cpuUsage" :mem-usage="memUsage" :disk-usage="diskUsage" :running-time="runningTime" />
<DeviceHardwareCard running-time-label="服务器运行时间" :cpu-usage="cpuUsage" :mem-usage="memUsage" :disk-usage="diskUsage" :running-time="runningTime" />
<ServerAlive :ndm-device="ndmDevice" :station="station" />
</NFlex>
</template>

View File

@@ -66,6 +66,9 @@ const { mutateAsync: validateDeviceIdDuplicated } = useMutation({
const deviceIdPattern = /^\d{4}(09|11)\d{4}$/;
if (!deviceIdPattern.test(deviceId)) throw new Error('设备ID不符合规范');
// 如果没有修改设备ID则不做ID唯一性校验
if (deviceId === ndmDevice.value.deviceId) return;
validatorAbortController.value.abort();
validatorAbortController.value = new AbortController();
@@ -80,8 +83,7 @@ const { mutateAsync: validateDeviceIdDuplicated } = useMutation({
const { mutate: updateDevice, isPending } = useMutation({
mutationFn: async () => {
await formInst.value?.validate().catch(() => {
window.$message.error('表单验失败');
return;
throw new Error('表单验失败');
});
abortController.value.abort();
@@ -103,6 +105,7 @@ const { mutate: updateDevice, isPending } = useMutation({
onSuccess: (newDevice) => {
localDevice.value = { ...newDevice };
deviceStore.patchDevice(station.value.code, { ...newDevice });
window.$message.success('更新成功');
},
onError: (error) => {
if (isCancel(error)) return;

View File

@@ -21,10 +21,10 @@ const { debugModeEnabled } = storeToRefs(settingStore);
const { ndmDevice, station } = toRefs(props);
const showPageHeader = computed(() => {
return !!route.query['from'];
return !!route.query['fromPage'];
});
const onBack = () => {
router.push({ path: `${route.query['from']}` });
router.push({ path: `${route.query['fromPage']}` });
};
const activeTabName = ref('当前诊断');

View File

@@ -54,6 +54,9 @@ const { mutateAsync: validateDeviceIdDuplicated } = useMutation({
const deviceIdPattern = /^\d{4}04\d{4}$/;
if (!deviceIdPattern.test(deviceId)) throw new Error('设备ID不符合规范');
// 如果没有修改设备ID则不做ID唯一性校验
if (deviceId === ndmDevice.value.deviceId) return;
validatorAbortController.value.abort();
validatorAbortController.value = new AbortController();
@@ -68,8 +71,7 @@ const { mutateAsync: validateDeviceIdDuplicated } = useMutation({
const { mutate: updateDevice, isPending: loading } = useMutation({
mutationFn: async () => {
await formInst.value?.validate().catch(() => {
window.$message.error('表单验失败');
return;
throw new Error('表单验失败');
});
abortController.value.abort();
@@ -84,6 +86,7 @@ const { mutate: updateDevice, isPending: loading } = useMutation({
onSuccess: (newDevice) => {
localDevice.value = { ...newDevice };
deviceStore.patchDevice(station.value.code, { ...newDevice });
window.$message.success('更新成功');
},
onError: (error) => {
if (isCancel(error)) return;

View File

@@ -4,11 +4,12 @@ import { useDeviceTree } from '@/composables';
import { DEVICE_TYPE_NAMES, DEVICE_TYPE_LITERALS, tryGetDeviceType, type DeviceType } from '@/enums';
import { isNvrCluster } from '@/helpers';
import { useDeviceStore, useStationStore } from '@/stores';
import { sleep } from '@/utils';
import { watchDebounced, watchImmediate } from '@vueuse/core';
import { watchImmediate } from '@vueuse/core';
import destr from 'destr';
import { isFunction } from 'es-toolkit';
import {
NButton,
NDropdown,
NFlex,
NInput,
NRadio,
@@ -18,6 +19,7 @@ import {
NTag,
NTree,
useThemeVars,
type DropdownOption,
type TagProps,
type TreeInst,
type TreeOption,
@@ -25,7 +27,7 @@ import {
type TreeProps,
} from 'naive-ui';
import { storeToRefs } from 'pinia';
import { computed, h, onMounted, ref, toRefs, useTemplateRef, watch, type CSSProperties } from 'vue';
import { computed, h, nextTick, onBeforeUnmount, ref, toRefs, useTemplateRef, watch, type CSSProperties } from 'vue';
const props = defineProps<{
station?: Station; // 支持渲染指定车站的设备树
@@ -39,7 +41,20 @@ const { station } = toRefs(props);
const themeVars = useThemeVars();
const { selectedStationCode, selectedDeviceType, selectedDevice, initFromRoute, selectDevice, routeDevice } = useDeviceTree();
const {
// 设备选择
selectedStationCode,
selectedDeviceType,
selectedDevice,
hasFromPage,
selectDevice,
routeDevice,
// 设备管理
exportDevice,
exportDeviceTemplate,
importDevice,
deleteDevice,
} = useDeviceTree();
const onSelectDevice = (device: NdmDeviceResultVO, stationCode: Station['code']) => {
selectDevice(device, stationCode);
@@ -56,23 +71,6 @@ const { stations } = storeToRefs(stationStore);
const deviceStore = useDeviceStore();
const { lineDevices } = storeToRefs(deviceStore);
onMounted(() => {
initFromRoute(lineDevices.value);
});
// lineDevices是shallowRef因此需要深度侦听才能获取内部变化
// 而单纯的深度侦听又可能会引发性能问题,因此尝试使用防抖侦听
watchDebounced(
lineDevices,
(newLineDevices) => {
initFromRoute(newLineDevices);
},
{
debounce: 500,
deep: true,
},
);
const deviceTabPanes = Object.values(DEVICE_TYPE_LITERALS).map((deviceType) => ({
name: deviceType,
tab: DEVICE_TYPE_NAMES[deviceType],
@@ -91,6 +89,90 @@ watch([selectedKeys, selectedDevice, selectedStationCode], ([, device, code]) =>
}
});
const abortController = ref(new AbortController());
const contextmenu = ref<{ x: number; y: number; stationCode?: Station['code']; deviceType?: DeviceType | null; device?: NdmDeviceResultVO }>({ x: 0, y: 0, deviceType: null });
const showContextmenu = ref(false);
const contextmenuOptions = computed<DropdownOption[]>(() => [
{
label: '导出设备',
key: 'export-device',
show: !!contextmenu.value.deviceType,
onSelect: () => {
const { stationCode, deviceType } = contextmenu.value;
// console.log(stationCode, deviceType);
showContextmenu.value = false;
if (!stationCode || !deviceType) return;
abortController.value.abort();
abortController.value = new AbortController();
exportDevice({ deviceType, stationCode, signal: abortController.value.signal });
},
},
{
label: '导入设备',
key: 'import-device',
show: !!contextmenu.value.deviceType,
onSelect: () => {
const { stationCode, deviceType } = contextmenu.value;
// console.log(stationCode, deviceType);
showContextmenu.value = false;
if (!stationCode || !deviceType) return;
abortController.value.abort();
abortController.value = new AbortController();
importDevice({ deviceType, stationCode, signal: abortController.value.signal });
},
},
{
label: '下载导入模板',
key: 'export-template',
// 导出模板功能有缺陷,暂时不展示
show: false,
onSelect: () => {
const { stationCode, deviceType } = contextmenu.value;
// console.log(stationCode, deviceType);
showContextmenu.value = false;
if (!stationCode || !deviceType) return;
abortController.value.abort();
abortController.value = new AbortController();
exportDeviceTemplate({ deviceType, stationCode, signal: abortController.value.signal });
},
},
{
label: '删除设备',
key: 'delete-device',
show: !!contextmenu.value.device,
onSelect: () => {
const { stationCode, device } = contextmenu.value;
// console.log(stationCode, device);
showContextmenu.value = false;
if (!stationCode || !device) return;
const id = device.id;
const deviceType = tryGetDeviceType(device.deviceType);
if (!id || !deviceType) return;
window.$dialog.destroyAll();
window.$dialog.warning({
title: '删除设备',
content: `确认删除设备 ${device.name || device.deviceId || device.id} 吗?`,
positiveText: '确认',
negativeText: '取消',
onPositiveClick: () => {
abortController.value.abort();
abortController.value = new AbortController();
deleteDevice({ id, deviceType, stationCode, signal: abortController.value.signal });
},
});
},
},
]);
const onSelectDropdownOption = (key: string, option: DropdownOption) => {
const onSelect = option['onSelect'];
if (isFunction(onSelect)) {
onSelect();
}
};
onBeforeUnmount(() => {
abortController.value.abort();
});
// ========== 设备树节点交互 ==========
const override: TreeOverrideNodeClickBehavior = ({ option }) => {
const hasChildren = (option.children?.length ?? 0) > 0;
@@ -107,15 +189,26 @@ const nodeProps: TreeProps['nodeProps'] = ({ option }) => {
if (option['device']) {
payload.stopPropagation();
const device = option['device'] as NdmDeviceResultVO;
const stationCode = option['stationCode'] as string;
const stationCode = option['stationCode'] as Station['code'];
// 区分是否需要跳转路由
// 当 props.station 存在时,说明当前是单独渲染车站的设备树,需要跳转路由到设备诊断页面
if (!station.value) {
onSelectDevice(device, stationCode);
} else {
onRouteDevice(device, station.value.code);
onRouteDevice(device, stationCode);
}
}
},
onContextmenu: (payload) => {
payload.stopPropagation();
payload.preventDefault();
const { clientX, clientY } = payload;
const stationCode = option['stationCode'] as Station['code'];
const deviceType = option['deviceType'] as DeviceType | undefined;
const device = option['device'] as NdmDeviceResultVO | undefined;
contextmenu.value = { x: clientX, y: clientY, stationCode, deviceType, device };
showContextmenu.value = true;
},
};
};
@@ -137,7 +230,7 @@ const renderIcmpStatistics = (onlineCount: number, offlineCount: number, count:
')',
]);
};
const renderDeviceNodePrefix = (device: NdmDeviceResultVO, stationCode: string) => {
const renderDeviceNodePrefix = (device: NdmDeviceResultVO, stationCode: Station['code']) => {
const renderViewDeviceButton = (device: NdmDeviceResultVO, stationCode: string) => {
return h(
NButton,
@@ -152,10 +245,11 @@ const renderDeviceNodePrefix = (device: NdmDeviceResultVO, stationCode: string)
e.stopPropagation();
// 选择设备
// 区分是否需要跳转路由
// 当 props.station 存在时,说明当前是单独渲染车站的设备树,需要跳转路由到设备诊断页面
if (!station.value) {
onSelectDevice(device, stationCode);
} else {
onRouteDevice(device, station.value.code);
onRouteDevice(device, stationCode);
}
},
},
@@ -170,7 +264,7 @@ const renderDeviceNodePrefix = (device: NdmDeviceResultVO, stationCode: string)
return h(NFlex, { size: 'small' }, { default: () => [renderViewDeviceButton(device, stationCode), renderDeviceStatusTag(device)] });
};
// 全线设备树
const lineDeviceTreeData = computed<Record<string, TreeOption[]>>(() => {
const lineDeviceTreeData = computed<Record<Station['code'], TreeOption[]>>(() => {
const treeData: Record<string, TreeOption[]> = {};
deviceTabPanes.forEach(({ name: paneName /* , tab: paneTab */ }) => {
treeData[paneName] = stations.value.map<TreeOption>((station) => {
@@ -217,6 +311,8 @@ const lineDeviceTreeData = computed<Record<string, TreeOption[]>>(() => {
device: nvrCluster,
};
}),
stationCode,
deviceType: activeTab.value,
};
}
return {
@@ -229,7 +325,7 @@ const lineDeviceTreeData = computed<Record<string, TreeOption[]>>(() => {
const device = dev as NdmDeviceResultVO;
return {
label: `${device.name}`,
key: device.id ?? `${device.name}`,
key: `${device.id}`,
prefix: () => renderDeviceNodePrefix(device, stationCode),
suffix: () => `${device.ipAddress}`,
// 当选择设备时,能获取到设备的所有信息,以及设备所属的车站
@@ -237,6 +333,8 @@ const lineDeviceTreeData = computed<Record<string, TreeOption[]>>(() => {
device,
};
}) ?? [],
stationCode,
deviceType: activeTab.value,
};
});
});
@@ -261,13 +359,13 @@ const stationDeviceTreeData = computed<TreeOption[]>(() => {
children: clusters.map<TreeOption>((device) => {
return {
label: `${device.name}`,
key: device.id ?? `${device.name}`,
key: `${device.id}`,
prefix: () => renderDeviceNodePrefix(device, stationCode),
suffix: () => `${device.ipAddress}`,
children: singletons.map<TreeOption>((device) => {
return {
label: `${device.name}`,
key: device.id ?? `${device.name}`,
key: `${device.id}`,
prefix: () => renderDeviceNodePrefix(device, stationCode),
suffix: () => `${device.ipAddress}`,
stationCode,
@@ -278,6 +376,8 @@ const stationDeviceTreeData = computed<TreeOption[]>(() => {
device,
};
}),
stationCode,
deviceType,
};
}
return {
@@ -287,13 +387,15 @@ const stationDeviceTreeData = computed<TreeOption[]>(() => {
children: stationDevices[deviceType].map<TreeOption>((device) => {
return {
label: `${device.name}`,
key: device.id ?? `${device.name}`,
key: `${device.id}`,
prefix: () => renderDeviceNodePrefix(device, stationCode),
suffix: () => `${device.ipAddress}`,
stationCode,
device,
};
}),
stationCode,
deviceType,
};
});
});
@@ -319,39 +421,57 @@ const searchFilter = (pattern: string, node: TreeOption): boolean => {
};
// ========== 设备树交互 ==========
const expandedKeys = ref<string[]>();
const animated = ref(true);
const expandedKeys = ref<string[]>([]);
const deviceTreeInst = useTemplateRef<TreeInst>('deviceTreeInst');
const onFoldDeviceTree = () => {
expandedKeys.value = [];
};
const onLocateDeviceTree = () => {
const stationCode = selectedStationCode.value;
const device = selectedDevice.value;
if (!stationCode || !device?.id) return;
const deviceTypeVal = tryGetDeviceType(device.deviceType);
if (!!deviceTypeVal) {
activeTab.value = deviceTypeVal;
}
const onLocateDeviceTree = async () => {
if (!selectedStationCode.value) return;
if (!selectedDevice.value) return;
const deviceType = tryGetDeviceType(selectedDevice.value.deviceType);
if (!deviceType) return;
if (!deviceTreeInst.value) return;
const expanded = [stationCode];
if (activeTab.value === DEVICE_TYPE_LITERALS.ndmNvr) {
const nvrs = lineDevices.value[stationCode]?.[DEVICE_TYPE_LITERALS.ndmNvr];
if (nvrs) {
const clusterKeys = nvrs.filter((nvr) => !!nvr.clusterList?.trim() && nvr.clusterList !== nvr.ipAddress).map((nvr) => String(nvr.id));
expanded.push(...clusterKeys);
animated.value = false;
// 定位设备类型
activeTab.value = deviceType;
// 展开选择的车站
expandedKeys.value = [selectedStationCode.value];
// 当选择录像机时,如果不是集群,进一步展开该录像机所在的集群节点
if (deviceType === DEVICE_TYPE_LITERALS.ndmNvr) {
const stationDevices = lineDevices.value[selectedStationCode.value];
if (stationDevices) {
const selectedNvr = selectedDevice.value as NdmNvrResultVO;
if (!isNvrCluster(selectedNvr)) {
const nvrs = stationDevices[DEVICE_TYPE_LITERALS.ndmNvr];
const clusters = nvrs.filter((nvr) => isNvrCluster(nvr) && nvr.clusterList?.includes(selectedNvr.clusterList ?? ''));
expandedKeys.value.push(...clusters.map((nvr) => `${nvr.id}`));
}
}
}
expandedKeys.value = expanded;
// 由于数据量大所以开启虚拟滚动,
// 但是无法知晓NTree内部的虚拟列表容器何时创建完成所以使用setTimeout延迟固定时间后执行滚动
scrollDeviceTreeToSelectedDevice();
// 等待设备树展开完成,滚动到选择的设备
await nextTick();
deviceTreeInst.value.scrollTo({ key: `${selectedDevice.value.id}`, behavior: 'smooth' });
animated.value = true;
};
async function scrollDeviceTreeToSelectedDevice() {
await sleep(350);
const inst = deviceTreeInst.value;
inst?.scrollTo({ key: selectedDevice?.value?.id ?? `${selectedDevice.value?.name}`, behavior: 'smooth' });
}
// 渲染全线设备树时,若是首次经过路由跳转而来选择设备,则定位设备树
const unwatchLocate = watch(selectedDevice, async (newDevice, oldDevice) => {
if (!!station.value) return;
if (!hasFromPage.value) return;
if (!!newDevice && !oldDevice) {
if (!!deviceTreeInst.value) {
await onLocateDeviceTree();
unwatchLocate();
}
}
});
</script>
<template>
@@ -365,15 +485,23 @@ async function scrollDeviceTreeToSelectedDevice() {
<NRadio value="10">在线</NRadio>
<NRadio value="20">离线</NRadio>
</NRadioGroup>
<NButton text size="tiny" type="info" @click="onFoldDeviceTree" style="margin-left: auto">收起</NButton>
<NButton text size="tiny" type="info" @click="onLocateDeviceTree">定位</NButton>
<template v-if="!station">
<NButton text size="tiny" type="info" @click="onFoldDeviceTree" style="margin-left: auto">收起</NButton>
<NButton text size="tiny" type="info" @click="onLocateDeviceTree">定位</NButton>
</template>
</NFlex>
</div>
<!-- 设备树 -->
<div style="overflow: hidden; flex: 1 1 auto; display: flex">
<div
style="overflow: hidden; flex: 1 1 auto; display: flex"
:style="{
// 当右键菜单显示时,禁用设备树的点击事件,避免在打开菜单时仍能点击设备树节点
'pointer-events': showContextmenu ? 'none' : 'auto',
}"
>
<template v-if="!station">
<div style="height: 100%; flex: 0 0 auto">
<NTabs v-model:value="activeTab" animated type="line" placement="left" style="height: 100%">
<NTabs v-model:value="activeTab" type="line" placement="left" style="height: 100%">
<NTab v-for="pane in deviceTabPanes" :key="pane.name" :name="pane.name" :tab="pane.tab"></NTab>
</NTabs>
</div>
@@ -386,6 +514,7 @@ async function scrollDeviceTreeToSelectedDevice() {
show-line
virtual-scroll
:ref="'deviceTreeInst'"
:animated="animated"
:selected-keys="selectedKeys"
:data="lineDeviceTreeData[activeTab]"
:show-irrelevant-nodes="false"
@@ -405,6 +534,7 @@ async function scrollDeviceTreeToSelectedDevice() {
show-line
virtual-scroll
:data="stationDeviceTreeData"
:animated="animated"
:show-irrelevant-nodes="false"
:pattern="searchPattern"
:filter="searchFilter"
@@ -415,6 +545,17 @@ async function scrollDeviceTreeToSelectedDevice() {
</template>
</div>
</div>
<NDropdown
placement="bottom-start"
trigger="manual"
:show="showContextmenu"
:x="contextmenu.x"
:y="contextmenu.y"
:options="contextmenuOptions"
@select="onSelectDropdownOption"
@clickoutside="() => (showContextmenu = false)"
/>
</template>
<style scoped lang="scss"></style>

View File

@@ -98,6 +98,8 @@ const exportFromIndexedDB = async <K extends IndexedDbStoreId>(storeId: K, optio
};
const importToIndexedDB = async <K extends IndexedDbStoreId>(storeId: K, options?: { successMsg?: string; errorMsg?: string }) => {
const { successMsg, errorMsg } = options ?? {};
pollingStore.stopPolling();
offlineDev.value = true;
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = '.json';
@@ -113,18 +115,20 @@ const importToIndexedDB = async <K extends IndexedDbStoreId>(storeId: K, options
reader.onload = async () => {
const data = destr<IndexedDbStoreStates[K]>(reader.result as string);
await localforage.setItem(storeId, data);
window.$message.success(successMsg ?? '导入数据成功');
window.$message.success(`${successMsg ?? '导入数据成功'},即将刷新页面`);
await sleep(2000);
window.location.reload();
};
};
};
const deleteFromIndexedDB = async (storeId: IndexedDbStoreId) => {
pollingStore.stopPolling();
offlineDev.value = true;
await localforage.removeItem(storeId).catch((error) => {
window.$message.error(`${error}`);
return;
});
window.$message.success('删除成功');
window.$message.success('删除成功,即将刷新页面');
await sleep(2000);
window.location.reload();
};

View File

@@ -1 +1,3 @@
export * from './use-device-management';
export * from './use-device-selection';
export * from './use-device-tree';

View File

@@ -0,0 +1,184 @@
import { deleteDeviceApi, exportDeviceApi, importDeviceApi, type ImportMsg, type NdmDevicePageQuery, type PageParams, type Station } from '@/apis';
import { DEVICE_TYPE_NAMES, type DeviceType } from '@/enums';
import { useDeviceStore, useStationStore } from '@/stores';
import { downloadByData, parseErrorFeedback } from '@/utils';
import { useMutation } from '@tanstack/vue-query';
import dayjs from 'dayjs';
import { storeToRefs } from 'pinia';
import { h, onBeforeUnmount } from 'vue';
import { useStationDevicesMutation } from '../query';
import { isCancel } from 'axios';
export const useDeviceManagement = () => {
const stationStore = useStationStore();
const { stations } = storeToRefs(stationStore);
const deviceStore = useDeviceStore();
const { lineDevices } = storeToRefs(deviceStore);
const { mutate: refreshStationDevices } = useStationDevicesMutation();
// 导出设备
const { mutate: exportDevice } = useMutation({
mutationFn: async (params: { deviceType: DeviceType; stationCode: Station['code']; signal?: AbortSignal }) => {
const { deviceType, stationCode, signal } = params;
const deviceTypeName = DEVICE_TYPE_NAMES[deviceType];
const stationDevices = lineDevices.value[stationCode];
if (!stationDevices) throw new Error(`该车站没有${deviceTypeName}`);
const devices = stationDevices[deviceType];
const pageQuery: PageParams<NdmDevicePageQuery> = {
model: {},
extra: {},
current: 1,
size: devices.length,
sort: 'id',
order: 'descending',
};
window.$loadingBar.start();
const data = await exportDeviceApi(deviceType, pageQuery, { stationCode, signal });
return data;
},
onSuccess: (data, { deviceType, stationCode }) => {
window.$loadingBar.finish();
const time = dayjs().format('YYYY-MM-DD_HH-mm-ss');
const stationName = stations.value.find((station) => station.code === stationCode)?.name ?? '';
const deviceTypeName = DEVICE_TYPE_NAMES[deviceType];
downloadByData(data, `${stationName}_${deviceTypeName}列表_${time}.xlsx`);
},
onError: (error) => {
if (isCancel(error)) return;
window.$loadingBar.error();
console.error(error);
const errorFeedback = parseErrorFeedback(error);
window.$message.error(errorFeedback);
},
});
// 下载设备导入模板
// FIXME: 采用导出空列表的方案但是后端生成的xlsx中会多一行空行如果直接再导入该文件就会多导入一个空设备
const { mutate: exportDeviceTemplate } = useMutation({
mutationFn: async (params: { deviceType: DeviceType; stationCode: Station['code']; signal?: AbortSignal }) => {
const { deviceType, stationCode, signal } = params;
const pageQuery: PageParams<NdmDevicePageQuery> = {
model: {},
extra: {},
current: 1,
size: 0,
sort: 'id',
order: 'descending',
};
window.$loadingBar.start();
const data = await exportDeviceApi(deviceType, pageQuery, { stationCode, signal });
return data;
},
onSuccess: (data, { deviceType, stationCode }) => {
window.$loadingBar.finish();
const time = dayjs().format('YYYY-MM-DD_HH-mm-ss');
const stationName = stations.value.find((station) => station.code === stationCode)?.name ?? '';
const deviceTypeName = DEVICE_TYPE_NAMES[deviceType];
downloadByData(data, `${stationName}_${deviceTypeName}导入模板_${time}.xlsx`);
},
onError: (error) => {
if (isCancel(error)) return;
window.$loadingBar.error();
console.error(error);
const errorFeedback = parseErrorFeedback(error);
window.$message.error(errorFeedback);
},
});
// 导入设备
const { mutate: importDevice } = useMutation({
mutationFn: async (params: { deviceType: DeviceType; stationCode: Station['code']; signal?: AbortSignal }) => {
const { deviceType, stationCode, signal } = params;
const data = await new Promise<ImportMsg>((resolve) => {
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = '.xlsx';
fileInput.click();
fileInput.onchange = async () => {
const file = fileInput.files?.[0];
// console.log(file);
if (!file) {
window.$message.error('导入失败');
return;
}
window.$loadingBar.start();
const data = await importDeviceApi(deviceType, file, { stationCode, signal });
resolve(data);
};
});
return data;
},
onSuccess: (data, { stationCode, signal }) => {
window.$loadingBar.finish();
window.$dialog.success({
title: '导入成功',
content: () => {
return h('div', {}, [
h('p', {}, `新增数据:${data.insertNum}`),
h('p', {}, `更新数据:${data.updateNum}`),
h('p', {}, `不变数据:${data.unchangedNum}`),
h('p', {}, `错误数据:${data.wrongNum}`),
data.wrongLines.map((line) => h('p', { style: { 'margin-left': '8px' } }, `${line.rowNum}行:${line.msg}`)),
]);
},
});
const station = stations.value.find((station) => station.code === stationCode);
if (station) {
refreshStationDevices({ station, signal });
}
},
onError: (error) => {
if (isCancel(error)) return;
window.$loadingBar.error();
console.error(error);
const errorFeedback = parseErrorFeedback(error);
window.$message.error(errorFeedback);
},
});
// 删除设备
const { mutate: deleteDevice } = useMutation({
mutationFn: async (params: { id: string; deviceType: DeviceType; stationCode: Station['code']; signal?: AbortSignal }) => {
const { id, deviceType, stationCode, signal } = params;
window.$loadingBar.start();
return await deleteDeviceApi(deviceType, id, { stationCode, signal });
},
onSuccess: (_, { stationCode, signal }) => {
window.$loadingBar.finish();
window.$message.success('删除成功');
const station = stations.value.find((station) => station.code === stationCode);
if (station) {
refreshStationDevices({ station, signal });
}
},
onError: (error) => {
if (isCancel(error)) return;
window.$loadingBar.error();
console.error(error);
const errorFeedback = parseErrorFeedback(error);
window.$message.error(errorFeedback);
},
});
onBeforeUnmount(() => {
window.$loadingBar.finish();
});
return {
exportDevice,
exportDeviceTemplate,
importDevice,
deleteDevice,
};
};

View File

@@ -0,0 +1,116 @@
import type { LineDevices, NdmDeviceResultVO } from '@/apis';
import { tryGetDeviceType, type DeviceType } from '@/enums';
import { useDeviceStore } from '@/stores';
import { watchDebounced } from '@vueuse/core';
import { storeToRefs } from 'pinia';
import { computed, onMounted, ref, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router';
export const useDeviceSelection = () => {
const route = useRoute();
const router = useRouter();
const deviceStore = useDeviceStore();
const { lineDevices } = storeToRefs(deviceStore);
const selectedStationCode = ref<string>();
const selectedDeviceType = ref<DeviceType>();
const selectedDevice = ref<NdmDeviceResultVO>();
const hasFromPage = computed(() => !!route.query['fromPage']);
const initFromRoute = (lineDevices: LineDevices) => {
const { stationCode, deviceType, deviceDbId } = route.query;
if (stationCode) {
selectedStationCode.value = stationCode as string;
}
if (deviceType) {
selectedDeviceType.value = deviceType as DeviceType;
}
if (deviceDbId && selectedStationCode.value && selectedDeviceType.value) {
const selectedDeviceDbId = deviceDbId as string;
const stationDevices = lineDevices[selectedStationCode.value];
if (stationDevices) {
const devices = stationDevices[selectedDeviceType.value];
if (devices) {
const device = devices.find((device) => device.id === selectedDeviceDbId);
if (device) {
selectedDevice.value = device;
}
}
}
}
};
const selectDevice = (device: NdmDeviceResultVO, stationCode: string) => {
selectedDevice.value = device;
selectedStationCode.value = stationCode;
const deviceType = tryGetDeviceType(device.deviceType);
if (deviceType) {
selectedDeviceType.value = deviceType;
}
};
const routeDevice = (device: NdmDeviceResultVO, stationCode: string, to: { path: string }) => {
const deviceDbId = device.id;
const deviceType = tryGetDeviceType(device.deviceType);
router.push({
path: to.path,
query: {
stationCode,
deviceType,
deviceDbId,
fromPage: route.path,
},
});
};
const syncToRoute = () => {
const query = { ...route.query };
// 当选中的设备发生变化时删除fromPage参数
if (selectedDevice.value?.id && route.query.deviceDbId !== selectedDevice.value.id) {
delete query['fromPage'];
}
if (selectedStationCode.value) {
query['stationCode'] = selectedStationCode.value;
}
if (selectedDeviceType.value) {
query['deviceType'] = selectedDeviceType.value;
}
if (selectedDevice.value?.id) {
query['deviceDbId'] = selectedDevice.value.id;
}
router.replace({ query });
};
watch(selectedDevice, syncToRoute);
// lineDevices是shallowRef因此需要深度侦听才能获取内部变化
// 而单纯的深度侦听又可能会引发性能问题,因此尝试使用防抖侦听
watchDebounced(
lineDevices,
(newLineDevices) => {
initFromRoute(newLineDevices);
},
{
debounce: 500,
deep: true,
},
);
onMounted(() => {
initFromRoute(lineDevices.value);
});
return {
selectedStationCode,
selectedDeviceType,
selectedDevice,
hasFromPage,
initFromRoute,
selectDevice,
routeDevice,
};
};

View File

@@ -1,89 +1,12 @@
import type { LineDevices, NdmDeviceResultVO } from '@/apis';
import { tryGetDeviceType, type DeviceType } from '@/enums';
import { ref, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useDeviceManagement } from './use-device-management';
import { useDeviceSelection } from './use-device-selection';
export const useDeviceTree = () => {
const route = useRoute();
const router = useRouter();
const selectedStationCode = ref<string>();
const selectedDeviceType = ref<DeviceType>();
const selectedDevice = ref<NdmDeviceResultVO>();
const initFromRoute = (lineDevices: LineDevices) => {
const { stationCode, deviceType, deviceDbId } = route.query;
if (stationCode) {
selectedStationCode.value = stationCode as string;
}
if (deviceType) {
selectedDeviceType.value = deviceType as DeviceType;
}
if (deviceDbId && selectedStationCode.value && selectedDeviceType.value) {
const selectedDeviceDbId = deviceDbId as string;
const stationDevices = lineDevices[selectedStationCode.value];
if (stationDevices) {
const devices = stationDevices[selectedDeviceType.value];
if (devices) {
const device = devices.find((device) => device.id === selectedDeviceDbId);
if (device) {
selectedDevice.value = device;
}
}
}
}
};
const selectDevice = (device: NdmDeviceResultVO, stationCode: string) => {
selectedDevice.value = device;
selectedStationCode.value = stationCode;
const deviceType = tryGetDeviceType(device.deviceType);
if (deviceType) {
selectedDeviceType.value = deviceType;
}
};
const routeDevice = (device: NdmDeviceResultVO, stationCode: string, to: { path: string }) => {
const deviceDbId = device.id;
const deviceType = tryGetDeviceType(device.deviceType);
router.push({
path: to.path,
query: {
stationCode,
deviceType,
deviceDbId,
from: route.path,
},
});
};
const syncToRoute = () => {
const query = { ...route.query };
// 当选中的设备发生变化时删除from参数
if (selectedDevice.value?.id && route.query.deviceDbId !== selectedDevice.value.id) {
delete query['from'];
}
if (selectedStationCode.value) {
query['stationCode'] = selectedStationCode.value;
}
if (selectedDeviceType.value) {
query['deviceType'] = selectedDeviceType.value;
}
if (selectedDevice.value?.id) {
query['deviceDbId'] = selectedDevice.value.id;
}
router.replace({ query });
};
watch(selectedDevice, syncToRoute);
const deviceSelection = useDeviceSelection();
const deviceManagement = useDeviceManagement();
return {
selectedStationCode,
selectedDeviceType,
selectedDevice,
initFromRoute,
selectDevice,
routeDevice,
...deviceSelection,
...deviceManagement,
};
};

View File

@@ -1,4 +1,5 @@
export * from './use-line-alarms-query';
export * from './use-line-devices-query';
export * from './use-line-stations-query';
export * from './use-verify-user-query';
export * from './use-version-check-query';

View File

@@ -0,0 +1,28 @@
import { verifyApi } from '@/apis';
import { VERIFY_USER_QUERY_KEY } from '@/constants';
import { useSettingStore } from '@/stores';
import { useQuery, useQueryClient } from '@tanstack/vue-query';
import { storeToRefs } from 'pinia';
import { computed, watch } from 'vue';
export const useVerifyUserQuery = () => {
const queryClient = useQueryClient();
const settingStore = useSettingStore();
const { offlineDev } = storeToRefs(settingStore);
watch(offlineDev, (offline) => {
if (offline) {
queryClient.cancelQueries({ queryKey: [VERIFY_USER_QUERY_KEY] });
}
});
return useQuery({
queryKey: [VERIFY_USER_QUERY_KEY],
enabled: computed(() => !offlineDev.value),
refetchInterval: 10 * 1000,
queryFn: async ({ signal }) => {
await verifyApi({ signal });
return null;
},
});
};

View File

@@ -1,39 +1,24 @@
import { verifyApi, type VersionInfo } from '@/apis';
import { type VersionInfo } from '@/apis';
import { VERSION_CHECK_QUERY_KEY } from '@/constants';
import { useSettingStore, useUserStore } from '@/stores';
import { useQuery, useQueryClient } from '@tanstack/vue-query';
import { useQuery } from '@tanstack/vue-query';
import axios from 'axios';
import { useThemeVars } from 'naive-ui';
import { storeToRefs } from 'pinia';
import { computed, h, ref, watch } from 'vue';
import { h, ref, watch } from 'vue';
export const useVersionCheckQuery = () => {
const localVersionInfo = ref<VersionInfo>();
const showDialog = ref<boolean>(false);
const themeVars = useThemeVars();
const queryClient = useQueryClient();
const userStore = useUserStore();
const { userLoginResult } = storeToRefs(userStore);
const settingStore = useSettingStore();
const { offlineDev } = storeToRefs(settingStore);
const { data: remoteVersionInfo, dataUpdatedAt } = useQuery({
queryKey: [VERSION_CHECK_QUERY_KEY],
enabled: computed(() => !offlineDev.value),
refetchInterval: 10 * 1000,
queryFn: async ({ signal }) => {
if (!!userLoginResult.value?.token) await verifyApi({ signal });
const { data } = await axios.get<VersionInfo>(`/manifest.json?t=${Date.now()}`, { signal });
return data;
},
});
watch(offlineDev, (offline) => {
if (offline) {
queryClient.cancelQueries({ queryKey: [VERSION_CHECK_QUERY_KEY] });
}
});
watch(dataUpdatedAt, () => {
const newVersionInfo = remoteVersionInfo.value;
if (!newVersionInfo) return;

View File

@@ -1,4 +1,5 @@
export const LINE_ALARMS_QUERY_KEY = 'line-alarms';
export const LINE_DEVICES_QUERY_KEY = 'line-devices';
export const LINE_STATIONS_QUERY_KEY = 'line-stations';
export const VERIFY_USER_QUERY_KEY = 'verify-user';
export const VERSION_CHECK_QUERY_KEY = 'version-check';

View File

@@ -1,6 +1,6 @@
<script setup lang="ts">
import { SettingsDrawer, SyncCameraResultModal } from '@/components';
import { useLineStationsQuery, useStompClient } from '@/composables';
import { useLineStationsQuery, useStompClient, useVerifyUserQuery } from '@/composables';
import { useAlarmStore, useSettingStore, useUserStore } from '@/stores';
import { parseErrorFeedback } from '@/utils';
import { useIsFetching, useIsMutating, useMutation } from '@tanstack/vue-query';
@@ -40,6 +40,7 @@ const { menuCollpased, offlineDev } = storeToRefs(settingStore);
const { syncCameraResult, afterCheckSyncCameraResult } = useStompClient();
useVerifyUserQuery();
useLineStationsQuery();
// 带key的query和mutation用于全局轮询 可用于渲染loading状态

View File

@@ -32,7 +32,17 @@ const { mutate: login, isPending: loading } = useMutation({
onError: (error) => {
console.error(error);
const errorFeedback = parseErrorFeedback(error);
window.$message.error(errorFeedback);
window.$dialog.destroyAll();
window.$dialog.error({
closable: false,
maskClosable: false,
title: '错误提示',
content: errorFeedback,
positiveText: '确认',
onPositiveClick: () => {
window.$message.destroyAll();
},
});
},
});
</script>

View File

@@ -1,3 +1,32 @@
<script lang="ts">
const vimpOperationTypeOptions: SelectOption[] = [
{ label: '视频点播', value: 10001 },
{ label: '视频回放', value: 10002 },
// { label: '停止视频回放', value: 10003 },
// { label: '回放暂停', value: 10004 },
// { label: '回放恢复', value: 10005 },
// { label: '回放倍速播放', value: 10006 },
// { label: '回放拖动播放', value: 10007 },
{ label: '云台指令', value: 10008 },
{ label: '根据国标码查录像', value: 10009 },
{ label: '下载录像', value: 10010 },
{ label: '停止下载录像', value: 10011 },
{ label: '获取预置位列表', value: 10012 },
{ label: '设置本平台分屏数量', value: 20001 },
{ label: '设置本平台monitor播放的摄像机rtsp流', value: 20002 },
{ label: '停止本平台monitor播放的摄像机rtsp流', value: 20003 },
{ label: '启动非报警时序', value: 30001 },
{ label: '停止非报警时序', value: 30002 },
{ label: '暂停非报警时序', value: 30003 },
{ label: '调用组切', value: 30004 },
{ label: '启动报警时序', value: 40001 },
{ label: '停止报警时序', value: 40002 },
{ label: '分页查询报警', value: 50001 },
{ label: '确认报警', value: 50002 },
{ label: '删除报警', value: 50004 },
];
</script>
<script setup lang="ts">
import { exportVimpLogApi, pageVimpLogApi, type NdmVimpLog, type NdmVimpLogResultVO, type PageQueryExtra, type Station } from '@/apis';
import { useStationStore } from '@/stores';
@@ -28,6 +57,7 @@ import { computed, h, reactive, ref, watch, watchEffect } from 'vue';
interface SearchFields extends PageQueryExtra<NdmVimpLog> {
stationCode?: Station['code'];
logType_in: number[];
createdTime: [string, string];
}
@@ -43,11 +73,13 @@ const stationSelectOptions = computed(() => {
});
const searchFields = ref<SearchFields>({
logType_in: [],
createdTime: [dayjs().startOf('date').subtract(1, 'week').format('YYYY-MM-DD HH:mm:ss'), dayjs().endOf('date').format('YYYY-MM-DD HH:mm:ss')] as [string, string],
});
const resetSearchFields = () => {
searchFields.value = {
stationCode: onlineStations.value.at(0)?.code,
logType_in: [],
createdTime: [dayjs().startOf('date').subtract(1, 'week').format('YYYY-MM-DD HH:mm:ss'), dayjs().endOf('date').format('YYYY-MM-DD HH:mm:ss')] as [string, string],
};
};
@@ -266,6 +298,9 @@ watch(
clearable
/>
</NFormItemGi>
<NFormItemGi :span="1" label="操作类型" label-placement="left">
<NSelect v-model:value="searchFields.logType_in" :options="vimpOperationTypeOptions" multiple clearable />
</NFormItemGi>
<NFormItemGi span="1" label="时间" label-placement="left">
<NDatePicker v-model:formatted-value="searchFields.createdTime" type="datetimerange" />
</NFormItemGi>

View File

@@ -1,13 +1,13 @@
import { useStationStore } from './station';
import { usePollingStore } from './polling';
import { userClient, type LoginParams, type LoginResult, type Station } from '@/apis';
import { NDM_USER_STORE_ID } from '@/constants';
import type { Result } from '@/types';
import { AesEncryption, getAppEnvConfig } from '@/utils';
import axios, { AxiosError } from 'axios';
import dayjs from 'dayjs';
import { defineStore } from 'pinia';
import { computed, ref } from 'vue';
import { NDM_USER_STORE_ID } from '@/constants';
const getHeaders = () => {
const { lampAuthorization, lampClientId, lampClientSecret } = getAppEnvConfig();
@@ -49,24 +49,12 @@ export const useUserStore = defineStore(
key,
grantType,
};
const { data: respData } = await axios.post<Result<LoginResult>>(`/api/oauth/anyTenant/login`, body, { headers: getHeaders() });
if (!respData.isSuccess) {
console.error(respData);
window.$dialog.destroyAll();
window.$dialog.error({
closable: false,
maskClosable: false,
title: '错误提示',
content: respData.msg,
positiveText: '确认',
onPositiveClick: () => {
window.$message.destroyAll();
},
});
throw new AxiosError(respData.msg, `${respData.code}`);
} else {
userLoginResult.value = respData.data;
}
// 如果发生 http 错误,会被登录页中的 useMutation 捕获,此处无需 try-catch
const { data: result } = await axios.post<Result<LoginResult>>(`/api/oauth/anyTenant/login`, body, { headers: getHeaders() });
// 尽管请求成功,但需要判断是否发生业务错误
// 如果发生业务错误,则需要显式抛出,登录页会捕获该错误并提示用户
if (!result.isSuccess) throw new Error(result.msg);
userLoginResult.value = result.data;
};
const userLogout = async () => {

View File

@@ -1,10 +1,10 @@
export interface Result<T = unknown> {
isSuccess: boolean;
code: number;
data: T;
errorMsg: string;
extra: unknown;
isSuccess: boolean;
msg: string;
path?: string;
errorMsg: string;
timestamp: string;
path: string | null;
extra: Record<string, any> | null;
}

View File

@@ -114,23 +114,54 @@ export class RequestClient {
}
}
export const parseErrorFeedback = (error: Error) => {
let errorFeedback: string;
if (!isAxiosError(error)) {
errorFeedback = error.message;
} else {
const axiosError = error as AxiosError;
const respData = axiosError.response?.data as Record<string, string | number> | string | undefined;
if (!respData) {
const { code, message } = axiosError;
errorFeedback = `${code} : ${message}`;
} else {
if (typeof respData === 'string') {
errorFeedback = respData;
} else {
errorFeedback = `${respData['path']}: ${respData['msg'] ?? respData['errorMsg'] ?? respData['error']}`;
}
}
// 从响应中解析出数据
export const unwrapResponse = <T>(resp: Response<T>) => {
const [err, data, result] = resp;
// 如果 err 存在说明有 http 错误,那么直接抛出错误,
// 如果没有 err那么接下来判断是否存在业务错误。
// 如果 result 中 isSuccess 为 false说明有业务错误
// 如果 result 不存在,说明 retRaw 为 true是直接返回 data 的情况。
if (err) throw err;
// 不能判断 !result 从而提前结束分支,
// 因为当 retRaw 为 true 时result 总是为 null。
if (result) {
const { isSuccess, path, msg, errorMsg } = result;
if (!isSuccess) throw new Error(`${path ? `${path}: ` : ''}${msg || errorMsg || '请求失败'}`);
}
return errorFeedback;
// 不做严格判空null == undefined -> true
if (data == null) throw new Error('响应数据为空');
return data;
};
// 从错误中解析出错误信息
export const parseErrorFeedback = (error: Error) => {
// 当发生 http 错误时unwrapResponse 会直接抛出错误,
// 所以如果不是 AxiosError说明是业务错误直接返回错误信息。
if (!isAxiosError(error)) {
return error.message;
}
// 如果是 AxiosError说明是 http 错误,尝试获取 result
const result = error.response?.data;
// 如果 result 不存在,返回 error 中原生的 code + message
if (!result) {
const { code, message } = error;
return `${code ? `code: ${code}, ` : ''}${message ? `message: ${message}` : ''}`;
}
// 如果 result 存在,判断是否是字符串,
// 如果是字符串,说明是一些奇怪的、直接返回的错误信息,直接返回;
// 如果不是字符串,说明是 Result 类型,返回其中的 path + msg + errorMsg。
if (typeof result === 'string') {
return result;
}
const { path, msg, errorMsg } = result as Result;
return `${path ? `${path}: ` : ''}${msg || errorMsg || '请求失败'}`;
};

View File

@@ -105,12 +105,13 @@ const line10ApiProxyList: ProxyItem[] = [
const apiProxyList: ProxyItem[] = [
// { key: '/minio', target: 'http://10.15.128.10:9000', rewrite: ['/minio', ''] },
// { key: '/api', target: 'http://10.15.128.10:18760' },
// { key: '/ws', target: 'ws://10.15.128.10:18103', ws: true },
...line04ApiProxyList,
{ key: '/minio', target: 'http://10.18.128.10:9000', rewrite: ['/minio', ''] },
{ key: '/api', target: 'http://10.18.128.10:18760' },
...line04ApiProxyList,
...line10ApiProxyList,
// { key: '/ws', target: 'ws://10.15.128.10:18103', ws: true },
{ key: '/ws', target: 'ws://10.18.128.10:18103', ws: true },
...line10ApiProxyList,
];
// https://vite.dev/config/