| | |
| | | <template> |
| | | <div class="charts-container"> |
| | | <div id="preWarning"></div> |
| | | <!-- <div id="preWarning"></div>--> |
| | | <div class="table-wrapper"> |
| | | <table class="scrollable-table"> |
| | | <thead> |
| | | <tr> |
| | | <th>企业名称</th> |
| | | <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.name }}</td> |
| | | <td>{{ item.info }}</td> |
| | | <td :class="{'red': item.type == '红色','yellow': item.type == '黄色','blue': item.type == '蓝色'}">{{ item.type }}</td> |
| | | </tr> |
| | | <tr v-for="(item,index) in loopData" :key="`loop-${item.id}`"> |
| | | <td>{{ item.name }}</td> |
| | | <td>{{ item.info }}</td> |
| | | <td :class="{'red': item.type == '红色','yellow': item.type == '黄色','blue': item.type == '蓝色'}">{{ item.type }}</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 = [ |
| | | {name: '企业1',info: 'XXXXXXXX',type: '红色' }, |
| | | {name: '企业2',info: 'XXXXXXXX',type: '蓝色' }, |
| | | {name: '企业3',info: 'XXXXXXXX',type: '黄色' }, |
| | | {name: '企业4',info: 'XXXXXXXX',type: '蓝色' }, |
| | | {name: '企业5',info: 'XXXXXXXX',type: '红色' }, |
| | | {name: '企业6',info: 'XXXXXXXX',type: '黄色' }, |
| | | {name: '企业7',info: 'XXXXXXXX',type: '蓝色' } |
| | | ] |
| | | // 配置参数 |
| | | const visibleRows = 7 // 显示的行数 |
| | | const scrollSpeed = 0.5 // 每次滚动的像素数 |
| | | const rowHeight = 36 // 行高,与CSS一致 |
| | | const viewport = ref(null) |
| | | const scrollPosition = ref(0) |
| | | let animationFrame = null |
| | | |
| | | onMounted(()=>{ |
| | | initChart() |
| | | // getList() |
| | | // 设置视口高度 |
| | | if (viewport.value) { |
| | | viewport.value.style.height = `${visibleRows * rowHeight}px` |
| | | } |
| | | // 延迟启动滚动,确保初始渲染完成 |
| | | setTimeout(() => { |
| | | scrollAnimation() |
| | | }, 100) |
| | | }) |
| | | |
| | | const initChart =()=>{ |
| | | 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() |
| | | if(res.code == 200){ |
| | | if(res.data && Array.isArray(res.data) && res.data.length>0){ |
| | | initChart(res.data) |
| | | }else{ |
| | | initChart([]) |
| | | } |
| | | }else{ |
| | | ElMessage.warning(res.message) |
| | | } |
| | | } |
| | | |
| | | const initChart =(data)=>{ |
| | | var chartDom = document.getElementById('preWarning'); |
| | | var myChart = echarts.init(chartDom); |
| | | var option; |
| | |
| | | ], |
| | | xAxis: { |
| | | type: 'category', |
| | | data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], |
| | | data: data.map(i=>i.day) || [], |
| | | axisLabel:{ |
| | | color: '#fff' |
| | | }, |
| | |
| | | ], |
| | | series: [ |
| | | { |
| | | data: [150, 230, 224, 218, 135, 147, 260], |
| | | data: data.map(i=>i.count) || [], |
| | | type: 'line', |
| | | label:{ |
| | | show: true, |
| | |
| | | .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; |
| | | flex: 1; |
| | | } |
| | | .red{ |
| | | color: red |
| | | } |
| | | .yellow{ |
| | | color: yellow |
| | | } |
| | | .blue{ |
| | | color: #51ccff |
| | | } |
| | | th { |
| | | position: sticky; |
| | | top: 0; |
| | | z-index: 10; /* 确保表头在内容之上 */ |
| | | } |
| | | tr{ |
| | | background: rgb(6,38,87); |
| | | display: flex; |
| | | |
| | | &: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> |