班组信息 - + 机器人信息 - - + + 实时数据 - 累计数据 - - + + 任务列表({{ taskCount }})
- +
主摄像头
- @@ -44,16 +40,16 @@
左侧摄像头
-
-
右前摄像头
+
热成像
- @@ -65,7 +61,8 @@
告警事件总览 - + - - +
告警事件列表
告警类型 | -
+
{{ selectedType }}
    -
  • {{ type }}
  • +
  • + {{ type }} +
@@ -106,10 +114,13 @@ @mouseenter="handleMouseEnter('alarmList')" @mouseleave="handleMouseLeave('alarmList')" > - +
数据加载中...
+
+
-
{{ alarm.time }}
-
+
{{ alarm.time.replace("T", " ") }}
+
{{ alarm.level }}
{{ alarm.content }}
- 超时 + 超时
- 已处理 + 已处理
@@ -152,10 +171,13 @@ @mouseenter="handleMouseEnter('meterList')" @mouseleave="handleMouseLeave('meterList')" > - +
数据加载中...
+
+
-
{{ alarm.time }}
-
+
{{ alarm.time.replace("T", " ") }}
+
{{ alarm.level }}
{{ alarm.content }}
- 超时 + 超时
- 已处理 + 已处理
@@ -239,16 +269,16 @@ import EnvDataGrid from "@/components/detail/EnvDataGrid.vue"; import TaskList from "@/components/detail/TaskList.vue"; import CameraSection from "@/components/detail/CameraSection.vue"; import AddData from "@/components/detail/AddData.vue"; -import AlarmActions from '@/components/AlarmActions.vue' -import AlarmDetailModal from '@/components/dialog/AlarmDetailModal.vue' -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 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'; +import AlarmActions from "@/components/AlarmActions.vue"; +import AlarmDetailModal from "@/components/dialog/AlarmDetailModal.vue"; +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 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"; const router = useRouter(); const route = useRoute(); const robotId = ref(route.params.id || "X32305000019"); @@ -261,12 +291,12 @@ const currentDate = ref("2025.12.15 星期五"); const groupInfo = ref([ { icon: "./img/info1.png", label: "当前分组", value: "室外巡检" }, { icon: "./img/info2.png", label: "当前班次", value: "00:00-23:59" }, - { icon: "./img/info3.png", label: "当前人员", value: "员工1" } + { icon: "./img/info3.png", label: "当前人员", value: "员工1" }, ]); const addData = ref([ - { label: "累计运行", value: "1253h" }, - { label: "累计里程", value: "137.8km" }, - { label: "充电次数", value: "305" } + { label: "累计运行", value: "1253h" }, + { label: "累计里程", value: "137.8km" }, + { label: "充电次数", value: "305" }, ]); // 机器人信息数据 @@ -275,7 +305,7 @@ const robotInfoItems = ref([ { label: "电量:", value: "47 %" }, { label: "电池电压:", value: "5 v" }, { label: "运行模式:", value: "充电中" }, - { label: "急停:", value: "关闭" } + { label: "急停:", value: "关闭" }, ]); // 环境数据 @@ -283,12 +313,12 @@ const envData = ref([ { icon: "./img/pic1.png", label: "温度", value: "33°C" }, { icon: "./img/pic2.png", label: "湿度", value: "33°C" }, { icon: "./img/pic3.png", label: "PM2.5", value: "37ug/m3" }, - { icon: "./img/pic4.png", label: "PM10", value: "43ug/m3" } + { icon: "./img/pic4.png", label: "PM10", value: "43ug/m3" }, ]); // 任务列表数据 // const tasks = ref([ -// { area: "A区设备巡检任务", time: "2025-05-01 12:30:50", status: "已完成", type: "completed" }, +// { area: "A区设备巡检任务", time: "2025-05-01 12:30:50", status: "已完成", type: "completed" }, // { area: "B区监控压力巡检", time: "2025-05-01 12:30:00", status: "进行中", type: "running" }, // { area: "C区监控管压任务", time: "2025-05-01 12:30:50", status: "进行中", type: "running" }, // { area: "A区设备巡检任务", time: "2025-05-01 12:30:00", status: "已完成", type: "completed" }, @@ -312,24 +342,21 @@ const tasks = ref([]); // 获取任务列表的异步函数 const fetchTasks = async (robotId) => { try { - const res = await robotApi.getRobotTaskList({ robotId: robotId}); + const res = await robotApi.getRobotTaskList({ robotId: robotId }); if (res.code === 200 && Array.isArray(res.data)) { // 适配后端字段到前端字段 - tasks.value = res.data.map(item => ({ - area: item.taskName || '', - time: item.times || '', - status: item.status === '0' ? '待执行' : '执行中', - type: item.status === '0' ? 'pending' : 'running' + tasks.value = res.data.map((item) => ({ + area: item.taskName || "", + time: item.times || "", + status: item.status === "0" ? "待执行" : "执行中", + type: item.status === "0" ? "pending" : "running", })); - console.log("--------------------------------",tasks.value); - + console.log("--------------------------------", tasks.value); } else { tasks.value = []; - } } catch (error) { tasks.value = []; - } }; // 摄像头控制函数 @@ -348,22 +375,22 @@ const isUnmounting = ref(false); const goBack = () => { // Set unmounting flag to prevent reconnection isUnmounting.value = true; - + // 关闭WebSocket连接 if (wsClient) { - console.log('关闭WebSocket连接'); + console.log("关闭WebSocket连接"); wsClient.close(); wsClient = null; } - + // 清空视频流地址,触发WebRTCPlayer组件卸载 - console.log('清空视频流地址,释放WebRTC资源'); + console.log("清空视频流地址,释放WebRTC资源"); streamUrls.value = { - ptz: '', - front: '', - therm: '' + ptz: "", + front: "", + therm: "", }; - + // 延迟一小段时间再导航,确保资源被释放 setTimeout(() => { router.push("/"); @@ -547,7 +574,10 @@ const meterAlarms = computed(() => // 统计数量,包括超时未处理的告警 // 这些计算属性将不再直接用于显示,而是作为备用和调试用途 const pendingCountComputed = computed( - () => alarmList.value.filter((a) => a.status === "pending" || a.status === "timeout").length + () => + alarmList.value.filter( + (a) => a.status === "pending" || a.status === "timeout" + ).length ); const doneCountComputed = computed( () => alarmList.value.filter((a) => a.status === "done").length @@ -569,10 +599,10 @@ const pageSize = ref(999); // Tab切换 const alarmTab = ref("pending"); // "pending" | "done" const setAlarmTab = async (tab) => { + isLoading.value = true; // 1. 优先设置loading alarmTab.value = tab; - - // 切换标签时重新获取告警列表和数量 - await getAlarmEventList(); + alarmList.value = []; // 2. 立即清空数据,防止旧数据闪现 + await getAlarmEventList(); // 3. 请求新数据 await getAlarmEventCount(); }; @@ -580,52 +610,53 @@ const setAlarmTab = async (tab) => { const handleViewAll = async () => { try { isLoading.value = true; - + // 构建接口参数 const params = { number: robotId.value, offset: (currentPage.value - 1) * pageSize.value, - limit: pageSize.value + limit: pageSize.value, }; // 如果不是全部告警,添加etypeName参数 - if (selectedType.value && selectedType.value !== '全部告警') { + if (selectedType.value && selectedType.value !== "全部告警") { params.etypeName = selectedType.value; } - + // 使用新API获取告警事件列表 const res = await robotApi.getAlarmDetailList(params); - + if (res.code === 200 && res.data && res.data.length > 0) { - console.log('获取告警事件列表成功:', res.data); - + console.log("获取告警事件列表成功:", res.data); + // 将API返回的数据传递给EventDetailModal组件 showViewAllModal.value = true; - + // 更新告警事件列表数据 currentEvent.value = { - mainImage: res.data[0].imagePreview || '', + mainImage: res.data[0].imagePreview || "", subImages: res.data[0].imageList || [], - type: res.data[0].etypeName || '', - time: res.data[0].createTime || '', + type: res.data[0].etypeName || "", + time: res.data[0].createTime || "", robotName: res.data[0].name || robotId.value, - status: res.data[0].handle === '1' ? '已处理' : '未处理' + status: res.data[0].handle === "1" ? "已处理" : "未处理", }; - + // 更新monitorList,右侧显示所有告警事件 - monitorList.value = res.data.map(item => ({ - title: item.etypeName || '未知告警', - image: item.imagePreview || '' + monitorList.value = res.data.map((item) => ({ + title: item.etypeName || "未知告警", + image: item.imagePreview || "", })); - + // 将完整数据传递给弹窗组件 eventListData.value = res.data; } else { - console.error('获取告警事件列表失败或列表为空'); - window.$message && window.$message.error('获取告警事件列表失败或列表为空'); + console.error("获取告警事件列表失败或列表为空"); + window.$message && + window.$message.error("获取告警事件列表失败或列表为空"); } } catch (error) { - console.error('获取告警事件列表异常:', error); - window.$message && window.$message.error('获取告警事件列表失败'); + console.error("获取告警事件列表异常:", error); + window.$message && window.$message.error("获取告警事件列表失败"); } finally { isLoading.value = false; } @@ -634,34 +665,34 @@ const handleViewAll = async () => { // 一键处理按钮 const handleProcessAll = async () => { showConfirmDialog.value = true; - confirmDialogMessage.value = '是否确认处理所有未处理的告警事件?'; + confirmDialogMessage.value = "是否确认处理所有未处理的告警事件?"; }; // 添加一键处理OCR告警的方法 const handleOcrAlerts = async () => { try { const res = await robotApi.handleOcrAlerts({ - number: robotId.value + number: robotId.value, }); if (res.code === 200) { - console.log('一键处理OCR告警成功'); - + console.log("一键处理OCR告警成功"); + // 重新获取告警列表 - if (alarmTab.value === 'pending') { + if (alarmTab.value === "pending") { await getUnhandledAlarmMessages(); } else { await getHandledAlarmMessages(); } - + // 更新告警数量 await getAlarmEventCount(); - + return true; } return false; } catch (error) { - console.error('一键处理OCR告警失败:', error); + console.error("一键处理OCR告警失败:", error); return false; } }; @@ -669,59 +700,64 @@ const handleOcrAlerts = async () => { // 添加确认处理函数 const handleConfirmProcess = async () => { try { - console.log('开始一键处理所有告警'); - + console.log("开始一键处理所有告警"); + // 分类告警 - const ocrAlarms = alarmList.value.filter(a => - (a.status === 'pending' || a.status === 'timeout') && - (a.group === "meter" || a.content === "日常巡检") + 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 !== "日常巡检" + + const eventAlarms = alarmList.value.filter( + (a) => + (a.status === "pending" || a.status === "timeout") && + a.group === "event" && + a.content !== "日常巡检" ); - - console.log(`分类完成: OCR告警 ${ocrAlarms.length}个, 事件告警 ${eventAlarms.length}个`); - + + console.log( + `分类完成: OCR告警 ${ocrAlarms.length}个, 事件告警 ${eventAlarms.length}个` + ); + // 处理OCR告警 if (ocrAlarms.length > 0) { const ocrSuccess = await handleOcrAlerts(); - console.log(`OCR告警处理${ocrSuccess ? '成功' : '失败'}`); + console.log(`OCR告警处理${ocrSuccess ? "成功" : "失败"}`); } - + // 处理事件告警 if (eventAlarms.length > 0) { - const eventIds = eventAlarms.map(a => a.id); + const eventIds = eventAlarms.map((a) => a.id); const res = await robotApi.handleAlarmEvent({ - eventIds: eventIds + eventIds: eventIds, }); if (res.code === 200) { - console.log('事件告警处理成功'); + console.log("事件告警处理成功"); // 更新本地状态 - alarmList.value = alarmList.value.map(a => - eventIds.includes(a.id) ? { ...a, status: 'done', isRead: true } : a + alarmList.value = alarmList.value.map((a) => + eventIds.includes(a.id) ? { ...a, status: "done", isRead: true } : a ); } else { - console.error('事件告警处理失败:', res); + console.error("事件告警处理失败:", res); } } - + // 重新获取告警列表 - if (alarmTab.value === 'pending') { + if (alarmTab.value === "pending") { await getUnhandledAlarmMessages(); } else { await getHandledAlarmMessages(); } - + // 更新告警数量 await getAlarmEventCount(); - + // 关闭确认对话框 showConfirmDialog.value = false; } catch (error) { - console.error('一键处理失败:', error); + console.error("一键处理失败:", error); // 关闭确认对话框 showConfirmDialog.value = false; } @@ -729,44 +765,51 @@ const handleConfirmProcess = async () => { // 添加数据加载状态 const isLoading = ref(false); -const loadingError = ref(''); +const loadingError = ref(""); // 获取告警数量 const getAlarmEventCount = async () => { try { isLoading.value = true; - loadingError.value = ''; - - console.log('开始获取告警数量,机器人ID:', robotId.value); + loadingError.value = ""; + + console.log("开始获取告警数量,机器人ID:", robotId.value); const res = await robotApi.getAlarmEventCount({ - number: robotId.value + number: robotId.value, }); if (res.code === 200) { - console.log('获取告警数量成功,原始数据:', res.data); + console.log("获取告警数量成功,原始数据:", res.data); alertCount.value = res.data.alert_count || 0; processedCount.value = res.data.processed_count || 0; allCount.value = res.data.all_count || 0; // 从接口返回的all_count中获取全部告警数量 - + // 直接更新显示用的计数 pendingCount.value = alertCount.value; doneCount.value = processedCount.value; - - console.log('告警数量更新:', { + + console.log("告警数量更新:", { 未处理: alertCount.value, 已处理: processedCount.value, 全部: allCount.value, // 添加日志输出 当前标签: alarmTab.value, pendingCount: pendingCount.value, - doneCount: doneCount.value + doneCount: doneCount.value, }); } else { - console.error('获取告警数量失败,错误码:', res.code, '错误信息:', res.msg || res.message); - loadingError.value = `获取告警数量失败: ${res.msg || res.message || '未知错误'}`; + console.error( + "获取告警数量失败,错误码:", + res.code, + "错误信息:", + res.msg || res.message + ); + loadingError.value = `获取告警数量失败: ${ + res.msg || res.message || "未知错误" + }`; } } catch (error) { - console.error('获取告警数量失败,异常:', error); - loadingError.value = `获取告警数量失败: ${error.message || '未知错误'}`; + console.error("获取告警数量失败,异常:", error); + loadingError.value = `获取告警数量失败: ${error.message || "未知错误"}`; } finally { isLoading.value = false; } @@ -776,45 +819,50 @@ const getAlarmEventCount = async () => { const getUnhandledAlarmMessages = async () => { try { isLoading.value = true; - loadingError.value = ''; - - console.log('获取未处理告警消息列表,参数:', { + loadingError.value = ""; + + console.log("获取未处理告警消息列表,参数:", { skip: (currentPage.value - 1) * pageSize.value, limit: pageSize.value, - number: robotId.value + number: robotId.value, }); - + const res = await robotApi.getUnhandledAlarmMessages({ skip: (currentPage.value - 1) * pageSize.value, limit: pageSize.value, - number: robotId.value + number: robotId.value, }); if (res.code === 200) { - console.log('获取未处理告警消息列表成功:', res.data); + 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 || [] - })); + 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 => ({ + const smokeAlarms = res.data.smoke_message.map((item) => ({ id: item.eventId, messageId: item.messageId, group: "event", @@ -824,14 +872,14 @@ const getUnhandledAlarmMessages = async () => { type: "danger", content: "吸烟报警", time: formatEventTime(item.createTime), - imageUrl: item.imageUrl || [] + 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 => ({ + const longStayAlarms = res.data.long_stay_message.map((item) => ({ id: item.eventId, messageId: item.messageId, group: "event", @@ -841,14 +889,17 @@ const getUnhandledAlarmMessages = async () => { type: "warning", content: "长时间滞留报警", time: formatEventTime(item.createTime), - imageUrl: item.imageUrl || [] + 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 => ({ + 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", @@ -859,14 +910,17 @@ const getUnhandledAlarmMessages = async () => { content: "空气质量报警", time: formatEventTime(item.createTime), imageUrl: item.imageUrl || [], - temperature: item.temperature + 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 => ({ + 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", @@ -876,14 +930,17 @@ const getUnhandledAlarmMessages = async () => { type: "danger", content: "急停按下", time: formatEventTime(item.createTime), - imageUrl: item.imageUrl || [] + 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 => ({ + 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", @@ -893,14 +950,17 @@ const getUnhandledAlarmMessages = async () => { type: "warning", content: "语音未接通", time: formatEventTime(item.createTime), - imageUrl: item.imageUrl || [] + 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 => ({ + 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", // 日常巡检归类为仪表类 @@ -911,17 +971,21 @@ const getUnhandledAlarmMessages = async () => { content: "日常巡检", time: formatEventTime(item.createTime), imageUrl: item.imageUrl || [], - temperature: item.temperature + temperature: item.temperature, })); alarmList.value.push(...inspectAlarms); } } else { - console.error('获取未处理告警消息列表失败:', res); - loadingError.value = `获取未处理告警消息列表失败: ${res.msg || res.message || '未知错误'}`; + console.error("获取未处理告警消息列表失败:", res); + loadingError.value = `获取未处理告警消息列表失败: ${ + res.msg || res.message || "未知错误" + }`; } } catch (error) { - console.error('获取未处理告警消息列表失败:', error); - loadingError.value = `获取未处理告警消息列表失败: ${error.message || '未知错误'}`; + console.error("获取未处理告警消息列表失败:", error); + loadingError.value = `获取未处理告警消息列表失败: ${ + error.message || "未知错误" + }`; } finally { isLoading.value = false; } @@ -933,34 +997,39 @@ const getHandledAlarmMessages = async () => { const res = await robotApi.getHandledAlarmMessages({ skip: (currentPage.value - 1) * pageSize.value, limit: pageSize.value, - number: robotId.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 - })); + 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 => ({ + const smokeAlarms = res.data.smoke_message.map((item) => ({ id: item.eventId, messageId: item.messageId, group: "event", @@ -971,14 +1040,14 @@ const getHandledAlarmMessages = async () => { content: "吸烟报警", time: formatEventTime(item.createTime), imageUrl: item.imageUrl || [], - remark: item.remark + 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 => ({ + const longStayAlarms = res.data.long_stay_message.map((item) => ({ id: item.eventId, messageId: item.messageId, group: "event", @@ -989,14 +1058,17 @@ const getHandledAlarmMessages = async () => { content: "长时间滞留报警", time: formatEventTime(item.createTime), imageUrl: item.imageUrl || [], - remark: item.remark + 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 => ({ + 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", @@ -1008,14 +1080,17 @@ const getHandledAlarmMessages = async () => { time: formatEventTime(item.createTime), imageUrl: item.imageUrl || [], temperature: item.temperature, - remark: item.remark + 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 => ({ + 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", @@ -1026,14 +1101,17 @@ const getHandledAlarmMessages = async () => { content: "急停按下", time: formatEventTime(item.createTime), imageUrl: item.imageUrl || [], - remark: item.remark + 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 => ({ + 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", @@ -1044,14 +1122,17 @@ const getHandledAlarmMessages = async () => { content: "语音未接通", time: formatEventTime(item.createTime), imageUrl: item.imageUrl || [], - remark: item.remark + 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 => ({ + 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", // 日常巡检归类为仪表类 @@ -1063,19 +1144,19 @@ const getHandledAlarmMessages = async () => { time: formatEventTime(item.createTime), imageUrl: item.imageUrl || [], temperature: item.temperature, - remark: item.remark + remark: item.remark, })); alarmList.value.push(...inspectAlarms); } } } catch (error) { - console.error('获取已处理告警消息列表失败:', error); + console.error("获取已处理告警消息列表失败:", error); } }; // 修改原有的获取告警事件列表函数,根据当前tab调用对应的API const getAlarmEventList = async () => { - if (alarmTab.value === 'pending') { + if (alarmTab.value === "pending") { await getUnhandledAlarmMessages(); } else { await getHandledAlarmMessages(); @@ -1085,9 +1166,12 @@ const getAlarmEventList = async () => { // 过滤当前Tab下的告警 const filteredEventAlarms = computed(() => { let list = eventAlarms.value; - if (selectedType.value && selectedType.value !== '全部告警') { + if (selectedType.value && selectedType.value !== "全部告警") { // 按类型过滤,匹配etypeName或content字段 - list = list.filter(a => a.etypeName === selectedType.value || a.content === selectedType.value); + list = list.filter( + (a) => + a.etypeName === selectedType.value || a.content === selectedType.value + ); } if (alarmTab.value === "pending") { return list.filter((a) => a.status === "pending" || a.status === "timeout"); @@ -1101,7 +1185,9 @@ const filteredEventAlarms = computed(() => { const filteredMeterAlarms = computed(() => { if (alarmTab.value === "pending") { // 未处理标签下显示待处理和超时未处理的告警 - return meterAlarms.value.filter((a) => a.status === "pending" || a.status === "timeout"); + return meterAlarms.value.filter( + (a) => a.status === "pending" || a.status === "timeout" + ); } if (alarmTab.value === "done") { return meterAlarms.value.filter((a) => a.status === "done"); @@ -1117,7 +1203,7 @@ let scrollTimer = null; let isHovering = ref({ alarmList: false, meterList: false, - taskList: false + taskList: false, }); const startAutoScroll = () => { @@ -1126,21 +1212,21 @@ const startAutoScroll = () => { const el = alarmListRef.value; const meterEl = meterAlarmListRef.value; const taskEl = taskListRef.value; - + if (el && !isHovering.value.alarmList) { el.scrollTop += 1; if (el.scrollTop >= el.scrollHeight - el.clientHeight) { el.scrollTop = 0; } } - + if (meterEl && !isHovering.value.meterList) { meterEl.scrollTop += 1; if (meterEl.scrollTop >= meterEl.scrollHeight - meterEl.clientHeight) { meterEl.scrollTop = 0; } } - + if (taskEl && !isHovering.value.taskList) { taskEl.scrollTop += 1; if (taskEl.scrollTop >= taskEl.scrollHeight - taskEl.clientHeight) { @@ -1163,47 +1249,51 @@ const handleMouseLeave = (listType) => { }; onMounted(async () => { - console.log('RobotDetail组件挂载,机器人ID:', robotId.value); - + console.log("RobotDetail组件挂载,机器人ID:", robotId.value); + // 先登录获取token await login(); - console.log('登录成功,开始初始化数据'); - + console.log("登录成功,开始初始化数据"); + // 启动自动滚动 startAutoScroll(); - + // 获取任务列表DOM引用 nextTick(() => { - const taskListElement = document.querySelector('.task-list'); + const taskListElement = document.querySelector(".task-list"); if (taskListElement) { taskListRef.value = taskListElement; // 为任务列表添加鼠标事件 - taskListElement.addEventListener('mouseenter', () => handleMouseEnter('taskList')); - taskListElement.addEventListener('mouseleave', () => handleMouseLeave('taskList')); + taskListElement.addEventListener("mouseenter", () => + handleMouseEnter("taskList") + ); + taskListElement.addEventListener("mouseleave", () => + handleMouseLeave("taskList") + ); } }); - + try { // 先获取告警数量 - 确保在获取列表前先更新数量 - console.log('开始初始化告警数据'); + console.log("开始初始化告警数据"); await getAlarmEventCount(); - console.log('告警数量获取完成,开始获取告警列表'); - + console.log("告警数量获取完成,开始获取告警列表"); + // 然后获取告警列表 await getAlarmEventList(); - console.log('告警列表获取完成'); - + console.log("告警列表获取完成"); + // 再次确认计数正确 - 防止列表加载过程中数量变化 await getAlarmEventCount(); - - console.log('数据初始化完成,当前计数:', { + + console.log("数据初始化完成,当前计数:", { pendingCount: pendingCount.value, doneCount: doneCount.value, alertCount: alertCount.value, - processedCount: processedCount.value + processedCount: processedCount.value, }); } catch (error) { - console.error('初始化告警数据失败:', error); + console.error("初始化告警数据失败:", error); } // 获取告警类型 @@ -1211,10 +1301,10 @@ onMounted(async () => { // 获取告警类型 const res = await robotApi.getEventTypes(); if (res.code === 200 && Array.isArray(res.data)) { - eventTypeOptions.value = ['全部告警', ...res.data]; + eventTypeOptions.value = ["全部告警", ...res.data]; } } catch (e) { - console.error('获取告警类型失败', e); + console.error("获取告警类型失败", e); } }); @@ -1222,8 +1312,12 @@ onUnmounted(() => { stopAutoScroll(); // 移除任务列表的事件监听 if (taskListRef.value) { - taskListRef.value.removeEventListener('mouseenter', () => handleMouseEnter('taskList')); - taskListRef.value.removeEventListener('mouseleave', () => handleMouseLeave('taskList')); + taskListRef.value.removeEventListener("mouseenter", () => + handleMouseEnter("taskList") + ); + taskListRef.value.removeEventListener("mouseleave", () => + handleMouseLeave("taskList") + ); } }); @@ -1255,12 +1349,12 @@ const fetchAlarmDetail = async (messageId) => { try { const res = await robotApi.getAlarmEventDetail(messageId); if (res.code === 200) { - console.log('获取告警详情成功:', res.data); + console.log("获取告警详情成功:", res.data); return res.data; } return null; } catch (error) { - console.error('获取告警详情失败:', error); + console.error("获取告警详情失败:", error); return null; } }; @@ -1272,23 +1366,26 @@ const showAlarmDetail = async (alarm) => { if (alarm.messageId) { detailData = await fetchAlarmDetail(alarm.messageId); } - + // 合并详情数据和本地数据 currentAlarmData.value = { - title: '告警详情', + title: "告警详情", 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'), + 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: detailData?.name || robotId.value, - status: alarm.status === 'pending' ? '未处理' : '已处理', - temperature: detailData?.temperature || alarm.temperature || '', - remark: detailData?.remark || alarm.remark || '' + status: alarm.status === "pending" ? "未处理" : "已处理", + temperature: detailData?.temperature || alarm.temperature || "", + remark: detailData?.remark || alarm.remark || "", }; - + // 添加视频流地址 if (detailData) { // 添加子图片(如果有) @@ -1299,18 +1396,19 @@ const showAlarmDetail = async (alarm) => { } else { currentAlarmData.value.subImages = []; } - + // 添加视频流地址 - currentAlarmData.value.flvPtz = detailData.flvPtz || ''; - currentAlarmData.value.flvTherm = detailData.flvTherm || ''; - currentAlarmData.value.flvThermLight = detailData.flvThermLight || ''; + 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) - : []; + currentAlarmData.value.subImages = + alarm.imageUrl && alarm.imageUrl.length > 1 + ? alarm.imageUrl.slice(1) + : []; } - + showAlarmModal.value = true; }; @@ -1321,23 +1419,26 @@ const showMeterDetail = async (alarm) => { if (alarm.messageId) { detailData = await fetchAlarmDetail(alarm.messageId); } - + // 合并详情数据和本地数据 currentMeterData.value = { - title: '仪表异常详情', + title: "仪表异常详情", 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'), + 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: detailData?.name || robotId.value, - status: alarm.status === 'pending' ? '未处理' : '已处理', - temperature: detailData?.temperature || alarm.temperature || '', - remark: detailData?.remark || alarm.remark || '' + status: alarm.status === "pending" ? "未处理" : "已处理", + temperature: detailData?.temperature || alarm.temperature || "", + remark: detailData?.remark || alarm.remark || "", }; - + // 添加视频流地址 if (detailData) { // 添加子图片(如果有) @@ -1348,24 +1449,25 @@ const showMeterDetail = async (alarm) => { } else { currentMeterData.value.subImages = []; } - + // 添加视频流地址 - currentMeterData.value.flvPtz = detailData.flvPtz || ''; - currentMeterData.value.flvTherm = detailData.flvTherm || ''; - currentMeterData.value.flvThermLight = detailData.flvThermLight || ''; + 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) - : []; + currentMeterData.value.subImages = + alarm.imageUrl && alarm.imageUrl.length > 1 + ? alarm.imageUrl.slice(1) + : []; } - + showMeterModal.value = true; }; // 处理告警确认 const handleAlarmConfirm = async (data) => { - console.log('确认处理告警:', data); + console.log("确认处理告警:", data); // 调用处理单个告警的方法 await handleSingleAlarm(data); showAlarmModal.value = false; @@ -1373,7 +1475,7 @@ const handleAlarmConfirm = async (data) => { // 处理告警上报 const handleAlarmReport = async (data) => { - console.log('处理并上报告警:', data); + console.log("处理并上报告警:", data); // 调用处理单个告警的方法 await handleSingleAlarm(data); showAlarmModal.value = false; @@ -1381,7 +1483,7 @@ const handleAlarmReport = async (data) => { // 处理仪表确认 const handleMeterConfirm = async (data) => { - console.log('确认处理仪表异常:', data); + console.log("确认处理仪表异常:", data); // 调用处理单个告警的方法 await handleSingleAlarm(data); showMeterModal.value = false; @@ -1389,7 +1491,7 @@ const handleMeterConfirm = async (data) => { // 处理仪表上报 const handleMeterReport = async (data) => { - console.log('处理并上报仪表异常:', data); + console.log("处理并上报仪表异常:", data); // 调用处理单个告警的方法 await handleSingleAlarm(data); showMeterModal.value = false; @@ -1400,95 +1502,98 @@ const handleSingleAlarm = async (data) => { try { // 获取当前告警的messageId const messageId = data.messageId; - + if (!messageId) { - console.error('缺少messageId,无法处理告警'); + console.error("缺少messageId,无法处理告警"); return; } - + const res = await robotApi.handleSingleAlarmEvent({ messageId: messageId, - remark: data.remark || '', - number: robotId.value + remark: data.remark || "", + number: robotId.value, }); if (res.code === 200) { - console.log('处理单个告警成功'); - + console.log("处理单个告警成功"); + // 重新获取告警列表 await getAlarmEventList(); - + // 更新告警数量 await getAlarmEventCount(); } } catch (error) { - console.error('处理单个告警失败:', error); + console.error("处理单个告警失败:", error); } }; const showViewAllModal = ref(false); const currentEvent = ref({ - mainImage: '../assets/img/camera-thumb1.jpg', - subImages: ['../assets/img/camera-thumb2.jpg', '../assets/img/camera-thumb3.jpg'], - type: '紧急避障', - time: '2025-05-01 12:30:09', - robotName: 'X32305000019', - status: '未处理' + mainImage: "../assets/img/camera-thumb1.jpg", + subImages: [ + "../assets/img/camera-thumb2.jpg", + "../assets/img/camera-thumb3.jpg", + ], + type: "紧急避障", + time: "2025-05-01 12:30:09", + robotName: "X32305000019", + status: "未处理", }); const monitorList = ref([ - { title: '监控视图1', image: '../assets/img/camera-thumb1.jpg' }, - { title: '监控视图2', image: '../assets/img/camera-thumb2.jpg' }, - { title: '监控视图3', image: '../assets/img/camera-thumb3.jpg' }, - { title: '监控视图4', image: '../assets/img/camera-thumb1.jpg' }, - { title: '监控视图5', image: '../assets/img/camera-thumb2.jpg' }, - { title: '监控视图6', image: '../assets/img/camera-thumb3.jpg' } + { title: "监控视图1", image: "../assets/img/camera-thumb1.jpg" }, + { title: "监控视图2", image: "../assets/img/camera-thumb2.jpg" }, + { title: "监控视图3", image: "../assets/img/camera-thumb3.jpg" }, + { title: "监控视图4", image: "../assets/img/camera-thumb1.jpg" }, + { title: "监控视图5", image: "../assets/img/camera-thumb2.jpg" }, + { title: "监控视图6", image: "../assets/img/camera-thumb3.jpg" }, ]); const handleConfirm = async (data) => { - console.log('确认处理告警事件:', data); - + console.log("确认处理告警事件:", data); + // 如果有messageId,处理单个告警事件 if (data.messageId) { try { const res = await robotApi.handleSingleAlarmEvent({ messageId: data.messageId, - remark: data.remark || '', - number: robotId.value + remark: data.remark || "", + number: robotId.value, }); if (res.code === 200) { - console.log('处理单个告警成功'); - + console.log("处理单个告警成功"); + // 更新告警列表 await getAlarmEventList(); - + // 更新告警数量 await getAlarmEventCount(); - + // 关闭弹窗 showViewAllModal.value = false; } else { - console.error('处理单个告警失败:', res); + console.error("处理单个告警失败:", res); } } catch (error) { - console.error('处理单个告警异常:', error); + console.error("处理单个告警异常:", error); } } else { - console.log('缺少messageId,无法处理告警'); + console.log("缺少messageId,无法处理告警"); showViewAllModal.value = false; } }; const handleReport = async (data) => { - console.log('处理并上报告警事件:', data); - + console.log("处理并上报告警事件:", data); + // 处理逻辑与handleConfirm相同,可能后续会有不同 await handleConfirm(data); }; // 登录接口调用 -const tenantInfoId = ref(''); +const tenantInfoId = ref(""); const login = async () => { try { const res = await robotApi.login({ @@ -1496,13 +1601,13 @@ const login = async () => { password: "123456", userType: "2", crc: "0f007401b091", - lang: "zh_CN" + lang: "zh_CN", }); if (res.code === 200) { - localStorage.setItem('token', res.data.token); + localStorage.setItem("token", res.data.token); // 更新用户信息 tenantInfoId.value = res.data.userInfo.tenantInfoId; - console.log("tenantInfoId.value-----------",tenantInfoId.value); + console.log("tenantInfoId.value-----------", tenantInfoId.value); // getGroupInfoData(); // 初始化WebSocket连接 initWebSocket(); @@ -1510,22 +1615,22 @@ const login = async () => { } return false; } catch (error) { - console.error('登录失败:', error); + console.error("登录失败:", error); return false; } }; // 视频流地址 const streamUrls = ref({ - ptz: '', // 云台摄像头 - front: '', // 前置摄像头 - therm: '' // 热成像 + ptz: "", // 云台摄像头 + front: "", // 前置摄像头 + therm: "", // 热成像 }); // 在开发环境中添加测试用的视频流地址 if (import.meta.env.DEV) { // 仅在开发环境中设置测试地址 - console.log('开发环境,设置测试视频流地址'); + console.log("开发环境,设置测试视频流地址"); // 这里可以设置测试用的WebRTC流地址 // 使用您的实际服务器地址 // streamUrls.value = { @@ -1533,81 +1638,112 @@ if (import.meta.env.DEV) { // front: 'webrtc://qvs-live.thirdmonitor.concoai.com:447/rtc/v1/play/31011500991180041301_34020000001320000002', // therm: 'webrtc://qvs-live.thirdmonitor.concoai.com:447/rtc/v1/play/31011500991180041301_34020000001320000003' // }; - + // 测试函数:手动设置流地址并测试连接 window.testWebRTC = (streamId) => { // 使用正确的URL格式,只到/rtc/v1/play - const baseUrl = 'webrtc://qvs-live.thirdmonitor.concoai.com:447/rtc/v1/play/'; + const baseUrl = + "webrtc://qvs-live.thirdmonitor.concoai.com:447/rtc/v1/play/"; streamUrls.value = { ptz: baseUrl + streamId, - front: '', - therm: '' + front: "", + therm: "", }; - console.log('设置测试流地址:', streamUrls.value.ptz); - + console.log("设置测试流地址:", streamUrls.value.ptz); + // 添加一个直接测试函数,用于测试不同的URL格式 window.testWebRTCFormat = (format) => { let testUrl; - if (format === 'short') { + if (format === "short") { // 只到/rtc/v1/play - testUrl = 'webrtc://qvs-live.thirdmonitor.concoai.com:447/rtc/v1/play'; + testUrl = "webrtc://qvs-live.thirdmonitor.concoai.com:447/rtc/v1/play"; } else { // 包含流ID - testUrl = 'webrtc://qvs-live.thirdmonitor.concoai.com:447/rtc/v1/play/' + streamId; + testUrl = + "webrtc://qvs-live.thirdmonitor.concoai.com:447/rtc/v1/play/" + + streamId; } streamUrls.value = { ptz: testUrl, - front: '', - therm: '' + front: "", + therm: "", }; - console.log('测试不同URL格式:', testUrl); + console.log("测试不同URL格式:", testUrl); }; }; } // 添加上一次的状态记录 const lastStatus = ref({ - power: '', - temperature: '', - humidity: '', - pm2_5: '', - pm10: '', - voltage: '', - ststusName: '', - buttonStop: '', + power: "", + temperature: "", + humidity: "", + pm2_5: "", + pm10: "", + voltage: "", + ststusName: "", + buttonStop: "", // 添加视频流地址相关字段 - flvPtz: '', - flvTherm: '', - flvThermLight: '' + flvPtz: "", + flvTherm: "", + flvThermLight: "", }); // 检查数据是否发生变化 const hasStatusChanged = (newData) => { - const keys = ['power', 'temperature', 'humidity', 'pm2_5', 'pm10', 'voltage', 'ststusName', 'buttonStop']; - const changed = keys.some(key => lastStatus.value[key] !== newData[key]); + const keys = [ + "power", + "temperature", + "humidity", + "pm2_5", + "pm10", + "voltage", + "ststusName", + "buttonStop", + ]; + const changed = keys.some((key) => lastStatus.value[key] !== newData[key]); if (changed) { - console.log('状态数据发生变化:', keys.filter(key => lastStatus.value[key] !== newData[key])); + console.log( + "状态数据发生变化:", + keys.filter((key) => lastStatus.value[key] !== newData[key]) + ); } return changed; }; // 检查视频流地址是否发生变化 const hasStreamUrlsChanged = (newData) => { - const streamKeys = ['flvPtz', 'flvTherm', 'flvThermLight']; - const changed = streamKeys.some(key => lastStatus.value[key] !== newData[key] && newData[key]); + const streamKeys = ["flvPtz", "flvTherm", "flvThermLight"]; + const changed = streamKeys.some( + (key) => lastStatus.value[key] !== newData[key] && newData[key] + ); if (changed) { - console.log('视频流地址发生变化:', streamKeys.filter(key => lastStatus.value[key] !== newData[key] && newData[key])); + console.log( + "视频流地址发生变化:", + streamKeys.filter( + (key) => lastStatus.value[key] !== newData[key] && newData[key] + ) + ); } return changed; }; // 更新上一次的状态 const updateLastStatus = (data) => { - const keys = ['power', 'temperature', 'humidity', 'pm2_5', 'pm10', 'voltage', 'ststusName', 'buttonStop']; - keys.forEach(key => { + const keys = [ + "power", + "temperature", + "humidity", + "pm2_5", + "pm10", + "voltage", + "ststusName", + "buttonStop", + ]; + keys.forEach((key) => { lastStatus.value[key] = data[key]; }); - + // 更新视频流地址状态 if (data.flvPtz) lastStatus.value.flvPtz = data.flvPtz; if (data.flvTherm) lastStatus.value.flvTherm = data.flvTherm; @@ -1628,21 +1764,21 @@ const debounce = (fn, delay) => { // WebSocket相关数据 let wsClient = null; // 改为全局变量 const robotInfo = ref({ - robotId: '', - groupingId: '', - onlineStatus: '', - number: '' + robotId: "", + groupingId: "", + onlineStatus: "", + number: "", }); const robotStatus = ref({ - power: '', - temperature: '', - humidity: '', - pm2_5: '', - pm10: '', - voltage: '', - ststusName: '', - mileage: '' + power: "", + temperature: "", + humidity: "", + pm2_5: "", + pm10: "", + voltage: "", + ststusName: "", + mileage: "", }); // 添加防抖处理的getRobotDetailData @@ -1651,24 +1787,24 @@ const debouncedGetRobotDetail = debounce(async (robotId) => { const res = await robotApi.getRobotDetail(robotId); if (res.code === 200) { console.log("机器人详情数据:", res.data); - + // 更新视频流地址(只在地址发生变化时更新) const streamChanged = { ptz: res.data.flvPtz !== lastStatus.value.flvPtz, therm: res.data.flvTherm !== lastStatus.value.flvTherm, - front: res.data.flvThermLight !== lastStatus.value.flvThermLight + front: res.data.flvThermLight !== lastStatus.value.flvThermLight, }; - + if (streamChanged.ptz && res.data.flvPtz) { console.log("更新云台摄像头地址"); streamUrls.value.ptz = res.data.flvPtz; } - + if (streamChanged.therm && res.data.flvTherm) { console.log("更新热成像摄像头地址"); streamUrls.value.therm = res.data.flvTherm; } - + if (streamChanged.front && res.data.flvThermLight) { console.log("更新前置摄像头地址"); streamUrls.value.front = res.data.flvThermLight; @@ -1683,39 +1819,60 @@ const debouncedGetRobotDetail = debounce(async (robotId) => { pm10: res.data.pm10, voltage: res.data.voltage, ststusName: res.data.ststusName, - mileage: res.data.totalMileage + mileage: res.data.totalMileage, }; - + // 更新环境数据 envData.value = [ - { icon: "./img/pic1.png", label: "温度", value: `${res.data.temperature}°C` }, - { icon: "./img/pic2.png", label: "湿度", value: `${res.data.humidity}%` }, - { icon: "./img/pic3.png", label: "PM2.5", value: `${res.data.pm2_5}ug/m3` }, - { icon: "./img/pic4.png", label: "PM10", value: `${res.data.pm10}ug/m3` } + { + icon: "./img/pic1.png", + label: "温度", + value: `${res.data.temperature}°C`, + }, + { + icon: "./img/pic2.png", + label: "湿度", + value: `${res.data.humidity}%`, + }, + { + icon: "./img/pic3.png", + label: "PM2.5", + value: `${res.data.pm2_5}ug/m3`, + }, + { + icon: "./img/pic4.png", + label: "PM10", + value: `${res.data.pm10}ug/m3`, + }, ]; - + // 更新机器人信息 robotInfoItems.value = [ { label: "电量:", value: `${res.data.power}%` }, { label: "电池电压:", value: `${res.data.voltage}v` }, { label: "运行模式:", value: res.data.ststusName }, - { label: "急停:", value: res.data.buttonStopStatus === "1" ? "关闭" : "开启" } + { + label: "急停:", + value: res.data.buttonStopStatus === "1" ? "关闭" : "开启", + }, ]; - + // 更新累计数据 addData.value = [ { label: "累计运行", value: `${res.data.totalRunTime}h` }, { label: "累计里程", value: `${res.data.totalMileage}km` }, - { label: "充电次数", value: res.data.powerNums } + { label: "充电次数", value: res.data.powerNums }, ]; - + // 更新lastStatus中的视频流地址 lastStatus.value.flvPtz = res.data.flvPtz || lastStatus.value.flvPtz; - lastStatus.value.flvTherm = res.data.flvTherm || lastStatus.value.flvTherm; - lastStatus.value.flvThermLight = res.data.flvThermLight || lastStatus.value.flvThermLight; + lastStatus.value.flvTherm = + res.data.flvTherm || lastStatus.value.flvTherm; + lastStatus.value.flvThermLight = + res.data.flvThermLight || lastStatus.value.flvThermLight; } } catch (error) { - console.error('获取机器人详情失败:', error); + console.error("获取机器人详情失败:", error); } }, 1000); // 1秒的防抖时间 @@ -1723,13 +1880,13 @@ const debouncedGetRobotDetail = debounce(async (robotId) => { const initWebSocket = () => { // 如果组件正在卸载,不要重新连接 if (isUnmounting.value) { - console.log('组件正在卸载,跳过WebSocket连接'); + console.log("组件正在卸载,跳过WebSocket连接"); return; } // 如果已存在连接,先关闭 if (wsClient) { - console.log('关闭已存在的WebSocket连接'); + console.log("关闭已存在的WebSocket连接"); wsClient.close(); wsClient = null; } @@ -1737,48 +1894,48 @@ const initWebSocket = () => { const timestamp = Date.now(); const userId = `rbsstaff1_${timestamp}_${tenantInfoId.value}`; const wsUrl = `wss://rest.concoai.com/imserver/${userId}`; - + wsClient = new WebSocketClient({ url: wsUrl, onMessage: handleWebSocketMessage, onError: (error) => { - console.error('WebSocket错误:', error); + console.error("WebSocket错误:", error); }, onClose: () => { - console.log('WebSocket连接关闭'); + console.log("WebSocket连接关闭"); wsClient = null; - + // 只有在非卸载状态下才尝试重连 if (!isUnmounting.value) { - console.log('尝试重新连接WebSocket...'); + console.log("尝试重新连接WebSocket..."); setTimeout(() => { initWebSocket(); }, 3000); } }, onOpen: () => { - console.log('WebSocket连接成功'); - } + console.log("WebSocket连接成功"); + }, }); - + wsClient.connect(); }; // 修改handleWebSocketMessage函数 const handleWebSocketMessage = (data) => { - console.log('收到WebSocket消息:', data); - + console.log("收到WebSocket消息:", data); + // 处理第一类消息 - 机器人基本信息 - if (data.socketType === '7') { + if (data.socketType === "7") { // 避免重复处理相同的机器人信息 if (robotInfo.value.robotId !== data.robotId) { robotInfo.value = { robotId: data.robotId, groupingId: data.groupingId, onlineStatus: data.onlineStatus, - number: data.number + number: data.number, }; - + // 获取班组信息 getDutyInfo(data.robotId); // 首次获取机器人详情 @@ -1786,25 +1943,25 @@ const handleWebSocketMessage = (data) => { fetchTasks(robotInfo.value.robotId); } } - + // 处理第二类消息 - 机器人状态信息 - if (data.socketType === '1') { + if (data.socketType === "1") { // 检查关键数据是否发生变化 const statusChanged = hasStatusChanged(data); const streamUrlsChanged = hasStreamUrlsChanged(data); - + if (statusChanged || streamUrlsChanged) { - console.log('数据发生变化,调用详情接口'); + console.log("数据发生变化,调用详情接口"); debouncedGetRobotDetail(data.robotId); updateLastStatus(data); } else { - console.log('数据未发生变化,跳过详情接口调用'); + console.log("数据未发生变化,跳过详情接口调用"); } } - + // 处理第三类消息 - 事件未处理通知 - if (data.socketType === '2') { - console.log('收到未处理事件通知:', data); + if (data.socketType === "2") { + console.log("收到未处理事件通知:", data); // 将新的未处理事件添加到告警列表 const newAlarm = { id: data.eventId, @@ -1816,11 +1973,13 @@ const handleWebSocketMessage = (data) => { content: data.eName, time: formatEventTime(data.eventInsDate || data.time), position: data.positonName, - imageUrl: data.imgUrl + imageUrl: data.imgUrl, }; - + // 检查是否已存在相同ID的告警 - const existingIndex = alarmList.value.findIndex(a => a.id === newAlarm.id); + const existingIndex = alarmList.value.findIndex( + (a) => a.id === newAlarm.id + ); if (existingIndex === -1) { // 不存在则添加到列表头部 alarmList.value = [newAlarm, ...alarmList.value]; @@ -1828,23 +1987,25 @@ const handleWebSocketMessage = (data) => { // 存在则更新 alarmList.value.splice(existingIndex, 1, newAlarm); } - + // 更新告警计数 - console.log('更新后的未处理告警数量:', pendingCount.value); + console.log("更新后的未处理告警数量:", pendingCount.value); } - + // 处理第四类消息 - 事件超时未处理通知 - if (data.socketType === '6') { - console.log('收到超时未处理事件通知:', data); + if (data.socketType === "6") { + console.log("收到超时未处理事件通知:", data); // 更新对应的告警状态为超时未处理 - const existingIndex = alarmList.value.findIndex(a => a.id === data.eventId); + const existingIndex = alarmList.value.findIndex( + (a) => a.id === data.eventId + ); if (existingIndex !== -1) { // 更新告警状态 const updatedAlarm = { ...alarmList.value[existingIndex], status: "timeout", // 添加一个新的状态值表示超时未处理 content: data.eName, - time: formatEventTime(data.eventInsDate) + time: formatEventTime(data.eventInsDate), }; alarmList.value.splice(existingIndex, 1, updatedAlarm); } else { @@ -1857,7 +2018,7 @@ const handleWebSocketMessage = (data) => { isRead: false, type: "danger", // 超时未处理都标记为危险级别 content: data.eName, - time: formatEventTime(data.eventInsDate) + time: formatEventTime(data.eventInsDate), }; alarmList.value = [newTimeoutAlarm, ...alarmList.value]; } @@ -1866,13 +2027,13 @@ const handleWebSocketMessage = (data) => { // 添加格式化事件时间的辅助函数 const formatEventTime = (timeStr) => { - if (!timeStr) return ''; - + if (!timeStr) return ""; + // 如果是格式化的日期时间字符串,直接返回 - if (timeStr.includes('-')) { + if (timeStr.includes("-")) { return timeStr; } - + // 如果是yyyyMMddHHmmss格式 if (timeStr.length === 14) { const year = timeStr.substring(0, 4); @@ -1883,7 +2044,7 @@ const formatEventTime = (timeStr) => { const second = timeStr.substring(12, 14); return `${year}-${month}-${day} ${hour}:${minute}:${second}`; } - + return timeStr; }; @@ -1894,20 +2055,32 @@ const getDutyInfo = async (robotId) => { if (res.code === 200) { // 更新班组信息 groupInfo.value = [ - { icon: "./img/info1.png", label: "当前分组", value: res.data.groupName || "室外巡检" }, - { icon: "./img/info2.png", label: "当前班次", value: res.data.dutyTime || "00:00-23:59" }, - { icon: "./img/info3.png", label: "当前人员", value: res.data.dutyUser || "员工1" } + { + icon: "./img/info1.png", + label: "当前分组", + value: res.data.groupName || "室外巡检", + }, + { + icon: "./img/info2.png", + label: "当前班次", + value: res.data.dutyTime || "00:00-23:59", + }, + { + icon: "./img/info3.png", + label: "当前人员", + value: res.data.dutyUser || "员工1", + }, ]; } } catch (error) { - console.error('获取班组信息失败:', error); + console.error("获取班组信息失败:", error); } }; // 组件卸载时关闭WebSocket连接 onUnmounted(() => { if (wsClient) { - console.log('组件卸载,关闭WebSocket连接'); + console.log("组件卸载,关闭WebSocket连接"); wsClient.close(); wsClient = null; } @@ -1915,15 +2088,13 @@ onUnmounted(() => { // 添加确认弹窗相关的状态 const showConfirmDialog = ref(false); -const confirmDialogMessage = ref(''); +const confirmDialogMessage = ref(""); // 监听alarmTab变化 watch(alarmTab, async (newValue) => { - console.log('标签切换为:', newValue); - // 获取告警列表 + isLoading.value = true; // 1. 优先设置loading + alarmList.value = []; // 2. 立即清空数据 await getAlarmEventList(); - - // 重新获取告警数量 await getAlarmEventCount(); }); @@ -1931,27 +2102,24 @@ watch(alarmTab, async (newValue) => { const handleAlarmEvent = async (alarm) => { try { // 如果是仪表类型的告警或日常巡检,直接处理,不弹窗 - if (alarm.group === "meter" || alarm.content === "日常巡检") { - // 检查 messageId 是否存在 - if (!alarm.messageId) { - window.$message && window.$message.error('缺少 messageId,无法处理该告警'); - return; - } - // 可选:添加 loading 状态 - isLoading.value = true; - try { - await handleSingleAlarm({ messageId: alarm.messageId }); - } catch (err) { - window.$message && window.$message.error('处理仪表识别告警失败'); - } finally { - isLoading.value = false; - } - } else { - // 其他类型的告警弹窗详情 - await showAlarmDetail(alarm); + + // 检查 messageId 是否存在 + if (!alarm.messageId) { + window.$message && + window.$message.error("缺少 messageId,无法处理该告警"); + return; + } + // 可选:添加 loading 状态 + isLoading.value = true; + try { + await handleSingleAlarm({ messageId: alarm.messageId }); + } catch (err) { + window.$message && window.$message.error("处理仪表识别告警失败"); + } finally { + isLoading.value = false; } } catch (error) { - console.error('处理告警事件失败:', error); + console.error("处理告警事件失败:", error); } }; @@ -1960,7 +2128,7 @@ const handleAlarmEvent = async (alarm) => { const eventListData = ref([]); // 告警类型选项 -const eventTypeOptions = ref(['全部告警']); +const eventTypeOptions = ref(["全部告警"]); // 页面加载时获取告警类型 onMounted(async () => { @@ -1968,12 +2136,11 @@ onMounted(async () => { // 获取告警类型 const res = await robotApi.getEventTypes(); if (res.code === 200 && Array.isArray(res.data)) { - eventTypeOptions.value = ['全部告警', ...res.data]; + eventTypeOptions.value = ["全部告警", ...res.data]; } } catch (e) { - console.error('获取告警类型失败', e); + console.error("获取告警类型失败", e); } - }); @@ -1990,14 +2157,14 @@ onMounted(async () => { /* 主体内容样式 */ .main-content { display: flex; - height: calc(100vh - 50px); - padding:32px; + height: calc(100% - 100px); + padding: 32px; gap: 10px; } /* 左侧面板 */ .left-panel { - width: 25rem; + width: 400px; display: flex; flex-direction: column; gap: 15px; @@ -2033,12 +2200,14 @@ onMounted(async () => { /* 右侧面板 */ .right-panel { - width: 25rem; + width: 400px; display: flex; flex-direction: column; gap: 10px; } - +.alarm-overview{ + margin:10px 0; +} /* 告警相关样式 */ .tab-item { padding: 10px; @@ -2095,7 +2264,7 @@ onMounted(async () => { .alarm-time { font-size: 12px; - color: #00CEEA; + color: #00ceea; margin-bottom: 5px; } @@ -2124,12 +2293,12 @@ onMounted(async () => { display: flex; align-items: center; justify-content: flex-start; - gap: 8px; + gap: 10px; } .alarm-icon { width: 16px; - height: 16px; + height: 16px; border-radius: 50%; } @@ -2143,7 +2312,6 @@ onMounted(async () => { justify-content: flex-end; } - .alarm-counts { display: flex; align-items: center; @@ -2188,7 +2356,7 @@ onMounted(async () => { position: relative; background: rgba(0, 145, 169, 0.2); color: #b9e8ff; - border: 1px solid #0091A9; + border: 1px solid #0091a9; border-radius: 2px; padding: 2px 14px; font-size: 13px; @@ -2213,7 +2381,7 @@ onMounted(async () => { } .process-all-btn { - background: #0091A9; + background: #0091a9; color: #b9e8ff; border: none; border-radius: 2px; @@ -2240,7 +2408,7 @@ onMounted(async () => { } .filter-divider { - color: #00ffff; + color: #b9e8ff; margin: 0 4px; } @@ -2251,7 +2419,9 @@ onMounted(async () => { flex: 1; position: relative; cursor: pointer; - min-width: 120px; + min-width: 300px; + max-width: 300px; + width: 300px; outline: none; } @@ -2276,28 +2446,44 @@ onMounted(async () => { .alarm-select-dropdown { position: absolute; - right: 0; - top: 110%; + right: -10px; + top: 130%; background: #00273A; - border-radius: 4px; - box-shadow: 0 2px 8px rgba(0,0,0,0.15); - min-width: 100px; + border-radius: 6px; + border:1px solid #335261; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + width: 320px; z-index: 10; padding: 0; margin: 0; list-style: none; } +.alarm-select-dropdown li:first-child { + border-radius: 6px 6px 0 0; +} +.alarm-select-dropdown li:last-child { + border-radius: 0 0 6px 6px; +} .alarm-select-dropdown li { - color: #b9e8ff; - padding: 6px 16px; + color: #B9E8FF; + background: #00273A; + padding: 0 16px; cursor: pointer; white-space: nowrap; + font-size: 13px; + line-height: 36px; + margin: 0; +} + +.alarm-select-dropdown li.active { + background: #0091A9; + color: #fff; } .alarm-select-dropdown li:hover { - background: #00ffff22; - color: #00ffff; + background: #005C71; + color: #fff; } .alarm-level { @@ -2331,14 +2517,15 @@ onMounted(async () => { flex-direction: column; gap: 15px; } -.thumbnail-container1{ +.thumbnail-container1 { height: 100%; flex: 1; display: flex; flex-direction: column; gap: 15px; position: relative; - + + border-radius: 8px; } .camera-title { font-size: 14px; @@ -2347,15 +2534,17 @@ onMounted(async () => { /* background: rgba(0, 21, 31, 0.3); */ background-color: rgba(0, 0, 0, 0.6); /* border-radius: 4px 4px 0 0; */ + border-radius: 8px; } .camera-feed { - height: 55vh; /* 从60vh改为70vh */ + height: 57vh; /* 从60vh改为70vh */ background: rgba(0, 21, 31, 0.3); border-radius: 4px; border: 1px solid rgba(185, 232, 255, 0.3); position: relative; overflow: hidden; + border-radius: 8px; } .camera-feed img { @@ -2377,13 +2566,19 @@ onMounted(async () => { display: flex; flex: 1; gap: 10px; + /* 移除固定高度,使用flex布局 */ } +.camera-thumbnails img { + width: 100%; + height: 100%; + object-fit: cover; +} .thumbnail-container { width: 100%; background: rgba(0, 21, 31, 0.3); - border-radius: 4px; + border-radius: 8px; /* overflow: hidden; */ position: relative; display: flex; /* 添加flex布局 */ @@ -2401,15 +2596,16 @@ onMounted(async () => { left: 0; right: 0; padding: 10px 5px; - font-size: 12px; + font-size: 14px; background: rgba(0, 0, 0, 0.2); z-index: 1; } .thumbnail { width: 100%; - flex: 1; /* 使用flex:1代替固定高度 */ - + border-radius: 8px !important; + height: 28vh; + position: relative; background: rgba(0, 21, 31, 0.3); display: flex; /* 添加flex布局 */ flex-direction: column; /* 垂直方向flex */ @@ -2438,7 +2634,7 @@ onMounted(async () => { left: 0; width: 100vw; height: 100vh; - background:url('../../assets/img/alert2.png') no-repeat; + background: url("../../assets/img/alert2.png") no-repeat; background-size: 100% 100%; display: flex; align-items: center; @@ -2465,7 +2661,7 @@ onMounted(async () => { } .title { - color: #B9E8FF; + color: #b9e8ff; font-size: 28px; letter-spacing: 4px; padding-left: 20px; @@ -2498,9 +2694,10 @@ onMounted(async () => { gap: 10px; } -.main-monitor, .monitor-item { +.main-monitor, +.monitor-item { position: relative; - border: 1px solid rgba(0,206,234,0.7); + border: 1px solid rgba(0, 206, 234, 0.7); border-radius: 4px; overflow: hidden; } @@ -2516,7 +2713,7 @@ onMounted(async () => { width: 100%; padding: 5px 10px; background: rgba(0, 21, 31, 0.2); - color: #B9E8FF; + color: #b9e8ff; font-size: 12px; z-index: 1; } @@ -2544,7 +2741,7 @@ onMounted(async () => { .event-info { background: rgba(0, 21, 31, 0.5); - border: 1px solid rgba(0,206,234,0.7); + border: 1px solid rgba(0, 206, 234, 0.7); border-radius: 4px; } @@ -2556,17 +2753,17 @@ onMounted(async () => { display: flex; align-items: center; justify-content: space-between; - border-bottom: 1px solid #244C60; + border-bottom: 1px solid #244c60; padding: 10px; } .info-item .label { - color: #B9E8FF; + color: #b9e8ff; margin: 0; } .info-item .label:before { - content: ''; + content: ""; display: inline-block; width: 10px; height: 10px; @@ -2576,7 +2773,7 @@ onMounted(async () => { } .info-item .value { - color: #B9E8FF; + color: #b9e8ff; margin: 0; } @@ -2589,7 +2786,7 @@ onMounted(async () => { min-height: 130px; background: rgba(0, 21, 31, 0.3); border-radius: 4px; - color: #B9E8FF; + color: #b9e8ff; padding: 10px; outline: none; } @@ -2637,7 +2834,7 @@ onMounted(async () => { cursor: pointer; font-size: 14px; background-size: 100% 100%; - color: #C6F4FF; + color: #c6f4ff; } .btn.cancel { @@ -2657,7 +2854,9 @@ onMounted(async () => { display: flex; align-items: center; padding: 0 32px; - background: rgba(0, 21, 31, 0.5); + /* background: rgba(0, 21, 31, 0.5); */ + background: url("../assets/img/header.png") no-repeat; + background-size: 100% 100%; border-bottom: 1px solid rgba(185, 232, 255, 0.1); } @@ -2672,14 +2871,14 @@ onMounted(async () => { } .back-icon { - width: 20px; - height: 20px; + width: 32px; + height: 32px; background: url("../assets/img/return.png") no-repeat; background-size: 100% 100%; } .robot-id { - font-size: var(--fsize1); + font-size: var(--fsize6); font-weight: bold; background: linear-gradient(to top, #b9e8ff 48%, #fff 80%); -webkit-background-clip: text; @@ -2703,4 +2902,22 @@ onMounted(async () => { border-radius: 10px; margin-left: 5px; } +.handled-text { + color: #00ffff; + font-size: var(--fsize4); +} + +.loading-state { + text-align: center; + padding: 20px; + color: #b9e8ff; +} + +.loading-text { + font-size: var(--fsize3); + color: #b9e8ff; + position: relative; + padding-left: 1.25vw; /* 24px at 1920px width */ +} + diff --git a/vite.config.js b/vite.config.js index 8d1e1bd..080c6c4 100644 --- a/vite.config.js +++ b/vite.config.js @@ -2,19 +2,54 @@ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import { resolve } from "path"; import dotenv from 'dotenv'; + +import postcsspxtoviewport from 'postcss-px-to-viewport' // https://vitejs.dev/config/ // 加载环境变量 -dotenv.config({ - path: process.env.NODE_ENV === 'production' ? '.env.production' : '.env.development', -}); +// dotenv.config({ +// path: process.env.NODE_ENV === 'production' ? '.env.production' : '.env.development', +// }); export default defineConfig({ base:'./' , plugins: [vue()], - resolve: { + //在这配置插件内容 + css: { + postcss: { + plugins: [ + postcsspxtoviewport({ + // 要转化的单位 + unitToConvert: 'px', + // UI设计稿的大小 + viewportWidth: 1920, + // 转换后的精度 + unitPrecision: 6, + // 字体转换后的单位 + fontViewportUnit: 'vw', + // 能转换的属性,*表示所有属性,!border表示border不转 + propList: ['*'], + // 指定不转换为视窗单位的类名, + selectorBlackList: ['ignore-'], + // 最小转换的值,小于等于1不转 + minPixelValue: 1, + // 是否在媒体查询的css代码中也进行转换,默认false + mediaQuery: false, + // 是否转换后直接更换属性值 + replace: true, + // 忽略某些文件夹下的文件或特定文件,例如 'node_modules' 下的文件 + exclude: [], + // 包含那些文件或者特定文件 + include: [], + // 是否处理横屏情况 + landscape: false + }), + ] + } + }, + resolve: { alias: { "@": resolve(__dirname, "./src") },