refactor: 重构NVR集群处理逻辑,优化设备树展示
抽离NVR集群相关的公共工具函数,重构isNvrCluster的实现逻辑。修复原有设备树中NVR分组的逻辑错误,原本会将所有单机NVR添加到每个集群的子节点中,现在会正确将单机NVR分配到对应集群,未归属的单机NVR作为独立节点。简化设备树组件的代码,统一使用封装后的工具方法处理集群分组。修复设备定位时的集群匹配逻辑,使用更准确的IP包含判断。初始化设备树数据的默认空值,修正类型定义错误。
This commit is contained in:
@@ -8,7 +8,7 @@ const createDeviceNodeKey = (stationCode?: Station['code'], device?: NdmDeviceRe
|
|||||||
import { initStationDevices, type NdmDeviceResultVO, type NdmNvrResultVO, type Station } from '@/apis';
|
import { initStationDevices, type NdmDeviceResultVO, type NdmNvrResultVO, type Station } from '@/apis';
|
||||||
import { useDeviceTree, usePermission, type UseDeviceTreeReturn } from '@/composables';
|
import { useDeviceTree, usePermission, type UseDeviceTreeReturn } from '@/composables';
|
||||||
import { DEVICE_TYPE_NAMES, DEVICE_TYPE_LITERALS, tryGetDeviceType, type DeviceType, PERMISSION_TYPE_LITERALS } from '@/enums';
|
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 { useDeviceStore, usePermissionStore } from '@/stores';
|
||||||
import { watchDebounced, watchImmediate } from '@vueuse/core';
|
import { watchDebounced, watchImmediate } from '@vueuse/core';
|
||||||
import destr from 'destr';
|
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)] });
|
return h(NFlex, { size: 'small' }, { default: () => [renderViewDeviceButton(device, stationCode), renderDeviceStatusTag(device)] });
|
||||||
};
|
};
|
||||||
// 全线设备树
|
// 全线设备树
|
||||||
const lineDeviceTreeData = computed<Record<Station['code'], TreeOption[]>>(() => {
|
const lineDeviceTreeData = computed<Record<DeviceType, TreeOption[]>>(() => {
|
||||||
const treeData: Record<string, 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 */ }) => {
|
deviceTabPanes.forEach(({ name: paneName /* , tab: paneTab */ }) => {
|
||||||
treeData[paneName] = stations.value.map<TreeOption>((station) => {
|
treeData[paneName] = stations.value.map<TreeOption>((station) => {
|
||||||
const { name: stationName, code: stationCode } = station;
|
const { name: stationName, code: stationCode } = station;
|
||||||
@@ -307,28 +317,39 @@ const lineDeviceTreeData = computed<Record<Station['code'], TreeOption[]>>(() =>
|
|||||||
const offlineDevices = devices?.filter((device) => device.deviceStatus === '20');
|
const offlineDevices = devices?.filter((device) => device.deviceStatus === '20');
|
||||||
// 对于录像机,需要根据clusterList字段以分号分隔设备IP,进一步形成子树结构
|
// 对于录像机,需要根据clusterList字段以分号分隔设备IP,进一步形成子树结构
|
||||||
if (paneName === DEVICE_TYPE_LITERALS.ndmNvr) {
|
if (paneName === DEVICE_TYPE_LITERALS.ndmNvr) {
|
||||||
const nvrs = devices as NdmNvrResultVO[];
|
const nvrDevices = devices as NdmNvrResultVO[];
|
||||||
const nvrClusters: NdmNvrResultVO[] = [];
|
|
||||||
const nvrSingletons: NdmNvrResultVO[] = [];
|
const { nvrClusters, nvrTreeMap, nvrStandalones } = createNvrClusterRelationship(nvrDevices);
|
||||||
for (const device of nvrs) {
|
|
||||||
if (isNvrCluster(device)) {
|
|
||||||
nvrClusters.push(device);
|
|
||||||
} else {
|
|
||||||
nvrSingletons.push(device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
label: stationName,
|
label: stationName,
|
||||||
key: stationCode,
|
key: stationCode,
|
||||||
prefix: () => renderStationNodePrefix(station),
|
prefix: () => renderStationNodePrefix(station),
|
||||||
suffix: () => renderIcmpStatistics(onlineDevices?.length ?? 0, offlineDevices?.length ?? 0, devices?.length ?? 0),
|
suffix: () => renderIcmpStatistics(onlineDevices?.length ?? 0, offlineDevices?.length ?? 0, devices?.length ?? 0),
|
||||||
children: nvrClusters.map<TreeOption>((cluster) => {
|
children: [
|
||||||
|
...nvrClusters.map((cluster) => {
|
||||||
return {
|
return {
|
||||||
label: `${cluster.name}`,
|
label: `${cluster.name}`,
|
||||||
key: createDeviceNodeKey(stationCode, cluster),
|
key: createDeviceNodeKey(stationCode, cluster),
|
||||||
prefix: () => renderDeviceNodePrefix(cluster, stationCode),
|
prefix: () => renderDeviceNodePrefix(cluster, stationCode),
|
||||||
suffix: () => `${cluster.ipAddress}`,
|
suffix: () => `${cluster.ipAddress}`,
|
||||||
children: nvrSingletons.map<TreeOption>((device) => {
|
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 {
|
return {
|
||||||
label: `${device.name}`,
|
label: `${device.name}`,
|
||||||
key: createDeviceNodeKey(stationCode, device),
|
key: createDeviceNodeKey(stationCode, device),
|
||||||
@@ -339,15 +360,12 @@ const lineDeviceTreeData = computed<Record<Station['code'], TreeOption[]>>(() =>
|
|||||||
device: device,
|
device: device,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
// 当选择设备时,能获取到设备的所有信息,以及设备所属的车站
|
],
|
||||||
stationCode,
|
|
||||||
device: cluster,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
stationCode,
|
stationCode,
|
||||||
deviceType: activeTab.value,
|
deviceType: activeTab.value,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
// 非录像机设备
|
||||||
return {
|
return {
|
||||||
label: stationName,
|
label: stationName,
|
||||||
key: stationCode,
|
key: stationCode,
|
||||||
@@ -382,20 +400,36 @@ const stationDeviceTreeData = computed<TreeOption[]>(() => {
|
|||||||
const onlineCount = stationDevices[deviceType].filter((device) => device.deviceStatus === '10').length;
|
const onlineCount = stationDevices[deviceType].filter((device) => device.deviceStatus === '10').length;
|
||||||
const offlineCount = stationDevices[deviceType].filter((device) => device.deviceStatus === '20').length;
|
const offlineCount = stationDevices[deviceType].filter((device) => device.deviceStatus === '20').length;
|
||||||
if (deviceType === DEVICE_TYPE_LITERALS.ndmNvr) {
|
if (deviceType === DEVICE_TYPE_LITERALS.ndmNvr) {
|
||||||
const nvrs = stationDevices[deviceType] as NdmNvrResultVO[];
|
const nvrDevices = stationDevices[deviceType] as NdmNvrResultVO[];
|
||||||
const clusters = nvrs.filter((nvr) => isNvrCluster(nvr));
|
|
||||||
const singletons = nvrs.filter((nvr) => !isNvrCluster(nvr));
|
const { nvrClusters, nvrTreeMap, nvrStandalones } = createNvrClusterRelationship(nvrDevices);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
label: `${DEVICE_TYPE_NAMES[deviceType]}`,
|
label: `${DEVICE_TYPE_NAMES[deviceType]}`,
|
||||||
key: deviceType,
|
key: deviceType,
|
||||||
suffix: () => renderIcmpStatistics(onlineCount, offlineCount, nvrs.length),
|
suffix: () => renderIcmpStatistics(onlineCount, offlineCount, nvrDevices.length),
|
||||||
children: clusters.map<TreeOption>((cluster) => {
|
children: [
|
||||||
|
...nvrClusters.map((cluster) => {
|
||||||
return {
|
return {
|
||||||
label: `${cluster.name}`,
|
label: `${cluster.name}`,
|
||||||
key: createDeviceNodeKey(stationCode, cluster),
|
key: createDeviceNodeKey(stationCode, cluster),
|
||||||
prefix: () => renderDeviceNodePrefix(cluster, stationCode),
|
prefix: () => renderDeviceNodePrefix(cluster, stationCode),
|
||||||
suffix: () => `${cluster.ipAddress}`,
|
suffix: () => `${cluster.ipAddress}`,
|
||||||
children: singletons.map<TreeOption>((device) => {
|
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 {
|
return {
|
||||||
label: `${device.name}`,
|
label: `${device.name}`,
|
||||||
key: createDeviceNodeKey(stationCode, device),
|
key: createDeviceNodeKey(stationCode, device),
|
||||||
@@ -405,14 +439,12 @@ const stationDeviceTreeData = computed<TreeOption[]>(() => {
|
|||||||
device,
|
device,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
stationCode,
|
],
|
||||||
device: cluster,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
stationCode,
|
stationCode,
|
||||||
deviceType,
|
deviceType,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
// 非录像机设备
|
||||||
return {
|
return {
|
||||||
label: `${DEVICE_TYPE_NAMES[deviceType]}`,
|
label: `${DEVICE_TYPE_NAMES[deviceType]}`,
|
||||||
key: deviceType,
|
key: deviceType,
|
||||||
@@ -488,9 +520,13 @@ const onLocateDeviceTree = async () => {
|
|||||||
if (stationDevices) {
|
if (stationDevices) {
|
||||||
const selectedNvr = selectedDevice.value as NdmNvrResultVO;
|
const selectedNvr = selectedDevice.value as NdmNvrResultVO;
|
||||||
if (!isNvrCluster(selectedNvr)) {
|
if (!isNvrCluster(selectedNvr)) {
|
||||||
const nvrs = stationDevices[DEVICE_TYPE_LITERALS.ndmNvr];
|
const nvrDevices = stationDevices[DEVICE_TYPE_LITERALS.ndmNvr];
|
||||||
const clusters = nvrs.filter((nvr) => isNvrCluster(nvr) && nvr.clusterList?.includes(selectedNvr.clusterList ?? ''));
|
const clusters = nvrDevices.filter((device) => {
|
||||||
expandedKeys.value.push(...clusters.map((nvr) => createDeviceNodeKey(stationCode, nvr)));
|
if (!isNvrCluster(device)) return false;
|
||||||
|
const cluster = device;
|
||||||
|
return nvrInCluster(selectedNvr, cluster);
|
||||||
|
});
|
||||||
|
expandedKeys.value.push(...clusters.map((cluster) => createDeviceNodeKey(stationCode, cluster)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,56 @@
|
|||||||
import type { NdmNvrResultVO } from '@/apis';
|
import type { NdmNvrResultVO } from '@/apis';
|
||||||
|
|
||||||
|
// 解析 clusterList 字段
|
||||||
|
export const parseIpListFromClusterList = (nvr: NdmNvrResultVO) => {
|
||||||
|
const ipList = (nvr.clusterList ?? '').split(';');
|
||||||
|
return ipList.map((ip) => ip.trim()).filter((ip) => !!ip);
|
||||||
|
};
|
||||||
|
|
||||||
export const isNvrCluster = (maybeNvrCluster: NdmNvrResultVO) => {
|
export const isNvrCluster = (maybeNvrCluster: NdmNvrResultVO) => {
|
||||||
const { ipAddress, clusterList } = maybeNvrCluster;
|
const { ipAddress } = maybeNvrCluster;
|
||||||
if (!clusterList?.trim()) return false;
|
const ipList = parseIpListFromClusterList(maybeNvrCluster);
|
||||||
if (clusterList === ipAddress) return false;
|
if (ipList.length === 0) return false;
|
||||||
|
if (ipList.length === 1 && ipList.at(0) === ipAddress) return false;
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const nvrInCluster = (nvr: NdmNvrResultVO, cluster: NdmNvrResultVO) => {
|
||||||
|
const { ipAddress } = nvr;
|
||||||
|
if (!ipAddress) return false;
|
||||||
|
const ipList = parseIpListFromClusterList(cluster);
|
||||||
|
return ipList.includes(ipAddress);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createNvrClusterRelationship = (nvrDevices: NdmNvrResultVO[]) => {
|
||||||
|
const nvrClusters = nvrDevices.filter((nvr) => isNvrCluster(nvr));
|
||||||
|
const nvrNotClusters = nvrDevices.filter((nvr) => !isNvrCluster(nvr));
|
||||||
|
const nodedNvrIpAddressSet = new Set<string | null>();
|
||||||
|
const nvrStandalones: NdmNvrResultVO[] = [];
|
||||||
|
const nvrTreeMap = new Map<string, NdmNvrResultVO[]>();
|
||||||
|
// 遍历所有非集群录像机,将它们分配到对应的录像机集群中
|
||||||
|
for (const nvr of nvrNotClusters) {
|
||||||
|
for (const cluster of nvrClusters) {
|
||||||
|
if (nvrInCluster(nvr, cluster)) {
|
||||||
|
if (!!cluster.ipAddress) {
|
||||||
|
// 写入录像机与集群的关系
|
||||||
|
nvrTreeMap.set(cluster.ipAddress, [...(nvrTreeMap.get(cluster.ipAddress) ?? []), nvr]);
|
||||||
|
// 记录已分配的录像机IP地址
|
||||||
|
nodedNvrIpAddressSet.add(nvr.ipAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 分配完成后,过滤出未分配的录像机,形成录像机单机列表
|
||||||
|
nvrNotClusters.forEach((device) => {
|
||||||
|
if (!nodedNvrIpAddressSet.has(device.ipAddress)) {
|
||||||
|
nvrStandalones.push(device);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
nvrClusters,
|
||||||
|
// nvrNotClusters,
|
||||||
|
nvrTreeMap,
|
||||||
|
nvrStandalones,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user