Files
lc_frontend/src/views/screen/components/AlertList.vue
2025-12-13 20:55:17 +08:00

316 lines
7.8 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="list-content">
<div v-if="title" class="list-title">
<span>{{ title }}</span>
<img width="50%" src="@/assets/images/line_1.png" />
</div>
<div class="list" :style="{ maxHeight: maxHeight }">
<!-- 表格头部 -->
<div v-if="tableTitle && tableTitle.length > 0" class="table-header">
<div class="header-item" v-for="(title, index) in tableTitle" :key="index">
{{ title.name }}
</div>
</div>
<div class="list-wrapper" ref="listWrapperRef" @mouseenter="handleMouseEnter" @mouseleave="handleMouseLeave">
<!-- 表格模式 -->
<template v-if="tableTitle && tableTitle.length > 0">
<div @click="handleItemClick(item)" class="table-row cursor-pointer" v-for="(item, index) in listData" :key="`table-${index}`" @mouseenter="handleMouseEnter">
<div class="table-cell" v-for="(title, cellIndex) in tableTitle" :key="`cell-${index}-${cellIndex}`">
{{ item[title.key] || '-' }}
</div>
</div>
</template>
<!-- 列表模式 -->
<template v-else>
<div @click="handleItemClick(item)" class="list-item cursor-pointer" v-for="(item, index) in listData" :key="`list-${index}`" @mouseenter="handleMouseEnter">
<span class="alert-text" :class="[{ error: item.alarm_level_code == 'severity' }, { warn: item.alarm_level_code == 'major' }]">
{{ (index + 1) }} {{ item.description }}
</span>
</div>
</template>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue'
interface AlertItem {
description: string
alarm_level_code: string
alarm_status: string
alarm_biz_id: string
}
interface TableTitle {
name: string
key: string
}
interface Props {
title?: string
listData: AlertItem[]
maxHeight?: string
autoScroll?: boolean
scrollSpeed?: number
scrollInterval?: number,
tableTitle?: TableTitle[]
}
const props = withDefaults(defineProps<Props>(), {
title: '', // 标题
maxHeight: '23vh', // 最大高度
autoScroll: true, // 是否自动滚动
scrollSpeed: 1, // 每次滚动的像素数
scrollInterval: 3000 // 每次滚动的间隔时间,单位毫秒
})
const listWrapperRef = ref<HTMLElement | null>(null)
let scrollTimer: NodeJS.Timeout | null = null
let isScrolling = false
const handleItemClick = (item: AlertItem) => {
window.open(`http://10.0.64.20/configcenter/console/device-manage`, '_blank')
}
// 自动滚动功能
const startAutoScroll = (resetToTop: boolean = false) => {
if (!props.autoScroll || !listWrapperRef.value) return
const wrapper = listWrapperRef.value
const scrollHeight = wrapper.scrollHeight
const clientHeight = wrapper.clientHeight
// 只有当内容高度超过容器高度时才启动滚动
if (scrollHeight <= clientHeight) return
// 如果是首次启动或需要重置,从顶部开始
if (resetToTop) {
wrapper.scrollTop = 0
}
isScrolling = true
const scroll = () => {
if (!isScrolling || !wrapper) return
const currentScrollTop = wrapper.scrollTop
const maxScrollTop = scrollHeight - clientHeight
// 向下滚动
const nextScrollTop = currentScrollTop + props.scrollSpeed
if (nextScrollTop >= maxScrollTop) {
// 滚动到底部,瞬间跳转到顶部
// 先移除平滑滚动效果,实现瞬间跳转
const originalScrollBehavior = wrapper.style.scrollBehavior
wrapper.style.scrollBehavior = 'auto'
wrapper.scrollTop = 0
// 恢复平滑滚动效果
setTimeout(() => {
wrapper.style.scrollBehavior = originalScrollBehavior || 'smooth'
}, 0)
// 短暂延迟后继续滚动
scrollTimer = setTimeout(scroll, 100)
return
} else {
wrapper.scrollTop = nextScrollTop
}
scrollTimer = setTimeout(scroll, 50) // 每50ms滚动一次实现平滑效果
}
scroll()
}
// 停止自动滚动
const stopAutoScroll = () => {
isScrolling = false
scrollTimer && clearTimeout(scrollTimer)
scrollTimer = null
}
// 鼠标悬停时暂停滚动
const handleMouseEnter = () => {
stopAutoScroll()
}
// 鼠标离开时恢复滚动
const handleMouseLeave = () => {
if (props.autoScroll) {
// 延迟启动滚动,避免鼠标快速进出
// 从当前位置继续向下滚动
setTimeout(() => {
startAutoScroll(false)
}, 500)
}
}
// 监听数据变化,重新启动滚动
watch(() => props.listData, () => {
nextTick(() => {
if (props.autoScroll) {
stopAutoScroll()
setTimeout(() => {
// 数据更新后从顶部重新开始
startAutoScroll(true)
}, 1000) // 数据更新后1秒开始滚动
}
})
}, { deep: true })
onMounted(() => {
if (props.autoScroll) {
// 组件挂载后延迟启动滚动,从顶部开始
setTimeout(() => {
startAutoScroll(true)
}, 2000)
}
})
onUnmounted(() => {
stopAutoScroll()
})
</script>
<style scoped lang="scss">
.cursor-pointer {
cursor: pointer;
}
.list-content {
display: flex;
width: 68%;
height: 100%;
flex-direction: column;
align-items: flex-end;
.list-title {
display: flex;
flex-direction: column;
align-items: center;
}
.list {
display: flex;
width: 100%;
flex: 1;
flex-direction: column;
align-items: center;
overflow: hidden;
.table-header {
display: flex;
width: 100%;
background: linear-gradient(135deg, #1e40af 0%, #3b82f6 100%);
border-radius: 0.37vh 0.37vh 0 0;
margin-bottom: 2px;
.header-item {
flex: 1;
padding: 0.5vh 0.4vw;
font-size: 0.75rem;
color: #ffffff;
font-weight: bold;
text-align: center;
border-right: 1px solid rgba(255, 255, 255, 0.2);
&:last-child {
border-right: none;
}
}
}
.list-wrapper {
display: flex;
width: 100%;
height: 100%;
margin-top: 10px;
overflow-y: auto;
flex-direction: column;
row-gap: 4px;
scroll-behavior: smooth; // 平滑滚动效果
overflow-x: hidden;
/* 自定义滚动条样式 */
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-track {
background: rgb(51 65 85 / 30%);
border-radius: 3px;
}
&::-webkit-scrollbar-thumb {
background: rgb(59 130 246 / 60%);
border-radius: 3px;
}
&::-webkit-scrollbar-thumb:hover {
background: rgb(59 130 246 / 80%);
}
}
.table-row {
display: flex;
width: 100%;
background: rgb(51 65 85 / 30%);
border: 1px solid #1e40af;
border-radius: 0.37vh;
margin-bottom: 4px;
transition: all 0.2s ease;
&:hover {
background: rgb(51 65 85 / 50%);
transform: translateX(2px);
}
.table-cell {
flex: 1;
padding: 0.5vh 0.4vw;
font-size: 0.75rem;
color: #ffffff;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
border-right: 1px solid rgba(255, 255, 255, 0.1);
&:last-child {
border-right: none;
}
}
}
.list-item {
display: inline-flex;
padding: 0.5vh 0.4vw;
font-size: 0.75rem;
background: rgb(51 65 85 / 30%);
border: 1px solid #1e40af;
border-radius: 0.37vh;
align-items: center;
justify-content: center;
transition: all 0.2s ease; // 添加过渡效果
&:hover {
background: rgb(51 65 85 / 50%);
transform: translateX(2px); // 悬停时轻微右移
}
.alert-text.error {
color: #f00;
}
.alert-text.warn {
color: #ff0;
}
}
}
}
</style>