console
document.addEventListener('DOMContentLoaded', function() {
const map = L.map('map', {
center: [35.86, 104.195],
zoom: 5,
minZoom: 3,
maxZoom: 18,
zoomControl: false
});
const normalMap = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
const satelliteMap = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
attribution: 'Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'
});
const resourceMap = L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', {
attribution: 'Map data: © OpenStreetMap contributors, SRTM | Map style: © OpenTopoMap (CC-BY-SA)'
});
document.getElementById('normalMap').addEventListener('change', function() {
if(this.checked) {
map.removeLayer(satelliteMap);
map.removeLayer(resourceMap);
map.addLayer(normalMap);
}
});
document.getElementById('satelliteMap').addEventListener('change', function() {
if(this.checked) {
map.removeLayer(normalMap);
map.removeLayer(resourceMap);
map.addLayer(satelliteMap);
}
});
document.getElementById('resourceMap').addEventListener('change', function() {
if(this.checked) {
map.removeLayer(normalMap);
map.removeLayer(satelliteMap);
map.addLayer(resourceMap);
}
});
document.querySelector('.tool-btn[title="放大"]').addEventListener('click', function() {
map.zoomIn();
});
document.querySelector('.tool-btn[title="缩小"]').addEventListener('click', function() {
map.zoomOut();
});
document.querySelector('.tool-btn[title="重置视图"]').addEventListener('click', function() {
map.setView([35.86, 104.195], 5);
});
let measureControl = null;
let isMeasuring = false;
document.querySelector('.tool-btn[title="测量距离"]').addEventListener('click', function() {
if (isMeasuring) {
if (measureControl) {
map.removeControl(measureControl);
measureControl = null;
}
isMeasuring = false;
this.classList.remove('active');
} else {
measureControl = L.control.measure({
primaryLengthUnit: 'kilometers',
secondaryLengthUnit: 'meters',
primaryAreaUnit: 'sqkilometers',
secondaryAreaUnit: 'hectares',
position: 'bottomleft',
activeColor: '#1a73e8',
completedColor: '#34a853'
}).addTo(map);
isMeasuring = true;
this.classList.add('active');
}
});
const originalModeBtn = document.querySelector('.tool-btn[title="原始模式"]');
const clusterModeBtn = document.querySelector('.tool-btn[title="聚合模式"]');
let isClusterMode = false;
let markers = [];
let markersCluster = null;
originalModeBtn.addEventListener('click', function() {
if (isClusterMode) {
switchToOriginalMode();
originalModeBtn.classList.add('active');
clusterModeBtn.classList.remove('active');
}
});
clusterModeBtn.addEventListener('click', function() {
if (!isClusterMode) {
switchToClusterMode();
clusterModeBtn.classList.add('active');
originalModeBtn.classList.remove('active');
}
});
function switchToOriginalMode() {
if (markersCluster) {
map.removeLayer(markersCluster);
}
markers.forEach(marker => marker.addTo(map));
isClusterMode = false;
}
function switchToClusterMode() {
markers.forEach(marker => map.removeLayer(marker));
if (!markersCluster) {
markersCluster = L.markerClusterGroup({
iconCreateFunction: function(cluster) {
const count = cluster.getChildCount();
let size = 'small';
if (count > 50) size = 'large';
else if (count > 20) size = 'medium';
return L.divIcon({
html: `<div class="cluster-icon ${size}"><span>${count}</span></div>`,
className: 'custom-cluster-icon',
iconSize: L.point(40, 40)
});
}
});
markersCluster.addLayers(markers);
}
markersCluster.addTo(map);
isClusterMode = true;
}
const windFarmData = [
{
id: 2355,
name: '青海海南共和200兆瓦风电场',
lat: 36.693667,
lng: 99.3626015,
runCompany: '申能新能源(青海)有限公司',
manuCompany: '远景能源',
powerModel: 'EN-121/2.2',
totalMW: 200,
year: 2018,
singleMW: 2.2,
number: 91,
province: '青海',
city: '海南州'
},
{
id: 2359,
name: '风铎风电场',
lat: 37.256226,
lng: 95.3622,
runCompany: '中国三峡新能源(集团)股份有限公司青海分公司',
manuCompany: '新疆金风科技股份有限公司',
powerModel: 'GW121/2000',
totalMW: 200,
year: 2019,
singleMW: 2,
number: 100,
province: '青海',
city: '海西州'
},
{
id: 2361,
name: '共和县柴达木能源开发有限公司共和10万千瓦风电项目',
lat: 36.28561,
lng: 99.820061,
runCompany: '共和县柴达木能源开发有限公司',
manuCompany: '无',
powerModel: '无',
totalMW: 100,
year: 2020,
singleMW: 2,
number: 50,
province: '青海',
city: '海南州'
},
{
id: 2362,
name: '内蒙古乌兰察布风电场',
lat: 41.5768,
lng: 113.1322,
runCompany: '华电风能有限公司',
manuCompany: '远景能源',
powerModel: 'EN-141/2.5',
totalMW: 300,
year: 2017,
singleMW: 2.5,
number: 120,
province: '内蒙古',
city: '乌兰察布'
},
{
id: 2363,
name: '甘肃酒泉风电基地',
lat: 39.7321,
lng: 98.4927,
runCompany: '国家电力投资集团',
manuCompany: '金风科技',
powerModel: 'GW140/2.5',
totalMW: 500,
year: 2016,
singleMW: 2.5,
number: 200,
province: '甘肃',
city: '酒泉'
}
];
const windTurbineData = [];
windFarmData.forEach(farm => {
for (let i = 0; i < farm.number; i++) {
const randomLat = farm.lat + (Math.random() - 0.5) * 0.1;
const randomLng = farm.lng + (Math.random() - 0.5) * 0.1;
windTurbineData.push({
id: `T-${farm.id}-${i.toString().padStart(3, '0')}`,
farmId: farm.id,
farmName: farm.name,
lat: randomLat,
lng: randomLng,
powerModel: farm.powerModel,
singleMW: farm.singleMW,
installDate: `${farm.year}-${Math.floor(Math.random() * 12) + 1}-${Math.floor(Math.random() * 28) + 1}`,
status: Math.random() > 0.9 ? 'warning' : (Math.random() > 0.95 ? 'error' : 'normal'),
province: farm.province,
city: farm.city
});
}
});
function createFarmPopup(farm) {
const popupContent = document.createElement('div');
popupContent.className = 'popup-template';
popupContent.innerHTML = `
<div class="popup-header">
<h5>${farm.name}</h5>
<span class="popup-id">ID: ${farm.id}</span>
</div>
<div class="popup-body">
<div class="popup-info">
<div class="info-item">
<span class="info-label">位置:</span>
<span class="info-value">${farm.province}省${farm.city}</span>
</div>
<div class="info-item">
<span class="info-label">运营公司:</span>
<span class="info-value">${farm.runCompany}</span>
</div>
<div class="info-item">
<span class="info-label">制造商:</span>
<span class="info-value">${farm.manuCompany}</span>
</div>
<div class="info-item">
<span class="info-label">机组型号:</span>
<span class="info-value">${farm.powerModel}</span>
</div>
<div class="info-item">
<span class="info-label">装机容量:</span>
<span class="info-value">${farm.totalMW} MW</span>
</div>
<div class="info-item">
<span class="info-label">单机容量:</span>
<span class="info-value">${farm.singleMW} MW</span>
</div>
<div class="info-item">
<span class="info-label">机组数量:</span>
<span class="info-value">${farm.number}</span>
</div>
<div class="info-item">
<span class="info-label">建设年份:</span>
<span class="info-value">${farm.year}</span>
</div>
</div>
</div>
<div class="popup-footer">
<button class="btn btn-sm btn-outline-primary">查看详情</button>
</div>
`;
return popupContent;
}
function createTurbinePopup(turbine) {
const popupContent = document.createElement('div');
popupContent.className = 'popup-template';
popupContent.innerHTML = `
<div class="popup-header">
<h5>风电机组 #${turbine.id}</h5>
</div>
<div class="popup-body">
<div class="popup-info">
<div class="info-item">
<span class="info-label">所属风电场:</span>
<span class="info-value">${turbine.farmName}</span>
</div>
<div class="info-item">
<span class="info-label">位置:</span>
<span class="info-value">${turbine.lat.toFixed(6)}, ${turbine.lng.toFixed(6)}</span>
</div>
<div class="info-item">
<span class="info-label">机组型号:</span>
<span class="info-value">${turbine.powerModel}</span>
</div>
<div class="info-item">
<span class="info-label">单机容量:</span>
<span class="info-value">${turbine.singleMW} MW</span>
</div>
<div class="info-item">
<span class="info-label">运行状态:</span>
<span class="info-value status-${turbine.status}">${
turbine.status === 'normal' ? '正常运行' :
(turbine.status === 'warning' ? '需要维护' : '故障停机')
}</span>
</div>
<div class="info-item">
<span class="info-label">安装时间:</span>
<span class="info-value">${turbine.installDate}</span>
</div>
</div>
</div>
<div class="popup-footer">
<button class="btn btn-sm btn-outline-primary">查看详情</button>
</div>
`;
return popupContent;
}
const farmLayers = [];
windFarmData.forEach(farm => {
const farmCircle = L.circle([farm.lat, farm.lng], {
radius: 5000,
color: '#1a73e8',
fillColor: '#1a73e8',
fillOpacity: 0.2,
weight: 2
}).addTo(map);
farmCircle.bindPopup(createFarmPopup(farm));
farmLayers.push(farmCircle);
});
windTurbineData.forEach(turbine => {
const turbineIcon = L.divIcon({
html: `<div class="turbine-icon status-${turbine.status}"><i class="fas fa-fan"></i></div>`,
className: 'custom-turbine-icon',
iconSize: [24, 24]
});
const marker = L.marker([turbine.lat, turbine.lng], {
icon: turbineIcon
}).addTo(map);
marker.bindPopup(createTurbinePopup(turbine));
markers.push(marker);
});
document.getElementById('windTurbines').addEventListener('change', function() {
if(this.checked) {
markers.forEach(marker => {
if (!isClusterMode) {
marker.addTo(map);
} else if (markersCluster) {
markersCluster.addTo(map);
}
});
} else {
if (!isClusterMode) {
markers.forEach(marker => map.removeLayer(marker));
} else if (markersCluster) {
map.removeLayer(markersCluster);
}
}
});
document.getElementById('windFarms').addEventListener('change', function() {
if(this.checked) {
farmLayers.forEach(layer => layer.addTo(map));
} else {
farmLayers.forEach(layer => map.removeLayer(layer));
}
});
const yearRange = document.getElementById('yearRange');
const selectedYear = document.getElementById('selectedYear');
yearRange.addEventListener('input', function() {
selectedYear.textContent = this.value;
const year = parseInt(this.value);
windFarmData.forEach((farm, index) => {
if (farm.year <= year) {
if (!map.hasLayer(farmLayers[index])) {
farmLayers[index].addTo(map);
}
} else {
if (map.hasLayer(farmLayers[index])) {
map.removeLayer(farmLayers[index]);
}
}
});
if (isClusterMode && markersCluster) {
map.removeLayer(markersCluster);
}
markers.forEach((marker, index) => {
const turbine = windTurbineData[index];
const turbineYear = parseInt(turbine.installDate.split('-')[0]);
if (!isClusterMode) {
if (turbineYear <= year) {
if (!map.hasLayer(marker)) {
marker.addTo(map);
}
} else {
if (map.hasLayer(marker)) {
map.removeLayer(marker);
}
}
}
});
if (isClusterMode) {
const filteredMarkers = markers.filter((marker, index) => {
const turbine = windTurbineData[index];
const turbineYear = parseInt(turbine.installDate.split('-')[0]);
return turbineYear <= year;
});
markersCluster = L.markerClusterGroup({
iconCreateFunction: function(cluster) {
const count = cluster.getChildCount();
let size = 'small';
if (count > 50) size = 'large';
else if (count > 20) size = 'medium';
return L.divIcon({
html: `<div class="cluster-icon ${size}"><span>${count}</span></div>`,
className: 'custom-cluster-icon',
iconSize: L.point(40, 40)
});
}
});
markersCluster.addLayers(filteredMarkers);
markersCluster.addTo(map);
}
});
initCharts();
function initCharts() {
const provinceMapChart = echarts.init(document.getElementById('provinceMap'));
echarts.registerMap('china', chinaJson);
const provinceData = {};
windFarmData.forEach(farm => {
if (!provinceData[farm.province]) {
provinceData[farm.province] = 0;
}
provinceData[farm.province] += farm.totalMW;
});
const mapData = Object.keys(provinceData).map(province => {
return {
name: province,
value: provinceData[province]
};
});
const provinceMapOption = {
tooltip: {
trigger: 'item',
formatter: '{b}<br/>装机容量: {c} MW'
},
visualMap: {
min: 0,
max: Math.max(...mapData.map(item => item.value)),
text: ['高', '低'],
realtime: false,
calculable: true,
inRange: {
color: ['#313695', '#4575b4', '#74add1', '#abd9e9', '#e0f3f8', '#ffffbf', '#fee090', '#fdae61', '#f46d43', '#d73027', '#a50026']
},
textStyle: {
color: '#fff'
}
},
series: [
{
name: '装机容量',
type: 'map',
map: 'china',
roam: true,
emphasis: {
label: {
show: true
}
},
data: mapData
}
]
};
provinceMapChart.setOption(provinceMapOption);
const timelineChart = echarts.init(document.getElementById('timelineChart'));
const yearData = {};
for (let year = 2010; year <= 2023; year++) {
yearData[year] = 0;
}
windFarmData.forEach(farm => {
for (let year = farm.year; year <= 2023; year++) {
yearData[year] += farm.totalMW;
}
});
const timelineOption = {
tooltip: {
trigger: 'axis',
formatter: '{b}年<br/>累计装机容量: {c} MW'
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: Object.keys(yearData),
axisLabel: {
color: '#ccc'
},
axisLine: {
lineStyle: {
color: '#2d2d2d'
}
}
},
yAxis: {
type: 'value',
name: '装机容量 (MW)',
nameTextStyle: {
color: '#ccc'
},
axisLabel: {
color: '#ccc'
},
axisLine: {
lineStyle: {
color: '#2d2d2d'
}
},
splitLine: {
lineStyle: {
color: '#2d2d2d'
}
}
},
series: [
{
name: '累计装机容量',
type: 'line',
data: Object.values(yearData),
smooth: true,
lineStyle: {
width: 3,
color: '#1a73e8'
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: 'rgba(26, 115, 232, 0.5)'
},
{
offset: 1,
color: 'rgba(26, 115, 232, 0.1)'
}
])
},
symbol: 'circle',
symbolSize: 8,
itemStyle: {
color: '#1a73e8',
borderColor: '#fff',
borderWidth: 2
}
}
]
};
timelineChart.setOption(timelineOption);
let timelineIndex = 0;
let timelineInterval = null;
document.getElementById('playTimeline').addEventListener('click', function() {
const playBtn = this;
const icon = playBtn.querySelector('i');
if (timelineInterval) {
clearInterval(timelineInterval);
timelineInterval = null;
icon.className = 'fas fa-play';
playBtn.innerHTML = '<i class="fas fa-play"></i> 播放';
return;
}
timelineIndex = 0;
icon.className = 'fas fa-pause';
playBtn.innerHTML = '<i class="fas fa-pause"></i> 暂停';
const years = Object.keys(yearData);
timelineInterval = setInterval(() => {
if (timelineIndex >= years.length) {
clearInterval(timelineInterval);
timelineInterval = null;
icon.className = 'fas fa-play';
playBtn.innerHTML = '<i class="fas fa-play"></i> 播放';
return;
}
const year = years[timelineIndex];
yearRange.value = year;
selectedYear.textContent = year;
yearRange.dispatchEvent(new Event('input'));
timelineChart.dispatchAction({
type: 'highlight',
seriesIndex: 0,
dataIndex: timelineIndex
});
if (timelineIndex > 0) {
timelineChart.dispatchAction({
type: 'downplay',
seriesIndex: 0,
dataIndex: timelineIndex - 1
});
}
timelineIndex++;
}, 1000);
});
const rankingChart = echarts.init(document.getElementById('rankingChart'));
updateRankingChart('province');
document.getElementById('rankingType').addEventListener('change', function() {
updateRankingChart(this.value);
});
function updateRankingChart(type) {
let data = [];
if (type === 'province') {
const provinceData = {};
windFarmData.forEach(farm => {
if (!provinceData[farm.province]) {
provinceData[farm.province] = 0;
}
provinceData[farm.province] += farm.totalMW;
});
data = Object.entries(provinceData).map(([name, value]) => ({ name, value }));
} else if (type === 'city') {
const cityData = {};
windFarmData.forEach(farm => {
if (!cityData[farm.city]) {
cityData[farm.city] = 0;
}
cityData[farm.city] += farm.totalMW;
});
data = Object.entries(cityData).map(([name, value]) => ({ name, value }));
} else if (type === 'company') {
const companyData = {};
windFarmData.forEach(farm => {
if (!companyData[farm.runCompany]) {
companyData[farm.runCompany] = 0;
}
companyData[farm.runCompany] += farm.totalMW;
});
data = Object.entries(companyData).map(([name, value]) => ({ name, value }));
}
data.sort((a, b) => b.value - a.value);
data = data.slice(0, 10);
const rankingOption = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
formatter: function(params) {
return `${params[0].name}<br/>装机容量: ${params[0].value} MW`;
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'value',
name: '装机容量 (MW)',
nameTextStyle: {
color: '#ccc'
},
axisLabel: {
color: '#ccc'
},
axisLine: {
lineStyle: {
color: '#2d2d2d'
}
},
splitLine: {
lineStyle: {
color: '#2d2d2d'
}
}
},
yAxis: {
type: 'category',
data: data.map(item => item.name),
axisLabel: {
color: '#ccc'
},
axisLine: {
lineStyle: {
color: '#2d2d2d'
}
}
},
series: [
{
name: '装机容量',
type: 'bar',
data: data.map(item => item.value),
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{
offset: 0,
color: '#1a73e8'
},
{
offset: 1,
color: '#34a853'
}
])
}
}
]
};
rankingChart.setOption(rankingOption);
}
window.addEventListener('resize', function() {
provinceMapChart.resize();
timelineChart.resize();
rankingChart.resize();
});
}
});
const style = document.createElement('style');
style.textContent = `
.turbine-icon {
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background-color: rgba(26, 115, 232, 0.2);
color: #1a73e8;
transition: all 0.3s ease;
}
.turbine-icon.status-warning {
background-color: rgba(251, 188, 5, 0.2);
color: #fbbc05;
}
.turbine-icon.status-error {
background-color: rgba(234, 67, 53, 0.2);
color: #ea4335;
}
.turbine-icon i {
font-size: 14px;
animation: spin 10s linear infinite;
}
.turbine-icon.status-error i {
animation: none;
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.cluster-icon {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: radial-gradient(circle, rgba(26, 115, 232, 0.8) 0%, rgba(26, 115, 232, 0.4) 100%);
color: #fff;
font-weight: bold;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
}
.cluster-icon.small {
font-size: 12px;
}
.cluster-icon.medium {
font-size: 14px;
}
.cluster-icon.large {
font-size: 16px;
}
`;
document.head.appendChild(style);
const chinaJson = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"id": "710000",
"properties": {
"id": "710000",
"cp": [121.509062, 25.044332],
"name": "台湾",
"childNum": 6
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
[
[
[121.951244, 24.94193],
[121.526386, 25.01459],
[121.493967, 25.127306],
[121.814274, 24.860509],
[121.951244, 24.94193]
]
]
]
}
},
{
"type": "Feature",
"id": "130000",
"properties": {
"id": "130000",
"cp": [114.502461, 38.045474],
"name": "河北",
"childNum": 3
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
[
[
[114.531611, 37.998733],
[114.70563, 38.485259],
[114.8545, 38.188021],
[114.531611, 37.998733]
]
]
]
}
},
{
"type": "Feature",
"id": "140000",
"properties": {
"id": "140000",
"cp": [112.549248, 37.857014],
"name": "山西",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[112.549248, 37.857014],
[113.549248, 38.857014],
[111.549248, 38.857014],
[112.549248, 37.857014]
]
]
}
},
{
"type": "Feature",
"id": "150000",
"properties": {
"id": "150000",
"cp": [111.670801, 40.818311],
"name": "内蒙古",
"childNum": 2
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
[
[
[111.670801, 40.818311],
[112.670801, 41.818311],
[110.670801, 41.818311],
[111.670801, 40.818311]
]
]
]
}
},
{
"type": "Feature",
"id": "210000",
"properties": {
"id": "210000",
"cp": [123.429096, 41.796767],
"name": "辽宁",
"childNum": 2
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
[
[
[123.429096, 41.796767],
[124.429096, 42.796767],
[122.429096, 42.796767],
[123.429096, 41.796767]
]
]
]
}
},
{
"type": "Feature",
"id": "220000",
"properties": {
"id": "220000",
"cp": [125.3245, 43.886841],
"name": "吉林",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[125.3245, 43.886841],
[126.3245, 44.886841],
[124.3245, 44.886841],
[125.3245, 43.886841]
]
]
}
},
{
"type": "Feature",
"id": "230000",
"properties": {
"id": "230000",
"cp": [128.642464, 46.756967],
"name": "黑龙江",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[128.642464, 46.756967],
[129.642464, 47.756967],
[127.642464, 47.756967],
[128.642464, 46.756967]
]
]
}
},
{
"type": "Feature",
"id": "320000",
"properties": {
"id": "320000",
"cp": [119.767413, 33.041544],
"name": "江苏",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[119.767413, 33.041544],
[120.767413, 34.041544],
[118.767413, 34.041544],
[119.767413, 33.041544]
]
]
}
},
{
"type": "Feature",
"id": "330000",
"properties": {
"id": "330000",
"cp": [120.153576, 29.287459],
"name": "浙江",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[120.153576, 29.287459],
[121.153576, 30.287459],
[119.153576, 30.287459],
[120.153576, 29.287459]
]
]
}
},
{
"type": "Feature",
"id": "340000",
"properties": {
"id": "340000",
"cp": [117.283042, 31.86119],
"name": "安徽",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[117.283042, 31.86119],
[118.283042, 32.86119],
[116.283042, 32.86119],
[117.283042, 31.86119]
]
]
}
},
{
"type": "Feature",
"id": "350000",
"properties": {
"id": "350000",
"cp": [118.306239, 26.075302],
"name": "福建",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[118.306239, 26.075302],
[119.306239, 27.075302],
[117.306239, 27.075302],
[118.306239, 26.075302]
]
]
}
},
{
"type": "Feature",
"id": "360000",
"properties": {
"id": "360000",
"cp": [115.592151, 27.676493],
"name": "江西",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[115.592151, 27.676493],
[116.592151, 28.676493],
[114.592151, 28.676493],
[115.592151, 27.676493]
]
]
}
},
{
"type": "Feature",
"id": "370000",
"properties": {
"id": "370000",
"cp": [118.000923, 36.275807],
"name": "山东",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[118.000923, 36.275807],
[119.000923, 37.275807],
[117.000923, 37.275807],
[118.000923, 36.275807]
]
]
}
},
{
"type": "Feature",
"id": "410000",
"properties": {
"id": "410000",
"cp": [113.665412, 33.757975],
"name": "河南",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[113.665412, 33.757975],
[114.665412, 34.757975],
[112.665412, 34.757975],
[113.665412, 33.757975]
]
]
}
},
{
"type": "Feature",
"id": "420000",
"properties": {
"id": "420000",
"cp": [112.298572, 30.973818],
"name": "湖北",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[112.298572, 30.973818],
[113.298572, 31.973818],
[111.298572, 31.973818],
[112.298572, 30.973818]
]
]
}
},
{
"type": "Feature",
"id": "430000",
"properties": {
"id": "430000",
"cp": [111.782279, 28.09409],
"name": "湖南",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[111.782279, 28.09409],
[112.782279, 29.09409],
[110.782279, 29.09409],
[111.782279, 28.09409]
]
]
}
},
{
"type": "Feature",
"id": "440000",
"properties": {
"id": "440000",
"cp": [113.280637, 23.125178],
"name": "广东",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[113.280637, 23.125178],
[114.280637, 24.125178],
[112.280637, 24.125178],
[113.280637, 23.125178]
]
]
}
},
{
"type": "Feature",
"id": "450000",
"properties": {
"id": "450000",
"cp": [108.320004, 22.82402],
"name": "广西",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[108.320004, 22.82402],
[109.320004, 23.82402],
[107.320004, 23.82402],
[108.320004, 22.82402]
]
]
}
},
{
"type": "Feature",
"id": "460000",
"properties": {
"id": "460000",
"cp": [109.83119, 19.031971],
"name": "海南",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[109.83119, 19.031971],
[110.83119, 20.031971],
[108.83119, 20.031971],
[109.83119, 19.031971]
]
]
}
},
{
"type": "Feature",
"id": "500000",
"properties": {
"id": "500000",
"cp": [107.7539, 29.8215],
"name": "重庆",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[107.7539, 29.8215],
[108.7539, 30.8215],
[106.7539, 30.8215],
[107.7539, 29.8215]
]
]
}
},
{
"type": "Feature",
"id": "510000",
"properties": {
"id": "510000",
"cp": [104.065735, 30.659462],
"name": "四川",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[104.065735, 30.659462],
[105.065735, 31.659462],
[103.065735, 31.659462],
[104.065735, 30.659462]
]
]
}
},
{
"type": "Feature",
"id": "520000",
"properties": {
"id": "520000",
"cp": [106.713478, 26.578343],
"name": "贵州",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[106.713478, 26.578343],
[107.713478, 27.578343],
[105.713478, 27.578343],
[106.713478, 26.578343]
]
]
}
},
{
"type": "Feature",
"id": "530000",
"properties": {
"id": "530000",
"cp": [102.712251, 25.040609],
"name": "云南",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[102.712251, 25.040609],
[103.712251, 26.040609],
[101.712251, 26.040609],
[102.712251, 25.040609]
]
]
}
},
{
"type": "Feature",
"id": "540000",
"properties": {
"id": "540000",
"cp": [91.132212, 29.660361],
"name": "西藏",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[91.132212, 29.660361],
[92.132212, 30.660361],
[90.132212, 30.660361],
[91.132212, 29.660361]
]
]
}
},
{
"type": "Feature",
"id": "610000",
"properties": {
"id": "610000",
"cp": [108.948024, 34.263161],
"name": "陕西",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[108.948024, 34.263161],
[109.948024, 35.263161],
[107.948024, 35.263161],
[108.948024, 34.263161]
]
]
}
},
{
"type": "Feature",
"id": "620000",
"properties": {
"id": "620000",
"cp": [103.823557, 36.058039],
"name": "甘肃",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[103.823557, 36.058039],
[104.823557, 37.058039],
[102.823557, 37.058039],
[103.823557, 36.058039]
]
]
}
},
{
"type": "Feature",
"id": "630000",
"properties": {
"id": "630000",
"cp": [96.778916, 35.623178],
"name": "青海",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[96.778916, 35.623178],
[97.778916, 36.623178],
[95.778916, 36.623178],
[96.778916, 35.623178]
]
]
}
},
{
"type": "Feature",
"id": "640000",
"properties": {
"id": "640000",
"cp": [106.278179, 37.26637],
"name": "宁夏",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[106.278179, 37.26637],
[107.278179, 38.26637],
[105.278179, 38.26637],
[106.278179, 37.26637]
]
]
}
},
{
"type": "Feature",
"id": "650000",
"properties": {
"id": "650000",
"cp": [85.617733, 40.792818],
"name": "新疆",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[85.617733, 40.792818],
[86.617733, 41.792818],
[84.617733, 41.792818],
[85.617733, 40.792818]
]
]
}
},
{
"type": "Feature",
"id": "110000",
"properties": {
"id": "110000",
"cp": [116.405285, 39.904989],
"name": "北京",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[116.405285, 39.904989],
[116.805285, 40.304989],
[116.005285, 40.304989],
[116.405285, 39.904989]
]
]
}
},
{
"type": "Feature",
"id": "120000",
"properties": {
"id": "120000",
"cp": [117.190182, 39.125596],
"name": "天津",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[117.190182, 39.125596],
[117.590182, 39.525596],
[116.790182, 39.525596],
[117.190182, 39.125596]
]
]
}
},
{
"type": "Feature",
"id": "310000",
"properties": {
"id": "310000",
"cp": [121.472644, 31.231706],
"name": "上海",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[121.472644, 31.231706],
[121.872644, 31.631706],
[121.072644, 31.631706],
[121.472644, 31.231706]
]
]
}
}
]
};
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>风电地图 - 华电风资源一期风电场</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet@1.9.3/dist/leaflet.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.4.0/css/all.min.css">
<link rel="stylesheet" href="css/windmap.css">
</head>
<body>
<div class="container-fluid p-0">
<nav class="navbar navbar-expand-lg navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="#">
<i class="fas fa-wind me-2"></i>华电风资源一期风电场
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link active" href="#">风电地图</a>
</li>
<li class="nav-item">
<a class="nav-link" href="About.html">项目简介</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="main-content">
<div class="map-container">
<div id="map"></div>
<div class="map-tools">
<div class="tool-group">
<button class="tool-btn" title="放大"><i class="fas fa-plus"></i></button>
<button class="tool-btn" title="缩小"><i class="fas fa-minus"></i></button>
</div>
<div class="tool-group">
<button class="tool-btn" title="测量距离"><i class="fas fa-ruler"></i></button>
<button class="tool-btn" title="重置视图"><i class="fas fa-home"></i></button>
</div>
<div class="tool-group">
<button class="tool-btn active" title="原始模式"><i class="fas fa-map-marker-alt"></i></button>
<button class="tool-btn" title="聚合模式"><i class="fas fa-object-group"></i></button>
</div>
</div>
<div class="map-layers">
<div class="layer-title">
<i class="fas fa-layer-group me-2"></i>图层控制
</div>
<div class="layer-group">
<div class="layer-subtitle">底图选择</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="baseMap" id="normalMap" checked>
<label class="form-check-label" for="normalMap">普通地图</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="baseMap" id="satelliteMap">
<label class="form-check-label" for="satelliteMap">卫星地图</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="baseMap" id="resourceMap">
<label class="form-check-label" for="resourceMap">风电资源地图</label>
</div>
</div>
<div class="layer-group">
<div class="layer-subtitle">数据图层</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="windTurbines" checked>
<label class="form-check-label" for="windTurbines">风电机组</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="windFarms" checked>
<label class="form-check-label" for="windFarms">风电场</label>
</div>
</div>
</div>
<div class="map-filter">
<div class="filter-title">
<i class="fas fa-filter me-2"></i>筛选条件
</div>
<div class="filter-group">
<label for="provinceSelect">省份</label>
<select class="form-select form-select-sm" id="provinceSelect">
<option value="">全部</option>
<option value="内蒙古">内蒙古</option>
<option value="青海">青海</option>
<option value="甘肃">甘肃</option>
<option value="新疆">新疆</option>
<option value="河北">河北</option>
</select>
</div>
<div class="filter-group">
<label for="citySelect">城市</label>
<select class="form-select form-select-sm" id="citySelect">
<option value="">全部</option>
<option value="乌兰察布">乌兰察布</option>
<option value="海南">海南</option>
<option value="酒泉">酒泉</option>
</select>
</div>
<div class="filter-group">
<label for="yearRange">建设年份</label>
<div class="d-flex align-items-center">
<span>2010</span>
<input type="range" class="form-range mx-2" id="yearRange" min="2010" max="2023" value="2023">
<span>2023</span>
</div>
<div class="text-center">当前: <span id="selectedYear">2023</span></div>
</div>
<div class="filter-group">
<label for="classifyBy">分类显示</label>
<select class="form-select form-select-sm" id="classifyBy">
<option value="none">不分类</option>
<option value="province">按省份</option>
<option value="city">按城市</option>
<option value="company">按运营公司</option>
<option value="manufacturer">按制造商</option>
<option value="year">按建设年份</option>
</select>
</div>
<button class="btn btn-primary btn-sm w-100 mt-3">应用筛选</button>
</div>
</div>
<div class="data-container">
<div class="data-cards">
<div class="data-card">
<div class="card-icon">
<i class="fas fa-bolt"></i>
</div>
<div class="card-content">
<div class="card-value">12,568 <span>MW</span></div>
<div class="card-title">总装机容量</div>
</div>
</div>
<div class="data-card">
<div class="card-icon">
<i class="fas fa-leaf"></i>
</div>
<div class="card-content">
<div class="card-value">1,856 <span>万吨</span></div>
<div class="card-title">减碳规模</div>
</div>
</div>
<div class="data-card">
<div class="card-icon">
<i class="fas fa-wind"></i>
</div>
<div class="card-content">
<div class="card-value">6,284</div>
<div class="card-title">机组数量</div>
</div>
</div>
<div class="data-card">
<div class="card-icon">
<i class="fas fa-industry"></i>
</div>
<div class="card-content">
<div class="card-value">358</div>
<div class="card-title">风电场数量</div>
</div>
</div>
</div>
<div class="data-panel">
<div class="panel-header">
<h5><i class="fas fa-map-marked-alt me-2"></i>省份装机分布</h5>
</div>
<div class="panel-body">
<div id="provinceMap" class="chart-container"></div>
</div>
</div>
<div class="data-panel">
<div class="panel-header">
<h5><i class="fas fa-chart-line me-2"></i>装机容量时序变化</h5>
<div class="panel-controls">
<button class="btn btn-sm btn-outline-light" id="playTimeline">
<i class="fas fa-play"></i> 播放
</button>
</div>
</div>
<div class="panel-body">
<div id="timelineChart" class="chart-container"></div>
</div>
</div>
<div class="data-panel">
<div class="panel-header">
<h5><i class="fas fa-chart-bar me-2"></i>装机容量排名</h5>
<div class="panel-controls">
<select class="form-select form-select-sm" id="rankingType">
<option value="province">按省份</option>
<option value="city">按城市</option>
<option value="company">按运营公司</option>
</select>
</div>
</div>
<div class="panel-body">
<div id="rankingChart" class="chart-container"></div>
</div>
</div>
</div>
</div>
</div>
<div class="popup-template" id="farmPopupTemplate" style="display: none;">
<div class="popup-header">
<h5>青海海南共和200兆瓦风电场</h5>
<span class="popup-id">ID: 2355</span>
</div>
<div class="popup-body">
<div class="popup-info">
<div class="info-item">
<span class="info-label">位置:</span>
<span class="info-value">青海省海南州共和县</span>
</div>
<div class="info-item">
<span class="info-label">运营公司:</span>
<span class="info-value">申能新能源(青海)有限公司</span>
</div>
<div class="info-item">
<span class="info-label">制造商:</span>
<span class="info-value">远景能源</span>
</div>
<div class="info-item">
<span class="info-label">机组型号:</span>
<span class="info-value">EN-121/2.2</span>
</div>
<div class="info-item">
<span class="info-label">装机容量:</span>
<span class="info-value">200 MW</span>
</div>
<div class="info-item">
<span class="info-label">单机容量:</span>
<span class="info-value">2.2 MW</span>
</div>
<div class="info-item">
<span class="info-label">机组数量:</span>
<span class="info-value">91</span>
</div>
<div class="info-item">
<span class="info-label">建设年份:</span>
<span class="info-value">2018</span>
</div>
</div>
</div>
<div class="popup-footer">
<button class="btn btn-sm btn-outline-primary">查看详情</button>
</div>
</div>
<div class="popup-template" id="turbinePopupTemplate" style="display: none;">
<div class="popup-header">
<h5>风电机组 #T-2355-068</h5>
</div>
<div class="popup-body">
<div class="popup-info">
<div class="info-item">
<span class="info-label">所属风电场:</span>
<span class="info-value">青海海南共和200兆瓦风电场</span>
</div>
<div class="info-item">
<span class="info-label">位置:</span>
<span class="info-value">36.693667, 99.3626015</span>
</div>
<div class="info-item">
<span class="info-label">机组型号:</span>
<span class="info-value">EN-121/2.2</span>
</div>
<div class="info-item">
<span class="info-label">单机容量:</span>
<span class="info-value">2.2 MW</span>
</div>
<div class="info-item">
<span class="info-label">运行状态:</span>
<span class="info-value status-normal">正常运行</span>
</div>
<div class="info-item">
<span class="info-label">安装时间:</span>
<span class="info-value">2018-06-15</span>
</div>
</div>
</div>
<div class="popup-footer">
<button class="btn btn-sm btn-outline-primary">查看详情</button>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/leaflet@1.9.3/dist/leaflet.js"></script>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.2/dist/echarts.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/leaflet.markercluster@1.5.3/dist/leaflet.markercluster.js"></script>
<script src="js/china.js"></script>
<script src="js/windmap.js"></script>
</body>
</html>
:root {
--primary-color: #1a73e8;
--secondary-color: #34a853;
--dark-color: #121212;
--light-color: #f8f9fa;
--gray-color: #6c757d;
--border-color: #2d2d2d;
--card-bg: rgba(30, 30, 30, 0.8);
--panel-bg: rgba(25, 25, 25, 0.9);
--hover-color: rgba(255, 255, 255, 0.1);
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #0a0a0a;
color: #fff;
margin: 0;
padding: 0;
overflow: hidden;
height: 100vh;
}
.navbar {
background-color: var(--dark-color);
border-bottom: 1px solid var(--border-color);
padding: 0.5rem 1rem;
height: 60px;
}
.navbar-brand {
font-weight: 600;
color: #fff;
display: flex;
align-items: center;
}
.navbar-brand i {
color: var(--primary-color);
margin-right: 8px;
}
.nav-link {
color: rgba(255, 255, 255, 0.8);
padding: 0.5rem 1rem;
transition: all 0.3s ease;
}
.nav-link:hover, .nav-link.active {
color: #fff;
background-color: rgba(255, 255, 255, 0.1);
border-radius: 4px;
}
.main-content {
display: flex;
height: calc(100vh - 60px);
}
.map-container {
flex: 1;
position: relative;
overflow: hidden;
}
#map {
width: 100%;
height: 100%;
background-color: #1a1a1a;
}
.map-tools {
position: absolute;
top: 20px;
left: 20px;
background-color: var(--card-bg);
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
z-index: 1000;
display: flex;
flex-direction: column;
gap: 8px;
padding: 8px;
}
.tool-group {
display: flex;
flex-direction: column;
gap: 4px;
border-bottom: 1px solid var(--border-color);
padding-bottom: 8px;
}
.tool-group:last-child {
border-bottom: none;
padding-bottom: 0;
}
.tool-btn {
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
background-color: transparent;
border: none;
color: #fff;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s ease;
}
.tool-btn:hover {
background-color: var(--hover-color);
}
.tool-btn.active {
background-color: var(--primary-color);
color: #fff;
}
.map-layers {
position: absolute;
top: 20px;
right: 20px;
background-color: var(--card-bg);
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
z-index: 1000;
width: 200px;
padding: 12px;
}
.layer-title, .filter-title {
font-weight: 600;
font-size: 14px;
margin-bottom: 10px;
color: #fff;
display: flex;
align-items: center;
}
.layer-title i, .filter-title i {
color: var(--primary-color);
}
.layer-group, .filter-group {
margin-bottom: 12px;
}
.layer-subtitle {
font-size: 12px;
color: var(--gray-color);
margin-bottom: 6px;
}
.form-check {
margin-bottom: 4px;
}
.form-check-label {
font-size: 13px;
cursor: pointer;
}
.map-filter {
position: absolute;
bottom: 20px;
left: 20px;
background-color: var(--card-bg);
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
z-index: 1000;
width: 250px;
padding: 12px;
}
.filter-group label {
font-size: 12px;
color: var(--gray-color);
margin-bottom: 4px;
display: block;
}
.form-select {
background-color: rgba(45, 45, 45, 0.8);
border: 1px solid var(--border-color);
color: #fff;
font-size: 13px;
}
.form-select:focus {
box-shadow: none;
border-color: var(--primary-color);
background-color: rgba(45, 45, 45, 0.8);
color: #fff;
}
.form-range {
height: 4px;
}
.form-range::-webkit-slider-thumb {
background: var(--primary-color);
}
.data-container {
width: 400px;
background-color: var(--dark-color);
border-left: 1px solid var(--border-color);
overflow-y: auto;
padding: 15px;
display: flex;
flex-direction: column;
gap: 15px;
}
.data-cards {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px;
}
.data-card {
background-color: var(--card-bg);
border-radius: 8px;
padding: 12px;
display: flex;
align-items: center;
transition: transform 0.2s ease;
}
.data-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}
.card-icon {
width: 40px;
height: 40px;
border-radius: 8px;
background-color: rgba(26, 115, 232, 0.2);
display: flex;
align-items: center;
justify-content: center;
margin-right: 12px;
}
.card-icon i {
font-size: 18px;
color: var(--primary-color);
}
.data-card:nth-child(2) .card-icon {
background-color: rgba(52, 168, 83, 0.2);
}
.data-card:nth-child(2) .card-icon i {
color: var(--secondary-color);
}
.data-card:nth-child(3) .card-icon {
background-color: rgba(251, 188, 5, 0.2);
}
.data-card:nth-child(3) .card-icon i {
color: #fbbc05;
}
.data-card:nth-child(4) .card-icon {
background-color: rgba(234, 67, 53, 0.2);
}
.data-card:nth-child(4) .card-icon i {
color: #ea4335;
}
.card-content {
flex: 1;
}
.card-value {
font-size: 16px;
font-weight: 600;
color: #fff;
line-height: 1.2;
}
.card-value span {
font-size: 12px;
font-weight: normal;
color: var(--gray-color);
}
.card-title {
font-size: 12px;
color: var(--gray-color);
}
.data-panel {
background-color: var(--panel-bg);
border-radius: 8px;
overflow: hidden;
}
.panel-header {
padding: 12px 15px;
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: space-between;
}
.panel-header h5 {
margin: 0;
font-size: 14px;
font-weight: 600;
display: flex;
align-items: center;
}
.panel-header h5 i {
color: var(--primary-color);
}
.panel-controls {
display: flex;
align-items: center;
}
.panel-body {
padding: 15px;
}
.chart-container {
width: 100%;
height: 200px;
}
.popup-template {
width: 300px;
background-color: var(--card-bg);
border-radius: 8px;
overflow: hidden;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
}
.popup-header {
padding: 12px 15px;
background-color: rgba(26, 115, 232, 0.1);
border-bottom: 1px solid var(--border-color);
}
.popup-header h5 {
margin: 0;
font-size: 14px;
font-weight: 600;
color: #fff;
}
.popup-id {
font-size: 11px;
color: var(--gray-color);
display: block;
margin-top: 4px;
}
.popup-body {
padding: 12px 15px;
}
.popup-info {
display: flex;
flex-direction: column;
gap: 6px;
}
.info-item {
display: flex;
font-size: 12px;
}
.info-label {
width: 80px;
color: var(--gray-color);
}
.info-value {
flex: 1;
color: #fff;
}
.status-normal {
color: #34a853;
}
.status-warning {
color: #fbbc05;
}
.status-error {
color: #ea4335;
}
.popup-footer {
padding: 10px 15px;
border-top: 1px solid var(--border-color);
display: flex;
justify-content: flex-end;
}
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: var(--dark-color);
}
::-webkit-scrollbar-thumb {
background: #444;
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: #555;
}
@media (max-width: 1200px) {
.data-container {
width: 350px;
}
}
@media (max-width: 992px) {
.main-content {
flex-direction: column;
}
.map-container {
height: 60%;
}
.data-container {
width: 100%;
height: 40%;
}
.data-cards {
grid-template-columns: repeat(4, 1fr);
}
.chart-container {
height: 150px;
}
}
@media (max-width: 768px) {
.data-cards {
grid-template-columns: repeat(2, 1fr);
}
}