feat: migrate id and improve interaction
- use DB id of device to mark tree node key - dblclick offlien device node to jump to DevicePage - dblclick device node to view device detail
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { NInput, NModal, NTree } from 'naive-ui';
|
import { NButton, NInput, NModal, NTree } from 'naive-ui';
|
||||||
import { computed, ref, toRefs, watch } from 'vue';
|
import { computed, ref, toRefs, watch } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import type { TreeOption, TreeOverrideNodeClickBehavior } from 'naive-ui';
|
import type { TreeOption, TreeOverrideNodeClickBehavior } from 'naive-ui';
|
||||||
@@ -17,6 +17,7 @@ import type {
|
|||||||
import { useQueryControlStore } from '@/stores/query-control';
|
import { useQueryControlStore } from '@/stores/query-control';
|
||||||
import { DeviceType, DeviceTypeName } from '@/enums/device-type';
|
import { DeviceType, DeviceTypeName } from '@/enums/device-type';
|
||||||
import type { Station } from '@/apis/domains';
|
import type { Station } from '@/apis/domains';
|
||||||
|
import { h } from 'vue';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
station: Station;
|
station: Station;
|
||||||
@@ -51,8 +52,8 @@ watch(show, (newValue) => {
|
|||||||
|
|
||||||
const searchPattern = ref('');
|
const searchPattern = ref('');
|
||||||
const searchFilter: (pattern: string, node: TreeOption) => boolean = (pattern, node) => {
|
const searchFilter: (pattern: string, node: TreeOption) => boolean = (pattern, node) => {
|
||||||
const device = node['device'] as NdmDeviceVO;
|
const device = node['device'] as NdmDeviceVO | undefined;
|
||||||
const { name, ipAddress } = device;
|
const { name, ipAddress } = device ?? {};
|
||||||
return (name ?? '').includes(pattern) || (ipAddress ?? '').includes(pattern);
|
return (name ?? '').includes(pattern) || (ipAddress ?? '').includes(pattern);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -75,12 +76,38 @@ const treeData = computed<TreeOption[]>(() => {
|
|||||||
label: `${device.name}`,
|
label: `${device.name}`,
|
||||||
key: device.id,
|
key: device.id,
|
||||||
suffix: () => `${device.ipAddress ?? '未知IP地址'}`,
|
suffix: () => `${device.ipAddress ?? '未知IP地址'}`,
|
||||||
|
prefix: () => {
|
||||||
|
return h(
|
||||||
|
NButton,
|
||||||
|
{
|
||||||
|
text: true,
|
||||||
|
size: 'tiny',
|
||||||
|
type: 'info',
|
||||||
|
onClick: () => {
|
||||||
|
const queryControlStore = useQueryControlStore();
|
||||||
|
queryControlStore.enablePolling();
|
||||||
|
const dev = device as NdmDeviceVO;
|
||||||
|
router.push({
|
||||||
|
path: '/device',
|
||||||
|
query: {
|
||||||
|
stationCode: station.value.code,
|
||||||
|
deviceType: dev.deviceType,
|
||||||
|
deviceDBId: dev.id,
|
||||||
|
from: route.path,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ default: () => h('span', '查看') },
|
||||||
|
);
|
||||||
|
},
|
||||||
device,
|
device,
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 双击设备节点,跳转到`实时设备状`态页面
|
||||||
const nodeProps = ({ option }: { option: TreeOption }) => {
|
const nodeProps = ({ option }: { option: TreeOption }) => {
|
||||||
return {
|
return {
|
||||||
ondblclick: () => {
|
ondblclick: () => {
|
||||||
@@ -93,7 +120,7 @@ const nodeProps = ({ option }: { option: TreeOption }) => {
|
|||||||
query: {
|
query: {
|
||||||
stationCode: station.value.code,
|
stationCode: station.value.code,
|
||||||
deviceType: device.deviceType,
|
deviceType: device.deviceType,
|
||||||
deviceId: device.deviceId,
|
deviceDBId: device.id,
|
||||||
from: route.path,
|
from: route.path,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -103,7 +130,7 @@ const nodeProps = ({ option }: { option: TreeOption }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const override: TreeOverrideNodeClickBehavior = ({ option }) => {
|
const override: TreeOverrideNodeClickBehavior = ({ option }) => {
|
||||||
if (option.children) {
|
if (!option['device']) {
|
||||||
return 'toggleExpand';
|
return 'toggleExpand';
|
||||||
}
|
}
|
||||||
return 'default';
|
return 'default';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { NdmDeviceResultVO, NdmDeviceVO, NdmNvrResultVO } from '@/apis/models/device';
|
import type { NdmDeviceResultVO, NdmNvrResultVO } from '@/apis/models/device';
|
||||||
import { useLineDevicesQuery } from '@/composables/query/use-line-devices-query';
|
import { useLineDevicesQuery } from '@/composables/query/use-line-devices-query';
|
||||||
import { DeviceType, DeviceTypeName, type DeviceTypeCode, type DeviceTypeKey } from '@/enums/device-type';
|
import { DeviceType, DeviceTypeName, type DeviceTypeCode, type DeviceTypeKey } from '@/enums/device-type';
|
||||||
import { useStationStore } from '@/stores/station';
|
import { useStationStore } from '@/stores/station';
|
||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
NTabs,
|
NTabs,
|
||||||
NTag,
|
NTag,
|
||||||
NTree,
|
NTree,
|
||||||
|
type TagProps,
|
||||||
type TreeInst,
|
type TreeInst,
|
||||||
type TreeOption,
|
type TreeOption,
|
||||||
} from 'naive-ui';
|
} from 'naive-ui';
|
||||||
@@ -70,16 +71,10 @@ const lineDeviceTreeData = computed<Record<string, TreeOption[]>>(() => {
|
|||||||
.map<TreeOption>((nvrCluster) => {
|
.map<TreeOption>((nvrCluster) => {
|
||||||
return {
|
return {
|
||||||
label: `${nvrCluster.name}`,
|
label: `${nvrCluster.name}`,
|
||||||
key: nvrCluster.deviceId,
|
key: nvrCluster.id,
|
||||||
suffix: () => `${nvrCluster.ipAddress}`,
|
suffix: () => `${nvrCluster.ipAddress}`,
|
||||||
prefix: () => {
|
prefix: () => {
|
||||||
return h(
|
return renderDeviceNodePrefix(nvrCluster, stationCode);
|
||||||
NTag,
|
|
||||||
{ type: nvrCluster.deviceStatus === '10' ? 'success' : nvrCluster.deviceStatus === '20' ? 'error' : 'warning', size: 'tiny' },
|
|
||||||
{
|
|
||||||
default: () => (nvrCluster.deviceStatus === '10' ? '在线' : nvrCluster.deviceStatus === '20' ? '离线' : '未知'),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
children: nvrs
|
children: nvrs
|
||||||
.filter((nvr) => {
|
.filter((nvr) => {
|
||||||
@@ -88,16 +83,10 @@ const lineDeviceTreeData = computed<Record<string, TreeOption[]>>(() => {
|
|||||||
.map<TreeOption>((nvr) => {
|
.map<TreeOption>((nvr) => {
|
||||||
return {
|
return {
|
||||||
label: `${nvr.name}`,
|
label: `${nvr.name}`,
|
||||||
key: nvr.deviceId,
|
key: nvr.id,
|
||||||
suffix: () => `${nvr.ipAddress}`,
|
suffix: () => `${nvr.ipAddress}`,
|
||||||
prefix: () => {
|
prefix: () => {
|
||||||
return h(
|
return renderDeviceNodePrefix(nvrCluster, stationCode);
|
||||||
NTag,
|
|
||||||
{ type: nvr.deviceStatus === '10' ? 'success' : nvr.deviceStatus === '20' ? 'error' : 'warning', size: 'tiny' },
|
|
||||||
{
|
|
||||||
default: () => (nvr.deviceStatus === '10' ? '在线' : nvr.deviceStatus === '20' ? '离线' : '未知'),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
// 当选择设备时,能获取到设备的所有信息,以及设备所属的车站
|
// 当选择设备时,能获取到设备的所有信息,以及设备所属的车站
|
||||||
device: nvr,
|
device: nvr,
|
||||||
@@ -117,16 +106,10 @@ const lineDeviceTreeData = computed<Record<string, TreeOption[]>>(() => {
|
|||||||
suffix: () => `(${onlineDevices?.length ?? 0}/${offlineDevices?.length ?? 0}/${devices?.length ?? 0})`,
|
suffix: () => `(${onlineDevices?.length ?? 0}/${offlineDevices?.length ?? 0}/${devices?.length ?? 0})`,
|
||||||
children: lineDevices.value?.[stationCode][paneName].map<TreeOption>((device) => ({
|
children: lineDevices.value?.[stationCode][paneName].map<TreeOption>((device) => ({
|
||||||
label: `${device.name}`,
|
label: `${device.name}`,
|
||||||
key: device.deviceId,
|
key: device.id,
|
||||||
suffix: () => `${device.ipAddress}`,
|
suffix: () => `${device.ipAddress}`,
|
||||||
prefix: () => {
|
prefix: () => {
|
||||||
return h(
|
return renderDeviceNodePrefix(device, stationCode);
|
||||||
NTag,
|
|
||||||
{ type: device.deviceStatus === '10' ? 'success' : device.deviceStatus === '20' ? 'error' : 'warning', size: 'tiny' },
|
|
||||||
{
|
|
||||||
default: () => (device.deviceStatus === '10' ? '在线' : device.deviceStatus === '20' ? '离线' : '未知'),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
// 当选择设备时,能获取到设备的所有信息,以及设备所属的车站
|
// 当选择设备时,能获取到设备的所有信息,以及设备所属的车站
|
||||||
device,
|
device,
|
||||||
@@ -137,25 +120,56 @@ const lineDeviceTreeData = computed<Record<string, TreeOption[]>>(() => {
|
|||||||
});
|
});
|
||||||
return treeData;
|
return treeData;
|
||||||
});
|
});
|
||||||
|
const renderDeviceNodePrefix = (device: NdmDeviceResultVO, stationCode: string) => {
|
||||||
|
const renderDeviceStatusTag = (device: NdmDeviceResultVO) => {
|
||||||
|
const { deviceStatus } = device;
|
||||||
|
const tagType: TagProps['type'] = deviceStatus === '10' ? 'success' : deviceStatus === '20' ? 'error' : 'warning';
|
||||||
|
const tagText = device.deviceStatus === '10' ? '在线' : device.deviceStatus === '20' ? '离线' : '未知';
|
||||||
|
return h(NTag, { type: tagType, size: 'tiny' }, () => tagText);
|
||||||
|
};
|
||||||
|
const renderViewDeviceButton = (device: NdmDeviceResultVO, stationCode: string) => {
|
||||||
|
return h(
|
||||||
|
NButton,
|
||||||
|
{
|
||||||
|
text: true,
|
||||||
|
size: 'tiny',
|
||||||
|
type: 'info',
|
||||||
|
onClick: () => {
|
||||||
|
selectedDevice.value = device; // 更新选中的设备
|
||||||
|
router.replace({
|
||||||
|
query: {
|
||||||
|
...route.query,
|
||||||
|
stationCode: stationCode,
|
||||||
|
deviceType: selectedTab.value,
|
||||||
|
deviceDBId: device.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
() => '查看',
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return h('div', [renderViewDeviceButton(device, stationCode), renderDeviceStatusTag(device)]);
|
||||||
|
};
|
||||||
|
|
||||||
const selectedTab = ref<DeviceTypeCode>(DeviceType.Camera);
|
const selectedTab = ref<DeviceTypeCode>(DeviceType.Camera);
|
||||||
const selectedKeys = ref<string[]>([]);
|
const selectedKeys = ref<string[]>();
|
||||||
const selectedDevice = ref<NdmDeviceResultVO>();
|
const selectedDevice = ref<NdmDeviceResultVO>();
|
||||||
// 从路由参数中设置选中的设备
|
// 从路由参数中设置选中的设备
|
||||||
const setFromRouteQuery = (query: LocationQuery) => {
|
const setFromRouteQuery = (query: LocationQuery) => {
|
||||||
const { stationCode, deviceType, deviceId } = query;
|
const { stationCode, deviceType, deviceDBId } = query;
|
||||||
let stnCode = '';
|
let stnCode = '';
|
||||||
let devType: DeviceTypeCode = DeviceType.Camera;
|
let devType: DeviceTypeCode = DeviceType.Camera;
|
||||||
let devId = '';
|
let devDBId = '';
|
||||||
if (stationCode) stnCode = stationCode as string;
|
if (stationCode) stnCode = stationCode as string;
|
||||||
if (deviceType) devType = deviceType as DeviceTypeCode;
|
if (deviceType) devType = deviceType as DeviceTypeCode;
|
||||||
if (deviceId) devId = deviceId as string;
|
if (deviceDBId) devDBId = deviceDBId as string;
|
||||||
selectedTab.value = devType;
|
selectedTab.value = devType;
|
||||||
selectedKeys.value = [devId];
|
selectedKeys.value = [devDBId];
|
||||||
const stnDevices = lineDevices.value?.[stnCode];
|
const stnDevices = lineDevices.value?.[stnCode];
|
||||||
const devices = stnDevices?.[devType];
|
const devices = stnDevices?.[devType];
|
||||||
// 如果没找到,那还是赋值为原来选择的设备
|
// 如果没找到,那还是赋值为原来选择的设备
|
||||||
selectedDevice.value = devices?.find((device) => device.deviceId === deviceId) ?? selectedDevice.value;
|
selectedDevice.value = devices?.find((device) => device.id === devDBId) ?? selectedDevice.value;
|
||||||
};
|
};
|
||||||
// 页面加载时,需要设置选中的设备
|
// 页面加载时,需要设置选中的设备
|
||||||
onMounted(() => setFromRouteQuery(route.query));
|
onMounted(() => setFromRouteQuery(route.query));
|
||||||
@@ -163,19 +177,20 @@ onMounted(() => setFromRouteQuery(route.query));
|
|||||||
watch(lineDevices, () => setFromRouteQuery(route.query), { immediate: true });
|
watch(lineDevices, () => setFromRouteQuery(route.query), { immediate: true });
|
||||||
// 更改选择的设备类型时,更新路由查询参数
|
// 更改选择的设备类型时,更新路由查询参数
|
||||||
watch(selectedTab, (newTab) => router.replace({ query: { ...route.query, deviceType: newTab } }));
|
watch(selectedTab, (newTab) => router.replace({ query: { ...route.query, deviceType: newTab } }));
|
||||||
// 点击设备时更新路由查询参数
|
// 双击设备时更新路由查询参数
|
||||||
const nodeProps = ({ option }: { option: TreeOption }) => {
|
const nodeProps = ({ option }: { option: TreeOption }) => {
|
||||||
return {
|
return {
|
||||||
onclick: () => {
|
ondblclick: () => {
|
||||||
if (option['device']) {
|
if (option['device']) {
|
||||||
const device = option['device'] as NdmDeviceResultVO;
|
const device = option['device'] as NdmDeviceResultVO;
|
||||||
selectedDevice.value = device;
|
selectedKeys.value = device.id ? [device.id] : undefined; // 更新选中的树节点
|
||||||
|
selectedDevice.value = device; // 更新选中的设备
|
||||||
router.replace({
|
router.replace({
|
||||||
query: {
|
query: {
|
||||||
...route.query,
|
...route.query,
|
||||||
stationCode: option['stationCode'] as string,
|
stationCode: option['stationCode'] as string,
|
||||||
deviceType: selectedTab.value,
|
deviceType: selectedTab.value,
|
||||||
deviceId: device.deviceId,
|
deviceDBId: device.id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -196,7 +211,7 @@ const searchPattern = computed(() => {
|
|||||||
});
|
});
|
||||||
const searchFilter = (pattern: string, node: TreeOption): boolean => {
|
const searchFilter = (pattern: string, node: TreeOption): boolean => {
|
||||||
const { search, status } = destr<{ search: string; status: string }>(pattern);
|
const { search, status } = destr<{ search: string; status: string }>(pattern);
|
||||||
const device = node['device'] as NdmDeviceVO | undefined;
|
const device = node['device'] as NdmDeviceResultVO | undefined;
|
||||||
const { name, ipAddress, deviceStatus } = device ?? {};
|
const { name, ipAddress, deviceStatus } = device ?? {};
|
||||||
const searchMatched = (name ?? '').includes(search) || (ipAddress ?? '').includes(search);
|
const searchMatched = (name ?? '').includes(search) || (ipAddress ?? '').includes(search);
|
||||||
const statusMatched = status === '' || status === deviceStatus;
|
const statusMatched = status === '' || status === deviceStatus;
|
||||||
@@ -210,14 +225,14 @@ const onClickLocateDeviceTree = () => {
|
|||||||
// 等待Tab切换完成后再执行滚动
|
// 等待Tab切换完成后再执行滚动
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
const inst = deviceTreeInsts.value?.at(0);
|
const inst = deviceTreeInsts.value?.at(0);
|
||||||
inst?.scrollTo({ key: selectedDevice.value?.deviceId, behavior: 'smooth' });
|
inst?.scrollTo({ key: selectedDevice.value?.id, behavior: 'smooth' });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NLayout has-sider style="height: 100%">
|
<NLayout has-sider style="height: 100%">
|
||||||
<NLayoutSider bordered :width="540" :collapsed-width="0" show-trigger="bar">
|
<NLayoutSider bordered :width="600" :collapsed-width="0" show-trigger="bar">
|
||||||
<div style="height: 100%; display: flex; flex-direction: column">
|
<div style="height: 100%; display: flex; flex-direction: column">
|
||||||
<div style="flex-shrink: 0; padding: 12px">
|
<div style="flex-shrink: 0; padding: 12px">
|
||||||
<NInput v-model:value="searchInput" placeholder="搜索设备名称或IP地址" clearable />
|
<NInput v-model:value="searchInput" placeholder="搜索设备名称或IP地址" clearable />
|
||||||
@@ -256,7 +271,7 @@ const onClickLocateDeviceTree = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</NLayoutSider>
|
</NLayoutSider>
|
||||||
<NLayoutContent :content-style="{ 'padding-left': '24px' }">
|
<NLayoutContent :content-style="{ 'padding-left': '24px', 'padding-top': '6px' }">
|
||||||
<NPageHeader v-if="route.query['from']" title="" @back="onClickBack">
|
<NPageHeader v-if="route.query['from']" title="" @back="onClickBack">
|
||||||
<template #back>
|
<template #back>
|
||||||
<NIcon>
|
<NIcon>
|
||||||
@@ -266,7 +281,7 @@ const onClickLocateDeviceTree = () => {
|
|||||||
</template>
|
</template>
|
||||||
</NPageHeader>
|
</NPageHeader>
|
||||||
<div>selectedKeys: {{ selectedKeys }}</div>
|
<div>selectedKeys: {{ selectedKeys }}</div>
|
||||||
<div>selectedDevice:</div>
|
<div>selectedDevice: {{ selectedDevice?.deviceId }}</div>
|
||||||
<NScrollbar style="width: 500px; height: 400px; border: solid 1px salmon">
|
<NScrollbar style="width: 500px; height: 400px; border: solid 1px salmon">
|
||||||
<pre>{{ selectedDevice }}</pre>
|
<pre>{{ selectedDevice }}</pre>
|
||||||
</NScrollbar>
|
</NScrollbar>
|
||||||
|
|||||||
Reference in New Issue
Block a user