修改报警规则

main
suixy 6 days ago
parent addfecccf4
commit b2f017ee01

@ -11,7 +11,7 @@ VITE_APP_CONTEXT_PATH = './'
# 桌面端没有 Vite/Nginx 代理,这里必须配置为真实后端地址。
# 如后端部署在其他机器,请改为对应地址,例如 'https://example.com/prod-api'。
VITE_APP_BASE_API = 'http://localhost:8080'
VITE_APP_BASE_API = 'http://10.254.59.44:8080'
# 监控地址
VITE_APP_MONITOR_ADMIN = '/admin/applications'

@ -3,6 +3,33 @@
<router-view />
<RealtimeAlarmModal />
<div v-if="offlineAlertActive" class="offline-breath-overlay"></div>
<div v-if="offlineDialogVisible" class="offline-panel-mask">
<section class="offline-panel">
<header class="offline-panel-header">
<span class="offline-panel-title">离线报警</span>
<el-button text type="danger" @click="clearOfflineAlarms"></el-button>
</header>
<el-table
ref="offlineAlarmTableRef"
:data="offlineAlarmRows.length > 8 ? [...offlineAlarmRows, ...offlineAlarmRows] : offlineAlarmRows"
border
:show-header="false"
height="320"
empty-text="暂无离线报警"
@mouseenter="offlineTablePaused = true"
@mouseleave="offlineTablePaused = false"
>
<el-table-column label="离线设备">
<template #default="scope">
{{ `设备${scope.row.monitorId}离线` }}
</template>
</el-table-column>
</el-table>
<footer class="offline-panel-footer">
<el-button type="primary" @click="clearOfflineAlarms"></el-button>
</footer>
</section>
</div>
<el-dialog
v-model="alarmDetailVisible"
title="报警信息"
@ -45,22 +72,20 @@
</template>
<script setup lang="ts">
import { ElMessage, ElMessageBox } from 'element-plus';
import { ElMessage, ElMessageBox, ElNotification } from 'element-plus';
import { listEmsAlarmActionStep } from '@/api/ems/base/emsAlarmActionStep';
import { handleExceptions } from '@/api/ems/record/recordAlarmData';
import RealtimeAlarmModal from '@/components/alarm/RealtimeAlarmModal.vue';
import { useSettingsStore } from '@/store/modules/settings';
import { handleThemeStyle } from '@/utils/theme';
import { useAppStore } from '@/store/modules/app';
import {
closeAlarmReminder,
connectAlarmReminder,
onAlarmReminderExtra,
onAlarmReminderOvertime
} from '@/utils/alarmReminder';
import { closeAlarmReminder, connectAlarmReminder, onAlarmReminderExtra, onAlarmReminderOvertime } from '@/utils/alarmReminder';
const appStore = useAppStore();
const offlineAlertActive = ref(false);
const offlineDialogVisible = ref(false);
const offlineAlarmTableRef = ref();
const offlineAlarmRows = ref<Array<{ monitorId: string }>>([]);
const alarmDetailVisible = ref(false);
const alarmDetailTab = ref('alarm');
const alarmHandling = ref(false);
@ -68,7 +93,12 @@ const currentActionSteps = ref<any[]>([]);
const currentAlarmContents = ref<string[]>([]);
const alarmDetailQueue = ref<any[]>([]);
const currentAlarmDetail = ref<any | null>(null);
const offlineTablePaused = ref(false);
let offlineAlertTimer: ReturnType<typeof setTimeout> | null = null;
let notificationTimer: ReturnType<typeof setTimeout> | null = null;
let offlineTableScrollFrame: number | null = null;
let offlineTableLastScrollAt = 0;
const notificationQueue: Array<{ message: string; title: string }> = [];
const playAlarmVoice = (message: string) => {
if (typeof window === 'undefined' || !('speechSynthesis' in window)) {
@ -97,6 +127,36 @@ const triggerAlarmEffect = () => {
});
};
const showAlarmNotification = (message: string, title: string) => {
notificationQueue.push({ message, title });
if (notificationTimer) {
return;
}
const flushNotificationQueue = () => {
const current = notificationQueue.shift();
if (!current) {
notificationTimer = null;
return;
}
ElNotification({
title: current.title,
message: current.message,
type: 'error',
duration: 0
});
notificationTimer = setTimeout(() => {
flushNotificationQueue();
}, 100);
};
flushNotificationQueue();
};
const showAlarmDialog = (message: string, title: string) => {
ElMessageBox.alert(message, title, {
type: 'warning',
@ -176,6 +236,77 @@ const handleGlobalAlarm = (message: string, title: string) => {
triggerAlarmEffect();
};
const stopOfflineTableScroll = () => {
if (offlineTableScrollFrame !== null) {
cancelAnimationFrame(offlineTableScrollFrame);
offlineTableScrollFrame = null;
}
offlineTableLastScrollAt = 0;
};
const startOfflineTableScroll = async () => {
if (offlineTableScrollFrame !== null) {
return;
}
if (!offlineDialogVisible.value || offlineAlarmRows.value.length <= 1) {
return;
}
await nextTick();
const tableEl = offlineAlarmTableRef.value?.$el as HTMLElement | undefined;
const bodyWrapper = tableEl?.querySelector('.el-scrollbar__wrap') as HTMLElement | null;
if (!bodyWrapper || bodyWrapper.scrollHeight <= bodyWrapper.clientHeight) {
return;
}
const scrollStep = (timestamp: number) => {
if (!offlineDialogVisible.value || offlineAlarmRows.value.length <= 1) {
stopOfflineTableScroll();
return;
}
if (!offlineTableLastScrollAt) {
offlineTableLastScrollAt = timestamp;
}
if (offlineTablePaused.value) {
offlineTableLastScrollAt = timestamp;
} else if (timestamp - offlineTableLastScrollAt >= 16) {
offlineTableLastScrollAt = timestamp;
if (bodyWrapper.scrollTop + bodyWrapper.clientHeight >= bodyWrapper.scrollHeight) {
bodyWrapper.scrollTop = 0;
} else {
bodyWrapper.scrollTop += 1;
if (bodyWrapper.scrollTop > 40 * offlineAlarmRows.value.length) {
bodyWrapper.scrollTop -= 40 * offlineAlarmRows.value.length;
}
}
}
offlineTableScrollFrame = requestAnimationFrame(scrollStep);
};
offlineTableScrollFrame = requestAnimationFrame(scrollStep);
};
const clearOfflineAlarms = () => {
stopOfflineTableScroll();
offlineTablePaused.value = false;
offlineAlarmRows.value = [];
offlineDialogVisible.value = false;
};
const handleOvertimeAlarm = (monitorId: string) => {
playAlarmVoice(`设备${monitorId}离线`);
triggerAlarmEffect();
offlineAlarmRows.value.push({ monitorId });
offlineDialogVisible.value = true;
};
const deferAlarmDetail = () => {
closeCurrentAlarmDetail();
openNextAlarmDetail();
@ -217,7 +348,7 @@ onAlarmReminderOvertime((device) => {
return;
}
handleGlobalAlarm(`设备${monitorId}离线`, '离线报警');
handleOvertimeAlarm(monitorId);
});
onAlarmReminderExtra((payload) => {
@ -238,12 +369,28 @@ onMounted(() => {
});
});
watch([offlineDialogVisible, () => offlineAlarmRows.value.length], ([visible, length]) => {
if (!visible || length <= 1) {
stopOfflineTableScroll();
return;
}
void startOfflineTableScroll();
});
onBeforeUnmount(() => {
if (offlineAlertTimer) {
clearTimeout(offlineAlertTimer);
offlineAlertTimer = null;
}
if (notificationTimer) {
clearTimeout(notificationTimer);
notificationTimer = null;
}
stopOfflineTableScroll();
if (typeof window !== 'undefined' && 'speechSynthesis' in window) {
window.speechSynthesis.cancel();
}
@ -260,6 +407,48 @@ onBeforeUnmount(() => {
padding: 4px 0;
}
.offline-panel-mask {
position: fixed;
inset: 0;
z-index: 3000;
display: flex;
align-items: center;
justify-content: center;
pointer-events: none;
}
.offline-panel {
width: 560px;
max-width: calc(100vw - 32px);
padding: 16px;
border-radius: 16px;
background: #fff;
box-shadow: 0 18px 48px rgba(15, 23, 42, 0.22);
pointer-events: auto;
}
.offline-panel-header,
.offline-panel-footer {
display: flex;
align-items: center;
}
.offline-panel-header {
justify-content: space-between;
margin-bottom: 12px;
}
.offline-panel-footer {
justify-content: flex-end;
margin-top: 16px;
}
.offline-panel-title {
font-size: 18px;
font-weight: 600;
color: #303133;
}
.alarm-info-item {
font-size: 14px;
line-height: 1.8;

@ -1,6 +1,7 @@
import { listBaseMonitorInfo } from '@/api/ems/base/baseMonitorInfo';
const websocketUrl = 'ws://119.45.202.115:7164/ws';
const websocketUrl = 'ws://10.254.59.44:7164/ws';
// const websocketUrl = 'ws://119.45.202.115:7164/ws';
// const websocketUrl = 'ws://119.45.202.115:7181/ws';
const deviceDataTime = {};
const overtime = 1000 * 60 * 60;
@ -94,7 +95,7 @@ export const connectAlarmReminder = async (onMessage, url = websocketUrl) => {
alarmReminderSocket.close();
}
await initDeviceDataTime();
// await initDeviceDataTime();
alarmReminderSocket = new WebSocket(url);

Loading…
Cancel
Save