fix: DevicePage fix

- remove dblclick of device node
- auto scroll to selected device node
This commit is contained in:
yangsy
2025-08-18 15:05:45 +08:00
parent d0168f84bc
commit f044671528

View File

@@ -24,9 +24,10 @@ import {
type TagProps, type TagProps,
type TreeInst, type TreeInst,
type TreeOption, type TreeOption,
type TreeOverrideNodeClickBehavior,
} from 'naive-ui'; } from 'naive-ui';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { h, nextTick, onMounted, useTemplateRef } from 'vue'; import { h, onMounted, useTemplateRef } from 'vue';
import { computed, ref, watch } from 'vue'; import { computed, ref, watch } from 'vue';
import { useRoute, useRouter, type LocationQuery } from 'vue-router'; import { useRoute, useRouter, type LocationQuery } from 'vue-router';
import { destr } from 'destr'; import { destr } from 'destr';
@@ -49,6 +50,7 @@ const stationStore = useStationStore();
const { stationList } = storeToRefs(stationStore); const { stationList } = storeToRefs(stationStore);
const { data: lineDevices } = useLineDevicesQuery(); const { data: lineDevices } = useLineDevicesQuery();
// ========== 设备树数据 ==========
const lineDeviceTreeData = computed<Record<string, TreeOption[]>>(() => { const lineDeviceTreeData = computed<Record<string, TreeOption[]>>(() => {
const treeData: Record<string, TreeOption[]> = {}; const treeData: Record<string, TreeOption[]> = {};
deviceTabPanes.forEach(({ name: paneName /* , tab: paneTab */ }) => { deviceTabPanes.forEach(({ name: paneName /* , tab: paneTab */ }) => {
@@ -86,7 +88,7 @@ const lineDeviceTreeData = computed<Record<string, TreeOption[]>>(() => {
key: nvr.id, key: nvr.id,
suffix: () => `${nvr.ipAddress}`, suffix: () => `${nvr.ipAddress}`,
prefix: () => { prefix: () => {
return renderDeviceNodePrefix(nvrCluster, stationCode); return renderDeviceNodePrefix(nvr, stationCode);
}, },
// 当选择设备时,能获取到设备的所有信息,以及设备所属的车站 // 当选择设备时,能获取到设备的所有信息,以及设备所属的车站
device: nvr, device: nvr,
@@ -152,11 +154,13 @@ const renderDeviceNodePrefix = (device: NdmDeviceResultVO, stationCode: string)
return h('div', [renderViewDeviceButton(device, stationCode), renderDeviceStatusTag(device)]); 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 expandedKeys = ref<string[]>();
const selectedDevice = ref<NdmDeviceResultVO>(); const selectedDevice = ref<NdmDeviceResultVO>();
// 从路由参数中设置选中的设备 // 从路由参数中设置选中的设备
const setFromRouteQuery = (query: LocationQuery) => { const setDeviceTreePropsFromRouteQuery = (query: LocationQuery) => {
const { stationCode, deviceType, deviceDBId } = query; const { stationCode, deviceType, deviceDBId } = query;
let stnCode = ''; let stnCode = '';
let devType: DeviceTypeCode = DeviceType.Camera; let devType: DeviceTypeCode = DeviceType.Camera;
@@ -164,6 +168,7 @@ const setFromRouteQuery = (query: LocationQuery) => {
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 (deviceDBId) devDBId = deviceDBId as string; if (deviceDBId) devDBId = deviceDBId as string;
selectedTab.value = devType; selectedTab.value = devType;
selectedKeys.value = [devDBId]; selectedKeys.value = [devDBId];
const stnDevices = lineDevices.value?.[stnCode]; const stnDevices = lineDevices.value?.[stnCode];
@@ -172,35 +177,24 @@ const setFromRouteQuery = (query: LocationQuery) => {
selectedDevice.value = devices?.find((device) => device.id === devDBId) ?? selectedDevice.value; selectedDevice.value = devices?.find((device) => device.id === devDBId) ?? selectedDevice.value;
}; };
// 页面加载时,需要设置选中的设备 // 页面加载时,需要设置选中的设备
onMounted(() => setFromRouteQuery(route.query)); onMounted(() => {
setDeviceTreePropsFromRouteQuery(route.query);
scrollDeviceTreeToSelectedDevice();
});
// 页面加载时设备数据可能不存在,因此当设备数据更新时,需要重新设置选中的设备 // 页面加载时设备数据可能不存在,因此当设备数据更新时,需要重新设置选中的设备
watch(lineDevices, () => setFromRouteQuery(route.query), { immediate: true }); watch(lineDevices, () => setDeviceTreePropsFromRouteQuery(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 override: TreeOverrideNodeClickBehavior = ({ option }) => {
const nodeProps = ({ option }: { option: TreeOption }) => { if (!option['device']) {
return { return 'none';
ondblclick: () => { }
if (option['device']) { return 'default';
const device = option['device'] as NdmDeviceResultVO;
selectedKeys.value = device.id ? [device.id] : undefined; // 更新选中的树节点
selectedDevice.value = device; // 更新选中的设备
router.replace({
query: {
...route.query,
stationCode: option['stationCode'] as string,
deviceType: selectedTab.value,
deviceDBId: device.id,
},
});
}
},
};
}; };
// ========== 设备树搜索 ==========
const searchInput = ref(''); const searchInput = ref('');
const statusInput = ref(''); const statusInput = ref('');
// 设备树将搜索框和单选框的值都交给NTree的pattern属性 // 设备树将搜索框和单选框的值都交给NTree的pattern属性
// 但是如果一个车站下没有匹配的设备,那么这个车站节点也不会显示 // 但是如果一个车站下没有匹配的设备,那么这个车站节点也不会显示
const searchPattern = computed(() => { const searchPattern = computed(() => {
@@ -218,15 +212,22 @@ const searchFilter = (pattern: string, node: TreeOption): boolean => {
return searchMatched && statusMatched; return searchMatched && statusMatched;
}; };
// ========== 设备树交互 ==========
// 定位到选中的设备 // 定位到选中的设备
const deviceTreeInsts = useTemplateRef<TreeInst[]>('deviceTreeInsts'); const deviceTreeInsts = useTemplateRef<TreeInst[]>('deviceTreeInsts');
const onClickLocateDeviceTree = () => { const onClickLocateDeviceTree = () => {
selectedTab.value = (selectedDevice.value?.deviceType ?? selectedTab.value) as DeviceTypeCode; selectedTab.value = (selectedDevice.value?.deviceType ?? selectedTab.value) as DeviceTypeCode;
// 等待Tab切换完成后再执行滚动 selectedKeys.value = selectedDevice.value?.id ? [selectedDevice.value.id] : undefined;
nextTick(() => { expandedKeys.value = route.query['stationCode'] ? [route.query['stationCode'] as string] : undefined;
// 由于数据量大所以开启虚拟滚动,
// 但是无法知晓NTree内部的虚拟列表容器何时创建完成所以使用setTimeout延迟固定时间后执行滚动
scrollDeviceTreeToSelectedDevice();
};
const scrollDeviceTreeToSelectedDevice = (timeout: number = 500) => {
setTimeout(() => {
const inst = deviceTreeInsts.value?.at(0); const inst = deviceTreeInsts.value?.at(0);
inst?.scrollTo({ key: selectedDevice.value?.id, behavior: 'smooth' }); inst?.scrollTo({ key: selectedDevice.value?.id, behavior: 'smooth' });
}); }, timeout);
}; };
</script> </script>
@@ -252,17 +253,18 @@ const onClickLocateDeviceTree = () => {
<NTabs v-model:value="selectedTab" animated type="line" placement="left" style="height: 100%"> <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"> <NTabPane v-for="pane in deviceTabPanes" :key="pane.name" :name="pane.name" :tab="pane.tab">
<NTree <NTree
:ref="'deviceTreeInsts'"
v-model:selected-keys="selectedKeys" v-model:selected-keys="selectedKeys"
v-model:expanded-keys="expandedKeys"
:ref="'deviceTreeInsts'"
:data="lineDeviceTreeData[pane.name]" :data="lineDeviceTreeData[pane.name]"
:node-props="nodeProps"
:show-irrelevant-nodes="false" :show-irrelevant-nodes="false"
:pattern="searchPattern" :pattern="searchPattern"
:filter="searchFilter" :filter="searchFilter"
:override-default-node-click-behavior="override"
default-expand-all
block-line block-line
block-node block-node
show-line show-line
default-expand-all
virtual-scroll virtual-scroll
style="height: 100%" style="height: 100%"
/> />
@@ -281,6 +283,7 @@ const onClickLocateDeviceTree = () => {
</template> </template>
</NPageHeader> </NPageHeader>
<div>selectedKeys: {{ selectedKeys }}</div> <div>selectedKeys: {{ selectedKeys }}</div>
<div>expandedKeys: {{ expandedKeys }}</div>
<div>selectedDevice: {{ selectedDevice?.deviceId }}</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>