fix(aiot): 截图持久化适配 + 告警 API 切换到 WVP + 图片代理

截图 / ROI:
- getSnapUrl: 非 force 模式直接返回 /snap/image 代理 URL(从 DB 读持久化截图,
  不触发 Edge),force 模式先请求 Edge 截图再返回代理 URL
- RoiCanvas: 添加 ResizeObserver 确保容器尺寸变化时重新初始化 canvas,
  onImageError 兜底初始化 canvas(截图失败仍可绘制/查看 ROI),
  snapUrl watcher 触发 canvas 重初始化

告警:
- alarm/index.ts: requestClient → wvpRequestClient,路径从
  /aiot/alarm/alert/* 切换到 /aiot/device/alert/*(走 WVP 后端)
- Alert 类型新增 imagePath、receivedAt、extraData 字段
- alarm list: 缩略图和详情图片通过 WVP /alert/image 代理端点显示,
  避免 COS presigned URL 过期问题
- device/index.ts: 新增 getAlertImageUrl() 构造告警图片代理 URL

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-04 09:21:48 +08:00
parent 71fd2a8576
commit 0d56b2f221
4 changed files with 91 additions and 34 deletions

View File

@@ -47,17 +47,32 @@ watch(() => props.drawMode, () => {
watch(() => props.snapUrl, () => {
loading.value = true;
errorMsg.value = '';
nextTick(() => initCanvas());
});
let resizeObserver: ResizeObserver | null = null;
onMounted(() => {
nextTick(() => {
initCanvas();
if (wrapper.value && typeof ResizeObserver !== 'undefined') {
resizeObserver = new ResizeObserver(() => {
if (wrapper.value && wrapper.value.clientWidth > 0) {
initCanvas();
}
});
resizeObserver.observe(wrapper.value);
}
window.addEventListener('resize', handleResize);
});
});
onUnmounted(() => {
window.removeEventListener('resize', handleResize);
if (resizeObserver) {
resizeObserver.disconnect();
resizeObserver = null;
}
});
function onImageLoad() {
@@ -68,6 +83,8 @@ function onImageLoad() {
function onImageError() {
loading.value = false;
errorMsg.value = '截图加载失败,请确认摄像头正在拉流';
// 关键:截图失败也初始化 canvas使 ROI 区域可见可操作
nextTick(() => initCanvas());
}
function initCanvas() {