feat:手动获取摄像头

This commit is contained in:
dzc committed 2026-08-21 15:50:49 +08:00
1 parent fa9701c92c
commit 4c8dc3c2a4
3 files changed
+74 -22

No files matched your search

+29
View File
@@ -77,6 +77,11 @@ export interface PlatformDeviceDifferenceAction {
deviceCodes?: string[];
}
/** 通道差异处理参数;编码为空时处理当前全部同类差异 */
export interface PlatformChannelDifferenceAction {
channelCodes?: string[];
}
export function getStatus() {
return request({
url: "/admin/third/platform/status",
@@ -126,3 +131,27 @@ export function deleteLocalExtraDevices(data: PlatformDeviceDifferenceAction = {
timeout: 300000,
});
}
/**
* 将 ICC 存在但本地缺少的通道同步到本地
*/
export function syncMissingChannels(data: PlatformChannelDifferenceAction = {}) {
return request({
url: "/admin/third/platform/channel/sync-missing",
method: "post",
data,
timeout: 300000,
});
}
/**
* 删除经 ICC 实时复核后仍属于本地多余的同步通道
*/
export function deleteLocalExtraChannels(data: PlatformChannelDifferenceAction = {}) {
return request({
url: "/admin/third/platform/channel/local-extra",
method: "delete",
data,
timeout: 300000,
});
}
@@ -24,22 +24,22 @@
<el-alert v-if="isDifferenceView" class="difference-alert" :type="differenceAlertType"
:closable="false" show-icon :title="differenceConclusion" />
<!-- 设备差异支持批量修复,也保留表格逐条处理入口。 -->
<section v-if="isDeviceDifferenceView" class="difference-actions" aria-label="设备差异处理">
<!-- 设备和通道差异均支持批量修复,也保留表格逐条处理入口。 -->
<section v-if="isActionableDifferenceView" class="difference-actions" :aria-label="`${resourceLabel}差异处理`">
<div class="difference-actions__description">
<strong>快速处理设备差异</strong>
<span>操作前会重新读取 ICC 并复核设备编码,已变化的数据会自动跳过。</span>
<strong>快速处理{{ resourceLabel }}差异</strong>
<span>操作前会重新读取 ICC 并复核{{ resourceLabel }}编码,已变化的数据会自动跳过。</span>
</div>
<div class="difference-actions__buttons">
<el-button type="primary" icon="RefreshRight" :loading="state.actionKey === 'SYNC_ALL'"
:disabled="actionBusy || state.result.remoteOnlyCount === 0"
v-auth="'admin_deviceManage_edit'" @click="handleDifferenceAction('SYNC')">
同步本地缺少设备({{ state.result.remoteOnlyCount }})
同步本地缺少{{ resourceLabel }}({{ state.result.remoteOnlyCount }})
</el-button>
<el-button type="danger" plain icon="Delete" :loading="state.actionKey === 'DELETE_ALL'"
:disabled="actionBusy || state.result.localOnlyCount === 0"
v-auth="'admin_deviceManage_del'" @click="handleDifferenceAction('DELETE')">
删除本地多余设备({{ state.result.localOnlyCount }})
删除本地多余{{ resourceLabel }}({{ state.result.localOnlyCount }})
</el-button>
</div>
</section>
@@ -64,7 +64,7 @@
</template>
</el-alert>
<el-table v-else :data="state.result.records" border :height="isDeviceDifferenceView ? 360 : 430"
<el-table v-else :data="state.result.records" border :height="isActionableDifferenceView ? 360 : 430"
empty-text="暂无明细数据"
row-key="code">
<el-table-column v-if="isDifferenceView" label="差异结论" width="190" fixed="left">
@@ -117,7 +117,7 @@
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column v-if="isDeviceDifferenceView" label="操作" width="104" align="center"
<el-table-column v-if="isActionableDifferenceView" label="操作" width="104" align="center"
fixed="right">
<template #default="{ row }">
<el-button v-if="row.differenceType === 'REMOTE_ONLY'" type="primary" link
@@ -171,7 +171,7 @@ const emptyResult = (): PlatformDetailResult => ({
});
const emit = defineEmits<{
/** 设备差异处理完成后通知主页面刷新统计。 */
/** 资源差异处理完成后通知主页面刷新统计。 */
(event: "changed"): void;
}>();
@@ -192,7 +192,8 @@ const state = reactive({
const isDevice = computed(() => state.query.resourceType === "DEVICE");
const isDifferenceView = computed(() => state.query.viewType === "DIFFERENCE");
const isDeviceDifferenceView = computed(() => isDevice.value && isDifferenceView.value);
const isActionableDifferenceView = computed(() => isDifferenceView.value);
const resourceLabel = computed(() => isDevice.value ? "设备" : "通道");
const actionBusy = computed(() => Boolean(state.actionKey));
// 兼容旧服务未完整返回 total 的情况,差异页可由双方独有数量准确计算总数。
@@ -260,7 +261,7 @@ const handleSizeChange = () => {
};
/**
* 同步 ICC 独有设备或删除本地独有设备,编码为空时处理当前全部同类差异
* 同步 ICC 独有资源或删除本地独有资源,编码为空时处理当前全部同类差异
*/
const handleDifferenceAction = async (action: "SYNC" | "DELETE", code?: string) => {
const isSync = action === "SYNC";
@@ -268,20 +269,30 @@ const handleDifferenceAction = async (action: "SYNC" | "DELETE", code?: string)
? 1
: isSync ? state.result.remoteOnlyCount : state.result.localOnlyCount;
const actionText = isSync ? "同步" : "删除";
const targetText = isSync ? "本地缺少设备" : "本地多余设备及其通道";
const targetText = isSync
? `本地缺少${resourceLabel.value}`
: isDevice.value ? "本地多余设备及其通道" : "本地多余通道";
try {
await useMessageBox().confirm(`确认${actionText}${code ? `设备 ${code}` : `全部 ${count} 条${targetText}`}?`);
await useMessageBox().confirm(`确认${actionText}${code ? `${resourceLabel.value} ${code}` : `全部 ${count} 条${targetText}`}?`);
} catch {
return;
}
state.actionKey = `${action}_${code || "ALL"}`;
try {
const actionApi = isSync
? VideoPlatformApi.syncMissingDevices
: VideoPlatformApi.deleteLocalExtraDevices;
const { data } = await actionApi({ deviceCodes: code ? [code] : [] });
useMessage().success(`${actionText}完成,共处理 ${Number(data) || 0} 条设备`);
let data: unknown;
if (isDevice.value) {
const response = isSync
? await VideoPlatformApi.syncMissingDevices({ deviceCodes: code ? [code] : [] })
: await VideoPlatformApi.deleteLocalExtraDevices({ deviceCodes: code ? [code] : [] });
data = response.data;
} else {
const response = isSync
? await VideoPlatformApi.syncMissingChannels({ channelCodes: code ? [code] : [] })
: await VideoPlatformApi.deleteLocalExtraChannels({ channelCodes: code ? [code] : [] });
data = response.data;
}
useMessage().success(`${actionText}完成,共处理 ${Number(data) || 0} 条${resourceLabel.value}`);
state.query.current = 1;
await loadDetail();
emit("changed");
@@ -88,11 +88,22 @@ const getSceneNodeValues = (nodes: SceneNode[] = []) => nodes
.map((node) => node.nodeType === "CHANNEL" ? node.channelCode : node.deviceCode)
.filter((value): value is string => Boolean(value))
const handleSourceNodesChange = (_values: string[], nodes: SceneNode[]) => {
form.sourceNodes = normalizeSceneNodes(nodes)
/** 选择器不是 Element Plus 原生表单控件,变更后需主动刷新对应字段校验状态。 */
const validateNodeField = (field: "sourceNodes" | "targetNodes") => {
nextTick(() => {
formRef.value?.validateField(field).catch(() => {})
})
}
const handleTargetNodesChange = (_values: string[], nodes: SceneNode[]) => {
const handleSourceNodesChange = (values: string[], nodes: SceneNode[] = []) => {
sourceNodeValues.value = Array.isArray(values) ? values : []
form.sourceNodes = normalizeSceneNodes(nodes)
validateNodeField("sourceNodes")
}
const handleTargetNodesChange = (values: string[], nodes: SceneNode[] = []) => {
targetNodeValues.value = Array.isArray(values) ? values : []
form.targetNodes = normalizeSceneNodes(nodes)
validateNodeField("targetNodes")
}
const open = async (id?: string | number) => {
@@ -104,7 +115,8 @@ const open = async (id?: string | number) => {
visible.value = true
}
const submit = async () => {
await formRef.value.validate()
const valid = await formRef.value.validate().catch(() => false)
if (!valid) return
saving.value = true
try { await (form.id ? api.updateConfig(form) : api.addConfig(form)); useMessage().success("保存成功"); visible.value = false; emit("refresh") } finally { saving.value = false }
}