From 36aef5041950fe26a399e25fc0860902f4685103 Mon Sep 17 00:00:00 2001 From: renna <576157508@qq.com> Date: Fri, 6 Jun 2025 19:05:32 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E5=A4=A7=E9=83=A8=E5=88=86?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 +- src/api/detail.js | 61 +- src/api/index.js | 7 + src/assets/img/empty.png | Bin 2976 -> 2899 bytes src/components/LatestAlarms.vue | 150 ++-- src/components/common/CarouselVideoPlayer.vue | 379 +++++++++ .../common/CarouselWebRTCPlayer.vue | 372 +++++++++ src/components/common/CustomWebRTCPlayer.vue | 266 ++++--- src/components/common/EmptyState.vue | 15 +- src/components/common/VideoPlayer.vue | 2 +- src/components/dialog/AlarmDetailModal.vue | 263 +++++-- src/components/dialog/EventDetailModal.vue | 142 +++- src/views/Home copy.vue | 162 ++-- src/views/Home.vue | 95 ++- src/views/RobotDetail.vue | 721 +++++++++++++++--- 15 files changed, 2222 insertions(+), 418 deletions(-) create mode 100644 src/components/common/CarouselVideoPlayer.vue create mode 100644 src/components/common/CarouselWebRTCPlayer.vue diff --git a/README.md b/README.md index ff3eb34..f013f4a 100644 --- a/README.md +++ b/README.md @@ -114,4 +114,7 @@ npm run build "clientip": "{\"osName\":\"Windows\",\"osVersion\":\"10\",\"browserName\":\"Chrome\",\"browserVersion\":\"137.0.0.0\",\"SDK_VERSION\":\"1.0.9\"}", "sdp": "v=0\r\no=...", // SDP内容 "streamurl": "webrtc://qvs-live.thirdmonitor.concoai.com:447/2xenzwqy2ppcl/31011500991180041301_34020000001320000001" -} \ No newline at end of file +} + +计划修改: +CarouselVideoPlayer修改逻辑 找不到流的时候 页面显示空状态 也进行轮播 而不是找不到匹配的视角或视角没有流,找第一个有效视角 \ No newline at end of file diff --git a/src/api/detail.js b/src/api/detail.js index a6c1921..d9f4294 100644 --- a/src/api/detail.js +++ b/src/api/detail.js @@ -1,4 +1,4 @@ -import request, { concoaiService } from '@/utils/request' +import request, { concoaiService,service } from '@/utils/request' // 机器人相关接口 export const robotApi = { @@ -28,7 +28,7 @@ export const robotApi = { // 机器人详情 getRobotDetail: (robotId) => { return concoaiService({ - url: `/robot/detail/${robotId} `, + url: `/robot/detail/${robotId}`, method: 'get', }) }, @@ -49,22 +49,59 @@ export const robotApi = { }) }, // 一键处理事件接口 - handleAlarmEvent: (data) => { - return concoaiService({ - url: `/event/handleEventBatch`, + // handleAlarmEvent: (data) => { + // return concoaiService({ + // url: `/event/handleEventBatch`, + // method: 'post', + // data + // }) + // }, + // 获取告警信息数量接口添加 + getAlarmEventCount: (params) => { + return service({ + url: `/api/v1/events/alertCount`, + method: 'get', + params + }) + }, + // 获取未处理告警消息列表 + getUnhandledAlarmMessages: (params) => { + return service({ + url: `/api/v1/events/messagesUnhandled`, + method: 'get', + params + }) + }, + // 获取已处理告警消息列表 + getHandledAlarmMessages: (params) => { + return service({ + url: `/api/v1/events/messagesHandled`, + method: 'get', + params + }) + }, + // 一键处理告警事件 + handleOcrAlerts: (params) => { + return service({ + url: `/api/v1/events/handleOcrAlerts`, + method: 'get', + params + }) + }, + // 处理单个告警事件 + handleSingleAlarmEvent: (data) => { + return service({ + url: `/api/v1/events/handleOcrAlert`, method: 'post', data }) }, - // 单个事件查看详情 - getAlarmEventDetail: (eventId) => { - return concoaiService({ - url: `/event/eventInfo/${eventId}`, + // 查看每一条消息的详情 + getAlarmEventDetail: (messageId) => { + return service({ + url: `/api/v1/events/alertFront/${messageId}`, method: 'get', }) } - - - } diff --git a/src/api/index.js b/src/api/index.js index f649817..3a52be1 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -63,5 +63,12 @@ export const homeApi = { url: '/api/v1/events/getMonitor', method: 'get', }) + }, + //查看告警消息详情 + getAlarmEventDetail:(messageId)=>{ + return service({ + url: `/api/v1/events/alertFront/${messageId}`, + method: 'get', + }) } } \ No newline at end of file diff --git a/src/assets/img/empty.png b/src/assets/img/empty.png index a3659206635e82a9ae64cd2c4eedacb461f11f0a..885283236d1c6330f998f2fbd9de1507895d4eb2 100644 GIT binary patch delta 2857 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 Hu0mjfPac&< delta 2934 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 diff --git a/src/components/LatestAlarms.vue b/src/components/LatestAlarms.vue index 6ed1068..1dd06c2 100644 --- a/src/components/LatestAlarms.vue +++ b/src/components/LatestAlarms.vue @@ -31,56 +31,56 @@ @@ -415,20 +415,72 @@ const stopScroll = () => { const showModal = ref(false); const currentAlarm = ref({}); -const showAlarmDetail = (alarm) => { - currentAlarm.value = { - ...alarm, - title: alarm.event, // 使用事件名称作为标题 - mainImage: alarm.image, - subImages: alarm.subImages || [], // 使用API返回的图片数组 - type: eventTypeMap[alarm.type] || alarm.event, // 使用事件类型映射 - time: alarm.time, - robotName: alarm.robotId, - status: alarm.handled ? '已处理' : '未处理', - temperature: alarm.temperature || '', - messageId: alarm.id // 用于处理时传递 - }; - showModal.value = true; +const showAlarmDetail = async (alarm) => { + try { + // 调用获取告警详情API + const res = await homeApi.getAlarmEventDetail(alarm.id); + + if (res.code === 200 && res.data) { + console.log('获取告警详情成功:', res.data); + + // 合并API返回的详细数据 + const detailData = res.data; + + currentAlarm.value = { + ...alarm, + title: alarm.event, // 使用事件名称作为标题 + mainImage: alarm.image, // 默认使用列表中的图片 + subImages: alarm.subImages || [], // 默认使用API返回的图片数组 + type: eventTypeMap[alarm.type] || alarm.event, // 使用事件类型映射 + time: alarm.time, + robotName: alarm.robotId, + status: alarm.handled ? '已处理' : '未处理', + temperature: alarm.temperature || '', + messageId: alarm.id, // 用于处理时传递 + + // 保存原始数据 + rawDetail: detailData + }; + + showModal.value = true; + } else { + console.error('获取告警详情失败:', res); + + // 如果API调用失败,仍然使用基本数据显示弹窗 + currentAlarm.value = { + ...alarm, + title: alarm.event, + mainImage: alarm.image, + subImages: alarm.subImages || [], + type: eventTypeMap[alarm.type] || alarm.event, + time: alarm.time, + robotName: alarm.robotId, + status: alarm.handled ? '已处理' : '未处理', + temperature: alarm.temperature || '', + messageId: alarm.id + }; + + showModal.value = true; + } + } catch (err) { + console.error('获取告警详情错误:', err); + + // 发生错误时,使用基本数据显示弹窗 + currentAlarm.value = { + ...alarm, + title: alarm.event, + mainImage: alarm.image, + subImages: alarm.subImages || [], + type: eventTypeMap[alarm.type] || alarm.event, + time: alarm.time, + robotName: alarm.robotId, + status: alarm.handled ? '已处理' : '未处理', + temperature: alarm.temperature || '', + messageId: alarm.id + }; + + showModal.value = true; + } }; const handleConfirm = async (data) => { diff --git a/src/components/common/CarouselVideoPlayer.vue b/src/components/common/CarouselVideoPlayer.vue new file mode 100644 index 0000000..4e4d3cc --- /dev/null +++ b/src/components/common/CarouselVideoPlayer.vue @@ -0,0 +1,379 @@ + + + + + \ No newline at end of file diff --git a/src/components/common/CarouselWebRTCPlayer.vue b/src/components/common/CarouselWebRTCPlayer.vue new file mode 100644 index 0000000..70ce3e7 --- /dev/null +++ b/src/components/common/CarouselWebRTCPlayer.vue @@ -0,0 +1,372 @@ + + + + + \ No newline at end of file diff --git a/src/components/common/CustomWebRTCPlayer.vue b/src/components/common/CustomWebRTCPlayer.vue index 89c0c50..4afd605 100644 --- a/src/components/common/CustomWebRTCPlayer.vue +++ b/src/components/common/CustomWebRTCPlayer.vue @@ -1,28 +1,31 @@ @@ -208,8 +281,42 @@ onUnmounted(() => { position: relative; overflow: hidden; display: flex; - justify-content: center; align-items: center; + justify-content: center; +} + +.empty-state { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + color: rgba(255, 255, 255, 0.5); + font-size: 14px; +} + +.video-empty-state { + background: #033347; + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +:deep(.empty-icon) { + height: 46px !important; +} + +:deep(.empty-title) { + display: none; +} + +:deep(.empty-subtitle) { + color: #fff; + font-size: 10px; + letter-spacing: 1px; + margin-top: 5px; } .player-container { @@ -221,25 +328,7 @@ onUnmounted(() => { .video-element { width: 100%; height: 100%; -} - -.video-fill { - object-fit: fill; /* 拉伸填充,可能会变形 */ -} - -.video-cover { - object-fit: cover; /* 保持比例填充,可能会裁剪 */ -} - -.video-stretch-height { - object-fit: none; /* 不调整大小 */ - width: 100%; - height: auto !important; /* 自动调整高度 */ - min-height: 100%; /* 确保至少填满容器高度 */ - position: absolute; - left: 0; - top: 50%; - transform: translateY(-50%); /* 垂直居中 */ + object-fit: cover; } .loading-overlay { @@ -248,51 +337,44 @@ onUnmounted(() => { left: 0; width: 100%; height: 100%; - background: rgba(3, 51, 71, 0.7); + background: rgba(0, 0, 0, 0.5); display: flex; flex-direction: column; - justify-content: center; align-items: center; - z-index: 10; + justify-content: center; + color: white; } .loading-spinner { width: 40px; height: 40px; - border: 3px solid rgba(42, 185, 208, 0.3); - border-top: 3px solid #2AB9D0; + border: 4px solid rgba(255, 255, 255, 0.3); + border-top: 4px solid #00a8ff; border-radius: 50%; animation: spin 1s linear infinite; margin-bottom: 10px; } .loading-text { - color: #B9E8FF; font-size: 14px; + color: #fff; +} + +.error-message { + position: absolute; + bottom: 10px; + left: 10px; + right: 10px; + background: rgba(255, 0, 0, 0.7); + color: white; + padding: 8px; + border-radius: 4px; + font-size: 12px; + text-align: center; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } - -.video-empty-state { - padding-top: 30px; - background: #033347; -} - -:deep(.empty-icon) { - width: 100px; - height: 74px; -} - -:deep(.empty-title) { - display: none; -} - -:deep(.empty-subtitle) { - color: #fff; - font-size: 10px; - letter-spacing: 1px; -} \ No newline at end of file diff --git a/src/components/common/EmptyState.vue b/src/components/common/EmptyState.vue index 77443c7..0cc4199 100644 --- a/src/components/common/EmptyState.vue +++ b/src/components/common/EmptyState.vue @@ -1,8 +1,8 @@ @@ -15,11 +15,11 @@ defineProps({ }, title: { type: String, - default: '暂无告警事件' + default: '' }, subtitle: { type: String, - default: '所有告警已处理完成' + default: '' } }); @@ -40,7 +40,7 @@ defineProps({ height: 80px; background-repeat: no-repeat; background-size: 100% 100%; - margin-bottom: 15px; + margin-bottom: 10px; } .empty-title { @@ -48,10 +48,15 @@ defineProps({ font-size: 20px; font-weight: bold; margin-bottom: 5px; + min-height: 20px; } .empty-subtitle { color: #2AB9D0; font-size: 16px; } + +.empty-subtitle.no-title { + margin-top: 0; +} \ No newline at end of file diff --git a/src/components/common/VideoPlayer.vue b/src/components/common/VideoPlayer.vue index bb793c5..8529ecd 100644 --- a/src/components/common/VideoPlayer.vue +++ b/src/components/common/VideoPlayer.vue @@ -354,7 +354,7 @@ onUnmounted(() => { } :deep(.empty-icon) { - width: 100px; + /* width: 100px; */ height: 74px; } diff --git a/src/components/dialog/AlarmDetailModal.vue b/src/components/dialog/AlarmDetailModal.vue index d45336b..722da8c 100644 --- a/src/components/dialog/AlarmDetailModal.vue +++ b/src/components/dialog/AlarmDetailModal.vue @@ -3,13 +3,16 @@

