| | |
| | | <template> |
| | | <div class="charts-container"> |
| | | <div id="preWarning"></div> |
| | | <div class="table-wrapper"> |
| | | <table class="scrollable-table"> |
| | | <thead> |
| | | <tr> |
| | | <th>预警信息</th> |
| | | <th>预警时间</th> |
| | | </tr> |
| | | </thead> |
| | | </table> |
| | | <div class="scroll-viewport" ref="viewport"> |
| | | <div class="scroll-content" :style="contentStyle"> |
| | | <table class="scrollable-table"> |
| | | <tbody> |
| | | <tr v-for="(item,index) in warningData" :key="item.id"> |
| | | <td>{{ item.warningInfo }}</td> |
| | | <td>{{ item.warningTime }}</td> |
| | | </tr> |
| | | <tr v-for="(item,index) in loopData" :key="`loop-${item.id}`"> |
| | | <td>{{ item.warningInfo }}</td> |
| | | <td>{{ item.warningTime }}</td> |
| | | </tr> |
| | | </tbody> |
| | | </table> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | <script setup> |
| | | import * as echarts from 'echarts'; |
| | | import {onMounted} from "vue"; |
| | | import {computed, onBeforeUnmount, onMounted, ref} from "vue"; |
| | | import {getCompanyMessage, getDailywarningCount} from "@/api/monitor/screenCharts"; |
| | | import {ElMessage} from "element-plus"; |
| | | |
| | | const warningData = [ |
| | | {warningInfo: '超期预警',warningTime: '2025-04-24 13:18:41' }, |
| | | {warningInfo: '超期预警',warningTime: '2025-04-23 13:00:21' }, |
| | | {warningInfo: '超期预警',warningTime: '2025-04-20 10:11:34' }, |
| | | {warningInfo: '超期预警',warningTime: '2025-04-18 09:28:51' }, |
| | | {warningInfo: '超期预警',warningTime: '2025-04-16 08:18:21' }, |
| | | {warningInfo: '超期预警',warningTime: '2025-04-15 05:12:21' }, |
| | | {warningInfo: '超期预警',warningTime: '2025-04-14 04:11:41' } |
| | | ] |
| | | // 配置参数 |
| | | const visibleRows = 5 // 显示的行数 |
| | | const scrollSpeed = 0.5 // 每次滚动的像素数 |
| | | const rowHeight = 36 // 行高,与CSS一致 |
| | | const viewport = ref(null) |
| | | const scrollPosition = ref(0) |
| | | let animationFrame = null |
| | | |
| | | onMounted(()=>{ |
| | | getList() |
| | | |
| | | // 设置视口高度 |
| | | if (viewport.value) { |
| | | viewport.value.style.height = `${visibleRows * rowHeight}px` |
| | | } |
| | | // 延迟启动滚动,确保初始渲染完成 |
| | | setTimeout(() => { |
| | | scrollAnimation() |
| | | }, 100) |
| | | }) |
| | | |
| | | onBeforeUnmount(() => { |
| | | if (animationFrame) { |
| | | cancelAnimationFrame(animationFrame) |
| | | } |
| | | }) |
| | | |
| | | // 复制前几行数据用于循环 |
| | | const loopData = computed(() => { |
| | | return warningData.slice(0, visibleRows) |
| | | }) |
| | | |
| | | // 内容区域样式 |
| | | const contentStyle = computed(() => { |
| | | return { |
| | | transform: `translateY(-${scrollPosition.value}px)` |
| | | } |
| | | }) |
| | | |
| | | // 滚动动画 |
| | | const scrollAnimation = () => { |
| | | const totalHeight = warningData.length * rowHeight |
| | | const loopHeight = loopData.value.length * rowHeight |
| | | // 更新滚动位置 |
| | | scrollPosition.value += scrollSpeed |
| | | // 当滚动到循环数据部分时,重置位置实现无缝衔接 |
| | | if (scrollPosition.value >= totalHeight) { |
| | | scrollPosition.value -= totalHeight |
| | | } |
| | | animationFrame = requestAnimationFrame(scrollAnimation) |
| | | } |
| | | |
| | | const getList = async () => { |
| | | const res = await getDailywarningCount() |
| | |
| | | .charts-container{ |
| | | width: 100%; |
| | | height: 100%; |
| | | display: flex; |
| | | } |
| | | |
| | | #preWarning{ |
| | | width: 100%; |
| | | flex: 2; |
| | | height: 100%; |
| | | } |
| | | |
| | | .table-wrapper { |
| | | position: relative; |
| | | flex: 1; |
| | | height: 100%; |
| | | border: 1px solid rgba(255,255,255,.1); |
| | | border-radius: 2px; |
| | | overflow: hidden; |
| | | |
| | | .scrollable-table { |
| | | width: 100%; |
| | | border-collapse: collapse; |
| | | |
| | | th,td { |
| | | padding: 12px 15px; |
| | | color: #fff; |
| | | text-align: left; |
| | | border-bottom: 1px solid rgba(255,255,255,.1); |
| | | height: 36px; /* 与rowHeight一致 */ |
| | | box-sizing: border-box; |
| | | font-size: 12px; |
| | | font-weight: normal; |
| | | } |
| | | th { |
| | | position: sticky; |
| | | top: 0; |
| | | z-index: 10; /* 确保表头在内容之上 */ |
| | | } |
| | | tr{ |
| | | background: rgb(6,38,87); |
| | | |
| | | &:nth-of-type(2n){ |
| | | background: rgb(19,72,127); |
| | | } |
| | | } |
| | | thead tr{ |
| | | background: rgba(0,0,0,0); |
| | | } |
| | | } |
| | | .scroll-viewport { |
| | | position: relative; |
| | | overflow: hidden; |
| | | .scroll-content { |
| | | will-change: transform; /* 优化性能 */ |
| | | } |
| | | .danger { |
| | | color: #ff2f2f; |
| | | animation: blink 1s infinite; |
| | | } |
| | | .warning { |
| | | color: yellow; |
| | | animation: blink 1s infinite; |
| | | } |
| | | } |
| | | } |
| | | </style> |