改造: 全局参数配置页面支持按设备查看和修改 ROI 绑定参数

This commit is contained in:
2026-04-10 12:51:49 +08:00
parent 6eea4e81cd
commit e0983a693a
2 changed files with 100 additions and 16 deletions

View File

@@ -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 {