状态:

-

{{ alarmData.status }}

+

+ {{ alarmData.status }} +

+
+
+

温度:

+

{{ alarmData.temperature }}

@@ -69,7 +149,13 @@
备注
-
+
@@ -86,46 +172,94 @@ @@ -361,6 +412,7 @@ const displayCameras = computed(() => { border-radius: 4px; overflow: hidden; position: relative; + /* border: 1px solid rgba(0, 168, 255, 0.2); */ } .camera-header { @@ -372,8 +424,8 @@ const displayCameras = computed(() => { display: flex; align-items: center; justify-content: space-between; - padding:4px 8px; - background: rgba(0, 0, 0, 0.2); + padding: 6px 10px; + background: rgba(0, 0, 0, 0.7); color: #B9E8FF; font-size: 12px; } @@ -382,15 +434,25 @@ const displayCameras = computed(() => { display: flex; align-items: center; gap: 5px; - img { - width: 14px; - height: 14px; - } +} + +.camera-title img { + width: 16px; + height: 16px; +} + +.camera-title p { + margin: 0; + font-size: 13px; + letter-spacing: 0.5px; } .camera-feed { height: 180px; overflow: hidden; + /* background: rgba(0, 21, 31, 0.5); */ + background: #033347; + border-radius: 4px; } .camera-feed img { @@ -398,30 +460,4 @@ const displayCameras = computed(() => { height: 100%; object-fit: cover; } - -.empty-state { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - height: 200px; - background: rgba(0, 21, 31, 0.5); - border-radius: 4px; - padding: 20px; -} - -.empty-icon { - width: 100px; - height: 74px; - margin-bottom: 10px; - /* opacity: 0.8; */ - background: url('../assets/img/empty.png') no-repeat; - background-size: 100% 100%; -} - -.empty-text { - color: #fff; - font-size: 10px; - letter-spacing: 1px; -} diff --git a/src/views/Home.vue b/src/views/Home.vue index b5f1a45..37acc9f 100644 --- a/src/views/Home.vue +++ b/src/views/Home.vue @@ -61,14 +61,22 @@ size="small" variant="search" font="small" - @change="handleViewChange(camera.title)" />
- +
@@ -91,7 +99,8 @@ import AlarmStatistics from '../components/AlarmStatistics.vue' import LatestAlarms from '../components/LatestAlarms.vue' import RobotListModal from '../components/dialog/RobotListModal.vue' import CustomSelect from '../components/common/CustomSelect.vue' -import VideoPlayer from '../components/common/VideoPlayer.vue' +import CarouselVideoPlayer from '../components/common/CarouselVideoPlayer.vue' +import CarouselWebRTCPlayer from '../components/common/CarouselWebRTCPlayer.vue' import { homeApi } from '../api/index' import Icon1 from '../assets/img/icon1.png' import Icon2 from '../assets/img/icon2.png' @@ -161,13 +170,15 @@ const fetchRobotStatistics = async () => { } }; +// 摄像头数据 const cameras = ref([ - { title: 'A区厂区监控', src: jkA, streamUrl: '' }, - { title: 'B区厂区监控', src: jkA, streamUrl: '' }, - { title: '追随机器人监控', src: jkRobot, streamUrl: '' }, - { title: '室外机器人监控', src: jkRobot, streamUrl: '' } + { title: 'A区厂区监控', src: jkA }, + { title: 'B区厂区监控', src: jkA }, + { title: '追随机器人监控', src: jkRobot }, + { title: '室外机器人监控', src: jkRobot } ]); +// 选中的视角 const selectedViews = ref({ 'A区厂区监控': '视角1', 'B区厂区监控': '视角1', @@ -175,6 +186,7 @@ const selectedViews = ref({ '室外机器人监控': '视角1' }); +// 视角选项 const viewOptions = ['视角1', '视角2']; // 存储所有视频流数据 @@ -188,9 +200,6 @@ const fetchMonitorStreams = async () => { if (res.code === 200) { monitorStreams.value = res.data || {}; console.log('获取监控视频流成功:', JSON.stringify(monitorStreams.value)); - - // 更新摄像头流URL - updateCameraStreams(); } else { console.error('获取监控视频流失败:', res); } @@ -199,30 +208,33 @@ const fetchMonitorStreams = async () => { } }; -// 更新摄像头流URL -const updateCameraStreams = () => { - cameras.value.forEach(camera => { - const cameraData = monitorStreams.value[camera.title]; - console.log(`处理摄像头 ${camera.title}:`, cameraData); - - if (cameraData) { - const viewName = selectedViews.value[camera.title]; - const newStreamUrl = cameraData[viewName] || ''; - console.log(`设置 ${camera.title} 的 ${viewName} 流URL:`, newStreamUrl); - camera.streamUrl = newStreamUrl; - } else { - console.warn(`未找到 ${camera.title} 的数据`); - } - }); - - // 打印更新后的摄像头数据 - console.log('更新后的摄像头数据:', JSON.stringify(cameras.value)); +// 判断流类型是否为WebRTC +const isWebRTCStream = (url) => { + if (!url) return false; + return url.startsWith('webrtc://') || url.includes('31011500991180041301'); }; -// 监听视角选择变化 -watch(selectedViews, (newViews) => { - updateCameraStreams(); -}, { deep: true }); +// 为摄像头获取视角列表 +const getViewsForCamera = (camera) => { + const cameraData = monitorStreams.value[camera.title]; + if (!cameraData) return []; + + return viewOptions.map(viewName => { + return { + name: viewName, + streamUrl: cameraData[viewName] || '' + }; + }).filter(view => view.streamUrl); // 过滤掉没有流URL的视角 +}; + +// 判断摄像头是否使用WebRTC +const isWebRTCCamera = (camera) => { + const views = getViewsForCamera(camera); + if (views.length === 0) return false; + + // 检查第一个视角是否为WebRTC流 + return isWebRTCStream(views[0].streamUrl); +}; const showRobotListModal = ref(false); @@ -232,19 +244,6 @@ const handleStatisticClick = (item) => { } }; -// 处理视角切换 -const handleViewChange = (cameraTitle) => { - const camera = cameras.value.find(c => c.title === cameraTitle); - if (camera) { - const cameraData = monitorStreams.value[cameraTitle]; - if (cameraData) { - const viewName = selectedViews.value[cameraTitle]; - camera.streamUrl = cameraData[viewName] || ''; - console.log(`切换 ${cameraTitle} 到 ${viewName}, URL: ${camera.streamUrl}`); - } - } -}; - // 组件挂载时获取数据 onMounted(() => { fetchRobotStatistics(); diff --git a/src/views/RobotDetail.vue b/src/views/RobotDetail.vue index 0543739..378e865 100644 --- a/src/views/RobotDetail.vue +++ b/src/views/RobotDetail.vue @@ -34,10 +34,9 @@
主摄像头
-
@@ -45,20 +44,18 @@
左侧摄像头
-
右前摄像头
-
@@ -76,6 +73,8 @@ @view-all="handleViewAll" @process-all="handleProcessAll" /> + + 告警事件列表 @@ -139,7 +138,7 @@ > 处理 - 已处理 @@ -159,7 +158,7 @@ v-if="filteredMeterAlarms.length === 0" title="暂无仪表异常" subtitle="暂无需要处理的仪表异常" - :iconSrc="require('../assets/img/empty_list.png')" + :iconSrc="emptyList" />
处理 - 已处理
@@ -247,7 +246,7 @@ import EventDetailModal from '@/components/dialog/EventDetailModal.vue' import TimeDisplay from '@/components/common/TimeDisplay.vue'; import { robotApi } from '@/api/detail'; import WebSocketClient from '@/utils/websocket'; -import WebRTCPlayer from '@/components/detail/WebRTCPlayer.vue'; +import CustomWebRTCPlayer from '@/components/common/CustomWebRTCPlayer.vue'; import ConfirmDialog from '@/components/dialog/ConfirmDialog.vue'; import EmptyState from '@/components/common/EmptyState.vue'; import emptyList from '@/assets/img/empty_list.png'; @@ -523,20 +522,34 @@ const meterAlarms = computed(() => ); // 统计数量,包括超时未处理的告警 -const pendingCount = computed( +// 这些计算属性将不再直接用于显示,而是作为备用和调试用途 +const pendingCountComputed = computed( () => alarmList.value.filter((a) => a.status === "pending" || a.status === "timeout").length ); -const doneCount = computed( +const doneCountComputed = computed( () => alarmList.value.filter((a) => a.status === "done").length ); -const unreadCount = computed( +const unreadCountComputed = computed( () => alarmList.value.filter((a) => !a.isRead).length ); +// 添加新的告警统计数据 - 这些将用于实际显示 +const pendingCount = ref(0); +const doneCount = ref(0); +const unreadCount = ref(0); +const alertCount = ref(0); +const processedCount = ref(0); +// 当前页面参数 +const currentPage = ref(1); +const pageSize = ref(999); // Tab切换 const alarmTab = ref("pending"); // "pending" | "done" -const setAlarmTab = (tab) => { +const setAlarmTab = async (tab) => { alarmTab.value = tab; + + // 切换标签时重新获取告警列表和数量 + await getAlarmEventList(); + await getAlarmEventCount(); }; // 查看全部按钮 @@ -550,59 +563,446 @@ const handleProcessAll = async () => { confirmDialogMessage.value = '是否确认处理所有未处理的告警事件?'; }; -// 添加确认处理函数 -const handleConfirmProcess = async () => { +// 添加一键处理OCR告警的方法 +const handleOcrAlerts = async () => { try { - // 获取所有未处理的告警ID - const pendingAlarms = alarmList.value - .filter(a => a.status === 'pending') - .map(a => a.id); - - if (pendingAlarms.length === 0) { - return; - } - - const res = await robotApi.handleAlarmEvent({ - eventIds: pendingAlarms + const res = await robotApi.handleOcrAlerts({ + number: robotId.value }); if (res.code === 200) { - // 更新本地状态 - alarmList.value = alarmList.value.map(a => - pendingAlarms.includes(a.id) ? { ...a, status: 'done', isRead: true } : a - ); + console.log('一键处理OCR告警成功'); // 重新获取告警列表 - await getAlarmEventList(); + if (alarmTab.value === 'pending') { + await getUnhandledAlarmMessages(); + } else { + await getHandledAlarmMessages(); + } + + // 更新告警数量 + await getAlarmEventCount(); + + return true; } + return false; } catch (error) { - console.error('一键处理失败:', error); + console.error('一键处理OCR告警失败:', error); + return false; } }; -// 添加获取告警事件列表的函数 -const getAlarmEventList = async () => { +// 添加确认处理函数 +const handleConfirmProcess = async () => { try { - const res = await robotApi.getAlarmEventList({ - robotId: robotId.value, - status: alarmTab.value // 'pending' 或 'done' + console.log('开始一键处理所有告警'); + + // 分类告警 + const ocrAlarms = alarmList.value.filter(a => + (a.status === 'pending' || a.status === 'timeout') && + (a.group === "meter" || a.content === "日常巡检") + ); + + const eventAlarms = alarmList.value.filter(a => + (a.status === 'pending' || a.status === 'timeout') && + a.group === "event" && a.content !== "日常巡检" + ); + + console.log(`分类完成: OCR告警 ${ocrAlarms.length}个, 事件告警 ${eventAlarms.length}个`); + + // 处理OCR告警 + if (ocrAlarms.length > 0) { + const ocrSuccess = await handleOcrAlerts(); + console.log(`OCR告警处理${ocrSuccess ? '成功' : '失败'}`); + } + + // 处理事件告警 + if (eventAlarms.length > 0) { + const eventIds = eventAlarms.map(a => a.id); + const res = await robotApi.handleAlarmEvent({ + eventIds: eventIds + }); + + if (res.code === 200) { + console.log('事件告警处理成功'); + // 更新本地状态 + alarmList.value = alarmList.value.map(a => + eventIds.includes(a.id) ? { ...a, status: 'done', isRead: true } : a + ); + } else { + console.error('事件告警处理失败:', res); + } + } + + // 重新获取告警列表 + if (alarmTab.value === 'pending') { + await getUnhandledAlarmMessages(); + } else { + await getHandledAlarmMessages(); + } + + // 更新告警数量 + await getAlarmEventCount(); + + // 关闭确认对话框 + showConfirmDialog.value = false; + } catch (error) { + console.error('一键处理失败:', error); + // 关闭确认对话框 + showConfirmDialog.value = false; + } +}; + +// 添加数据加载状态 +const isLoading = ref(false); +const loadingError = ref(''); + +// 获取告警数量 +const getAlarmEventCount = async () => { + try { + isLoading.value = true; + loadingError.value = ''; + + console.log('开始获取告警数量,机器人ID:', robotId.value); + const res = await robotApi.getAlarmEventCount({ + number: robotId.value }); if (res.code === 200) { - // 更新告警列表 - alarmList.value = res.data.map(item => ({ - id: item.id, - group: 'event', - level: item.level === 1 ? 'I级' : 'II级', - status: item.status === 1 ? 'pending' : 'done', - isRead: item.isRead === 1, - type: item.type === 1 ? 'warning' : 'danger', - content: item.content, - time: item.createTime - })); + console.log('获取告警数量成功,原始数据:', res.data); + alertCount.value = res.data.alert_count || 0; + processedCount.value = res.data.processed_count || 0; + + // 直接更新显示用的计数 + pendingCount.value = alertCount.value; + doneCount.value = processedCount.value; + + console.log('告警数量更新:', { + 未处理: alertCount.value, + 已处理: processedCount.value, + 当前标签: alarmTab.value, + pendingCount: pendingCount.value, + doneCount: doneCount.value + }); + } else { + console.error('获取告警数量失败,错误码:', res.code, '错误信息:', res.msg || res.message); + loadingError.value = `获取告警数量失败: ${res.msg || res.message || '未知错误'}`; } } catch (error) { - console.error('获取告警事件列表失败:', error); + console.error('获取告警数量失败,异常:', error); + loadingError.value = `获取告警数量失败: ${error.message || '未知错误'}`; + } finally { + isLoading.value = false; + } +}; + +// 修改获取未处理告警消息列表函数,确保参数正确传递 +const getUnhandledAlarmMessages = async () => { + try { + isLoading.value = true; + loadingError.value = ''; + + console.log('获取未处理告警消息列表,参数:', { + skip: (currentPage.value - 1) * pageSize.value, + limit: pageSize.value, + number: robotId.value + }); + + const res = await robotApi.getUnhandledAlarmMessages({ + skip: (currentPage.value - 1) * pageSize.value, + limit: pageSize.value, + number: robotId.value + }); + + if (res.code === 200) { + console.log('获取未处理告警消息列表成功:', res.data); + // 清空现有告警列表 + alarmList.value = []; + + // 处理高温感知报警 + if (res.data.high_temperature_message && res.data.high_temperature_message.length > 0) { + const highTempAlarms = res.data.high_temperature_message.map(item => ({ + id: item.eventId, + messageId: item.messageId, + group: "event", + level: "I级", // 高温报警为I级 + status: "pending", + isRead: false, + type: "warning", + content: "高温感知报警", + time: formatEventTime(item.createTime), + imageUrl: item.imageUrl || [] + })); + alarmList.value.push(...highTempAlarms); + } + + // 处理吸烟报警 + if (res.data.smoke_message && res.data.smoke_message.length > 0) { + const smokeAlarms = res.data.smoke_message.map(item => ({ + id: item.eventId, + messageId: item.messageId, + group: "event", + level: "I级", + status: "pending", + isRead: false, + type: "danger", + content: "吸烟报警", + time: formatEventTime(item.createTime), + imageUrl: item.imageUrl || [] + })); + alarmList.value.push(...smokeAlarms); + } + + // 处理长时间滞留报警 + if (res.data.long_stay_message && res.data.long_stay_message.length > 0) { + const longStayAlarms = res.data.long_stay_message.map(item => ({ + id: item.eventId, + messageId: item.messageId, + group: "event", + level: "II级", + status: "pending", + isRead: false, + type: "warning", + content: "长时间滞留报警", + time: formatEventTime(item.createTime), + imageUrl: item.imageUrl || [] + })); + alarmList.value.push(...longStayAlarms); + } + + // 处理空气质量报警 + if (res.data.air_quality_message && res.data.air_quality_message.length > 0) { + const airQualityAlarms = res.data.air_quality_message.map(item => ({ + id: item.eventId, + messageId: item.messageId, + group: "event", + level: "II级", + status: "pending", + isRead: false, + type: "warning", + content: "空气质量报警", + time: formatEventTime(item.createTime), + imageUrl: item.imageUrl || [], + temperature: item.temperature + })); + alarmList.value.push(...airQualityAlarms); + } + + // 处理急停按下 + if (res.data.stop_emergency_message && res.data.stop_emergency_message.length > 0) { + const emergencyAlarms = res.data.stop_emergency_message.map(item => ({ + id: item.eventId, + messageId: item.messageId, + group: "event", + level: "I级", + status: "pending", + isRead: false, + type: "danger", + content: "急停按下", + time: formatEventTime(item.createTime), + imageUrl: item.imageUrl || [] + })); + alarmList.value.push(...emergencyAlarms); + } + + // 处理语音未接通 + if (res.data.voice_connect_message && res.data.voice_connect_message.length > 0) { + const voiceAlarms = res.data.voice_connect_message.map(item => ({ + id: item.eventId, + messageId: item.messageId, + group: "event", + level: "II级", + status: "pending", + isRead: false, + type: "warning", + content: "语音未接通", + time: formatEventTime(item.createTime), + imageUrl: item.imageUrl || [] + })); + alarmList.value.push(...voiceAlarms); + } + + // 处理日常巡检(包括读表) + if (res.data.daily_inspect_message && res.data.daily_inspect_message.length > 0) { + const inspectAlarms = res.data.daily_inspect_message.map(item => ({ + id: item.eventId, + messageId: item.messageId, + group: "meter", // 日常巡检归类为仪表类 + level: "II级", + status: "pending", + isRead: false, + type: "warning", + content: "日常巡检", + time: formatEventTime(item.createTime), + imageUrl: item.imageUrl || [], + temperature: item.temperature + })); + alarmList.value.push(...inspectAlarms); + } + } else { + console.error('获取未处理告警消息列表失败:', res); + loadingError.value = `获取未处理告警消息列表失败: ${res.msg || res.message || '未知错误'}`; + } + } catch (error) { + console.error('获取未处理告警消息列表失败:', error); + loadingError.value = `获取未处理告警消息列表失败: ${error.message || '未知错误'}`; + } finally { + isLoading.value = false; + } +}; + +// 获取已处理告警消息列表 +const getHandledAlarmMessages = async () => { + try { + const res = await robotApi.getHandledAlarmMessages({ + skip: (currentPage.value - 1) * pageSize.value, + limit: pageSize.value, + number: robotId.value + }); + + if (res.code === 200) { + // 清空现有告警列表 + alarmList.value = []; + + // 处理高温感知报警 + if (res.data.high_temperature_message && res.data.high_temperature_message.length > 0) { + const highTempAlarms = res.data.high_temperature_message.map(item => ({ + id: item.eventId, + messageId: item.messageId, + group: "event", + level: "I级", // 高温报警为I级 + status: "done", + isRead: true, + type: "warning", + content: "高温感知报警", + time: formatEventTime(item.createTime), + imageUrl: item.imageUrl || [], + remark: item.remark + })); + alarmList.value.push(...highTempAlarms); + } + + // 处理吸烟报警 + if (res.data.smoke_message && res.data.smoke_message.length > 0) { + const smokeAlarms = res.data.smoke_message.map(item => ({ + id: item.eventId, + messageId: item.messageId, + group: "event", + level: "I级", + status: "done", + isRead: true, + type: "danger", + content: "吸烟报警", + time: formatEventTime(item.createTime), + imageUrl: item.imageUrl || [], + remark: item.remark + })); + alarmList.value.push(...smokeAlarms); + } + + // 处理长时间滞留报警 + if (res.data.long_stay_message && res.data.long_stay_message.length > 0) { + const longStayAlarms = res.data.long_stay_message.map(item => ({ + id: item.eventId, + messageId: item.messageId, + group: "event", + level: "II级", + status: "done", + isRead: true, + type: "warning", + content: "长时间滞留报警", + time: formatEventTime(item.createTime), + imageUrl: item.imageUrl || [], + remark: item.remark + })); + alarmList.value.push(...longStayAlarms); + } + + // 处理空气质量报警 + if (res.data.air_quality_message && res.data.air_quality_message.length > 0) { + const airQualityAlarms = res.data.air_quality_message.map(item => ({ + id: item.eventId, + messageId: item.messageId, + group: "event", + level: "II级", + status: "done", + isRead: true, + type: "warning", + content: "空气质量报警", + time: formatEventTime(item.createTime), + imageUrl: item.imageUrl || [], + temperature: item.temperature, + remark: item.remark + })); + alarmList.value.push(...airQualityAlarms); + } + + // 处理急停按下 + if (res.data.stop_emergency_message && res.data.stop_emergency_message.length > 0) { + const emergencyAlarms = res.data.stop_emergency_message.map(item => ({ + id: item.eventId, + messageId: item.messageId, + group: "event", + level: "I级", + status: "done", + isRead: true, + type: "danger", + content: "急停按下", + time: formatEventTime(item.createTime), + imageUrl: item.imageUrl || [], + remark: item.remark + })); + alarmList.value.push(...emergencyAlarms); + } + + // 处理语音未接通 + if (res.data.voice_connect_message && res.data.voice_connect_message.length > 0) { + const voiceAlarms = res.data.voice_connect_message.map(item => ({ + id: item.eventId, + messageId: item.messageId, + group: "event", + level: "II级", + status: "done", + isRead: true, + type: "warning", + content: "语音未接通", + time: formatEventTime(item.createTime), + imageUrl: item.imageUrl || [], + remark: item.remark + })); + alarmList.value.push(...voiceAlarms); + } + + // 处理日常巡检(包括读表) + if (res.data.daily_inspect_message && res.data.daily_inspect_message.length > 0) { + const inspectAlarms = res.data.daily_inspect_message.map(item => ({ + id: item.eventId, + messageId: item.messageId, + group: "meter", // 日常巡检归类为仪表类 + level: "II级", + status: "done", + isRead: true, + type: "warning", + content: "日常巡检", + time: formatEventTime(item.createTime), + imageUrl: item.imageUrl || [], + temperature: item.temperature, + remark: item.remark + })); + alarmList.value.push(...inspectAlarms); + } + } + } catch (error) { + console.error('获取已处理告警消息列表失败:', error); + } +}; + +// 修改原有的获取告警事件列表函数,根据当前tab调用对应的API +const getAlarmEventList = async () => { + if (alarmTab.value === 'pending') { + await getUnhandledAlarmMessages(); + } else { + await getHandledAlarmMessages(); } }; @@ -617,6 +1017,7 @@ const filteredEventAlarms = computed(() => { } return eventAlarms.value; }); + const filteredMeterAlarms = computed(() => { if (alarmTab.value === "pending") { // 未处理标签下显示待处理和超时未处理的告警 @@ -682,14 +1083,15 @@ const handleMouseLeave = (listType) => { }; onMounted(async () => { - login(); + console.log('RobotDetail组件挂载,机器人ID:', robotId.value); + // 先登录获取token - // const loginSuccess = await login(); - // if (loginSuccess) { - // // 获取机器人状态 - // await getRobotStatus(); - // } + await login(); + console.log('登录成功,开始初始化数据'); + + // 启动自动滚动 startAutoScroll(); + // 获取任务列表DOM引用 nextTick(() => { const taskListElement = document.querySelector('.task-list'); @@ -700,7 +1102,29 @@ onMounted(async () => { taskListElement.addEventListener('mouseleave', () => handleMouseLeave('taskList')); } }); - await getAlarmEventList(); + + try { + // 先获取告警数量 - 确保在获取列表前先更新数量 + console.log('开始初始化告警数据'); + await getAlarmEventCount(); + console.log('告警数量获取完成,开始获取告警列表'); + + // 然后获取告警列表 + await getAlarmEventList(); + console.log('告警列表获取完成'); + + // 再次确认计数正确 - 防止列表加载过程中数量变化 + await getAlarmEventCount(); + + console.log('数据初始化完成,当前计数:', { + pendingCount: pendingCount.value, + doneCount: doneCount.value, + alertCount: alertCount.value, + processedCount: processedCount.value + }); + } catch (error) { + console.error('初始化告警数据失败:', error); + } }); onUnmounted(() => { @@ -731,58 +1155,182 @@ const showMeterModal = ref(false); const currentAlarmData = ref({}); const currentMeterData = ref({}); +// 添加获取告警详情的方法 +const fetchAlarmDetail = async (messageId) => { + try { + const res = await robotApi.getAlarmEventDetail(messageId); + if (res.code === 200) { + console.log('获取告警详情成功:', res.data); + return res.data; + } + return null; + } catch (error) { + console.error('获取告警详情失败:', error); + return null; + } +}; + // 显示告警详情 -const showAlarmDetail = (alarm) => { +const showAlarmDetail = async (alarm) => { + // 如果有messageId,先获取详细信息 + let detailData = null; + if (alarm.messageId) { + detailData = await fetchAlarmDetail(alarm.messageId); + } + + // 合并详情数据和本地数据 currentAlarmData.value = { title: '告警详情', - mainImage: '../assets/img/camera-thumb1.jpg', // 替换为实际图片 - subImages: ['../assets/img/camera-thumb2.jpg', '../assets/img/camera-thumb3.jpg'], // 替换为实际图片 + messageId: alarm.messageId, + mainImage: detailData?.imageUrl && detailData.imageUrl.length > 0 + ? detailData.imageUrl[0] + : (alarm.imageUrl && alarm.imageUrl.length > 0 ? alarm.imageUrl[0] : '../assets/img/camera-thumb1.jpg'), + subImages: [], type: alarm.content, time: alarm.time, - robotName: robotId.value, - status: alarm.status === 'pending' ? '未处理' : '已处理' + robotName: detailData?.name || robotId.value, + status: alarm.status === 'pending' ? '未处理' : '已处理', + temperature: detailData?.temperature || alarm.temperature || '', + remark: detailData?.remark || alarm.remark || '' }; + + // 添加视频流地址 + if (detailData) { + // 添加子图片(如果有) + if (detailData.imageUrl && detailData.imageUrl.length > 1) { + currentAlarmData.value.subImages = detailData.imageUrl.slice(1); + } else if (alarm.imageUrl && alarm.imageUrl.length > 1) { + currentAlarmData.value.subImages = alarm.imageUrl.slice(1); + } else { + currentAlarmData.value.subImages = []; + } + + // 添加视频流地址 + currentAlarmData.value.flvPtz = detailData.flvPtz || ''; + currentAlarmData.value.flvTherm = detailData.flvTherm || ''; + currentAlarmData.value.flvThermLight = detailData.flvThermLight || ''; + } else { + // 使用本地数据 + currentAlarmData.value.subImages = alarm.imageUrl && alarm.imageUrl.length > 1 + ? alarm.imageUrl.slice(1) + : []; + } + showAlarmModal.value = true; }; // 显示仪表异常详情 -const showMeterDetail = (alarm) => { +const showMeterDetail = async (alarm) => { + // 如果有messageId,先获取详细信息 + let detailData = null; + if (alarm.messageId) { + detailData = await fetchAlarmDetail(alarm.messageId); + } + + // 合并详情数据和本地数据 currentMeterData.value = { title: '仪表异常详情', - mainImage: '../assets/img/camera-thumb1.jpg', // 替换为实际图片 - subImages: ['../assets/img/camera-thumb2.jpg', '../assets/img/camera-thumb3.jpg'], // 替换为实际图片 + messageId: alarm.messageId, + mainImage: detailData?.imageUrl && detailData.imageUrl.length > 0 + ? detailData.imageUrl[0] + : (alarm.imageUrl && alarm.imageUrl.length > 0 ? alarm.imageUrl[0] : '../assets/img/camera-thumb1.jpg'), + subImages: [], type: alarm.content, time: alarm.time, - robotName: robotId.value, - status: alarm.status === 'pending' ? '未处理' : '已处理' + robotName: detailData?.name || robotId.value, + status: alarm.status === 'pending' ? '未处理' : '已处理', + temperature: detailData?.temperature || alarm.temperature || '', + remark: detailData?.remark || alarm.remark || '' }; + + // 添加视频流地址 + if (detailData) { + // 添加子图片(如果有) + if (detailData.imageUrl && detailData.imageUrl.length > 1) { + currentMeterData.value.subImages = detailData.imageUrl.slice(1); + } else if (alarm.imageUrl && alarm.imageUrl.length > 1) { + currentMeterData.value.subImages = alarm.imageUrl.slice(1); + } else { + currentMeterData.value.subImages = []; + } + + // 添加视频流地址 + currentMeterData.value.flvPtz = detailData.flvPtz || ''; + currentMeterData.value.flvTherm = detailData.flvTherm || ''; + currentMeterData.value.flvThermLight = detailData.flvThermLight || ''; + } else { + // 使用本地数据 + currentMeterData.value.subImages = alarm.imageUrl && alarm.imageUrl.length > 1 + ? alarm.imageUrl.slice(1) + : []; + } + showMeterModal.value = true; }; // 处理告警确认 -const handleAlarmConfirm = (data) => { +const handleAlarmConfirm = async (data) => { console.log('确认处理告警:', data); + // 调用处理单个告警的方法 + await handleSingleAlarm(data); showAlarmModal.value = false; }; // 处理告警上报 -const handleAlarmReport = (data) => { +const handleAlarmReport = async (data) => { console.log('处理并上报告警:', data); + // 调用处理单个告警的方法 + await handleSingleAlarm(data); showAlarmModal.value = false; }; // 处理仪表确认 -const handleMeterConfirm = (data) => { +const handleMeterConfirm = async (data) => { console.log('确认处理仪表异常:', data); + // 调用处理单个告警的方法 + await handleSingleAlarm(data); showMeterModal.value = false; }; // 处理仪表上报 -const handleMeterReport = (data) => { +const handleMeterReport = async (data) => { console.log('处理并上报仪表异常:', data); + // 调用处理单个告警的方法 + await handleSingleAlarm(data); showMeterModal.value = false; }; +// 添加处理单个告警事件的方法 +const handleSingleAlarm = async (data) => { + try { + // 获取当前告警的messageId + const messageId = data.messageId; + + if (!messageId) { + console.error('缺少messageId,无法处理告警'); + return; + } + + const res = await robotApi.handleSingleAlarmEvent({ + messageId: messageId, + remark: data.remark || '', + number: robotId.value + }); + + if (res.code === 200) { + console.log('处理单个告警成功'); + + // 重新获取告警列表 + await getAlarmEventList(); + + // 更新告警数量 + await getAlarmEventCount(); + } + } catch (error) { + console.error('处理单个告警失败:', error); + } +}; + const showViewAllModal = ref(false); const currentEvent = ref({ mainImage: '../assets/img/camera-thumb1.jpg', @@ -1242,25 +1790,24 @@ const showConfirmDialog = ref(false); const confirmDialogMessage = ref(''); // 监听alarmTab变化 -watch(alarmTab, async () => { +watch(alarmTab, async (newValue) => { + console.log('标签切换为:', newValue); + // 获取告警列表 await getAlarmEventList(); + + // 重新获取告警数量 + await getAlarmEventCount(); }); // 添加处理单个告警事件的函数 const handleAlarmEvent = async (alarm) => { try { - // 调用API处理告警事件 - const res = await robotApi.handleAlarmEvent({ - eventIds: [alarm.id] - }); - - if (res.code === 200) { - // 更新本地状态 - alarm.status = 'done'; - alarm.isRead = true; - - // 刷新告警列表 - await getAlarmEventList(); + // 如果是仪表类型的告警或日常巡检,直接打开详情弹窗 + if (alarm.group === "meter" || alarm.content === "日常巡检") { + await showMeterDetail(alarm); + } else { + // 其他类型的告警也打开详情弹窗 + await showAlarmDetail(alarm); } } catch (error) { console.error('处理告警事件失败:', error);