feat: device params config modal
This commit is contained in:
@@ -163,7 +163,7 @@ const onModalClose = () => {};
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NModal v-model:show="show" preset="card" style="width: 100vw; height: 100vh" :title="`${station.name} - 设备告警详情`" @close="onModalClose">
|
<NModal v-model:show="show" preset="card" style="width: 100vw; height: 100vh" :title="`${station.name} - 设备告警详情`" :mask-closable="false" @close="onModalClose">
|
||||||
<div v-if="alarmCount === 0" style="text-align: center; padding: 20px; color: #6c757d">
|
<div v-if="alarmCount === 0" style="text-align: center; padding: 20px; color: #6c757d">
|
||||||
<span>当前没有设备告警</span>
|
<span>当前没有设备告警</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
231
src/components/device-params-config-modal.vue
Normal file
231
src/components/device-params-config-modal.vue
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
const DeviceConfigParamPrefix = {
|
||||||
|
Switch: 'SWITCH_',
|
||||||
|
Server: 'SERVER_',
|
||||||
|
Decoder: 'DECODER_',
|
||||||
|
Nvr: 'NVR_',
|
||||||
|
Box: 'BOX_',
|
||||||
|
Monitor: 'MONITOR_',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
type DeviceConfigParamPrefixType = (typeof DeviceConfigParamPrefix)[keyof typeof DeviceConfigParamPrefix];
|
||||||
|
|
||||||
|
interface DeviceParamItem {
|
||||||
|
id: string;
|
||||||
|
key: string;
|
||||||
|
name: string;
|
||||||
|
numValue?: number;
|
||||||
|
timeValue?: string;
|
||||||
|
suffix?: string;
|
||||||
|
step?: number;
|
||||||
|
min?: number;
|
||||||
|
max?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseNumericValue = (name: string, value: string) => {
|
||||||
|
let val = parseFloat(value);
|
||||||
|
const needMultiply = name.includes('流量') || name.includes('占用率');
|
||||||
|
if (needMultiply) val *= 100;
|
||||||
|
return val;
|
||||||
|
};
|
||||||
|
|
||||||
|
const deparseNumericValue = (name: string, value: number) => {
|
||||||
|
let val = value;
|
||||||
|
const needMultiply = name.includes('流量') || name.includes('占用率');
|
||||||
|
if (needMultiply) val /= 100;
|
||||||
|
return val;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getItemStep = (name: string) => {
|
||||||
|
if (name.includes('转速')) return 100;
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getItemMax = (name: string) => {
|
||||||
|
if (name.includes('转速')) return 50000;
|
||||||
|
return 100;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getItemSuffix = (name: string) => {
|
||||||
|
const percentLike = name.includes('流量') || name.includes('占用率') || name.includes('湿度');
|
||||||
|
const secondLike = name.includes('忽略丢失');
|
||||||
|
const currentLike = name.includes('电流');
|
||||||
|
const voltageLike = name.includes('电压');
|
||||||
|
const temperatureLike = name.includes('温');
|
||||||
|
const rpmLike = name.includes('转速');
|
||||||
|
if (percentLike) return '%';
|
||||||
|
if (secondLike) return '秒';
|
||||||
|
if (currentLike) return 'A';
|
||||||
|
if (voltageLike) return 'V';
|
||||||
|
if (temperatureLike) return '℃';
|
||||||
|
if (rpmLike) return '转/分';
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { Station } from '@/apis/domains';
|
||||||
|
import { postDefParameterPage, putDefParameter } from '@/apis/requests';
|
||||||
|
import { useQueryControlStore } from '@/stores/query-control';
|
||||||
|
import { useMutation } from '@tanstack/vue-query';
|
||||||
|
import { NForm, NFormItemGi, NGrid, NInputNumber, NModal, NTabPane, NTabs, NTimePicker } from 'naive-ui';
|
||||||
|
import { ref, toRefs, watch } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps<{ station: Station }>();
|
||||||
|
const { station } = toRefs(props);
|
||||||
|
const show = defineModel<boolean>('show', { required: true });
|
||||||
|
|
||||||
|
watch(show, (newValue) => {
|
||||||
|
const queryControlStore = useQueryControlStore();
|
||||||
|
if (newValue) {
|
||||||
|
console.log('对话框打开,停止轮询');
|
||||||
|
queryControlStore.disablePolling();
|
||||||
|
} else {
|
||||||
|
console.log('对话框关闭,开启轮询');
|
||||||
|
queryControlStore.enablePolling();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const tabPanes = [
|
||||||
|
{
|
||||||
|
tab: '交换机阈值',
|
||||||
|
name: DeviceConfigParamPrefix.Switch,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tab: '服务器阈值',
|
||||||
|
name: DeviceConfigParamPrefix.Server,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tab: '解码器阈值',
|
||||||
|
name: DeviceConfigParamPrefix.Decoder,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tab: '录像机阈值',
|
||||||
|
name: DeviceConfigParamPrefix.Nvr,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tab: '安防箱阈值',
|
||||||
|
name: DeviceConfigParamPrefix.Box,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tab: '显示器计划',
|
||||||
|
name: DeviceConfigParamPrefix.Monitor,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const activeTabName = ref<DeviceConfigParamPrefixType>(DeviceConfigParamPrefix.Switch);
|
||||||
|
|
||||||
|
const onBeforeTabLeave = (name: string, oldName: string): boolean | Promise<boolean> => {
|
||||||
|
saveDeviceParams({ tabName: oldName, params: deviceConfigParams.value });
|
||||||
|
getDeviceParams({ deviceKeyPrefix: name });
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onAfterModalEnter = () => {
|
||||||
|
getDeviceParams({ deviceKeyPrefix: activeTabName.value });
|
||||||
|
};
|
||||||
|
|
||||||
|
const onBeforeModalLeave = () => {
|
||||||
|
saveDeviceParams({ tabName: activeTabName.value, params: deviceConfigParams.value });
|
||||||
|
};
|
||||||
|
|
||||||
|
const deviceConfigParams = ref<DeviceParamItem[]>([]);
|
||||||
|
|
||||||
|
const { mutate: getDeviceParams } = useMutation({
|
||||||
|
mutationFn: async ({ deviceKeyPrefix }: { deviceKeyPrefix: string }) => {
|
||||||
|
const { records } = await postDefParameterPage(station.value.code, {
|
||||||
|
model: {},
|
||||||
|
extra: { key_likeRight: deviceKeyPrefix },
|
||||||
|
current: 1,
|
||||||
|
size: 1000,
|
||||||
|
sort: 'id',
|
||||||
|
order: 'descending',
|
||||||
|
});
|
||||||
|
return records;
|
||||||
|
},
|
||||||
|
onSuccess: (records) => {
|
||||||
|
deviceConfigParams.value = records.map<DeviceParamItem>((record) => {
|
||||||
|
if (record.key?.includes(DeviceConfigParamPrefix.Monitor)) {
|
||||||
|
return {
|
||||||
|
id: record.id ?? '',
|
||||||
|
key: record.key ?? '',
|
||||||
|
name: record.name ?? '',
|
||||||
|
timeValue: record.value ?? '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
id: record.id ?? '',
|
||||||
|
key: record.key ?? '',
|
||||||
|
name: record.name ?? '',
|
||||||
|
numValue: parseNumericValue(record.name ?? '', record.value ?? '0'),
|
||||||
|
suffix: getItemSuffix(record.name ?? ''),
|
||||||
|
step: getItemStep(record.name ?? ''),
|
||||||
|
min: 0,
|
||||||
|
max: getItemMax(record.name ?? ''),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
window.$message.error(error.message);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { mutate: saveDeviceParams } = useMutation({
|
||||||
|
mutationFn: async ({ tabName, params }: { tabName: string; params: DeviceParamItem[] }) => {
|
||||||
|
for (const item of params) {
|
||||||
|
if (tabName.includes(DeviceConfigParamPrefix.Monitor)) {
|
||||||
|
await putDefParameter(station.value.code, {
|
||||||
|
id: item.id,
|
||||||
|
key: item.key,
|
||||||
|
name: item.name,
|
||||||
|
value: item.timeValue,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await putDefParameter(station.value.code, {
|
||||||
|
id: item.id,
|
||||||
|
key: item.key,
|
||||||
|
name: item.name,
|
||||||
|
value: `${deparseNumericValue(item.name, item.numValue ?? 0)}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
window.$message.error(error.message);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NModal
|
||||||
|
v-model:show="show"
|
||||||
|
preset="card"
|
||||||
|
style="width: 800px; height: 600px"
|
||||||
|
:title="`${station.name} - 设备参数配置`"
|
||||||
|
:mask-closable="false"
|
||||||
|
@after-enter="onAfterModalEnter"
|
||||||
|
@before-leave="onBeforeModalLeave"
|
||||||
|
>
|
||||||
|
<NTabs v-model:value="activeTabName" type="card" @before-leave="onBeforeTabLeave">
|
||||||
|
<NTabPane v-for="pane in tabPanes" :key="pane.name" :tab="pane.tab" :name="pane.name">
|
||||||
|
<NForm>
|
||||||
|
<NGrid :cols="1">
|
||||||
|
<NFormItemGi v-for="item in deviceConfigParams" :key="item.key" label-placement="left" :span="1" :label="item.name">
|
||||||
|
<template v-if="activeTabName === DeviceConfigParamPrefix.Monitor">
|
||||||
|
<NTimePicker v-model:formatted-value="item.timeValue" />
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<NInputNumber v-model:value="item.numValue" :step="item.step" :min="item.min" :max="item.max" style="width: 100%">
|
||||||
|
<template #suffix>
|
||||||
|
<span>{{ item.suffix }}</span>
|
||||||
|
</template>
|
||||||
|
</NInputNumber>
|
||||||
|
</template>
|
||||||
|
</NFormItemGi>
|
||||||
|
</NGrid>
|
||||||
|
</NForm>
|
||||||
|
</NTabPane>
|
||||||
|
</NTabs>
|
||||||
|
</NModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
@@ -102,7 +102,7 @@ const onModalClose = () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NModal v-model:show="show" preset="card" style="width: 600px; height: 500px" :title="`${station.name} - 离线设备详情`" @close="onModalClose">
|
<NModal v-model:show="show" preset="card" style="width: 600px; height: 500px" :title="`${station.name} - 离线设备详情`" :mask-closable="false" @close="onModalClose">
|
||||||
<div v-if="offlineDeviceCount === 0" class="no-offline-devices">
|
<div v-if="offlineDeviceCount === 0" class="no-offline-devices">
|
||||||
<span>当前没有离线设备</span>
|
<span>当前没有离线设备</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { NCard, NStatistic, NTag, NGrid, NGi, NButton, NIcon, useThemeVars } from 'naive-ui';
|
import { NCard, NStatistic, NTag, NGrid, NGi, NButton, NIcon, useThemeVars, NSpace } from 'naive-ui';
|
||||||
import { Video as VideoIcon } from '@vicons/carbon';
|
import { Video as VideoIcon } from '@vicons/carbon';
|
||||||
|
import { ControlOutlined } from '@vicons/antd';
|
||||||
import { toRefs, computed, ref } from 'vue';
|
import { toRefs, computed, ref } from 'vue';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { DeviceType } from '@/enums/device-type';
|
import { DeviceType } from '@/enums/device-type';
|
||||||
@@ -9,6 +10,7 @@ import type { StationDevices } from '@/composables/query/use-line-devices-query'
|
|||||||
import type { StationAlarms } from '@/composables/query/use-line-alarms-query';
|
import type { StationAlarms } from '@/composables/query/use-line-alarms-query';
|
||||||
import OfflineDeviceDetailModal from './offline-device-detail-modal.vue';
|
import OfflineDeviceDetailModal from './offline-device-detail-modal.vue';
|
||||||
import DeviceAlarmDetailModal from './device-alarm-detail-modal.vue';
|
import DeviceAlarmDetailModal from './device-alarm-detail-modal.vue';
|
||||||
|
import DeviceParamsConfigModal from './device-params-config-modal.vue';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
station: Station;
|
station: Station;
|
||||||
@@ -33,23 +35,34 @@ const offlineDeviceCount = computed(() => {
|
|||||||
const devicAlarmCount = computed(() => {
|
const devicAlarmCount = computed(() => {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
Object.values(DeviceType).forEach((deviceType) => {
|
Object.values(DeviceType).forEach((deviceType) => {
|
||||||
count += stationAlarms.value[deviceType].occurred.length;
|
count += stationAlarms.value[deviceType].length;
|
||||||
});
|
});
|
||||||
return count;
|
return count;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 打开对话框
|
||||||
const offlineDeviceTreeModalShow = ref(false);
|
const offlineDeviceTreeModalShow = ref(false);
|
||||||
const deviceAlarmTreeModalShow = ref(false);
|
const deviceAlarmTreeModalShow = ref(false);
|
||||||
|
const deviceParamsConfigModalShow = ref(false);
|
||||||
// 打开对话框
|
|
||||||
const openOfflineDeviceTreeModal = () => {
|
const openOfflineDeviceTreeModal = () => {
|
||||||
if (online.value) {
|
if (online.value) {
|
||||||
offlineDeviceTreeModalShow.value = true;
|
offlineDeviceTreeModalShow.value = true;
|
||||||
|
} else {
|
||||||
|
window.$message.error('当前车站离线,无法查看');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const openDeviceAlarmTreeModal = () => {
|
const openDeviceAlarmTreeModal = () => {
|
||||||
if (online.value) {
|
if (online.value) {
|
||||||
deviceAlarmTreeModalShow.value = true;
|
deviceAlarmTreeModalShow.value = true;
|
||||||
|
} else {
|
||||||
|
window.$message.error('当前车站离线,无法查看');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const openDeviceConfigModal = () => {
|
||||||
|
if (online.value) {
|
||||||
|
deviceParamsConfigModalShow.value = true;
|
||||||
|
} else {
|
||||||
|
window.$message.error('当前车站离线,无法查看');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -84,11 +97,18 @@ const theme = useThemeVars();
|
|||||||
</NTag>
|
</NTag>
|
||||||
</template>
|
</template>
|
||||||
<template #default>
|
<template #default>
|
||||||
<NButton text @click="openVideoPlatform">
|
<NSpace>
|
||||||
<NIcon>
|
<NButton text @click="openVideoPlatform">
|
||||||
<VideoIcon />
|
<NIcon>
|
||||||
</NIcon>
|
<VideoIcon />
|
||||||
</NButton>
|
</NIcon>
|
||||||
|
</NButton>
|
||||||
|
<NButton text @click="openDeviceConfigModal">
|
||||||
|
<NIcon>
|
||||||
|
<ControlOutlined />
|
||||||
|
</NIcon>
|
||||||
|
</NButton>
|
||||||
|
</NSpace>
|
||||||
<NGrid :cols="2" :style="{ opacity: online ? '1' : '0.3' }">
|
<NGrid :cols="2" :style="{ opacity: online ? '1' : '0.3' }">
|
||||||
<NGi>
|
<NGi>
|
||||||
<NStatistic tabular-nums>
|
<NStatistic tabular-nums>
|
||||||
@@ -122,7 +142,10 @@ const theme = useThemeVars();
|
|||||||
|
|
||||||
<!-- 离线设备详情对话框 -->
|
<!-- 离线设备详情对话框 -->
|
||||||
<OfflineDeviceDetailModal v-model:show="offlineDeviceTreeModalShow" :station="station" :station-devices="stationDevices" />
|
<OfflineDeviceDetailModal v-model:show="offlineDeviceTreeModalShow" :station="station" :station-devices="stationDevices" />
|
||||||
|
<!-- 设备告警详情对话框 -->
|
||||||
<DeviceAlarmDetailModal v-model:show="deviceAlarmTreeModalShow" :station="station" :station-alarms="stationAlarms" />
|
<DeviceAlarmDetailModal v-model:show="deviceAlarmTreeModalShow" :station="station" :station-alarms="stationAlarms" />
|
||||||
|
<!-- 设备配置面板对话框 -->
|
||||||
|
<DeviceParamsConfigModal v-model:show="deviceParamsConfigModalShow" :station="station" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
Reference in New Issue
Block a user