refactor: 重构NVR集群处理逻辑,优化设备树展示

抽离NVR集群相关的公共工具函数,重构isNvrCluster的实现逻辑。修复原有设备树中NVR分组的逻辑错误,原本会将所有单机NVR添加到每个集群的子节点中,现在会正确将单机NVR分配到对应集群,未归属的单机NVR作为独立节点。简化设备树组件的代码,统一使用封装后的工具方法处理集群分组。修复设备定位时的集群匹配逻辑,使用更准确的IP包含判断。初始化设备树数据的默认空值,修正类型定义错误。
This commit is contained in:
yangsy
2026-05-20 21:18:54 +08:00
parent 0b39c9c602
commit ca7d6baa2e
2 changed files with 149 additions and 65 deletions
@@ -8,7 +8,7 @@ const createDeviceNodeKey = (stationCode?: Station['code'], device?: NdmDeviceRe
import { initStationDevices, type NdmDeviceResultVO, type NdmNvrResultVO, type Station } from '@/apis';
import { useDeviceTree, usePermission, type UseDeviceTreeReturn } from '@/composables';
import { DEVICE_TYPE_NAMES, DEVICE_TYPE_LITERALS, tryGetDeviceType, type DeviceType, PERMISSION_TYPE_LITERALS } from '@/enums';
import { isNvrCluster } from '@/helpers';
import { createNvrClusterRelationship, isNvrCluster, nvrInCluster } from '@/helpers';
import { useDeviceStore, usePermissionStore } from '@/stores';
import { watchDebounced, watchImmediate } from '@vueuse/core';
import destr from 'destr';
@@ -297,8 +297,18 @@ const renderDeviceNodePrefix = (device: NdmDeviceResultVO, stationCode: Station[
return h(NFlex, { size: 'small' }, { default: () => [renderViewDeviceButton(device, stationCode), renderDeviceStatusTag(device)] });
};
// 全线设备树
const lineDeviceTreeData = computed<Record<Station['code'], TreeOption[]>>(() => {
const treeData: Record<string, TreeOption[]> = {};
const lineDeviceTreeData = computed<Record<DeviceType, TreeOption[]>>(() => {
const treeData: Record<DeviceType, TreeOption[]> = {
[DEVICE_TYPE_LITERALS.ndmCamera]: [],
[DEVICE_TYPE_LITERALS.ndmNvr]: [],
[DEVICE_TYPE_LITERALS.ndmSwitch]: [],
[DEVICE_TYPE_LITERALS.ndmDecoder]: [],
[DEVICE_TYPE_LITERALS.ndmSecurityBox]: [],
[DEVICE_TYPE_LITERALS.ndmMediaServer]: [],
[DEVICE_TYPE_LITERALS.ndmVideoServer]: [],
[DEVICE_TYPE_LITERALS.ndmKeyboard]: [],
[DEVICE_TYPE_LITERALS.ndmAlarmHost]: [],
};
deviceTabPanes.forEach(({ name: paneName /* , tab: paneTab */ }) => {
treeData[paneName] = stations.value.map<TreeOption>((station) => {
const { name: stationName, code: stationCode } = station;
@@ -307,47 +317,55 @@ const lineDeviceTreeData = computed<Record<Station['code'], TreeOption[]>>(() =>
const offlineDevices = devices?.filter((device) => device.deviceStatus === '20');
// 对于录像机,需要根据clusterList字段以分号分隔设备IP,进一步形成子树结构
if (paneName === DEVICE_TYPE_LITERALS.ndmNvr) {
const nvrs = devices as NdmNvrResultVO[];
const nvrClusters: NdmNvrResultVO[] = [];
const nvrSingletons: NdmNvrResultVO[] = [];
for (const device of nvrs) {
if (isNvrCluster(device)) {
nvrClusters.push(device);
} else {
nvrSingletons.push(device);
}
}
const nvrDevices = devices as NdmNvrResultVO[];
const { nvrClusters, nvrTreeMap, nvrStandalones } = createNvrClusterRelationship(nvrDevices);
return {
label: stationName,
key: stationCode,
prefix: () => renderStationNodePrefix(station),
suffix: () => renderIcmpStatistics(onlineDevices?.length ?? 0, offlineDevices?.length ?? 0, devices?.length ?? 0),
children: nvrClusters.map<TreeOption>((cluster) => {
return {
label: `${cluster.name}`,
key: createDeviceNodeKey(stationCode, cluster),
prefix: () => renderDeviceNodePrefix(cluster, stationCode),
suffix: () => `${cluster.ipAddress}`,
children: nvrSingletons.map<TreeOption>((device) => {
return {
label: `${device.name}`,
key: createDeviceNodeKey(stationCode, device),
prefix: () => renderDeviceNodePrefix(device, stationCode),
suffix: () => `${device.ipAddress}`,
// 当选择设备时,能获取到设备的所有信息,以及设备所属的车站
stationCode,
device: device,
};
}),
// 当选择设备时,能获取到设备的所有信息,以及设备所属的车站
stationCode,
device: cluster,
};
}),
children: [
...nvrClusters.map((cluster) => {
return {
label: `${cluster.name}`,
key: createDeviceNodeKey(stationCode, cluster),
prefix: () => renderDeviceNodePrefix(cluster, stationCode),
suffix: () => `${cluster.ipAddress}`,
children: (nvrTreeMap.get(cluster.ipAddress ?? '') ?? []).map((clusterNode) => {
return {
label: `${clusterNode.name}`,
key: createDeviceNodeKey(stationCode, clusterNode),
prefix: () => renderDeviceNodePrefix(clusterNode, stationCode),
suffix: () => `${clusterNode.ipAddress}`,
// 当选择设备时,能获取到设备的所有信息,以及设备所属的车站
stationCode,
device: clusterNode,
};
}),
// 当选择设备时,能获取到设备的所有信息,以及设备所属的车站
stationCode,
device: cluster,
};
}),
...nvrStandalones.map((device) => {
return {
label: `${device.name}`,
key: createDeviceNodeKey(stationCode, device),
prefix: () => renderDeviceNodePrefix(device, stationCode),
suffix: () => `${device.ipAddress}`,
// 当选择设备时,能获取到设备的所有信息,以及设备所属的车站
stationCode,
device: device,
};
}),
],
stationCode,
deviceType: activeTab.value,
};
}
// 非录像机设备
return {
label: stationName,
key: stationCode,
@@ -382,37 +400,51 @@ const stationDeviceTreeData = computed<TreeOption[]>(() => {
const onlineCount = stationDevices[deviceType].filter((device) => device.deviceStatus === '10').length;
const offlineCount = stationDevices[deviceType].filter((device) => device.deviceStatus === '20').length;
if (deviceType === DEVICE_TYPE_LITERALS.ndmNvr) {
const nvrs = stationDevices[deviceType] as NdmNvrResultVO[];
const clusters = nvrs.filter((nvr) => isNvrCluster(nvr));
const singletons = nvrs.filter((nvr) => !isNvrCluster(nvr));
const nvrDevices = stationDevices[deviceType] as NdmNvrResultVO[];
const { nvrClusters, nvrTreeMap, nvrStandalones } = createNvrClusterRelationship(nvrDevices);
return {
label: `${DEVICE_TYPE_NAMES[deviceType]}`,
key: deviceType,
suffix: () => renderIcmpStatistics(onlineCount, offlineCount, nvrs.length),
children: clusters.map<TreeOption>((cluster) => {
return {
label: `${cluster.name}`,
key: createDeviceNodeKey(stationCode, cluster),
prefix: () => renderDeviceNodePrefix(cluster, stationCode),
suffix: () => `${cluster.ipAddress}`,
children: singletons.map<TreeOption>((device) => {
return {
label: `${device.name}`,
key: createDeviceNodeKey(stationCode, device),
prefix: () => renderDeviceNodePrefix(device, stationCode),
suffix: () => `${device.ipAddress}`,
stationCode,
device,
};
}),
stationCode,
device: cluster,
};
}),
suffix: () => renderIcmpStatistics(onlineCount, offlineCount, nvrDevices.length),
children: [
...nvrClusters.map((cluster) => {
return {
label: `${cluster.name}`,
key: createDeviceNodeKey(stationCode, cluster),
prefix: () => renderDeviceNodePrefix(cluster, stationCode),
suffix: () => `${cluster.ipAddress}`,
children: (nvrTreeMap.get(cluster.ipAddress ?? '') ?? []).map((clusterNode) => {
return {
label: `${clusterNode.name}`,
key: createDeviceNodeKey(stationCode, clusterNode),
prefix: () => renderDeviceNodePrefix(clusterNode, stationCode),
suffix: () => `${clusterNode.ipAddress}`,
stationCode,
device: clusterNode,
};
}),
stationCode,
device: cluster,
};
}),
...nvrStandalones.map((device) => {
return {
label: `${device.name}`,
key: createDeviceNodeKey(stationCode, device),
prefix: () => renderDeviceNodePrefix(device, stationCode),
suffix: () => `${device.ipAddress}`,
stationCode,
device,
};
}),
],
stationCode,
deviceType,
};
}
// 非录像机设备
return {
label: `${DEVICE_TYPE_NAMES[deviceType]}`,
key: deviceType,
@@ -488,9 +520,13 @@ const onLocateDeviceTree = async () => {
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) => createDeviceNodeKey(stationCode, nvr)));
const nvrDevices = stationDevices[DEVICE_TYPE_LITERALS.ndmNvr];
const clusters = nvrDevices.filter((device) => {
if (!isNvrCluster(device)) return false;
const cluster = device;
return nvrInCluster(selectedNvr, cluster);
});
expandedKeys.value.push(...clusters.map((cluster) => createDeviceNodeKey(stationCode, cluster)));
}
}
}