<template>
|
<div class="charts-container">
|
<div class="container-left">
|
<div class="filter">
|
<el-select
|
clearable
|
:teleported="false"
|
v-model="companyType"
|
filterable
|
placeholder="请选择企业类型"
|
style="flex: 1"
|
remote
|
remote-show-suffix
|
:remote-method="getList"
|
>
|
<el-option
|
v-for="item in typeList"
|
:key="item.id"
|
:label="item.name"
|
:value="item.id"
|
/>
|
</el-select>
|
<el-select
|
clearable
|
:teleported="false"
|
v-model="warningType"
|
filterable
|
placeholder="请选择风险等级"
|
style="flex: 1"
|
remote
|
remote-show-suffix
|
:remote-method="getList"
|
>
|
<el-option
|
v-for="item in warningList"
|
:key="item.id"
|
:label="item.name"
|
:value="item.id"
|
/>
|
</el-select>
|
</div>
|
<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,index) in companyData" :key="item.id">
|
<td>{{ index + 1 }}</td>
|
<td>{{ item.companyName }}</td>
|
<td>{{ item.warehouseCount }}</td>
|
<td>{{ item.warningCount }}</td>
|
</tr>
|
<tr v-for="(item,index) in loopData" :key="`loop-${item.id}`">
|
<td>{{ index + 1 }}</td>
|
<td>{{ item.companyName }}</td>
|
<td>{{ item.warehouseCount }}</td>
|
<td>{{ item.warningCount }}</td>
|
</tr>
|
</tbody>
|
</table>
|
</div>
|
</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";
|
import {getCompanyMessage} from "@/api/monitor/screenCharts";
|
|
// 表格数据
|
const companyData = ref([])
|
// 配置参数
|
const visibleRows = 8 // 显示的行数
|
const scrollSpeed = 0.5 // 每次滚动的像素数
|
const rowHeight = 40 // 行高,与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)
|
initMap()
|
|
})
|
|
onBeforeUnmount(() => {
|
if (animationFrame) {
|
cancelAnimationFrame(animationFrame)
|
}
|
})
|
|
const companyType = ref('')
|
const warningType = ref('')
|
const typeList = [
|
{
|
id: 0,
|
name: '研发类'
|
},
|
{
|
id: 1,
|
name: '生产类'
|
},
|
{
|
id: 2,
|
name: '中试类'
|
}
|
]
|
const warningList = [
|
{
|
id: 1,
|
name: '红'
|
},
|
{
|
id: 2,
|
name: '橙'
|
},
|
{
|
id: 3,
|
name: '黄'
|
},
|
{
|
id: 4,
|
name: '蓝'
|
}
|
]
|
|
const initMap=()=>{
|
var map = new BMapGL.Map("areaMap");
|
map.setMapType(BMAP_NORMAL_MAP);
|
var point = new BMapGL.Point(120.752833, 31.333439);
|
map.centerAndZoom(point, 12);
|
map.enableScrollWheelZoom(true);
|
map.setTilt(40);
|
map.setMapStyleV2({
|
styleId: 'c66f44df4e55ce8f0fa90205997df335'
|
});
|
// var bd = new BMapGL.Boundary();
|
// bd.get('苏州市工业园区', function (rs) {
|
// // console.log('外轮廓:', rs.boundaries[0])
|
// // console.log('内镂空:', rs.boundaries[1])
|
// var hole = new BMapGL.Polygon(rs.boundaries, {
|
// fillColor: 'blue',
|
// fillOpacity: 0.2
|
// });
|
// map.addOverlay(hole);
|
// });
|
var bd1 = new BMapGL.Boundary();
|
bd1.get('苏州市工业园区', function (rs) {
|
let count = rs.boundaries.length;
|
for (let i = 0; i < count; i++) {
|
let path = [];
|
let str = rs.boundaries[i].replace(' ', '');
|
let points = str.split(';');
|
for (let j = 0; j < points.length; j++) {
|
let lng = points[j].split(',')[0];
|
let lat = points[j].split(',')[1];
|
path.push(new BMapGL.Point(lng, lat));
|
}
|
let prism = new BMapGL.Prism(path, 200, {
|
topFillColor: '#5679ea',
|
topFillOpacity: 0.6,
|
sideFillColor: '#5679ea',
|
sideFillOpacity: 0.9
|
});
|
map.addOverlay(prism);
|
}
|
});
|
// var circle = new BMapGL.Circle(new BMapGL.Point(120.742833,31.333439),1000,{strokeColor:"blue", strokeWeight:2, strokeOpacity:0.5});
|
// map.addOverlay(circle);
|
var point = new BMapGL.Point(120.742833,31.333439);
|
var content = '企业一(10)';
|
var label = new BMapGL.Label(content, {
|
position: point,
|
offset: new BMapGL.Size(-20, -10)
|
})
|
map.addOverlay(label);
|
label.setStyle({
|
color: '#11FEEE',
|
fontSize: '14px',
|
border: 'none',
|
backgroundColor: '#293075'
|
})
|
label.setPosition(point)
|
}
|
const getList = async () => {
|
const res = await getCompanyMessage(companyType.value)
|
if(res.code == 200){
|
if(res.data && Array.isArray(res.data) && res.data.length>0){
|
companyData.value = res.data
|
const mapData = companyData.value.map(i=>{
|
return {
|
name: i.companyName + '(' + i.warningCount + ')',
|
value: [i.longitude,i.latitude]
|
}
|
})
|
// initChart(mapData)
|
}
|
|
}else{
|
ElMessage.warning(res.message)
|
}
|
}
|
|
// 复制前几行数据用于循环
|
const loopData = computed(() => {
|
return companyData.value.slice(0, visibleRows)
|
})
|
|
// 内容区域样式
|
const contentStyle = computed(() => {
|
return {
|
transform: `translateY(-${scrollPosition.value}px)`
|
}
|
})
|
|
// 滚动动画
|
const scrollAnimation = () => {
|
const totalHeight = companyData.value.length * rowHeight
|
const loopHeight = loopData.value.length * rowHeight
|
// 更新滚动位置
|
scrollPosition.value += scrollSpeed
|
// 当滚动到循环数据部分时,重置位置实现无缝衔接
|
if (scrollPosition.value >= totalHeight) {
|
scrollPosition.value -= totalHeight
|
}
|
animationFrame = requestAnimationFrame(scrollAnimation)
|
}
|
|
const initChart =(mapData)=>{
|
//获取echart对象
|
let dom = document.getElementById('areaMap')
|
if (dom) {
|
//初始化
|
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'
|
}
|
}
|
},
|
},
|
// 区域散点图
|
{
|
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: mapData,
|
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;
|
gap: 10px
|
}
|
|
.container-left{
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
width: 300px;
|
}
|
|
.filter{
|
width: 300px;
|
margin-top: 50px;
|
display: flex;
|
align-items: center;
|
}
|
:deep(.el-input__wrapper){
|
height: 28px;
|
box-shadow: none;
|
border: 1px solid #11FEEE;
|
background: rgba(6,24,88,.6);
|
color: #fff;
|
}
|
:deep(.el-input__inner){
|
color: #02CDE6;
|
}
|
:deep(.el-input .el-select__caret){
|
color: #02CDE6
|
}
|
:deep(.el-popper.is-light){
|
background: rgb(8,44,97);
|
.el-select-dropdown__item{
|
color: #fff;
|
&:hover{
|
background: #015fb0;
|
}
|
}
|
}
|
|
|
.table-wrapper {
|
position: relative;
|
width: 300px;
|
margin-top: 10px;
|
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: 100%;
|
}
|
:deep(.BMap_cpyCtrl) {
|
display: none !important;
|
}
|
:deep(.anchorBL) {
|
display: none !important;
|
}
|
</style>
|