feat: enhance device tree
This commit is contained in:
@@ -73,9 +73,8 @@ const treeData = computed<TreeOption[]>(() => {
|
||||
key: type,
|
||||
children: offlineDeviceList.map<TreeOption>((device) => ({
|
||||
label: `${device.name}`,
|
||||
key: device.deviceId,
|
||||
key: device.id,
|
||||
suffix: () => `${device.ipAddress ?? '未知IP地址'}`,
|
||||
isLeaf: true,
|
||||
device,
|
||||
})),
|
||||
};
|
||||
@@ -85,7 +84,7 @@ const treeData = computed<TreeOption[]>(() => {
|
||||
const nodeProps = ({ option }: { option: TreeOption }) => {
|
||||
return {
|
||||
ondblclick: () => {
|
||||
if (option.isLeaf) {
|
||||
if (option['device']) {
|
||||
const queryControlStore = useQueryControlStore();
|
||||
queryControlStore.enablePolling();
|
||||
const device = option['device'] as NdmDeviceVO;
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import type { NdmDeviceResultVO, NdmDeviceVO } from '@/apis/models/device';
|
||||
import type { NdmDeviceResultVO, NdmDeviceVO, NdmNvrResultVO } from '@/apis/models/device';
|
||||
import { useLineDevicesQuery } from '@/composables/query/use-line-devices-query';
|
||||
import { DeviceType, DeviceTypeName, type DeviceTypeCode, type DeviceTypeKey } from '@/enums/device-type';
|
||||
import { useStationStore } from '@/stores/station';
|
||||
import { ChevronBack } from '@vicons/ionicons5';
|
||||
import {
|
||||
NButton,
|
||||
NButtonGroup,
|
||||
NFlex,
|
||||
NIcon,
|
||||
NInput,
|
||||
NLayout,
|
||||
@@ -18,11 +21,11 @@ import {
|
||||
NTabs,
|
||||
NTag,
|
||||
NTree,
|
||||
type TreeInst,
|
||||
type TreeOption,
|
||||
type TreeOverrideNodeClickBehavior,
|
||||
} from 'naive-ui';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { h } from 'vue';
|
||||
import { h, nextTick, onMounted, useTemplateRef } from 'vue';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { useRoute, useRouter, type LocationQuery } from 'vue-router';
|
||||
import { destr } from 'destr';
|
||||
@@ -40,13 +43,6 @@ const deviceTabPanes = Object.keys(DeviceType).map((key) => {
|
||||
};
|
||||
});
|
||||
|
||||
const override: TreeOverrideNodeClickBehavior = ({ option }) => {
|
||||
if (option.children) {
|
||||
return 'toggleExpand';
|
||||
}
|
||||
return 'default';
|
||||
};
|
||||
|
||||
// 获取车站和设备数据
|
||||
const stationStore = useStationStore();
|
||||
const { stationList } = storeToRefs(stationStore);
|
||||
@@ -59,6 +55,62 @@ const lineDeviceTreeData = computed<Record<string, TreeOption[]>>(() => {
|
||||
const devices = lineDevices.value?.[stationCode][paneName];
|
||||
const onlineDevices = devices?.filter((device) => device.deviceStatus === '10');
|
||||
const offlineDevices = devices?.filter((device) => device.deviceStatus === '20');
|
||||
// 对于录像机,需要根据clusterList字段以分号分隔设备IP,进一步形成子树结构
|
||||
if (paneName === DeviceType.Nvr) {
|
||||
const nvrs = devices as NdmNvrResultVO[] | undefined;
|
||||
return {
|
||||
label: stationName,
|
||||
key: stationCode,
|
||||
suffix: () => `(${onlineDevices?.length ?? 0}/${offlineDevices?.length ?? 0}/${devices?.length ?? 0})`,
|
||||
children: nvrs
|
||||
?.filter((device) => {
|
||||
const nvr = device as NdmNvrResultVO;
|
||||
return !!nvr.clusterList?.trim() && nvr.clusterList !== nvr.ipAddress;
|
||||
})
|
||||
.map<TreeOption>((nvrCluster) => {
|
||||
return {
|
||||
label: `${nvrCluster.name}`,
|
||||
key: nvrCluster.deviceId,
|
||||
suffix: () => `${nvrCluster.ipAddress}`,
|
||||
prefix: () => {
|
||||
return h(
|
||||
NTag,
|
||||
{ type: nvrCluster.deviceStatus === '10' ? 'success' : nvrCluster.deviceStatus === '20' ? 'error' : 'warning', size: 'tiny' },
|
||||
{
|
||||
default: () => (nvrCluster.deviceStatus === '10' ? '在线' : nvrCluster.deviceStatus === '20' ? '离线' : '未知'),
|
||||
},
|
||||
);
|
||||
},
|
||||
children: nvrs
|
||||
.filter((nvr) => {
|
||||
return nvrCluster.clusterList?.includes(nvr.ipAddress ?? '');
|
||||
})
|
||||
.map<TreeOption>((nvr) => {
|
||||
return {
|
||||
label: `${nvr.name}`,
|
||||
key: nvr.deviceId,
|
||||
suffix: () => `${nvr.ipAddress}`,
|
||||
prefix: () => {
|
||||
return h(
|
||||
NTag,
|
||||
{ type: nvr.deviceStatus === '10' ? 'success' : nvr.deviceStatus === '20' ? 'error' : 'warning', size: 'tiny' },
|
||||
{
|
||||
default: () => (nvr.deviceStatus === '10' ? '在线' : nvr.deviceStatus === '20' ? '离线' : '未知'),
|
||||
},
|
||||
);
|
||||
},
|
||||
// 当选择设备时,能获取到设备的所有信息,以及设备所属的车站
|
||||
device: nvr,
|
||||
stationCode,
|
||||
};
|
||||
}),
|
||||
// 当选择设备时,能获取到设备的所有信息,以及设备所属的车站
|
||||
device: nvrCluster,
|
||||
stationCode,
|
||||
};
|
||||
}),
|
||||
};
|
||||
}
|
||||
return {
|
||||
label: stationName,
|
||||
key: stationCode,
|
||||
@@ -76,8 +128,7 @@ const lineDeviceTreeData = computed<Record<string, TreeOption[]>>(() => {
|
||||
},
|
||||
);
|
||||
},
|
||||
isLeaf: true,
|
||||
// TODO: 当选择设备时,能获取到设备的所有信息,以及设备所属的车站
|
||||
// 当选择设备时,能获取到设备的所有信息,以及设备所属的车站
|
||||
device,
|
||||
stationCode,
|
||||
})),
|
||||
@@ -90,7 +141,8 @@ const lineDeviceTreeData = computed<Record<string, TreeOption[]>>(() => {
|
||||
const selectedTab = ref<DeviceTypeCode>(DeviceType.Camera);
|
||||
const selectedKeys = ref<string[]>([]);
|
||||
const selectedDevice = ref<NdmDeviceResultVO>();
|
||||
const initFromRouteQuery = (query: LocationQuery) => {
|
||||
// 从路由参数中设置选中的设备
|
||||
const setFromRouteQuery = (query: LocationQuery) => {
|
||||
const { stationCode, deviceType, deviceId } = query;
|
||||
let stnCode = '';
|
||||
let devType: DeviceTypeCode = DeviceType.Camera;
|
||||
@@ -102,18 +154,20 @@ const initFromRouteQuery = (query: LocationQuery) => {
|
||||
selectedKeys.value = [devId];
|
||||
const stnDevices = lineDevices.value?.[stnCode];
|
||||
const devices = stnDevices?.[devType];
|
||||
// 如果没找到,那还是赋值为原来选择的设备
|
||||
selectedDevice.value = devices?.find((device) => device.deviceId === deviceId) ?? selectedDevice.value;
|
||||
};
|
||||
watch(
|
||||
() => route.query,
|
||||
(query) => initFromRouteQuery(query),
|
||||
{ immediate: true },
|
||||
);
|
||||
watch(lineDevices, () => initFromRouteQuery(route.query), { immediate: true });
|
||||
// 页面加载时,需要设置选中的设备
|
||||
onMounted(() => setFromRouteQuery(route.query));
|
||||
// 页面加载时设备数据可能不存在,因此当设备数据更新时,需要重新设置选中的设备
|
||||
watch(lineDevices, () => setFromRouteQuery(route.query), { immediate: true });
|
||||
// 更改选择的设备类型时,更新路由查询参数
|
||||
watch(selectedTab, (newTab) => router.replace({ query: { ...route.query, deviceType: newTab } }));
|
||||
// 点击设备时更新路由查询参数
|
||||
const nodeProps = ({ option }: { option: TreeOption }) => {
|
||||
return {
|
||||
ondblclick: () => {
|
||||
if (option.isLeaf) {
|
||||
onclick: () => {
|
||||
if (option['device']) {
|
||||
const device = option['device'] as NdmDeviceResultVO;
|
||||
selectedDevice.value = device;
|
||||
router.replace({
|
||||
@@ -128,7 +182,6 @@ const nodeProps = ({ option }: { option: TreeOption }) => {
|
||||
},
|
||||
};
|
||||
};
|
||||
watch(selectedTab, (newTab) => router.replace({ query: { ...route.query, deviceType: newTab } }));
|
||||
|
||||
const searchInput = ref('');
|
||||
const statusInput = ref('');
|
||||
@@ -149,28 +202,44 @@ const searchFilter = (pattern: string, node: TreeOption): boolean => {
|
||||
const statusMatched = status === '' || status === deviceStatus;
|
||||
return searchMatched && statusMatched;
|
||||
};
|
||||
|
||||
// 定位到选中的设备
|
||||
const deviceTreeInsts = useTemplateRef<TreeInst[]>('deviceTreeInsts');
|
||||
const onClickLocateDeviceTree = () => {
|
||||
selectedTab.value = (selectedDevice.value?.deviceType ?? selectedTab.value) as DeviceTypeCode;
|
||||
// 等待Tab切换完成后再执行滚动
|
||||
nextTick(() => {
|
||||
const inst = deviceTreeInsts.value?.at(0);
|
||||
inst?.scrollTo({ key: selectedDevice.value?.deviceId, behavior: 'smooth' });
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NLayout has-sider style="height: 100%">
|
||||
<NLayoutSider bordered :width="540" :collapsed-width="0" show-trigger="arrow-circle">
|
||||
<NLayoutSider bordered :width="540" :collapsed-width="0" show-trigger="bar">
|
||||
<div style="height: 100%; display: flex; flex-direction: column">
|
||||
<div style="flex-shrink: 0; padding: 12px">
|
||||
<NInput v-model:value="searchInput" placeholder="搜索设备名称或IP地址" />
|
||||
<NRadioGroup v-model:value="statusInput">
|
||||
<NRadio value="">全部</NRadio>
|
||||
<NRadio value="10">在线</NRadio>
|
||||
<NRadio value="20">离线</NRadio>
|
||||
</NRadioGroup>
|
||||
<NInput v-model:value="searchInput" placeholder="搜索设备名称或IP地址" clearable />
|
||||
<NFlex justify="space-between" align="center">
|
||||
<NRadioGroup v-model:value="statusInput">
|
||||
<NRadio value="">全部</NRadio>
|
||||
<NRadio value="10">在线</NRadio>
|
||||
<NRadio value="20">离线</NRadio>
|
||||
</NRadioGroup>
|
||||
<NButtonGroup>
|
||||
<NButton text size="tiny" type="info" @click="onClickLocateDeviceTree">定位</NButton>
|
||||
</NButtonGroup>
|
||||
</NFlex>
|
||||
</div>
|
||||
|
||||
<div style="flex: 1; min-height: 0">
|
||||
<NTabs v-model:value="selectedTab" animated type="line" placement="left" style="height: 100%">
|
||||
<NTabPane v-for="pane in deviceTabPanes" :key="pane.name" :name="pane.name" :tab="pane.tab">
|
||||
<NTree
|
||||
:ref="'deviceTreeInsts'"
|
||||
v-model:selected-keys="selectedKeys"
|
||||
:data="lineDeviceTreeData[pane.name]"
|
||||
:override-default-node-click-behavior="override"
|
||||
:node-props="nodeProps"
|
||||
:show-irrelevant-nodes="false"
|
||||
:pattern="searchPattern"
|
||||
@@ -187,7 +256,7 @@ const searchFilter = (pattern: string, node: TreeOption): boolean => {
|
||||
</div>
|
||||
</div>
|
||||
</NLayoutSider>
|
||||
<NLayoutContent>
|
||||
<NLayoutContent :content-style="{ 'padding-left': '24px' }">
|
||||
<NPageHeader v-if="route.query['from']" title="" @back="onClickBack">
|
||||
<template #back>
|
||||
<NIcon>
|
||||
@@ -197,10 +266,10 @@ const searchFilter = (pattern: string, node: TreeOption): boolean => {
|
||||
</template>
|
||||
</NPageHeader>
|
||||
<div>selectedKeys: {{ selectedKeys }}</div>
|
||||
<NScrollbar style="width: 500px; height: 400px">
|
||||
<div>selectedDevice:</div>
|
||||
<NScrollbar style="width: 500px; height: 400px; border: solid 1px salmon">
|
||||
<pre>{{ selectedDevice }}</pre>
|
||||
</NScrollbar>
|
||||
<!-- <pre style="width: 500px; height: 100%; overflow: scroll">{{ lineDevices }}</pre> -->
|
||||
</NLayoutContent>
|
||||
</NLayout>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user