feat(aiot): 摄像头管理页面改用cameraCode,隐藏app输入

- 表格列:将"应用名"改为"摄像头编码",显示cameraCode字段
- loadRoiCounts:使用cameraCode替代app/stream拼接
- handleRoiConfig:路由query使用cameraCode替代cameraId
- handleExport:使用cameraCode导出配置
- 表单:隐藏app输入框,编辑模式显示cameraCode(只读)
- 删除确认:显示cameraCode而非app/stream
- 验证逻辑:移除app字段必填验证

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-13 11:34:34 +08:00
parent 2583ed5335
commit 4bbc4b16dd

View File

@@ -54,8 +54,7 @@ const searchQuery = ref('');
const searchPulling = ref<string | undefined>(undefined);
const columns = [
{ title: '应用名', dataIndex: 'app', width: 100 },
{ title: '流ID', dataIndex: 'stream', width: 120 },
{ title: '摄像头编码', dataIndex: 'cameraCode', width: 180 },
{ title: '拉流地址', dataIndex: 'srcUrl', ellipsis: true },
{ title: '状态', key: 'pulling', width: 100 },
{ title: 'ROI', key: 'roiCount', width: 80, align: 'center' as const },
@@ -110,11 +109,12 @@ async function loadData() {
function loadRoiCounts() {
for (const cam of cameraList.value) {
const cameraId = `${cam.app}/${cam.stream}`;
getRoiByCameraId(cameraId)
const cameraCode = cam.cameraCode;
if (!cameraCode) continue;
getRoiByCameraId(cameraCode)
.then((data: any) => {
const items = Array.isArray(data) ? data : [];
roiCounts.value = { ...roiCounts.value, [cameraId]: items.length };
roiCounts.value = { ...roiCounts.value, [cameraCode]: items.length };
})
.catch(() => {});
}
@@ -169,6 +169,7 @@ function handleEdit(row: AiotDeviceApi.Camera) {
type: row.type || 'default',
app: row.app || '',
stream: row.stream || '',
cameraCode: row.cameraCode || '',
srcUrl: row.srcUrl || '',
timeout: row.timeout ?? 15,
rtspType: row.rtspType || '0',
@@ -185,10 +186,6 @@ function handleEdit(row: AiotDeviceApi.Camera) {
}
async function handleSave() {
if (!editForm.app?.trim()) {
message.warning('请输入应用名');
return;
}
if (!editForm.stream?.trim()) {
message.warning('请输入流ID');
return;
@@ -215,7 +212,7 @@ async function handleSave() {
function handleDelete(row: AiotDeviceApi.Camera) {
Modal.confirm({
title: '删除确认',
content: `确定删除摄像头 ${row.app}/${row.stream} `,
content: `确定删除摄像头 ${row.cameraCode || row.stream} `,
okType: 'danger',
async onOk() {
try {
@@ -252,9 +249,7 @@ function handleRoiConfig(row: AiotDeviceApi.Camera) {
router.push({
path: '/aiot/device/roi',
query: {
cameraId: `${row.app}/${row.stream}`,
app: row.app,
stream: row.stream,
cameraCode: row.cameraCode,
srcUrl: row.srcUrl,
},
});
@@ -263,15 +258,19 @@ function handleRoiConfig(row: AiotDeviceApi.Camera) {
// ==================== 配置导出 ====================
async function handleExport(row: AiotDeviceApi.Camera) {
const cameraId = `${row.app}/${row.stream}`;
const cameraCode = row.cameraCode;
if (!cameraCode) {
message.warning('摄像头编码为空,无法导出');
return;
}
try {
const data = await exportConfig(cameraId);
const data = await exportConfig(cameraCode);
const json = JSON.stringify(data, null, 2);
const blob = new Blob([json], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `config_${row.app}_${row.stream}.json`;
a.download = `config_${cameraCode}.json`;
a.click();
URL.revokeObjectURL(url);
} catch {
@@ -380,7 +379,7 @@ onMounted(() => {
</template>
<template v-else-if="column.key === 'roiCount'">
<Badge
:count="roiCounts[`${record.app}/${record.stream}`] ?? 0"
:count="roiCounts[record.cameraCode] ?? 0"
:number-style="{ backgroundColor: '#1677ff' }"
show-zero
/>
@@ -436,8 +435,8 @@ onMounted(() => {
]"
/>
</Form.Item>
<Form.Item label="应用名" required>
<Input v-model:value="editForm.app" placeholder="如: live" />
<Form.Item v-if="editForm.id" label="摄像头编码">
<Input :value="editForm.cameraCode" disabled />
</Form.Item>
<Form.Item label="流ID" required>
<Input