Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5f97e78a03 |
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ndm-web-platform",
|
"name": "ndm-web-platform",
|
||||||
"version": "0.0.0",
|
"version": "0.39.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
@@ -1,12 +1,4 @@
|
|||||||
[
|
[
|
||||||
{
|
|
||||||
"version": "0.40.0",
|
|
||||||
"date": "2026-04-10",
|
|
||||||
"changes": {
|
|
||||||
"fixes": [{ "content": "修复设备树搜索时节点错乱的问题" }, { "content": "将安防箱面板的“电路”统一更正为“空开”" }, { "content": "修复设备查询缓存键冲突问题" }],
|
|
||||||
"feats": [{ "content": "添加视频服务器双机热备状态" }]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"version": "0.39.0",
|
"version": "0.39.0",
|
||||||
"date": "2026-03-02",
|
"date": "2026-03-02",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"version": "0.40.0",
|
"version": "0.39.0",
|
||||||
"buildTime": "2026-04-10 15:42:03"
|
"buildTime": "2026-03-11 14:35:45"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
export interface HighAvailable {
|
|
||||||
pyip: string;
|
|
||||||
vip: string;
|
|
||||||
changeDate: string;
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
export * from './diag';
|
export * from './diag';
|
||||||
export * from './high-available';
|
|
||||||
export * from './link-description';
|
export * from './link-description';
|
||||||
export * from './station';
|
export * from './station';
|
||||||
|
|||||||
@@ -1,26 +1,5 @@
|
|||||||
import { ndmClient, userClient, type HighAvailable, type MediaServerStatus, type SendRtpInfo, type Station } from '@/apis';
|
import { ndmClient, userClient, type MediaServerStatus, type SendRtpInfo, type Station } from '@/apis';
|
||||||
import { unwrapNullableResponse, unwrapResponse } from '@/utils';
|
import { unwrapResponse } from '@/utils';
|
||||||
import destr from 'destr';
|
|
||||||
|
|
||||||
// {
|
|
||||||
// "code": 0,
|
|
||||||
// "data": "{pyip:\"10.18.128.14\",vip:\"10.18.128.6\",changeDate:\"2026-03-23 15:55:00\"}",
|
|
||||||
// "msg": "ok",
|
|
||||||
// "path": null,
|
|
||||||
// "extra": null,
|
|
||||||
// "timestamp": "1774421387908",
|
|
||||||
// "errorMsg": "",
|
|
||||||
// "isSuccess": true
|
|
||||||
// }
|
|
||||||
export const getHighAvailableApi = async (options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
|
||||||
const { stationCode, signal } = options ?? {};
|
|
||||||
const client = stationCode ? ndmClient : userClient;
|
|
||||||
const prefix = stationCode ? `/${stationCode}` : '';
|
|
||||||
const endpoint = `${prefix}/api/ndm/ndmServiceAvailable/highAvailable/get`;
|
|
||||||
const resp = await client.get<string | null>(endpoint, { signal });
|
|
||||||
const data = unwrapNullableResponse(resp);
|
|
||||||
return destr<HighAvailable | null>(data);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getAllPushApi = async (options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const getAllPushApi = async (options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
|
|||||||
@@ -92,8 +92,6 @@ const abortController = ref<AbortController>(new AbortController());
|
|||||||
|
|
||||||
const NVR_RECORD_CHECK_KEY = 'nvr-record-check-query';
|
const NVR_RECORD_CHECK_KEY = 'nvr-record-check-query';
|
||||||
|
|
||||||
const deviceUniqueKey = computed(() => [station.value.code, ndmDevice.value.id]);
|
|
||||||
|
|
||||||
const DAY_OFFSET = 90;
|
const DAY_OFFSET = 90;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -101,7 +99,7 @@ const {
|
|||||||
isFetching: loading,
|
isFetching: loading,
|
||||||
refetch: refetchRecordChecks,
|
refetch: refetchRecordChecks,
|
||||||
} = useQuery({
|
} = useQuery({
|
||||||
queryKey: computed(() => [NVR_RECORD_CHECK_KEY, deviceUniqueKey.value, ndmDevice.value.lastDiagTime]),
|
queryKey: computed(() => [NVR_RECORD_CHECK_KEY, ndmDevice.value.id, ndmDevice.value.lastDiagTime]),
|
||||||
enabled: computed(() => activeRequests.value),
|
enabled: computed(() => activeRequests.value),
|
||||||
refetchInterval: 30 * 1000,
|
refetchInterval: 30 * 1000,
|
||||||
gcTime: 0,
|
gcTime: 0,
|
||||||
@@ -410,13 +408,13 @@ const ndmRecordChecksPaged = computed(() => {
|
|||||||
return ndmRecordChecksFiltered.value.slice(startIndex, endIndex);
|
return ndmRecordChecksFiltered.value.slice(startIndex, endIndex);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 当车站号、设备IP、最后诊断时间或筛选类型变化时,重置分页为第一页
|
// 当设备ID、最后诊断时间或筛选类型变化时,重置分页为第一页
|
||||||
watch([() => station.value.code, () => ndmDevice.value.ipAddress, () => ndmDevice.value.lastDiagTime, filterType, searchInputDebounced], () => {
|
watch([() => ndmDevice.value.id, () => ndmDevice.value.lastDiagTime, filterType, searchInputDebounced], () => {
|
||||||
page.value = 1;
|
page.value = 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 当车站号、设备IP变化时,重置搜索内容,并将筛选类型重置为「全部」
|
// 当设备ID变化时,重置搜索内容,并将筛选类型重置为「全部」
|
||||||
watch([() => station.value.code, () => ndmDevice.value.ipAddress], () => {
|
watch([() => ndmDevice.value.id], () => {
|
||||||
searchInput.value = '';
|
searchInput.value = '';
|
||||||
filterType.value = 'all';
|
filterType.value = 'all';
|
||||||
});
|
});
|
||||||
|
|||||||
+4
-4
@@ -202,7 +202,7 @@ const contextmenuOptions = computed<DropdownOption[]>(() => [
|
|||||||
if (!lowerDevice) return;
|
if (!lowerDevice) return;
|
||||||
window.$dialog.warning({
|
window.$dialog.warning({
|
||||||
title: '确认解除关联吗?',
|
title: '确认解除关联吗?',
|
||||||
content: `将解除【空开${circuitIndex + 1}】与【${lowerDevice.name}】的关联关系。`,
|
content: `将解除【电路${circuitIndex + 1}】与【${lowerDevice.name}】的关联关系。`,
|
||||||
style: { width: '600px' },
|
style: { width: '600px' },
|
||||||
contentStyle: { height: '60px' },
|
contentStyle: { height: '60px' },
|
||||||
negativeText: '取消',
|
negativeText: '取消',
|
||||||
@@ -299,7 +299,7 @@ const { mutate: unlinkDevice } = useMutation({
|
|||||||
<NCard v-if="showCard" hoverable size="small">
|
<NCard v-if="showCard" hoverable size="small">
|
||||||
<template #header>
|
<template #header>
|
||||||
<NFlex align="center">
|
<NFlex align="center">
|
||||||
<span>空开状态</span>
|
<span>电路状态</span>
|
||||||
<NPopconfirm :positive-text="'确认'" :negative-text="'取消'" @positive-click="() => reboot()">
|
<NPopconfirm :positive-text="'确认'" :negative-text="'取消'" @positive-click="() => reboot()">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<NButton secondary size="small" :loading="rebooting">重合闸</NButton>
|
<NButton secondary size="small" :loading="rebooting">重合闸</NButton>
|
||||||
@@ -324,7 +324,7 @@ const { mutate: unlinkDevice } = useMutation({
|
|||||||
<span>{{ getCircuitStatusText(circuit) }}</span>
|
<span>{{ getCircuitStatusText(circuit) }}</span>
|
||||||
</template>
|
</template>
|
||||||
</NTag>
|
</NTag>
|
||||||
<span>空开{{ circuitIndex + 1 }}</span>
|
<span>电路{{ circuitIndex + 1 }}</span>
|
||||||
</NFlex>
|
</NFlex>
|
||||||
<NFlex justify="end" align="center">
|
<NFlex justify="end" align="center">
|
||||||
<NPopconfirm :positive-text="'确认'" :negative-text="'取消'" @positive-click="() => turnStatus({ circuitIndex: circuitIndex, newStatus: circuit.status !== 1 })">
|
<NPopconfirm :positive-text="'确认'" :negative-text="'取消'" @positive-click="() => turnStatus({ circuitIndex: circuitIndex, newStatus: circuit.status !== 1 })">
|
||||||
@@ -332,7 +332,7 @@ const { mutate: unlinkDevice } = useMutation({
|
|||||||
<NSwitch size="small" :value="circuit.status === 1" :loading="turning" />
|
<NSwitch size="small" :value="circuit.status === 1" :loading="turning" />
|
||||||
</template>
|
</template>
|
||||||
<template #default>
|
<template #default>
|
||||||
<span>确定要{{ circuit.status === 1 ? '关闭' : '开启' }}空开{{ circuitIndex + 1 }}吗?</span>
|
<span>确定要{{ circuit.status === 1 ? '关闭' : '开启' }}电路{{ circuitIndex + 1 }}吗?</span>
|
||||||
</template>
|
</template>
|
||||||
</NPopconfirm>
|
</NPopconfirm>
|
||||||
</NFlex>
|
</NFlex>
|
||||||
|
|||||||
+2
-2
@@ -60,7 +60,7 @@ const { mutate: linkPortToDevice, isPending: linking } = useMutation({
|
|||||||
const upperDeviceDbId = ndmDevice.value.id;
|
const upperDeviceDbId = ndmDevice.value.id;
|
||||||
if (!upperDeviceDbId) throw new Error('本设备没有ID');
|
if (!upperDeviceDbId) throw new Error('本设备没有ID');
|
||||||
|
|
||||||
if (circuitIndex.value === undefined) throw new Error('该空开不存在');
|
if (circuitIndex.value === undefined) throw new Error('该电路不存在');
|
||||||
|
|
||||||
if (!lowerDevice.value) throw new Error('请选择要关联的设备');
|
if (!lowerDevice.value) throw new Error('请选择要关联的设备');
|
||||||
const lowerDeviceType = tryGetDeviceType(lowerDevice.value?.deviceType);
|
const lowerDeviceType = tryGetDeviceType(lowerDevice.value?.deviceType);
|
||||||
@@ -195,7 +195,7 @@ const onCancel = () => {
|
|||||||
<template #header>
|
<template #header>
|
||||||
<span>{{ ndmDevice.name }}</span>
|
<span>{{ ndmDevice.name }}</span>
|
||||||
<span> - </span>
|
<span> - </span>
|
||||||
<span>空开{{ circuitIndex ? circuitIndex + 1 : '-' }}</span>
|
<span>电路{{ circuitIndex ? circuitIndex + 1 : '-' }}</span>
|
||||||
<span> - </span>
|
<span> - </span>
|
||||||
<span>关联设备</span>
|
<span>关联设备</span>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
+2
-2
@@ -31,7 +31,7 @@ const { ndmDevice, station } = toRefs(props);
|
|||||||
const showDetailModal = ref(false);
|
const showDetailModal = ref(false);
|
||||||
|
|
||||||
const detailTableColumns: DataTableColumns<SecurityBoxCircuitRowData> = [
|
const detailTableColumns: DataTableColumns<SecurityBoxCircuitRowData> = [
|
||||||
{ title: '空开序号', key: 'number' },
|
{ title: '电路序号', key: 'number' },
|
||||||
{
|
{
|
||||||
title: '状态',
|
title: '状态',
|
||||||
key: 'status',
|
key: 'status',
|
||||||
@@ -98,7 +98,7 @@ const tableColumns: DataTableColumns<SecurityBoxRuntimeRowData> = [
|
|||||||
},
|
},
|
||||||
// { title: '开关状态', key: 'switches' },
|
// { title: '开关状态', key: 'switches' },
|
||||||
{
|
{
|
||||||
title: '空开状态',
|
title: '电路状态',
|
||||||
key: 'circuits',
|
key: 'circuits',
|
||||||
render(rowData) {
|
render(rowData) {
|
||||||
const { info } = rowData.diagInfo;
|
const { info } = rowData.diagInfo;
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import ServerAlive from './server-alive.vue';
|
import ServerAlive from './server-alive.vue';
|
||||||
import ServerCard from './server-card.vue';
|
import ServerCard from './server-card.vue';
|
||||||
import ServerCurrentDiag from './server-current-diag.vue';
|
import ServerCurrentDiag from './server-current-diag.vue';
|
||||||
import ServerHighAvailable from './server-high-available.vue';
|
|
||||||
import ServerHistoryDiag from './server-history-diag.vue';
|
import ServerHistoryDiag from './server-history-diag.vue';
|
||||||
import ServerStreamPush from './server-stream-push.vue';
|
import ServerStreamPush from './server-stream-push.vue';
|
||||||
import ServerUpdate from './server-update.vue';
|
import ServerUpdate from './server-update.vue';
|
||||||
|
|
||||||
export { ServerAlive, ServerCard, ServerCurrentDiag, ServerHighAvailable, ServerHistoryDiag, ServerUpdate, ServerStreamPush };
|
export { ServerAlive, ServerCard, ServerCurrentDiag, ServerHistoryDiag, ServerUpdate, ServerStreamPush };
|
||||||
|
|||||||
@@ -23,11 +23,8 @@ const deviceType = computed(() => tryGetDeviceType(ndmDevice.value.deviceType));
|
|||||||
|
|
||||||
const MEDIA_SERVER_ALIVE_QUERY_KEY = 'media-server-alive-query';
|
const MEDIA_SERVER_ALIVE_QUERY_KEY = 'media-server-alive-query';
|
||||||
const VIDEO_SERVER_ALIVE_QUERY_KEY = 'video-server-alive-query';
|
const VIDEO_SERVER_ALIVE_QUERY_KEY = 'video-server-alive-query';
|
||||||
|
|
||||||
const deviceUniqueKey = computed(() => [station.value.code, ndmDevice.value.id]);
|
|
||||||
|
|
||||||
const { data: isMediaServerAlive } = useQuery({
|
const { data: isMediaServerAlive } = useQuery({
|
||||||
queryKey: computed(() => [MEDIA_SERVER_ALIVE_QUERY_KEY, deviceUniqueKey.value, ndmDevice.value.lastDiagTime]),
|
queryKey: computed(() => [MEDIA_SERVER_ALIVE_QUERY_KEY, ndmDevice.value.id, ndmDevice.value.lastDiagTime]),
|
||||||
enabled: computed(() => activeRequests.value && deviceType.value === DEVICE_TYPE_LITERALS.ndmMediaServer),
|
enabled: computed(() => activeRequests.value && deviceType.value === DEVICE_TYPE_LITERALS.ndmMediaServer),
|
||||||
refetchInterval: 30 * 1000,
|
refetchInterval: 30 * 1000,
|
||||||
gcTime: 0,
|
gcTime: 0,
|
||||||
@@ -37,7 +34,7 @@ const { data: isMediaServerAlive } = useQuery({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const { data: isSipServerAlive } = useQuery({
|
const { data: isSipServerAlive } = useQuery({
|
||||||
queryKey: computed(() => [VIDEO_SERVER_ALIVE_QUERY_KEY, deviceUniqueKey.value, ndmDevice.value.lastDiagTime]),
|
queryKey: computed(() => [VIDEO_SERVER_ALIVE_QUERY_KEY, ndmDevice.value.id, ndmDevice.value.lastDiagTime]),
|
||||||
enabled: computed(() => activeRequests.value && deviceType.value === DEVICE_TYPE_LITERALS.ndmVideoServer),
|
enabled: computed(() => activeRequests.value && deviceType.value === DEVICE_TYPE_LITERALS.ndmVideoServer),
|
||||||
refetchInterval: 30 * 1000,
|
refetchInterval: 30 * 1000,
|
||||||
gcTime: 0,
|
gcTime: 0,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { type NdmServerDiagInfo, type NdmServerResultVO, type Station } from '@/apis';
|
import { type NdmServerDiagInfo, type NdmServerResultVO, type Station } from '@/apis';
|
||||||
import { DeviceHardwareCard, DeviceHeaderCard, ServerAlive, ServerHighAvailable, ServerStreamPush } from '@/components';
|
import { DeviceHardwareCard, DeviceHeaderCard, ServerAlive, ServerStreamPush } from '@/components';
|
||||||
import destr from 'destr';
|
import destr from 'destr';
|
||||||
import { NFlex } from 'naive-ui';
|
import { NFlex } from 'naive-ui';
|
||||||
import { computed, toRefs } from 'vue';
|
import { computed, toRefs } from 'vue';
|
||||||
@@ -27,7 +27,6 @@ const runningTime = computed(() => lastDiagInfo.value?.commInfo?.系统运行时
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NFlex vertical>
|
<NFlex vertical>
|
||||||
<ServerHighAvailable :ndm-device="ndmDevice" :station="station" />
|
|
||||||
<DeviceHeaderCard :ndm-device="ndmDevice" :station="station" />
|
<DeviceHeaderCard :ndm-device="ndmDevice" :station="station" />
|
||||||
<DeviceHardwareCard running-time-label="服务器运行时间" :cpu-usage="cpuUsage" :mem-usage="memUsage" :disk-usage="diskUsage" :running-time="runningTime" />
|
<DeviceHardwareCard running-time-label="服务器运行时间" :cpu-usage="cpuUsage" :mem-usage="memUsage" :disk-usage="diskUsage" :running-time="runningTime" />
|
||||||
<ServerAlive :ndm-device="ndmDevice" :station="station" />
|
<ServerAlive :ndm-device="ndmDevice" :station="station" />
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { getHighAvailableApi, type NdmServerResultVO, type Station } from '@/apis';
|
|
||||||
import { DEVICE_TYPE_LITERALS, tryGetDeviceType } from '@/enums';
|
|
||||||
import { useSettingStore } from '@/stores';
|
|
||||||
import { useQuery, useQueryClient } from '@tanstack/vue-query';
|
|
||||||
import { NAlert, NCard, NFlex } from 'naive-ui';
|
|
||||||
import { storeToRefs } from 'pinia';
|
|
||||||
import { computed, toRefs, watch } from 'vue';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
ndmDevice: NdmServerResultVO;
|
|
||||||
station: Station;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const settingStore = useSettingStore();
|
|
||||||
const { activeRequests } = storeToRefs(settingStore);
|
|
||||||
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
|
|
||||||
const { ndmDevice, station } = toRefs(props);
|
|
||||||
|
|
||||||
const deviceType = computed(() => tryGetDeviceType(ndmDevice.value.deviceType));
|
|
||||||
|
|
||||||
const isVideoServer = computed(() => deviceType.value === DEVICE_TYPE_LITERALS.ndmVideoServer);
|
|
||||||
|
|
||||||
const SERVER_HIGH_AVAILABLE_QUERY_KEY = 'server-high-available-query';
|
|
||||||
|
|
||||||
const deviceUniqueKey = computed(() => [station.value.code, ndmDevice.value.id]);
|
|
||||||
|
|
||||||
const { data: highAvailable } = useQuery({
|
|
||||||
queryKey: computed(() => [SERVER_HIGH_AVAILABLE_QUERY_KEY, deviceUniqueKey.value, ndmDevice.value.lastDiagTime]),
|
|
||||||
enabled: computed(() => activeRequests.value && isVideoServer.value),
|
|
||||||
refetchInterval: 30 * 1000,
|
|
||||||
gcTime: 0,
|
|
||||||
queryFn: async ({ signal }) => {
|
|
||||||
const highAvailable = await getHighAvailableApi({ stationCode: station.value.code, signal });
|
|
||||||
return highAvailable;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
watch(activeRequests, (active) => {
|
|
||||||
if (!active) {
|
|
||||||
queryClient.cancelQueries({ queryKey: [SERVER_HIGH_AVAILABLE_QUERY_KEY] });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const showCard = computed(() => {
|
|
||||||
const { pyip: physicalIp } = highAvailable.value ?? {};
|
|
||||||
const ipAddressMatched = physicalIp === ndmDevice.value.ipAddress;
|
|
||||||
return isVideoServer.value && ipAddressMatched;
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<NAlert v-if="showCard && !!highAvailable" :bordered="false" type="success">
|
|
||||||
<template #header>
|
|
||||||
<NFlex :align="'center'">
|
|
||||||
<div>正在提供服务</div>
|
|
||||||
<NFlex :align="'center'" style="font-size: smaller">
|
|
||||||
<div>虚拟IP: {{ highAvailable.vip }}</div>
|
|
||||||
<div>启用时间: {{ highAvailable.changeDate }}</div>
|
|
||||||
</NFlex>
|
|
||||||
</NFlex>
|
|
||||||
</template>
|
|
||||||
</NAlert>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
||||||
@@ -25,10 +25,8 @@ const showCard = computed(() => deviceType.value === DEVICE_TYPE_LITERALS.ndmMed
|
|||||||
|
|
||||||
const SERVER_STREAM_PUSH_KEY = 'server-stream-push-query';
|
const SERVER_STREAM_PUSH_KEY = 'server-stream-push-query';
|
||||||
|
|
||||||
const deviceUniqueKey = computed(() => [station.value.code, ndmDevice.value.id]);
|
|
||||||
|
|
||||||
const { data: streamPushes } = useQuery({
|
const { data: streamPushes } = useQuery({
|
||||||
queryKey: computed(() => [SERVER_STREAM_PUSH_KEY, deviceUniqueKey.value, ndmDevice.value.lastDiagTime]),
|
queryKey: computed(() => [SERVER_STREAM_PUSH_KEY, ndmDevice.value.id, ndmDevice.value.lastDiagTime]),
|
||||||
enabled: computed(() => activeRequests.value && showCard.value),
|
enabled: computed(() => activeRequests.value && showCard.value),
|
||||||
refetchInterval: 30 * 1000,
|
refetchInterval: 30 * 1000,
|
||||||
gcTime: 0,
|
gcTime: 0,
|
||||||
|
|||||||
@@ -1,9 +1,3 @@
|
|||||||
<script lang="ts">
|
|
||||||
const createDeviceNodeKey = (stationCode?: Station['code'], device?: NdmDeviceResultVO) => {
|
|
||||||
return `${stationCode ?? ''}-${device?.id ?? ''}`;
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
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';
|
||||||
@@ -116,7 +110,7 @@ watchImmediate(selectedDeviceType, (newDeviceType) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const selectedKeys = computed(() => (selectedDevice.value?.id ? [createDeviceNodeKey(selectedStationCode.value, selectedDevice.value)] : undefined));
|
const selectedKeys = computed(() => (selectedDevice.value?.id ? [selectedDevice.value.id] : undefined));
|
||||||
watch([selectedKeys, selectedDevice, selectedStationCode], ([, device, code]) => {
|
watch([selectedKeys, selectedDevice, selectedStationCode], ([, device, code]) => {
|
||||||
if (device && code) {
|
if (device && code) {
|
||||||
onSelectDevice(device, code);
|
onSelectDevice(device, code);
|
||||||
@@ -322,26 +316,26 @@ const lineDeviceTreeData = computed<Record<Station['code'], TreeOption[]>>(() =>
|
|||||||
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<TreeOption>((nvrCluster) => {
|
||||||
return {
|
return {
|
||||||
label: `${cluster.name}`,
|
label: `${nvrCluster.name}`,
|
||||||
key: createDeviceNodeKey(stationCode, cluster),
|
key: nvrCluster.id ?? `${nvrCluster.name}`,
|
||||||
prefix: () => renderDeviceNodePrefix(cluster, stationCode),
|
prefix: () => renderDeviceNodePrefix(nvrCluster, stationCode),
|
||||||
suffix: () => `${cluster.ipAddress}`,
|
suffix: () => `${nvrCluster.ipAddress}`,
|
||||||
children: nvrSingletons.map<TreeOption>((device) => {
|
children: nvrSingletons.map<TreeOption>((nvr) => {
|
||||||
return {
|
return {
|
||||||
label: `${device.name}`,
|
label: `${nvr.name}`,
|
||||||
key: createDeviceNodeKey(stationCode, device),
|
key: nvr.id ?? `${nvr.name}`,
|
||||||
prefix: () => renderDeviceNodePrefix(device, stationCode),
|
prefix: () => renderDeviceNodePrefix(nvr, stationCode),
|
||||||
suffix: () => `${device.ipAddress}`,
|
suffix: () => `${nvr.ipAddress}`,
|
||||||
// 当选择设备时,能获取到设备的所有信息,以及设备所属的车站
|
// 当选择设备时,能获取到设备的所有信息,以及设备所属的车站
|
||||||
stationCode,
|
stationCode,
|
||||||
device: device,
|
device: nvr,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
// 当选择设备时,能获取到设备的所有信息,以及设备所属的车站
|
// 当选择设备时,能获取到设备的所有信息,以及设备所属的车站
|
||||||
stationCode,
|
stationCode,
|
||||||
device: cluster,
|
device: nvrCluster,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
stationCode,
|
stationCode,
|
||||||
@@ -358,7 +352,7 @@ const lineDeviceTreeData = computed<Record<Station['code'], TreeOption[]>>(() =>
|
|||||||
const device = dev as NdmDeviceResultVO;
|
const device = dev as NdmDeviceResultVO;
|
||||||
return {
|
return {
|
||||||
label: `${device.name}`,
|
label: `${device.name}`,
|
||||||
key: createDeviceNodeKey(stationCode, device),
|
key: `${device.name}${device.ipAddress}`,
|
||||||
prefix: () => renderDeviceNodePrefix(device, stationCode),
|
prefix: () => renderDeviceNodePrefix(device, stationCode),
|
||||||
suffix: () => `${device.ipAddress}`,
|
suffix: () => `${device.ipAddress}`,
|
||||||
// 当选择设备时,能获取到设备的所有信息,以及设备所属的车站
|
// 当选择设备时,能获取到设备的所有信息,以及设备所属的车站
|
||||||
@@ -389,16 +383,16 @@ const stationDeviceTreeData = computed<TreeOption[]>(() => {
|
|||||||
label: `${DEVICE_TYPE_NAMES[deviceType]}`,
|
label: `${DEVICE_TYPE_NAMES[deviceType]}`,
|
||||||
key: deviceType,
|
key: deviceType,
|
||||||
suffix: () => renderIcmpStatistics(onlineCount, offlineCount, nvrs.length),
|
suffix: () => renderIcmpStatistics(onlineCount, offlineCount, nvrs.length),
|
||||||
children: clusters.map<TreeOption>((cluster) => {
|
children: clusters.map<TreeOption>((device) => {
|
||||||
return {
|
return {
|
||||||
label: `${cluster.name}`,
|
label: `${device.name}`,
|
||||||
key: createDeviceNodeKey(stationCode, cluster),
|
key: `${device.name}${device.ipAddress}`,
|
||||||
prefix: () => renderDeviceNodePrefix(cluster, stationCode),
|
prefix: () => renderDeviceNodePrefix(device, stationCode),
|
||||||
suffix: () => `${cluster.ipAddress}`,
|
suffix: () => `${device.ipAddress}`,
|
||||||
children: singletons.map<TreeOption>((device) => {
|
children: singletons.map<TreeOption>((device) => {
|
||||||
return {
|
return {
|
||||||
label: `${device.name}`,
|
label: `${device.name}`,
|
||||||
key: createDeviceNodeKey(stationCode, device),
|
key: `${device.name}${device.ipAddress}`,
|
||||||
prefix: () => renderDeviceNodePrefix(device, stationCode),
|
prefix: () => renderDeviceNodePrefix(device, stationCode),
|
||||||
suffix: () => `${device.ipAddress}`,
|
suffix: () => `${device.ipAddress}`,
|
||||||
stationCode,
|
stationCode,
|
||||||
@@ -406,7 +400,7 @@ const stationDeviceTreeData = computed<TreeOption[]>(() => {
|
|||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
stationCode,
|
stationCode,
|
||||||
device: cluster,
|
device,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
stationCode,
|
stationCode,
|
||||||
@@ -420,7 +414,7 @@ const stationDeviceTreeData = computed<TreeOption[]>(() => {
|
|||||||
children: stationDevices[deviceType].map<TreeOption>((device) => {
|
children: stationDevices[deviceType].map<TreeOption>((device) => {
|
||||||
return {
|
return {
|
||||||
label: `${device.name}`,
|
label: `${device.name}`,
|
||||||
key: createDeviceNodeKey(stationCode, device),
|
key: `${device.name}${device.ipAddress}`,
|
||||||
prefix: () => renderDeviceNodePrefix(device, stationCode),
|
prefix: () => renderDeviceNodePrefix(device, stationCode),
|
||||||
suffix: () => `${device.ipAddress}`,
|
suffix: () => `${device.ipAddress}`,
|
||||||
stationCode,
|
stationCode,
|
||||||
@@ -468,7 +462,6 @@ const onFoldDeviceTree = () => {
|
|||||||
};
|
};
|
||||||
const onLocateDeviceTree = async () => {
|
const onLocateDeviceTree = async () => {
|
||||||
if (!selectedStationCode.value) return;
|
if (!selectedStationCode.value) return;
|
||||||
const stationCode = selectedStationCode.value;
|
|
||||||
if (!selectedDevice.value) return;
|
if (!selectedDevice.value) return;
|
||||||
const deviceType = tryGetDeviceType(selectedDevice.value.deviceType);
|
const deviceType = tryGetDeviceType(selectedDevice.value.deviceType);
|
||||||
if (!deviceType) return;
|
if (!deviceType) return;
|
||||||
@@ -480,24 +473,24 @@ const onLocateDeviceTree = async () => {
|
|||||||
activeTab.value = deviceType;
|
activeTab.value = deviceType;
|
||||||
|
|
||||||
// 展开选择的车站
|
// 展开选择的车站
|
||||||
expandedKeys.value.push(stationCode);
|
expandedKeys.value.push(selectedStationCode.value);
|
||||||
|
|
||||||
// 当选择录像机时,如果不是集群,进一步展开该录像机所在的集群节点
|
// 当选择录像机时,如果不是集群,进一步展开该录像机所在的集群节点
|
||||||
if (deviceType === DEVICE_TYPE_LITERALS.ndmNvr) {
|
if (deviceType === DEVICE_TYPE_LITERALS.ndmNvr) {
|
||||||
const stationDevices = lineDevices.value[stationCode];
|
const stationDevices = lineDevices.value[selectedStationCode.value];
|
||||||
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 nvrs = stationDevices[DEVICE_TYPE_LITERALS.ndmNvr];
|
||||||
const clusters = nvrs.filter((nvr) => isNvrCluster(nvr) && nvr.clusterList?.includes(selectedNvr.clusterList ?? ''));
|
const clusters = nvrs.filter((nvr) => isNvrCluster(nvr) && nvr.clusterList?.includes(selectedNvr.clusterList ?? ''));
|
||||||
expandedKeys.value.push(...clusters.map((nvr) => createDeviceNodeKey(stationCode, nvr)));
|
expandedKeys.value.push(...clusters.map((nvr) => `${nvr.id}`));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 等待设备树展开完成,滚动到选择的设备
|
// 等待设备树展开完成,滚动到选择的设备
|
||||||
await nextTick();
|
await nextTick();
|
||||||
deviceTreeInst.value.scrollTo({ key: createDeviceNodeKey(stationCode, selectedDevice.value), behavior: 'smooth' });
|
deviceTreeInst.value.scrollTo({ key: `${selectedDevice.value.id}`, behavior: 'smooth' });
|
||||||
|
|
||||||
animated.value = true;
|
animated.value = true;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -136,17 +136,6 @@ export const unwrapResponse = <T>(resp: HttpResponse<T>) => {
|
|||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const unwrapNullableResponse = <T>(resp: HttpResponse<T>) => {
|
|
||||||
const [err, data, result] = resp;
|
|
||||||
if (err) throw err;
|
|
||||||
if (result) {
|
|
||||||
const { isSuccess, path, msg, errorMsg } = result;
|
|
||||||
if (!isSuccess) throw new Error(`${path ? `${path}: ` : ''}${msg || errorMsg || '请求失败'}`);
|
|
||||||
}
|
|
||||||
if (data === undefined) throw new Error('响应数据不存在');
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 针对没有数据的响应,直接判断是否存在错误
|
// 针对没有数据的响应,直接判断是否存在错误
|
||||||
export const unwrapVoidResponse = (resp: HttpResponse<void>) => {
|
export const unwrapVoidResponse = (resp: HttpResponse<void>) => {
|
||||||
const [err, , result] = resp;
|
const [err, , result] = resp;
|
||||||
|
|||||||
Reference in New Issue
Block a user