This commit is contained in:
2025-10-17 10:31:13 +08:00
commit e6e86f2ce0
1043 changed files with 1031839 additions and 0 deletions

View File

@@ -0,0 +1,288 @@
<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 class="table-row" 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 class="list-item" 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 startAutoScroll = () => {
if (!props.autoScroll || !listWrapperRef.value) return
const wrapper = listWrapperRef.value
const scrollHeight = wrapper.scrollHeight
const clientHeight = wrapper.clientHeight
// 只有当内容高度超过容器高度时才启动滚动
if (scrollHeight <= clientHeight) return
isScrolling = true
let currentScrollTop = listWrapperRef.value.scrollTop
const scroll = () => {
if (!isScrolling || !wrapper) return
currentScrollTop += props.scrollSpeed
// 如果滚动到底部,重置到顶部
if (currentScrollTop >= scrollHeight - clientHeight) {
currentScrollTop = 0
wrapper.scrollTop = 0
} else {
wrapper.scrollTop = currentScrollTop
}
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()
}, 500)
}
}
// 监听数据变化,重新启动滚动
watch(() => props.listData, () => {
nextTick(() => {
if (props.autoScroll) {
stopAutoScroll()
setTimeout(() => {
startAutoScroll()
}, 1000) // 数据更新后1秒开始滚动
}
})
}, { deep: true })
onMounted(() => {
if (props.autoScroll) {
// 组件挂载后延迟启动滚动
setTimeout(() => {
startAutoScroll()
}, 2000)
}
})
onUnmounted(() => {
stopAutoScroll()
})
</script>
<style scoped lang="scss">
.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>