1 Commits

Author SHA1 Message Date
yangsy 20513557d7 release: v0.40.0 2026-04-10 15:58:29 +08:00
42 changed files with 115 additions and 452 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "ndm-web-platform",
"version": "0.41.0",
"version": "0.40.0",
"private": true,
"type": "module",
"engines": {
-20
View File
@@ -1,24 +1,4 @@
[
{
"version": "0.41.0",
"date": "2026-05-19",
"changes": {
"fixes": [
{ "content": "修复交换机端口卡片在端口数据为空时仍展示的问题" },
{ "content": "修复安防箱门禁状态和防雷状态显示错误的问题" },
{ "content": "修复解码器和录像机历史诊断中硬件占用卡片的加载状态同步问题" }
],
"feats": [
{ "content": "新增摄像机阈值配置功能" },
{ "content": "新增摄像机硬件占用率历史诊断记录卡片" },
{ "content": "优化设备诊断页的信息展示结构,支持按设备型号、网卡信息、接入信息等分组展示" },
{ "content": "为摄像机诊断信息新增网卡信息、IP信息和设备通用信息展示" },
{ "content": "新增多厂商安防箱支持,支持BeiDian和NingTech安防箱的空开控制与设备重启" },
{ "content": "安防箱配置新增开关数量和团体字符串(写)" },
{ "content": "为各类设备实体新增团体字符串(写)字段" }
]
}
},
{
"version": "0.40.0",
"date": "2026-04-10",
+2 -2
View File
@@ -1,4 +1,4 @@
{
"version": "0.41.0",
"buildTime": "2026-05-19 19:40:37"
"version": "0.40.0",
"buildTime": "2026-04-10 15:42:03"
}
@@ -1,45 +1,5 @@
export interface NdmCameraDiagInfo {
[key: string]: any;
ethInfo?: {
adminStatus?: string; // '1'
desc?: string; // 'IPcamera'
ifType?: string; // '5'
inDiscards?: string; // '0'
inErrors?: string; // '0'
inNUcastPkts?: string; // '0'
inOctets?: string; // '0'
inUcastPkts?: string; // '0'
inUnknownProtos?: string; // '0'
index?: string; // '1'
lastChange?: string; // '0:00:00.00'
mTU?: string; // '1500'
macAddress?: string; // '04:ee:cd:52:3a:a5'
operStatus?: string; // '1'
outDiscards?: string; // '0'
outErrors?: string; // '0'
outNUcastPkts?: string; // '0'
outOctets?: string; // '0'
outQLen?: string; // '0'
outUcastPkts?: string; // '0'
specific?: string; // '0.0'
speed?: string; // '10000000'
};
ipInfo?: {
broadcastAddress?: string; // '0'
iPAddress?: string; // '0'
index?: string; // '1'
mASK?: string; // '255.255.255.0'
reasmMaxSize?: string; // '0'
};
logTime?: string;
stCommonInfo?: {
设备ID?: string;
软件版本?: string;
设备厂商?: string;
设备别名?: string;
设备型号?: string;
硬件版本?: string;
内存使用率?: string;
CPU使用率?: string;
};
info?: string;
}
@@ -12,14 +12,8 @@ export interface NdmSecurityBoxDiagInfo {
];
stCommonInfo?: {
[key: string]: any;
设备ID?: string; // 'NTBoxMetro'
软件版本?: string; // 'V0101'
设备厂商?: string; // 'NingTech'
设备别名?: string; // 'SUN-IBOX'
设备型号?: string; // 'SUN-IBOX'
硬件版本?: string; // 'V0101'
内存使用率?: string; // '18'
CPU使用率?: string; // '1'
内存使用率?: string;
CPU使用率?: string;
};
}
@@ -20,9 +20,7 @@ export interface NdmSecurityBox extends BaseModel {
description: string;
deviceStatus: string;
deviceType: string;
circuitCount: number;
community: string;
writeCommunity: string;
frontendConfig: string;
linkDescription: string;
snmpEnabled: boolean;
@@ -21,7 +21,6 @@ export interface NdmSwitch extends BaseModel {
deviceStatus: string;
deviceType: string;
community: string;
writeCommunity: string;
frontendConfig: string;
linkDescription: string;
snmpEnabled: boolean;
@@ -30,7 +30,6 @@ export interface NdmNvr extends BaseModel {
deviceStatus: string;
deviceType: string;
community: string;
writeCommunity: string;
frontendConfig: string;
linkDescription: string;
snmpEnabled: boolean;
@@ -31,7 +31,6 @@ export interface NdmCamera extends BaseModel {
deviceType: string;
cameraType: string;
community: string;
writeCommunity: string;
frontendConfig: string;
linkDescription: string;
snmpEnabled: boolean;
@@ -28,7 +28,6 @@ export interface NdmDecoder extends BaseModel {
deviceStatus: string;
deviceType: string;
community: string;
writeCommunity: string;
frontendConfig: string;
linkDescription: string;
snmpEnabled: boolean;
@@ -21,7 +21,6 @@ export interface NdmKeyboard extends BaseModel {
deviceStatus: string;
deviceType: string;
community: string;
writeCommunity: string;
frontendConfig: string;
linkDescription: string;
snmpEnabled: boolean;
@@ -25,7 +25,6 @@ export interface NdmMediaServer extends BaseModel {
deviceStatus: string;
deviceType: string;
community: string;
writeCommunity: string;
frontendConfig: string;
linkDescription: string;
snmpEnabled: boolean;
@@ -25,7 +25,6 @@ export interface NdmVideoServer extends BaseModel {
deviceStatus: string;
deviceType: string;
community: string;
writeCommunity: string;
frontendConfig: string;
linkDescription: string;
snmpEnabled: boolean;
+4 -28
View File
@@ -93,46 +93,22 @@ export const probeSecurityBoxApi = async (ids: string[], options?: { stationCode
unwrapVoidResponse(resp);
};
// beidian安防箱切换空开状态
export const turnCircuitStatusBeidianApi = async (community: string, ipAddress: string, circuitIndex: number, status: number, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
export const turnCitcuitStatusApi = async (ipAddress: string, circuitIndex: number, status: number, 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/turnStatus`;
const resp = await client.post<boolean>(endpoint, { community, ipAddress, circuit: `${circuitIndex}`, status }, { signal });
const resp = await client.post<boolean>(endpoint, { community: 'public', ipAddress, circuit: `${circuitIndex}`, status }, { signal });
const data = unwrapResponse(resp);
return data;
};
// beidian安防箱重启
export const rebootSecurityBoxBeidianApi = async (community: string, ipAddress: string, options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
export const rebootSecurityBoxApi = async (ipAddress: string, 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/reboot`;
const resp = await client.post<boolean>(endpoint, { community, ipAddress }, { signal });
const data = unwrapResponse(resp);
return data;
};
// ningtech安防箱切换空开状态
export const turnCircuitStatusNingTechApi = async (community: string, ipAddress: string, circuitIndex: number, status: number, 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/turnStatusNingTech`;
const resp = await client.post<boolean>(endpoint, { community, ipAddress, circuit: `${circuitIndex}`, status }, { signal });
const data = unwrapResponse(resp);
return data;
};
// ningtech安防箱重启
export const rebootSecurityBoxNingTechApi = async (community: string, ipAddress: string, 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/rebootNingTech`;
const resp = await client.post<boolean>(endpoint, { community, ipAddress }, { signal });
const resp = await client.post<boolean>(endpoint, { community: 'public', ipAddress }, { signal });
const data = unwrapResponse(resp);
return data;
};
@@ -1,20 +1,16 @@
<script setup lang="ts">
import { NCard, NDescriptions, NDescriptionsItem, NFlex, NText } from 'naive-ui';
import { NCard, NDescriptions, NDescriptionsItem } from 'naive-ui';
import { computed, toRefs } from 'vue';
const props = defineProps<{
commonInfo?: Array<{
title: string;
items: Array<{
label: string;
value: string;
}>;
}>;
commonInfo?: Record<string, string>;
}>();
const { commonInfo } = toRefs(props);
const showCard = computed(() => (commonInfo.value?.length ?? 0) > 0);
const showCard = computed(() => !!commonInfo.value);
const commonInfoEntries = computed(() => Object.entries(commonInfo.value ?? {}));
</script>
<template>
@@ -22,20 +18,11 @@ const showCard = computed(() => (commonInfo.value?.length ?? 0) > 0);
<template #header>
<div>设备信息</div>
</template>
<template v-if="!!commonInfo && commonInfo.length > 0">
<NFlex vertical :size="16">
<template v-for="({ title, items }, index) in commonInfo" :key="`${title}-${index}`">
<NFlex vertical>
<NText strong :depth="3">{{ title }}</NText>
<NDescriptions bordered :label-placement="'left'" :columns="2">
<template v-for="({ label, value }, index) in items" :key="`${label}-${index}`">
<NDescriptionsItem :label="label">{{ value }}</NDescriptionsItem>
</template>
</NDescriptions>
</NFlex>
</template>
</NFlex>
</template>
<NDescriptions bordered label-placement="left" :columns="2">
<template v-for="item in commonInfoEntries" :key="item[0]">
<NDescriptionsItem :label="item[0]">{{ item[1] }}</NDescriptionsItem>
</template>
</NDescriptions>
</NCard>
</template>
@@ -1,18 +1,3 @@
<script lang="ts">
const parsePercentMetric = (raw?: string) => {
const trimmed = raw?.trim();
if (!!trimmed) {
const value = Number(trimmed);
if (Number.isFinite(value)) {
return value;
}
}
return undefined;
};
</script>
<script setup lang="ts">
import { ClockCheckIcon, CpuIcon, HardDriveIcon, MemoryStickIcon } from 'lucide-vue-next';
import { NCard, NFlex, NIcon, NProgress, type ProgressStatus } from 'naive-ui';
@@ -36,23 +21,25 @@ const showCard = computed(() => {
});
const cpuPercent = computed(() => {
return parsePercentMetric(cpuUsage.value);
if (!cpuUsage?.value) return 0;
return parseFloat(cpuUsage.value.replace('%', ''));
});
const memPercent = computed(() => {
return parsePercentMetric(memUsage.value);
if (!memUsage?.value) return 0;
return parseFloat(memUsage.value.replace('%', ''));
});
const diskPercent = computed(() => {
return parsePercentMetric(diskUsage.value);
if (!diskUsage?.value) return 0;
return parseFloat(diskUsage.value.replace('%', ''));
});
const formattedRunningTime = computed(() => {
return (runningTime?.value ?? '-').replace('days', '天');
});
const getProgressStatus = (percent?: number): ProgressStatus | undefined => {
if (!percent) return undefined;
const getProgressStatus = (percent: number): ProgressStatus => {
if (percent >= 90) return 'error';
if (percent >= 70) return 'warning';
return 'success';
@@ -69,17 +56,17 @@ const getProgressStatus = (percent?: number): ProgressStatus | undefined => {
<NFlex v-if="cpuUsage" style="width: 100%" align="center" :wrap="false">
<NIcon :component="CpuIcon" />
<span style="word-break: keep-all">{{ cpuUsageLabel || 'CPU' }}</span>
<NProgress :percentage="cpuPercent" :status="getProgressStatus(cpuPercent)">{{ cpuPercent ?? '-' }}%</NProgress>
<NProgress :percentage="cpuPercent" :status="getProgressStatus(cpuPercent)">{{ cpuPercent }}%</NProgress>
</NFlex>
<NFlex v-if="memUsage" style="width: 100%" align="center" :wrap="false">
<NIcon :component="MemoryStickIcon" />
<span style="word-break: keep-all">{{ memUsageLabel || '内存' }}</span>
<NProgress :percentage="memPercent" :status="getProgressStatus(memPercent)">{{ memPercent ?? '-' }}%</NProgress>
<NProgress :percentage="memPercent" :status="getProgressStatus(memPercent)">{{ memPercent }}%</NProgress>
</NFlex>
<NFlex v-if="diskUsage" style="width: 100%" align="center" :wrap="false">
<NIcon :component="HardDriveIcon" />
<span style="word-break: keep-all">{{ diskUsageLabel || '磁盘' }}</span>
<NProgress :percentage="diskPercent" :status="getProgressStatus(diskPercent)">{{ diskPercent ?? '-' }}%</NProgress>
<NProgress :percentage="diskPercent" :status="getProgressStatus(diskPercent)">{{ diskPercent }}%</NProgress>
</NFlex>
<NFlex v-if="runningTime" style="width: 100%" align="center" :wrap="false">
<NIcon :component="ClockCheckIcon" />
@@ -1,4 +1,3 @@
import type { ComponentInstance } from 'vue';
import DeviceCommonCard from './device-common-card.vue';
import DeviceHardwareCard from './device-hardware-card.vue';
import DeviceHeaderCard from './device-header-card.vue';
@@ -10,8 +9,6 @@ import SecurityBoxEnvCard from './security-box-env-card.vue';
import SwitchPortCard from './switch-port-card.vue';
import SwitchPortLinkModal from './switch-port-link-modal.vue';
export type DeviceCommonCardProps = ComponentInstance<typeof DeviceCommonCard>['$props'];
export {
DeviceCommonCard,
DeviceHardwareCard,
@@ -2,6 +2,8 @@
import {
detailDeviceApi,
probeDeviceApi,
rebootSecurityBoxApi,
turnCitcuitStatusApi,
updateDeviceApi,
type LinkDescription,
type NdmDeviceResultVO,
@@ -14,7 +16,6 @@ import { SecurityBoxCircuitLinkModal } from '@/components';
import { usePermission } from '@/composables';
import { SELECT_DEVICE_FN_INJECTION_KEY } from '@/constants';
import { PERMISSION_TYPE_LITERALS } from '@/enums';
import { normalizeSecurityBoxWriteCommunity, dispatchRebootSecurityBoxApi, dispatchTurnCircuitStatusApi, normalizeSecurityBoxCircuitIndex } from '@/helpers';
import { useDeviceStore, useSettingStore } from '@/stores';
import { parseErrorFeedback } from '@/utils';
import { useMutation } from '@tanstack/vue-query';
@@ -30,7 +31,6 @@ import { computed, inject, ref, toRefs } from 'vue';
const props = defineProps<{
ndmDevice: NdmSecurityBoxResultVO;
station: Station;
vendor?: string;
circuits?: NdmSecurityBoxCircuit[];
}>();
@@ -44,7 +44,7 @@ const { useLocalDB } = storeToRefs(settingStore);
const { hasPermission } = usePermission();
const { ndmDevice, station, vendor, circuits } = toRefs(props);
const { ndmDevice, station, circuits } = toRefs(props);
const showCard = computed(() => !!circuits.value && circuits.value.length > 0);
@@ -52,10 +52,6 @@ const localCircuits = ref<NdmSecurityBoxCircuit[]>([]);
watchImmediate(circuits, (newCircuits) => {
localCircuits.value = newCircuits?.map((circuit) => ({ ...circuit })) ?? [];
const circuitCount = ndmDevice.value.circuitCount;
if (!!circuitCount) {
localCircuits.value = localCircuits.value.slice(0, circuitCount);
}
});
const getCircuitStatusTagType = (circuit: NdmSecurityBoxCircuit): TagProps['type'] => {
@@ -83,19 +79,13 @@ const { mutate: turnStatus, isPending: turning } = useMutation({
window.$loadingBar.start();
const { circuitIndex, newStatus } = params;
const community = normalizeSecurityBoxWriteCommunity(ndmDevice.value, vendor.value);
const ipAddress = ndmDevice.value.ipAddress;
if (!ipAddress) {
if (!ndmDevice.value.ipAddress) {
throw new Error('设备IP地址不存在');
}
const status = newStatus ? 1 : 0;
const stationCode = station.value.code;
const signal = abortController.value.signal;
const turnCircuitStatusApi = dispatchTurnCircuitStatusApi(vendor.value);
const normalizedCircuitIndex = normalizeSecurityBoxCircuitIndex(circuitIndex, vendor.value);
await turnCircuitStatusApi(community, ipAddress, normalizedCircuitIndex, status, { stationCode, signal });
await turnCitcuitStatusApi(ndmDevice.value.ipAddress, circuitIndex, status, { stationCode, signal });
await probeDeviceApi(ndmDevice.value, { stationCode, signal });
return status;
},
@@ -120,17 +110,13 @@ const { mutate: reboot, isPending: rebooting } = useMutation({
window.$loadingBar.start();
const community = normalizeSecurityBoxWriteCommunity(ndmDevice.value, vendor.value);
const ipAddress = ndmDevice.value.ipAddress;
if (!ipAddress) {
if (!ndmDevice.value.ipAddress) {
throw new Error('设备IP地址不存在');
}
const stationCode = station.value.code;
const signal = abortController.value.signal;
const rebootSecurityBoxApi = dispatchRebootSecurityBoxApi(vendor.value);
await rebootSecurityBoxApi(community, ipAddress, { stationCode, signal });
await rebootSecurityBoxApi(ndmDevice.value.ipAddress, { stationCode, signal });
},
onSuccess: () => {
window.$loadingBar.finish();
@@ -16,23 +16,23 @@ const showCard = computed(() => {
return Object.values(props).some((value) => !!value);
});
// 防雷状态
const lightningProtectionStatus = computed(() => {
if (!switches?.value || switches.value.length < 2) return null;
const status = switches.value.at(0)!;
return status === 0 ? '失效' : status === 1 ? '生效' : '-';
});
// 门禁状态
const accessControlStatus = computed(() => {
if (!switches?.value || switches.value.length < 1) return null;
const status = switches.value.at(1)!;
const status = switches.value.at(0)!;
return status === 0 ? '开门' : status === 1 ? '关门' : '-';
});
// 防雷状态
const lightningProtectionStatus = computed(() => {
if (!switches?.value || switches.value.length < 2) return null;
const status = switches.value.at(1)!;
return status === 0 ? '正常' : status === 1 ? '失效' : '-';
});
const getStatusTagType = (status: string | null) => {
if (['生效', '关门'].includes(status ?? '')) return 'success';
if (['失效', '开门'].includes(status ?? '')) return 'error';
if (['正常'].includes(status ?? '')) return 'success';
if (['失效'].includes(status ?? '')) return 'error';
return 'default';
};
@@ -33,7 +33,7 @@ const { hasPermission } = usePermission();
const { ndmDevice, station, ports } = toRefs(props);
const showCard = computed(() => !!ports.value && ports.value.length > 0);
const showCard = computed(() => !!ports.value);
const switchSlots = computed(() => {
// 解析端口名称,将端口按槽位进行分组
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { NFlex } from 'naive-ui';
import { DeviceCommonCard, DeviceHeaderCard, type DeviceCommonCardProps } from '@/components';
import { DeviceCommonCard, DeviceHeaderCard } from '@/components';
import type { NdmAlarmHostResultVO, Station } from '@/apis';
import { computed, toRefs } from 'vue';
@@ -11,18 +11,13 @@ const props = defineProps<{
const { ndmDevice, station } = toRefs(props);
const commonInfo = computed<DeviceCommonCardProps['commonInfo']>(() => {
const commonInfo = computed(() => {
const { createdTime, updatedTime, manufacturer } = ndmDevice.value;
return [
{
title: '设备接入信息',
items: [
{ label: '创建时间', value: createdTime || '-' },
{ label: '更新时间', value: updatedTime || '-' },
{ label: '制造商', value: manufacturer || '-' },
],
},
];
return {
创建时间: createdTime ?? '-',
更新时间: updatedTime ?? '-',
制造商: manufacturer ?? '-',
};
});
</script>
@@ -16,12 +16,11 @@ const isCameraTypeCode = (code: string): code is CameraType => {
</script>
<script setup lang="ts">
import type { NdmCameraDiagInfo, NdmCameraResultVO, Station } from '@/apis';
import { DeviceCommonCard, DeviceHardwareCard, DeviceHeaderCard, type DeviceCommonCardProps } from '@/components';
import type { NdmCameraResultVO, Station } from '@/apis';
import { DeviceCommonCard, DeviceHeaderCard } from '@/components';
import { useSettingStore } from '@/stores';
import { useQuery, useQueryClient } from '@tanstack/vue-query';
import axios from 'axios';
import destr from 'destr';
import { NDescriptions, NDescriptionsItem, NFlex } from 'naive-ui';
import { storeToRefs } from 'pinia';
import { computed, toRefs, watch } from 'vue';
@@ -114,17 +113,7 @@ watch(activeRequests, (active) => {
}
});
const lastDiagInfo = computed(() => {
const result = destr<any>(ndmDevice.value.lastDiagInfo);
if (!result) return null;
if (typeof result !== 'object') return null;
return result as NdmCameraDiagInfo;
});
const commonInfo = computed<DeviceCommonCardProps['commonInfo']>(() => {
const { stCommonInfo } = lastDiagInfo.value ?? {};
const { 设备ID, 软件版本, 设备厂商, 设备别名, 设备型号, 硬件版本 } = stCommonInfo ?? {};
const { ethInfo, ipInfo } = lastDiagInfo.value ?? {};
const commonInfo = computed(() => {
const {
createdTime,
updatedTime,
@@ -137,58 +126,22 @@ const commonInfo = computed<DeviceCommonCardProps['commonInfo']>(() => {
onvifMinorIndex,
icmpEnabled,
community,
ipAddress,
//
} = ndmDevice.value;
let operStatus = '-';
if (!!ethInfo?.operStatus) {
operStatus = ethInfo?.operStatus === '1' ? '正常' : '异常';
}
return [
{
title: '设备型号信息',
items: [
{ label: '设备ID', value: 设备ID || '-' },
{ label: '软件版本', value: 软件版本 || '-' },
{ label: '设备厂商', value: 设备厂商 || manufacturer || '-' },
{ label: '设备别名', value: 设备别名 || '-' },
{ label: '设备型号', value: 设备型号 || '-' },
{ label: '硬件版本', value: 硬件版本 || '-' },
],
},
{
title: '设备网卡信息',
items: [
{ label: 'IP地址', value: ipAddress || '-' },
{ label: '子网掩码', value: ipInfo?.mASK || '-' },
{ label: 'MAC地址', value: ethInfo?.macAddress || '-' },
{ label: '连接速率', value: ethInfo?.speed || '-' },
{ label: 'MTU', value: ethInfo?.mTU || '-' },
{ label: '运行状态', value: operStatus },
],
},
{
title: '设备接入信息',
items: [
{ label: '创建时间', value: createdTime || '-' },
{ label: '更新时间', value: updatedTime || '-' },
{ label: 'GB28181启用', value: `${!!gb28181Enabled ? '是' : '否'}` },
{ label: 'ICMP启用', value: `${!!icmpEnabled ? '是' : '否'}` },
{ label: 'ONVIF用户名', value: onvifUsername || '-' },
{ label: 'ONVIF密码', value: onvifPassword || '-' },
{ label: 'ONVIF主流索引', value: `${onvifMajorIndex ?? '-'}` },
{ label: 'ONVIF辅流索引', value: `${onvifMinorIndex ?? '-'}` },
{ label: 'ONVIF端口', value: `${onvifPort ?? '-'}` },
{ label: '团体字符串', value: community || '-' },
],
},
];
return {
创建时间: createdTime ?? '-',
更新时间: updatedTime ?? '-',
制造商: manufacturer ?? '-',
GB28181启用: `${!!gb28181Enabled ? '是' : '否'}`,
ONVIF端口: `${onvifPort ?? '-'}`,
ONVIF用户名: onvifUsername ?? '-',
ONVIF密码: onvifPassword ?? '-',
ONVIF主流索引: `${onvifMajorIndex ?? '-'}`,
ONVIF辅流索引: `${onvifMinorIndex ?? '-'}`,
ICMP启用: `${!!icmpEnabled ? '是' : ''}`,
团体字符串: community ?? '-',
};
});
const cpuUsage = computed(() => lastDiagInfo.value?.stCommonInfo?.CPU使用率);
const memUsage = computed(() => lastDiagInfo.value?.stCommonInfo?.内存使用率);
</script>
<template>
@@ -202,7 +155,6 @@ const memUsage = computed(() => lastDiagInfo.value?.stCommonInfo?.内存使用
</template>
</DeviceHeaderCard>
<DeviceCommonCard :common-info="commonInfo" />
<DeviceHardwareCard :cpu-usage="cpuUsage" :mem-usage="memUsage" />
</NFlex>
</template>
@@ -1,14 +1,6 @@
<script setup lang="ts">
import type { NdmCameraResultVO, Station } from '@/apis';
import {
DeviceAlarmHistoryCard,
DeviceIcmpHistoryCard,
DeviceUsageHistoryCard,
HistoryDiagFilterCard,
type DeviceAlarmHistoryCardProps,
type DeviceIcmpHistoryCardProps,
type DeviceUsageHistoryCardProps,
} from '@/components';
import { DeviceAlarmHistoryCard, DeviceIcmpHistoryCard, HistoryDiagFilterCard, type DeviceAlarmHistoryCardProps, type DeviceIcmpHistoryCardProps } from '@/components';
import dayjs from 'dayjs';
import { NFlex, type SelectOption } from 'naive-ui';
import { onMounted, ref, toRefs, watch } from 'vue';
@@ -23,7 +15,6 @@ const { ndmDevice, station } = toRefs(props);
const historyDiagOptions: SelectOption[] = [
{ label: '设备状态', value: 'icmp' },
{ label: '设备告警', value: 'alarm' },
{ label: '硬件占用', value: 'usage' },
];
const getWeekRange = (): [number, number] => {
const now = dayjs();
@@ -36,8 +27,7 @@ const selected = ref<string[]>([...historyDiagOptions.map((option) => `${option.
const loading = ref<boolean>(false);
const icmpLoading = ref<boolean>(false);
const alarmLoading = ref<boolean>(false);
const usageLoading = ref<boolean>(false);
watch([icmpLoading, alarmLoading, usageLoading], (loadings) => {
watch([icmpLoading, alarmLoading], (loadings) => {
loading.value = loadings.some((loading) => loading);
});
const icmpHistoryQueryFn = ref<() => void>();
@@ -48,14 +38,9 @@ const alarmHistoryQueryFn = ref<() => void>();
const onExposeAlarmHistoryQueryFn: DeviceAlarmHistoryCardProps['onExposeQueryFn'] = (queryFn) => {
alarmHistoryQueryFn.value = queryFn;
};
const usageHistoryQueryFn = ref<() => void>();
const onExposeUsageHistoryQueryFn: DeviceUsageHistoryCardProps['onExposeQueryFn'] = (queryFn) => {
usageHistoryQueryFn.value = queryFn;
};
const queryData = () => {
if (selected.value.includes('icmp')) icmpHistoryQueryFn.value?.();
if (selected.value.includes('alarm')) alarmHistoryQueryFn.value?.();
if (selected.value.includes('usage')) usageHistoryQueryFn.value?.();
};
const onQuery = () => {
queryData();
@@ -85,16 +70,6 @@ onMounted(() => {
v-model:loading="alarmLoading"
@expose-query-fn="onExposeAlarmHistoryQueryFn"
/>
<DeviceUsageHistoryCard
v-if="selected.includes('usage')"
:ndm-device="ndmDevice"
:station="station"
:cpu-usage-field="'stCommonInfo.CPU使用率'"
:mem-usage-field="'stCommonInfo.内存使用率'"
v-model:range="range"
v-model:loading="usageLoading"
@expose-query-fn="onExposeUsageHistoryQueryFn"
/>
</NFlex>
</template>
@@ -1,6 +1,6 @@
<script setup lang="ts">
import type { NdmDecoderDiagInfo, NdmDecoderResultVO, Station } from '@/apis';
import { DeviceCommonCard, DeviceHardwareCard, DeviceHeaderCard, type DeviceCommonCardProps } from '@/components';
import { DeviceCommonCard, DeviceHardwareCard, DeviceHeaderCard } from '@/components';
import destr from 'destr';
import { NFlex } from 'naive-ui';
import { computed, toRefs } from 'vue';
@@ -19,23 +19,17 @@ const lastDiagInfo = computed(() => {
return result as NdmDecoderDiagInfo;
});
const commonInfo = computed<DeviceCommonCardProps['commonInfo']>(() => {
const commonInfo = computed(() => {
const { stCommonInfo } = lastDiagInfo.value ?? {};
const { 设备ID, 软件版本, 设备厂商, 设备别名, 设备型号, 硬件版本 } = stCommonInfo ?? {};
return [
{
title: '设备型号信息',
items: [
{ label: '设备ID', value: 设备ID || '-' },
{ label: '软件版本', value: 件版本 || '-' },
{ label: '设备厂商', value: 设备厂商 || '-' },
{ label: '设备别名', value: 设备别名 || '-' },
{ label: '设备型号', value: 设备型号 || '-' },
{ label: '硬件版本', value: 硬件版本 || '-' },
],
},
];
return {
设备ID: 设备ID ?? '-',
软件版本: 软件版本 ?? '-',
设备厂商: 设备厂商 ?? '-',
设备别名: 设备别名 ?? '-',
设备型号: 设备型号 ?? '-',
硬件版本: 件版本 ?? '-',
};
});
const cpuUsage = computed(() => lastDiagInfo.value?.stCommonInfo?.CPU使用率);
@@ -36,8 +36,7 @@ const selected = ref([...historyDiagOptions.map((option) => `${option.value}`)])
const loading = ref<boolean>(false);
const icmpLoading = ref<boolean>(false);
const alarmLoading = ref<boolean>(false);
const usageLoading = ref<boolean>(false);
watch([icmpLoading, alarmLoading, usageLoading], (loadings) => {
watch([icmpLoading, alarmLoading], (loadings) => {
loading.value = loadings.some((loading) => loading);
});
const icmpHistoryQueryFn = ref<() => void>();
@@ -92,7 +91,7 @@ onMounted(() => {
:cpu-usage-field="'stCommonInfo.CPU使用率'"
:mem-usage-field="'stCommonInfo.内存使用率'"
v-model:range="range"
v-model:loading="usageLoading"
v-model:loading="alarmLoading"
@expose-query-fn="onExposeUsageHistoryQueryFn"
/>
</NFlex>
@@ -1,6 +1,6 @@
<script setup lang="ts">
import type { NdmNvrDiagInfo, NdmNvrResultVO, Station } from '@/apis';
import { DeviceCommonCard, DeviceHardwareCard, DeviceHeaderCard, NvrDiskCard, NvrRecordCheckCard, type DeviceCommonCardProps } from '@/components';
import { DeviceCommonCard, DeviceHardwareCard, DeviceHeaderCard, NvrDiskCard, NvrRecordCheckCard } from '@/components';
import { isNvrCluster } from '@/helpers';
import destr from 'destr';
import { NFlex } from 'naive-ui';
@@ -20,24 +20,18 @@ const lastDiagInfo = computed(() => {
return result as NdmNvrDiagInfo;
});
const commonInfo = computed<DeviceCommonCardProps['commonInfo']>(() => {
const commonInfo = computed(() => {
const { stCommonInfo } = lastDiagInfo.value ?? {};
if (!stCommonInfo) return undefined;
const { 设备ID, 软件版本, 生产厂商, 设备别名, 设备型号, 硬件版本 } = stCommonInfo;
return [
{
title: '设备型号信息',
items: [
{ label: '设备ID', value: 设备ID || '-' },
{ label: '软件版本', value: 件版本 || '-' },
{ label: '生产厂商', value: 生产厂商 || '-' },
{ label: '设备别名', value: 设备别名 || '-' },
{ label: '设备型号', value: 设备型号 || '-' },
{ label: '硬件版本', value: 硬件版本 || '-' },
],
},
];
return {
设备ID: 设备ID ?? '-',
软件版本: 软件版本 ?? '-',
生产厂商: 生产厂商 ?? '-',
设备别名: 设备别名 ?? '-',
设备型号: 设备型号 ?? '-',
硬件版本: 件版本 ?? '-',
};
});
const cpuUsage = computed(() => lastDiagInfo.value?.stCommonInfo?.CPU使用率);
@@ -42,9 +42,8 @@ const selected = ref([...historyDiagOptions.value.map((option) => `${option.valu
const loading = ref<boolean>(false);
const icmpLoading = ref<boolean>(false);
const alarmLoading = ref<boolean>(false);
const usageLoading = ref<boolean>(false);
const diskLoading = ref<boolean>(false);
watch([icmpLoading, alarmLoading, usageLoading, diskLoading], (loadings) => {
watch([icmpLoading, alarmLoading, diskLoading], (loadings) => {
loading.value = loadings.some((loading) => loading);
});
const icmpHistoryQueryFn = ref<() => void>();
@@ -104,7 +103,7 @@ onMounted(() => {
:cpu-usage-field="'stCommonInfo.CPU使用率'"
:mem-usage-field="'stCommonInfo.内存使用率'"
v-model:range="range"
v-model:loading="usageLoading"
v-model:loading="alarmLoading"
@expose-query-fn="onExposeUsageHistoryQueryFn"
/>
<NvrDiskHistoryCard v-if="selected.includes('disk')" :ndm-device="ndmDevice" :station="station" v-model:range="range" v-model:loading="diskLoading" @expose-query-fn="onExposeDiskHistoryQueryFn" />
@@ -1,7 +1,6 @@
<script setup lang="ts">
import type { NdmSecurityBoxDiagInfo, NdmSecurityBoxResultVO, Station } from '@/apis';
import { DeviceCommonCard, DeviceHardwareCard, DeviceHeaderCard, SecurityBoxCircuitCard, SecurityBoxEnvCard, type DeviceCommonCardProps } from '@/components';
import { SECURITY_BOX_VENDOR_LITERALS } from '@/helpers';
import { DeviceCommonCard, DeviceHardwareCard, DeviceHeaderCard, SecurityBoxCircuitCard, SecurityBoxEnvCard } from '@/components';
import destr from 'destr';
import { NFlex } from 'naive-ui';
import { computed, toRefs } from 'vue';
@@ -20,37 +19,17 @@ const lastDiagInfo = computed(() => {
return result as NdmSecurityBoxDiagInfo;
});
// 解析安防箱厂商
const vendor = computed(() => {
// 先读取诊断信息中的 stCommonInfo 中的设备厂商
if (!!lastDiagInfo.value?.stCommonInfo?.设备厂商) {
return lastDiagInfo.value.stCommonInfo.设备厂商.trim().toLocaleLowerCase();
}
// 如果 stCommonInfo 中没有设备厂商,再读取 ndmDevice 中的 manufacturer
if (!!ndmDevice.value.manufacturer) {
return ndmDevice.value.manufacturer.trim().toLocaleLowerCase();
}
// 如果 ndmDevice 中也没有 manufacturer,返回 beidian 作为兜底
return SECURITY_BOX_VENDOR_LITERALS.beidian;
});
const commonInfo = computed<DeviceCommonCardProps['commonInfo']>(() => {
const commonInfo = computed(() => {
const { stCommonInfo } = lastDiagInfo.value ?? {};
const { 设备ID, 软件版本, 设备厂商, 设备别名, 设备型号, 硬件版本 } = stCommonInfo ?? {};
return [
{
title: '设备型号信息',
items: [
{ label: '设备ID', value: 设备ID || '-' },
{ label: '软件版本', value: 件版本 || '-' },
{ label: '设备厂商', value: 设备厂商 || '-' },
{ label: '设备别名', value: 设备别名 || '-' },
{ label: '设备型号', value: 设备型号 || '-' },
{ label: '硬件版本', value: 硬件版本 || '-' },
],
},
];
return {
设备ID: 设备ID ?? '',
软件版本: 软件版本 ?? '',
设备厂商: 设备厂商 ?? '',
设备别名: 设备别名 ?? '',
设备型号: 设备型号 ?? '',
硬件版本: 件版本 ?? '',
};
});
const cpuUsage = computed(() => lastDiagInfo.value?.stCommonInfo?.CPU使用率);
@@ -70,7 +49,7 @@ const circuits = computed(() => lastDiagInfo.value?.info?.at(0)?.circuits);
<DeviceCommonCard :common-info="commonInfo" />
<DeviceHardwareCard :cpu-usage="cpuUsage" :mem-usage="memUsage" />
<SecurityBoxEnvCard :fan-speeds="fanSpeeds" :temperature="temperature" :humidity="humidity" :switches="switches" />
<SecurityBoxCircuitCard :ndm-device="ndmDevice" :station="station" :vendor="vendor" :circuits="circuits" />
<SecurityBoxCircuitCard :ndm-device="ndmDevice" :station="station" :circuits="circuits" />
</NFlex>
</template>
@@ -6,7 +6,7 @@ import { useMutation } from '@tanstack/vue-query';
import { isCancel } from 'axios';
import destr from 'destr';
import { isString } from 'es-toolkit';
import { NButton, NCard, NFlex, NForm, NFormItem, NFormItemGi, NGrid, NInput, NInputNumber, NSwitch, type FormInst, type FormRules } from 'naive-ui';
import { NButton, NCard, NFlex, NForm, NFormItem, NFormItemGi, NGrid, NInput, NSwitch, type FormInst, type FormRules } from 'naive-ui';
import { computed, onBeforeUnmount, ref, toRefs, useTemplateRef, watch } from 'vue';
const props = defineProps<{
@@ -126,15 +126,9 @@ onBeforeUnmount(() => {
<NFormItem label-placement="left" label="型号">
<NInput v-model:value="localDevice.model" />
</NFormItem>
<NFormItem label-placement="left" label="开关数量">
<NInputNumber clearable v-model:value="localDevice.circuitCount" />
</NFormItem>
<NFormItem label-placement="left" label="团体字符串">
<NInput v-model:value="localDevice.community" />
</NFormItem>
<NFormItem label-placement="left" label="团体字符串(写)">
<NInput v-model:value="localDevice.writeCommunity" />
</NFormItem>
<NFormItem label-placement="left" label="设备描述">
<NInput v-model:value="localDevice.description" />
</NFormItem>
@@ -3,7 +3,7 @@ import { getHighAvailableApi, type NdmServerResultVO, type Station } from '@/api
import { DEVICE_TYPE_LITERALS, tryGetDeviceType } from '@/enums';
import { useSettingStore } from '@/stores';
import { useQuery, useQueryClient } from '@tanstack/vue-query';
import { NAlert, NFlex } from 'naive-ui';
import { NAlert, NCard, NFlex } from 'naive-ui';
import { storeToRefs } from 'pinia';
import { computed, toRefs, watch } from 'vue';
@@ -1,7 +1,6 @@
<script lang="ts">
// 设备参数配置在系统中的key前缀
const DEVICE_PARAM_PREFIXES = {
Camera: 'CAMERA_',
Switch: 'SWITCH_',
Server: 'SERVER_',
Decoder: 'DECODER_',
@@ -68,10 +67,6 @@ const getItemSuffix = (name: string) => {
};
const tabPanes = [
{
tab: '摄像机阈值',
name: DEVICE_PARAM_PREFIXES.Camera,
},
{
tab: '交换机阈值',
name: DEVICE_PARAM_PREFIXES.Switch,
@@ -114,7 +109,7 @@ const show = defineModel<boolean>('show', { required: true });
const { station } = toRefs(props);
const activeTabName = ref<DeviceParamPrefix>(DEVICE_PARAM_PREFIXES.Camera);
const activeTabName = ref<DeviceParamPrefix>(DEVICE_PARAM_PREFIXES.Switch);
const deviceParams = ref<DeviceParamItem[]>([]);
@@ -216,7 +211,7 @@ const onAfterModalEnter = () => {
const onBeforeModalLeave = () => {
saveDeviceParams({ tabName: activeTabName.value, items: deviceParams.value });
activeTabName.value = DEVICE_PARAM_PREFIXES.Camera;
activeTabName.value = DEVICE_PARAM_PREFIXES.Switch;
deviceParams.value = [];
};
</script>
-3
View File
@@ -1,3 +0,0 @@
export * from './nvr';
export * from './security-box';
export * from './switch';
-1
View File
@@ -1 +0,0 @@
export * from './util';
@@ -1,64 +0,0 @@
import { rebootSecurityBoxBeidianApi, rebootSecurityBoxNingTechApi, turnCircuitStatusBeidianApi, turnCircuitStatusNingTechApi, type NdmSecurityBoxResultVO } from '@/apis';
import { objectEntries } from '@vueuse/core';
const UNSUPPORTED_SECURITY_BOX_VENDOR = '不支持的安防箱厂商';
export const SECURITY_BOX_VENDOR_LITERALS = {
beidian: 'beidian',
ningtech: 'ningtech',
} as const;
export type SecurityBoxVendor = keyof typeof SECURITY_BOX_VENDOR_LITERALS;
export const resolveSecurityBoxVendor = (vendor?: string) => {
const entry = objectEntries(SECURITY_BOX_VENDOR_LITERALS).find(([, value]) => value === vendor);
return entry?.at(0);
};
export const normalizeSecurityBoxWriteCommunity = (ndmDevice: NdmSecurityBoxResultVO, vendor?: string) => {
const resolved = resolveSecurityBoxVendor(vendor);
if (resolved === SECURITY_BOX_VENDOR_LITERALS.beidian) {
const community = ndmDevice.community;
if (!community) throw new Error('团体字符串不存在');
return community;
}
if (resolved === SECURITY_BOX_VENDOR_LITERALS.ningtech) {
const community = ndmDevice.writeCommunity;
if (!community) throw new Error('团体字符串(写)不存在');
return community;
}
throw new Error(UNSUPPORTED_SECURITY_BOX_VENDOR);
};
export const normalizeSecurityBoxCircuitIndex = (index: number, vendor?: string) => {
const resolved = resolveSecurityBoxVendor(vendor);
if (resolved === SECURITY_BOX_VENDOR_LITERALS.beidian) {
return index;
}
if (resolved === SECURITY_BOX_VENDOR_LITERALS.ningtech) {
return index + 1;
}
throw new Error(UNSUPPORTED_SECURITY_BOX_VENDOR);
};
export const dispatchTurnCircuitStatusApi = (vendor?: string) => {
const resolved = resolveSecurityBoxVendor(vendor);
if (resolved === SECURITY_BOX_VENDOR_LITERALS.beidian) {
return turnCircuitStatusBeidianApi;
}
if (resolved === SECURITY_BOX_VENDOR_LITERALS.ningtech) {
return turnCircuitStatusNingTechApi;
}
throw new Error(UNSUPPORTED_SECURITY_BOX_VENDOR);
};
export const dispatchRebootSecurityBoxApi = (vendor?: string) => {
const resolved = resolveSecurityBoxVendor(vendor);
if (resolved === SECURITY_BOX_VENDOR_LITERALS.beidian) {
return rebootSecurityBoxBeidianApi;
}
if (resolved === SECURITY_BOX_VENDOR_LITERALS.ningtech) {
return rebootSecurityBoxNingTechApi;
}
throw new Error(UNSUPPORTED_SECURITY_BOX_VENDOR);
};
-1
View File
@@ -1 +0,0 @@
export * from './adapter';
-1
View File
@@ -1 +0,0 @@
export * from './util';
+3 -2
View File
@@ -1,2 +1,3 @@
export * from './device';
export * from './log';
export * from './device-alarm';
export * from './nvr-cluster';
export * from './switch-port';
-1
View File
@@ -1 +0,0 @@
export * from './render';
-1
View File
@@ -1 +0,0 @@
export * from './alarm-log';