<template>
|
<div class="charts-container">
|
<div class="table-wrapper">
|
<table class="scrollable-table">
|
<thead>
|
<tr>
|
<th>排名</th>
|
<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 in companyData" :key="item.id">
|
<td>{{ item.rank }}</td>
|
<td>{{ item.company }}</td>
|
<td>{{ item.warehouse }}</td>
|
<td :class="{'warning': item.warning}">{{ item.warning || '无' }}</td>
|
</tr>
|
<tr v-for="item in loopData" :key="`loop-${item.id}`">
|
<td>{{ item.rank }}</td>
|
<td>{{ item.company }}</td>
|
<td>{{ item.warehouse }}</td>
|
<td :class="{'warning': item.warning}">{{ item.warning || '无' }}</td>
|
</tr>
|
</tbody>
|
</table>
|
</div>
|
</div>
|
</div>
|
<div id="areaMap"></div>
|
</div>
|
</template>
|
<script setup>
|
import * as echarts from 'echarts';
|
import {onMounted, onBeforeUnmount, ref, computed, reactive} from "vue";
|
import SUZHOU from './map.json'
|
import {getAvoidList} from "@/api/hazardousChemicals/avoid";
|
import {ElMessage} from "element-plus";
|
// 表格数据
|
const companyData = [
|
{ id: 1, rank: 1, company: '化工企业A', warehouse: '仓库1', warning: '相忌预警' },
|
{ id: 2, rank: 2, company: '化工企业B', warehouse: '仓库2', warning: '' },
|
{ id: 3, rank: 3, company: '化工企业C', warehouse: '仓库3', warning: '超期预警' },
|
{ id: 4, rank: 4, company: '化工企业D', warehouse: '仓库4', warning: '' },
|
{ id: 5, rank: 5, company: '化工企业E', warehouse: '仓库5', warning: '' },
|
{ id: 6, rank: 6, company: '化工企业F', warehouse: '仓库6', warning: '超期预警' },
|
{ id: 7, rank: 7, company: '化工企业G', warehouse: '仓库7', warning: '' },
|
{ id: 8, rank: 8, company: '化工企业H', warehouse: '仓库8', warning: '' },
|
{ id: 9, rank: 9, company: '化工企业I', warehouse: '仓库9', warning: '' },
|
{ id: 10, rank: 10, company: '化工企业J', warehouse: '仓库10', warning: '' },
|
{ id: 11, rank: 11, company: '化工企业K', warehouse: '仓库11', warning: '' },
|
{ id: 12, rank: 12, company: '化工企业L', warehouse: '仓库12', warning: '' },
|
]
|
// const companyData = ref([])
|
// 配置参数
|
const visibleRows = 8 // 显示的行数
|
const scrollSpeed = 1 // 每次滚动的像素数
|
const rowHeight = 40 // 行高,与CSS一致
|
const viewport = ref(null)
|
const scrollPosition = ref(0)
|
let animationFrame = null
|
|
onMounted(()=>{
|
// getList()
|
initChart()
|
// 设置视口高度
|
if (viewport.value) {
|
viewport.value.style.height = `${visibleRows * rowHeight}px`
|
}
|
// 延迟启动滚动,确保初始渲染完成
|
setTimeout(() => {
|
scrollAnimation()
|
}, 100)
|
})
|
|
onBeforeUnmount(() => {
|
if (animationFrame) {
|
cancelAnimationFrame(animationFrame)
|
}
|
})
|
|
// const getList = async () => {
|
// const res = await getAvoidList({warningType: '', companyId: null})
|
// if(res.code == 200){
|
// companyData.value = res.data
|
// console.log(companyData.value,555)
|
// }else{
|
// ElMessage.warning(res.message)
|
// }
|
// }
|
|
// 复制前几行数据用于循环
|
const loopData = computed(() => {
|
return companyData.slice(0, visibleRows)
|
})
|
|
// 内容区域样式
|
const contentStyle = computed(() => {
|
return {
|
transform: `translateY(-${scrollPosition.value}px)`
|
}
|
})
|
|
// 滚动动画
|
const scrollAnimation = () => {
|
const totalHeight = companyData.length * rowHeight
|
const loopHeight = loopData.value.length * rowHeight
|
// 更新滚动位置
|
scrollPosition.value += scrollSpeed
|
// 当滚动到循环数据部分时,重置位置实现无缝衔接
|
if (scrollPosition.value >= totalHeight) {
|
scrollPosition.value -= totalHeight
|
}
|
animationFrame = requestAnimationFrame(scrollAnimation)
|
}
|
|
const initChart =()=>{
|
//获取echart对象
|
let dom = document.getElementById('areaMap')
|
if (dom) {
|
let data = [
|
{
|
name: "姑苏区",
|
value: Math.round(Math.random() * 100)
|
},
|
{
|
name: "虎丘区",
|
value: Math.round(Math.random() * 100)
|
},
|
{
|
name: "吴中区",
|
value: Math.round(Math.random() * 100)
|
},
|
]
|
//初始化
|
let myEchart = echarts.init(dom)
|
//注册地图
|
echarts.registerMap('苏州市', SUZHOU)
|
let option = {
|
geo: {
|
map: '苏州市',
|
aspectScale: 0.8,
|
layoutCenter: ['50%', '50%'], //地图位置
|
layoutSize: '75%',
|
itemStyle: {
|
normal: {
|
shadowColor: '#000',
|
shadowOffsetX: 0,
|
shadowOffsetY: 40,
|
opacity: 0.1
|
},
|
emphasis: {
|
areaColor: '#fff'
|
}
|
}
|
},
|
tooltip: {
|
trigger: 'item',
|
backgroundColor: 'rgba(166, 200, 76, 0.82)',
|
borderColor: '#FFFFCC',
|
showDelay: 0,
|
hideDelay: 0,
|
enterable: true,
|
transitionDuration: 0,
|
extraCssText: 'z-index:100',
|
formatter: function (params, ticket, callback) {
|
console.log('params', params.value);
|
//根据业务自己拓展要显示的内容
|
var res = ''
|
var name = params.name
|
var value = params.value[params.seriesIndex + 1] || params.value
|
res = "<span style='color:#fff;'>" + name + '</span><br/>数据:' + value
|
return res
|
}
|
},
|
|
series: [
|
{
|
tooltip: {
|
trigger: 'item',
|
},
|
name: '苏州市数据',
|
type: 'map',
|
map: '苏州市', // 自定义扩展图表类型
|
label: { // 文字
|
show: true,
|
color: '#fff',
|
fontSize: 10
|
},
|
itemStyle: { // 地图样式
|
shadowBlur: 10,
|
shadowColor: '#000',
|
areaColor: 'rgb(1,95,176)', //区域颜色
|
normal: {
|
areaColor: 'rgb(1,95,176)',
|
borderColor: '#02CDE6',
|
borderWidth: 1
|
},
|
emphasis: {
|
areaColor: 'rgb(3,26,65)',
|
label: {
|
color: '#fff'
|
}
|
}
|
},
|
data: data
|
},
|
// 区域散点图
|
{
|
type: 'effectScatter',
|
coordinateSystem: 'geo',
|
symbolSize: 10,
|
rippleEffect: {
|
period: 3,
|
scale: 10,
|
brushType: 'fill'
|
},
|
label: {
|
normal: {
|
show: true,
|
position: 'right',
|
formatter: '{b}',
|
color: 'yellow',
|
fontSize: 12
|
}
|
},
|
data: [
|
{ name: '斜塘街道 11', value: [120.697614, 31.288664] },
|
{ name: '桑田岛 12', value: [120.807510, 31.350300] },
|
{ name: '车坊 2', value: [120.627614, 31.338664] }
|
],
|
itemStyle: {
|
//坐标点颜色
|
normal: {
|
show: true,
|
color: 'skyblue',
|
shadowBlur: 20,
|
shadowColor: '#fff'
|
},
|
emphasis: {
|
areaColor: '#fff'
|
}
|
}
|
}
|
],
|
}
|
myEchart.setOption(option);
|
window.addEventListener('resize', function () {
|
myEchart.resize();
|
});
|
}
|
}
|
|
</script>
|
|
<style lang="postcss" scoped>
|
.charts-container{
|
width: 100%;
|
height: 100%;
|
display: flex;
|
align-items: flex-start;
|
}
|
.table-wrapper {
|
position: relative;
|
width: 300px;
|
margin-top: 30px;
|
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: 40px; /* 与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;
|
}
|
}
|
}
|
|
@keyframes blink {
|
0% { opacity: 1; }
|
50% { opacity: 0.8; }
|
100% { opacity: 1; }
|
}
|
|
#areaMap{
|
flex: 1;
|
height: 500px;
|
}
|
</style>
|