改造: 全局参数配置页面支持按设备查看和修改 ROI 绑定参数
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { AiotDeviceApi } from '#/api/aiot/device';
|
||||
import type { AiotEdgeApi } from '#/api/aiot/edge';
|
||||
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
|
||||
@@ -9,11 +10,18 @@ import {
|
||||
Form,
|
||||
InputNumber,
|
||||
message,
|
||||
Select,
|
||||
SelectOption,
|
||||
Spin,
|
||||
Tabs,
|
||||
} from 'ant-design-vue';
|
||||
|
||||
import { getAlgorithmList, saveAlgoGlobalParams } from '#/api/aiot/device';
|
||||
import {
|
||||
getAlgorithmList,
|
||||
saveAlgoGlobalParams,
|
||||
updateDeviceAlgoParams,
|
||||
} from '#/api/aiot/device';
|
||||
import { getDeviceList } from '#/api/aiot/edge';
|
||||
|
||||
// 参数名中英文映射(与 AlgorithmParamEditor 保持一致)
|
||||
const paramNameMap: Record<string, string> = {
|
||||
@@ -52,6 +60,22 @@ const paramDescMap: Record<string, string> = {
|
||||
confirm_congestion_sec: '车辆数持续超过阈值达到该时间后触发拥堵告警',
|
||||
};
|
||||
|
||||
// ==================== 设备选择器 ====================
|
||||
const selectedDeviceId = ref<string>('');
|
||||
const deviceList = ref<AiotEdgeApi.Device[]>([]);
|
||||
|
||||
const isDeviceMode = computed(() => selectedDeviceId.value !== '');
|
||||
|
||||
const saveButtonText = computed(() => {
|
||||
if (!isDeviceMode.value) return '保存全局默认';
|
||||
const device = deviceList.value.find(
|
||||
(d) => d.deviceId === selectedDeviceId.value,
|
||||
);
|
||||
const name = device?.deviceName || device?.deviceId || selectedDeviceId.value;
|
||||
return `保存到 ${name}`;
|
||||
});
|
||||
|
||||
// ==================== 算法数据 ====================
|
||||
const algorithms = ref<AiotDeviceApi.Algorithm[]>([]);
|
||||
const activeTab = ref<string>('');
|
||||
const loading = ref(false);
|
||||
@@ -89,20 +113,40 @@ function getParamDesc(key: string): string | undefined {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await loadAlgorithms();
|
||||
await Promise.all([loadDevices(), loadAlgorithms()]);
|
||||
});
|
||||
|
||||
async function loadDevices() {
|
||||
try {
|
||||
const list = await getDeviceList();
|
||||
deviceList.value = Array.isArray(list) ? list : [];
|
||||
} catch {
|
||||
deviceList.value = [];
|
||||
}
|
||||
}
|
||||
|
||||
async function onDeviceChange() {
|
||||
await loadAlgorithms();
|
||||
}
|
||||
|
||||
async function loadAlgorithms() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const data = await getAlgorithmList();
|
||||
const deviceId = selectedDeviceId.value || undefined;
|
||||
const data = await getAlgorithmList(deviceId);
|
||||
algorithms.value = Array.isArray(data) ? data : [];
|
||||
if (algorithms.value.length > 0) {
|
||||
activeTab.value = algorithms.value[0]!.algoCode || '';
|
||||
// 保持当前 tab,如果不存在则切到第一个
|
||||
const codes = algorithms.value.map((a) => a.algoCode);
|
||||
if (!codes.includes(activeTab.value)) {
|
||||
activeTab.value = algorithms.value[0]!.algoCode || '';
|
||||
}
|
||||
// 初始化所有算法的表单数据
|
||||
for (const algo of algorithms.value) {
|
||||
initFormData(algo);
|
||||
}
|
||||
} else {
|
||||
activeTab.value = '';
|
||||
}
|
||||
} catch {
|
||||
message.error('加载算法列表失败');
|
||||
@@ -174,8 +218,19 @@ async function handleSave() {
|
||||
}
|
||||
}
|
||||
|
||||
await saveAlgoGlobalParams(algo.algoCode, JSON.stringify(toSave));
|
||||
message.success('全局参数保存成功');
|
||||
if (isDeviceMode.value) {
|
||||
// 设备模式:保存到设备绑定
|
||||
await updateDeviceAlgoParams(
|
||||
selectedDeviceId.value,
|
||||
algo.algoCode,
|
||||
JSON.stringify(toSave),
|
||||
);
|
||||
message.success('设备参数保存成功');
|
||||
} else {
|
||||
// 全局模式:保存全局参数
|
||||
await saveAlgoGlobalParams(algo.algoCode, JSON.stringify(toSave));
|
||||
message.success('全局参数保存成功');
|
||||
}
|
||||
// 更新本地算法数据
|
||||
algo.globalParams = JSON.stringify(toSave);
|
||||
dirtyFieldsMap.value[algo.algoCode] = new Set();
|
||||
@@ -209,11 +264,25 @@ function isSkippedField(key: string, schema: any): boolean {
|
||||
|
||||
<template>
|
||||
<div class="algo-global-config">
|
||||
<Card title="算法全局参数配置">
|
||||
<Card title="算法参数配置">
|
||||
<template #extra>
|
||||
<span class="card-tip">
|
||||
配置各算法的全局默认参数,ROI 绑定时将以此为默认值
|
||||
</span>
|
||||
<div class="card-extra">
|
||||
<Select
|
||||
v-model:value="selectedDeviceId"
|
||||
style="width: 240px"
|
||||
placeholder="选择设备"
|
||||
@change="onDeviceChange"
|
||||
>
|
||||
<SelectOption value="">全局默认</SelectOption>
|
||||
<SelectOption
|
||||
v-for="d in deviceList"
|
||||
:key="d.deviceId"
|
||||
:value="d.deviceId"
|
||||
>
|
||||
{{ d.deviceName || d.deviceId }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<Spin :spinning="loading">
|
||||
@@ -314,7 +383,7 @@ function isSkippedField(key: string, schema: any): boolean {
|
||||
:loading="saving"
|
||||
@click="handleSave"
|
||||
>
|
||||
保存全局参数
|
||||
{{ saveButtonText }}
|
||||
</Button>
|
||||
</div>
|
||||
</Tabs.TabPane>
|
||||
@@ -324,7 +393,12 @@ function isSkippedField(key: string, schema: any): boolean {
|
||||
<!-- 帮助说明 -->
|
||||
<div class="help-tip">
|
||||
<span class="help-text">
|
||||
全局参数为各算法的默认配置,当 ROI 绑定算法时未单独设置参数,将使用此处的全局默认值。修改后需重新推送配置到边缘端才能生效。
|
||||
<template v-if="isDeviceMode">
|
||||
当前查看的是该设备各 ROI 绑定的实际参数。修改后将更新该设备所有 ROI 绑定中对应算法的参数,需重新推送配置到边缘端才能生效。
|
||||
</template>
|
||||
<template v-else>
|
||||
全局参数为各算法的默认配置,当 ROI 绑定算法时未单独设置参数,将使用此处的全局默认值。修改后需重新推送配置到边缘端才能生效。
|
||||
</template>
|
||||
</span>
|
||||
</div>
|
||||
</Card>
|
||||
@@ -336,9 +410,10 @@ function isSkippedField(key: string, schema: any): boolean {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.card-tip {
|
||||
font-size: 13px;
|
||||
color: #8c8c8c;
|
||||
.card-extra {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
|
||||
Reference in New Issue
Block a user