fix 实时监控收集

This commit is contained in:
Ironsp
2026-05-14 16:30:06 +08:00
parent 462ba124a2
commit 4c9cd63ac0

View File

@@ -104,11 +104,16 @@
<template #header>
<div class="flex w-full items-center justify-between gap-2 min-w-0 pr-8">
<span class="truncate">实时监控-北门出入门监控</span>
<el-button type="primary" class="shrink-0">绘制区域</el-button>
<el-button type="primary" class="shrink-0" @click="drawImage()">绘制区域</el-button>
</div>
</template>
<div class="monitor-video-wrap">
<div class="monitor-video-stack">
<video ref="monitorVideoRef" class="monitor-video" controls preload="metadata" :src="monitorVideoUrl">您的浏览器不支持视频播放</video>
<div v-show="monitorDrawCollecting" class="monitor-draw-overlay" @click.stop="onMonitorDrawClick" />
<div v-if="monitorRectPx" class="monitor-draw-rect" :style="drawRectStyle" />
<div v-if="monitorRectPx" class="monitor-draw-label" :style="drawLabelStyle">当前报警区域</div>
</div>
</div>
</el-dialog>
</div>
@@ -145,6 +150,31 @@ const dialog1 = reactive<DialogOption>({
const monitorVideoUrl = 'http://192.168.0.214:8088/5a0db047943df198aff98b650ac9e001.mp4';
const monitorVideoRef = ref<HTMLVideoElement>();
type MonitorPoint = { x: number; y: number };
const monitorDrawCollecting = ref(false);
const monitorDrawPoints = ref<MonitorPoint[]>([]);
const monitorRectPx = ref<{ left: number; top: number; width: number; height: number } | null>(null);
const drawRectStyle = computed<Record<string, string>>(() => {
const r = monitorRectPx.value;
if (!r) return {};
return {
left: `${r.left}px`,
top: `${r.top}px`,
width: `${Math.max(r.width, 2)}px`,
height: `${Math.max(r.height, 2)}px`
};
});
const drawLabelStyle = computed<Record<string, string>>(() => {
const r = monitorRectPx.value;
if (!r) return {};
return {
left: `${r.left + r.width / 2}px`,
top: `${r.top}px`
};
});
const initFormData = {
id: undefined,
code: undefined,
@@ -264,8 +294,15 @@ const handleActivate = async (row?: any) => {
};
/* 查看监控 */
const resetMonitorDraw = () => {
monitorDrawCollecting.value = false;
monitorDrawPoints.value = [];
monitorRectPx.value = null;
};
const handleSee = async (row?: any) => {
reset();
resetMonitorDraw();
const _id = row?.id || ids.value[0];
const res = await getDeviceInfo(_id);
Object.assign(form.value, res.data);
@@ -275,9 +312,102 @@ const handleSee = async (row?: any) => {
const handleMonitorDialogClose = (done: () => void) => {
monitorVideoRef.value?.pause();
monitorVideoRef.value && (monitorVideoRef.value.currentTime = 0);
resetMonitorDraw();
done();
};
/** 显示坐标(相对 video 元素) → 视频原始像素坐标,含 object-fit: contain 留白换算 */
const displayToVideoPixel = (px: number, py: number, video: HTMLVideoElement): { vx: number; vy: number } | null => {
const dw = video.clientWidth;
const dh = video.clientHeight;
const VW = video.videoWidth;
const VH = video.videoHeight;
if (dw <= 0 || dh <= 0 || VW <= 0 || VH <= 0) return null;
const scale = Math.min(dw / VW, dh / VH);
const contentW = VW * scale;
const contentH = VH * scale;
const offsetX = (dw - contentW) / 2;
const offsetY = (dh - contentH) / 2;
const vx = Math.min(VW, Math.max(0, (px - offsetX) / scale));
const vy = Math.min(VH, Math.max(0, (py - offsetY) / scale));
return { vx, vy };
};
/** 开始采集:视频上点击两个对角点 */
const drawImage = () => {
resetMonitorDraw();
monitorDrawCollecting.value = true;
proxy?.$modal.msgSuccess('请在视频画面上连续点击两个点,生成报警区域');
};
const onMonitorDrawClick = (e: MouseEvent) => {
if (!monitorDrawCollecting.value) return;
const video = monitorVideoRef.value;
if (!video) {
proxy?.$modal.msgWarning('视频未就绪');
return;
}
const rect = video.getBoundingClientRect();
const dw = rect.width;
const dh = rect.height;
if (dw <= 0 || dh <= 0) {
proxy?.$modal.msgWarning('无法获取视频区域尺寸');
return;
}
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
const p: MonitorPoint = { x, y };
monitorDrawPoints.value = [...monitorDrawPoints.value, p];
if (monitorDrawPoints.value.length < 2) return;
const [p1, p2] = monitorDrawPoints.value;
const VW = video.videoWidth;
const VH = video.videoHeight;
const v1 = displayToVideoPixel(p1.x, p1.y, video);
const v2 = displayToVideoPixel(p2.x, p2.y, video);
if (!v1 || !v2 || !VW || !VH) {
monitorDrawPoints.value = [];
proxy?.$modal.msgWarning('请等待视频加载完成后再绘制(需能读取原始宽高)');
return;
}
const left = Math.min(p1.x, p2.x);
const top = Math.min(p1.y, p2.y);
const width = Math.max(Math.abs(p2.x - p1.x), 1);
const height = Math.max(Math.abs(p2.y - p1.y), 1);
monitorRectPx.value = { left, top, width, height };
monitorDrawCollecting.value = false;
monitorDrawPoints.value = [];
const minVx = Math.min(v1.vx, v2.vx);
const maxVx = Math.max(v1.vx, v2.vx);
const minVy = Math.min(v1.vy, v2.vy);
const maxVy = Math.max(v1.vy, v2.vy);
const rw = Math.max(maxVx - minVx, 1);
const rh = Math.max(maxVy - minVy, 1);
const firstTopPctOfHeight = (v1.vy / VH) * 100;
const firstRightPctOfWidth = ((VW - v1.vx) / VW) * 100;
const rectWidthPctOfWidth = (rw / VW) * 100;
const rectHeightPctOfHeight = (rh / VH) * 100;
const payload = {
videoIntrinsicSize: { width: VW, height: VH },
firstPointTopOffset_pctOfVideoHeight: Number(firstTopPctOfHeight.toFixed(4)),
firstPointDistanceToRight_pctOfVideoWidth: Number(firstRightPctOfWidth.toFixed(4)),
rectWidth_pctOfVideoWidth: Number(rectWidthPctOfWidth.toFixed(4)),
rectHeight_pctOfVideoHeight: Number(rectHeightPctOfHeight.toFixed(4))
};
console.log('[监控区域] 四点百分比(相对视频原始宽高 videoWidth/videoHeight):', payload);
proxy?.$modal.msgSuccess(
`已采集(相对原视频):顶距${payload.firstPointTopOffset_pctOfVideoHeight}% 右距${payload.firstPointDistanceToRight_pctOfVideoWidth}% 框宽${payload.rectWidth_pctOfVideoWidth}% 框高${payload.rectHeight_pctOfVideoHeight}%`
);
};
/** 删除按钮操作 */
const handleDelete = async (row?: DemoVO) => {
const _ids = row?.id || ids.value;
@@ -333,12 +463,21 @@ onMounted(() => {
min-height: 0;
width: 100%;
display: flex;
align-items: center;
align-items: stretch;
justify-content: center;
background: #000;
}
.monitor-video-stack {
position: relative;
flex: 1;
min-height: 0;
width: 100%;
}
.monitor-video {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
max-width: 100%;
@@ -346,4 +485,33 @@ onMounted(() => {
object-fit: contain; //cover裁剪
background: #000;
}
.monitor-draw-overlay {
position: absolute;
inset: 0;
z-index: 2;
cursor: crosshair;
background: transparent;
}
.monitor-draw-rect {
position: absolute;
z-index: 3;
pointer-events: none;
box-sizing: border-box;
border: 2px dashed #2563eb;
background: rgba(37, 99, 235, 0.1);
}
.monitor-draw-label {
position: absolute;
z-index: 4;
pointer-events: none;
transform: translate(-50%, -100%);
margin-top: -6px;
color: #2563eb;
font-size: 14px;
font-weight: 600;
white-space: nowrap;
}
</style>