Files
ndm-web-client/src/components/station-page/device-params-config-modal.vue
2025-11-20 10:58:19 +08:00

249 lines
7.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script lang="ts">
// 设备参数配置在系统中的key前缀
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 { postDefParameterPage, putDefParameter, type Station } from '@/apis';
import { useQueryControlStore } from '@/stores';
import { useMutation } from '@tanstack/vue-query';
import { NForm, NFormItemGi, NGrid, NInputNumber, NModal, NTabPane, NTabs, NTimePicker, NSpin, NFlex } 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 });
const queryControlStore = useQueryControlStore();
watch(show, (newValue) => {
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 });
activeTabName.value = DeviceConfigParamPrefix.Switch;
deviceConfigParams.value = [];
};
const deviceConfigParams = ref<DeviceParamItem[]>([]);
const { mutate: getDeviceParams, isPending: paramsLoading } = useMutation({
mutationFn: async ({ deviceKeyPrefix }: { deviceKeyPrefix: string }) => {
if (!station.value) throw new Error('请先选择车站');
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) => {
console.error(error);
window.$message.error(error.message);
},
});
const { mutate: saveDeviceParams } = useMutation({
mutationFn: async ({ tabName, params }: { tabName: string; params: DeviceParamItem[] }) => {
if (!station.value) throw new Error('请先选择车站');
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) => {
console.error(error);
window.$message.error(error.message);
},
});
</script>
<template>
<NModal
v-model:show="show"
preset="card"
style="width: 800px; height: 600px"
:title="`${station?.name} - 设备参数配置`"
:close-on-esc="false"
: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">
<NFlex v-if="paramsLoading" :justify="'center'" :align="'center'">
<NSpin :show="paramsLoading" description="加载设备参数中..." />
</NFlex>
<NForm v-else>
<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>