Files
dyf-vue-ui/src/views/homeIndex/components/RealTimeAlarm.vue
2025-09-30 17:32:04 +08:00

239 lines
6.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="alarm-table-container">
<div class="alarm-table-header">
<div class="header-item">报警时间</div>
<div class="header-item">设备类型</div>
<div class="header-item">设备IMEI</div>
<div class="header-item">报警事件</div>
<div class="header-item">报警位置</div>
</div>
<div class="alarm-table-body">
<div ref="tableBody" class="alarm-table-body-inner">
<!-- 第一份数据 -->
<div v-for="(item, index) in displayData" :key="`first-${getKey(item, index)}`" class="alarm-item">
<div class="item-cell">{{ item.startTime }}</div>
<div class="item-cell">{{ item.deviceTypeName }}</div>
<div class="item-cell">{{ item.deviceImei }}</div>
<div class="item-cell alarm-event">
{{ getEventName(item.deviceAction) }}
</div>
<div class="item-cell loaction">{{ item.location }}</div>
</div>
<!-- 第二份数据用于无缝滚动 -->
<div v-for="(item, index) in displayData" :key="`second-${getKey(item, index)}`" class="alarm-item">
<div class="item-cell">{{ item.startTime }}</div>
<div class="item-cell">{{ item.deviceTypeName }}</div>
<div class="item-cell">{{ item.deviceImei }}</div>
<div class="item-cell alarm-event">
{{ getEventName(item.deviceAction) }}
</div>
<div class="item-cell loaction">{{ item.location }}</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { getRealtimeAlarm } from '@/api/homeIndex/index';
let alarmTimer = null;
const alarmData = ref([]);
const displayData = ref([]); // 显示的数据(复制一份用于无缝滚动)
const tableBody = ref(null);
const isScrolling = ref(true);
const animationId = ref(null);
const scrollPosition = ref(0);
const itemHeight = ref(0); // 动态计算每个条目的高度
// 获取报警事件名称
const getEventName = (action) => {
const eventMap = {
0: '强制报警',
1: '撞击闯入',
2: '自动报警',
3: '电子围栏告警'
};
return eventMap[action] || '未知报警';
};
// 获取实时报警数据
const getRealtimeAlarmData = () => {
getRealtimeAlarm().then((res) => {
if (res.data && res.data.length > 0) {
// 新数据插入到最前面
alarmData.value = [...res.data, ...alarmData.value];
// 限制数据量,避免性能问题
if (alarmData.value.length > 10) {
alarmData.value = alarmData.value.slice(0, 10);
}
// 更新显示数据
updateDisplayData();
// 新数据插入时重置滚动位置
scrollPosition.value = 0;
if (tableBody.value) {
tableBody.value.style.transform = `translateY(0)`;
}
}
});
};
// 更新显示数据(复制一份用于无缝滚动)
const updateDisplayData = () => {
displayData.value = [...alarmData.value];
// 计算每个条目的实际高度
nextTick(() => {
const items = document.querySelectorAll('.alarm-item');
if (items.length > 0) {
itemHeight.value = items[0].offsetHeight;
}
});
};
// 生成唯一的key
const getKey = (item, index) => {
return `${item.deviceImei}_${item.startTime}_${index}`;
};
// 开始滚动动画
const startScroll = () => {
if (!tableBody.value || displayData.value.length === 0) return;
stopScroll(); // 先停止之前的动画
const scrollSpeed = 0.3; // 滚动速度
const animate = () => {
if (!isScrolling.value) {
animationId.value = requestAnimationFrame(animate);
return;
}
scrollPosition.value += scrollSpeed;
// 当滚动超过一份数据的高度时,减去整段高度,实现无缝衔接
const oneListHeight = itemHeight.value * displayData.value.length;
if (itemHeight.value > 0 && scrollPosition.value >= oneListHeight) {
scrollPosition.value -= oneListHeight;
}
// 应用滚动效果
if (tableBody.value) {
tableBody.value.style.transform = `translateY(-${scrollPosition.value}px)`;
}
animationId.value = requestAnimationFrame(animate);
};
animationId.value = requestAnimationFrame(animate);
};
// 停止滚动动画
const stopScroll = () => {
if (animationId.value) {
cancelAnimationFrame(animationId.value);
animationId.value = null;
}
};
// 开始报警定时器
const startAlarmTimer = () => {
if (alarmTimer) {
clearInterval(alarmTimer);
}
getRealtimeAlarmData();
alarmTimer = setInterval(getRealtimeAlarmData, 30 * 10000);
};
// 清除报警定时器
const clearAlarmTimer = () => {
if (alarmTimer) {
clearInterval(alarmTimer);
alarmTimer = null;
}
};
// 监听数据变化
watch(displayData, (newData) => {
if (newData.length > 0) {
// 延迟一点确保DOM已更新
setTimeout(() => {
startScroll();
}, 100);
}
});
onMounted(() => {
startAlarmTimer();
});
onUnmounted(() => {
clearAlarmTimer();
stopScroll();
});
</script>
<style scoped lang="scss">
.alarm-table-container {
width: 100%;
border-radius: 4px;
overflow: hidden;
margin-top: 4vh;
padding: 1vw 0vw;
}
.alarm-table-header {
display: flex;
color: #00C0FF;
border-bottom: 1px dashed rgba(100, 150, 200, 0.3);
font-size: 0.8vw;
background-color: #0C2644;
}
.header-item {
flex: 1;
padding: 8px;
text-align: center;
}
.alarm-table-body {
height: 18vh;
overflow: hidden;
color: #fff;
position: relative;
}
.alarm-table-body-inner {
transition: none;
will-change: transform;
transform: translateZ(0);
backface-visibility: hidden;
}
.alarm-item {
display: flex;
border-bottom: 1px dashed rgba(100, 150, 200, 0.3);
font-size: 0.6vw;
align-items: center;
padding: 0.8vw;
height: 7vh;
box-sizing: border-box;
}
.item-cell {
flex: 1;
text-align: center;
}
.alarm-event {
color: #ff5252;
}
/* 隐藏滚动条 */
.alarm-table-body::-webkit-scrollbar {
display: none;
}
.alarm-table-body {
-ms-overflow-style: none;
scrollbar-width: none;
}
</style>