You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1182 lines
40 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>海洋数据成果展示</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.4.0/echarts.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts-gl/2.0.0/echarts-gl.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Arial, sans-serif;
}
body {
background: linear-gradient(135deg, #0c1a2d 0%, #0d2b4a 100%);
color: #e0f0ff;
overflow-x: hidden;
min-height: 100vh;
}
/* 新增顶部标题栏样式 */
.topDiv {
width: 100%;
height: 8vh;
background: url('/assets/img/topBg.png') top no-repeat;
background-size: 100% 115%;
display: flex;
justify-content: center;
z-index: 990;
position: relative;
/* background-color: rgba(12, 42, 73, 0.8); */
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
.topDiv .center {
font-family: PangMenZhengDao, 'Source Han Sans CN';
font-weight: 400;
font-size: 4.1vh;
color: #ffffff;
align-self: center;
margin-top: -40px;
}
.topDiv .left {
position: absolute;
top: 3vh;
left: 1vw;
color: #ffffff;
}
.topDiv .left span:nth-child(1) {
font-weight: bold;
font-size: 0.8vw;
margin-right: 0.5vh;
}
.topDiv .left span:nth-child(2) {
font-weight: bold;
font-size: 1.2vw;
}
.topDiv .right {
position: absolute;
top: 3vh;
right: 0;
color: #ffffff;
display: flex;
align-items: center;
}
.topDiv .right img:nth-child(1) {}
.topDiv .right span {
font-weight: bold;
font-size: 1.2vw;
}
.topDiv .icon-home {
position: absolute;
top: 1vh;
right: 8vw;
width: 4vh;
cursor: pointer;
}
/* 控制面板 */
.control-panel {
width: 100%;
padding: 15px 20px;
background: rgba(12, 42, 73, 0.7);
backdrop-filter: blur(10px);
border-bottom: 1px solid rgba(64, 156, 255, 0.3);
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 15px;
}
.control-group {
display: flex;
align-items: center;
gap: 10px;
}
.control-label {
font-size: 0.9rem;
color: #a0d2ff;
}
.control-select {
background: rgba(25, 55, 95, 0.8);
border: 1px solid rgba(64, 156, 255, 0.5);
border-radius: 4px;
color: #e0f0ff;
padding: 6px 12px;
font-size: 0.9rem;
cursor: pointer;
}
.control-button {
background: linear-gradient(90deg, #1a6aa9 0%, #4fc3f7 100%);
border: none;
border-radius: 4px;
color: #ffffff;
padding: 6px 15px;
font-size: 0.9rem;
cursor: pointer;
transition: all 0.3s ease;
}
.control-button:hover {
background: linear-gradient(90deg, #4fc3f7 0%, #1a6aa9 100%);
box-shadow: 0 0 10px rgba(79, 195, 247, 0.5);
}
/* 仪表盘布局 */
.dashboard {
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-template-rows: repeat(6, 1fr);
gap: 15px;
padding: 15px;
height: calc(100vh - 8vh - 80px);
}
.dashboard-item {
background: rgba(12, 42, 73, 0.7);
backdrop-filter: blur(10px);
border: 1px solid rgba(64, 156, 255, 0.3);
border-radius: 8px;
overflow: hidden;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
transition: all 0.3s ease;
position: relative;
}
.dashboard-item:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.4);
border-color: rgba(79, 195, 247, 0.5);
}
.dashboard-item-header {
background: url(/assets/img/common/homeTitle2.png) no-repeat;
background-position: center;
background-size: 120% 120%;
height: 50px;
padding: 10px 15px;
border-bottom: 1px solid rgba(79, 195, 247, 0.3);
text-align: center;
/* height: 40px;
background: linear-gradient(to right, #1a3a5f, #2a5a8f);
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 15px;
border-bottom: 1px solid rgba(79, 195, 247, 0.3);
text-align: center; */
}
.dashboard-item-title {
font-size: 1.2rem;
font-weight: 600;
color: #ffffff;
}
.dashboard-item-controls {
display: flex;
gap: 5px;
}
.dashboard-item-control {
background: rgba(255, 255, 255, 0.1);
border: none;
border-radius: 3px;
color: #e0f2ff;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease;
}
.dashboard-item-control:hover {
background: rgba(255, 255, 255, 0.2);
}
.dashboard-item-content {
height: calc(100% - 50px);
padding: 10px;
}
/* 特定布局 */
.earth-globe {
grid-column: 1 / 9;
grid-row: 1 / 5;
}
.time-series {
grid-column: 9 / 13;
grid-row: 1 / 4;
}
.stats-cards {
grid-column: 1 / 5;
grid-row: 5 / 7;
}
.profile-chart {
grid-column: 5 / 9;
grid-row: 5 / 7;
}
.data-table {
grid-column: 9 / 13;
grid-row: 4 / 7;
}
/* 统计卡片组 */
.stats-container {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
gap: 10px;
height: 100%;
}
.stat-card {
background: rgba(25, 55, 95, 0.6);
border-radius: 8px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
/* padding: 15px; */
border: 1px solid rgba(64, 156, 255, 0.2);
transition: all 0.3s ease;
}
.stat-card:hover {
transform: scale(1.03);
border-color: rgba(79, 195, 247, 0.5);
box-shadow: 0 0 15px rgba(79, 195, 247, 0.3);
}
.stat-value {
font-size: 2rem;
font-weight: bold;
color: #4fc3f7;
margin-bottom: 5px;
text-shadow: 0 0 10px rgba(79, 195, 247, 0.5);
}
.stat-label {
font-size: 0.9rem;
color: #a0d2ff;
text-align: center;
}
/* 数据表格 */
.data-table-content {
height: 100%;
overflow-y: auto;
}
.data-table-row {
display: flex;
justify-content: space-between;
padding: 10px 5px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.data-table-row:last-child {
border-bottom: none;
}
.data-table-cell {
flex: 1;
text-align: center;
font-size: 0.9rem;
}
.data-table-header {
font-weight: bold;
color: #4fc3f7;
border-bottom: 1px solid rgba(79, 195, 247, 0.3);
padding-bottom: 8px;
}
/* 响应式调整 */
@media (max-width: 1200px) {
.dashboard {
grid-template-columns: repeat(6, 1fr);
grid-template-rows: repeat(8, 1fr);
}
.earth-globe {
grid-column: 1 / 7;
grid-row: 1 / 5;
}
.time-series {
grid-column: 1 / 7;
grid-row: 5 / 7;
}
.stats-cards {
grid-column: 1 / 4;
grid-row: 7 / 9;
}
.profile-chart {
grid-column: 4 / 7;
grid-row: 7 / 9;
}
.data-table {
grid-column: 1 / 7;
grid-row: 9 / 11;
}
}
/* 滚动条样式 */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: rgba(12, 42, 73, 0.3);
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: rgba(106, 149, 201, 0.5);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(106, 149, 201, 0.7);
}
/* 动画效果 */
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(79, 195, 247, 0.4);
}
70% {
box-shadow: 0 0 0 10px rgba(79, 195, 247, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(79, 195, 247, 0);
}
}
.pulse {
animation: pulse 2s infinite;
}
</style>
</head>
<body>
<!-- 顶部标题栏 -->
<div class="topDiv">
<div class="left">
<span id="current-date">2025年9月17日</span>
<span id="current-time">15:18:00</span>
</div>
<div class="center">全球海洋环境综合监测与分析平台</div>
<div class="right">
<img src="/common/images/logo_gt.png" alt="" />
</div>
</div>
<!-- 控制面板 -->
<div class="control-panel">
<div class="control-group">
<span class="control-label">数据类型:</span>
<select class="control-select" id="dataTypeSelect">
<option value="temperature">海水温度</option>
<option value="chlorophyll">叶绿素浓度</option>
<option value="salinity">盐度</option>
<option value="wave">波浪数据</option>
</select>
</div>
<div class="control-group">
<span class="control-label">时间范围:</span>
<select class="control-select" id="timeRangeSelect">
<option value="24h">过去24小时</option>
<option value="7d">过去7天</option>
<option value="30d">过去30天</option>
<option value="custom">自定义</option>
</select>
</div>
<div class="control-group">
<span class="control-label">地理区域:</span>
<select class="control-select" id="regionSelect">
<option value="global">全球</option>
<option value="pacific">太平洋</option>
<option value="atlantic">大西洋</option>
<option value="indian">印度洋</option>
<option value="south-china-sea">南海</option>
</select>
</div>
<div class="control-group">
<button class="control-button" id="playAnimation">
<i class="fas fa-play"></i> 播放动画
</button>
<button class="control-button" id="pauseAnimation">
<i class="fas fa-pause"></i> 暂停
</button>
<button class="control-button" id="resetView">
<i class="fas fa-sync"></i> 重置视图
</button>
</div>
</div>
<!-- 仪表盘 -->
<div class="dashboard">
<!-- 3D地球模型 -->
<div class="dashboard-item earth-globe">
<div class="dashboard-item-header">
<div class="dashboard-item-title">全球海洋温度分布</div>
<!-- <div class="dashboard-item-controls">
<button class="dashboard-item-control"><i class="fas fa-expand"></i></button>
</div> -->
</div>
<div class="dashboard-item-content" id="earthGlobe"></div>
</div>
<!-- 时序图 -->
<div class="dashboard-item time-series">
<div class="dashboard-item-header">
<div class="dashboard-item-title">特定区域时序变化</div>
<!-- <div class="dashboard-item-controls">
<button class="dashboard-item-control"><i class="fas fa-expand"></i></button>
</div> -->
</div>
<div class="dashboard-item-content" id="timeSeriesChart"></div>
</div>
<!-- 统计卡片组 -->
<div class="dashboard-item stats-cards">
<div class="dashboard-item-header">
<div class="dashboard-item-title">关键指标</div>
<!-- <div class="dashboard-item-controls">
<button class="dashboard-item-control"><i class="fas fa-sync"></i></button>
</div> -->
</div>
<div class="dashboard-item-content">
<div class="stats-container">
<div class="stat-card pulse">
<div class="stat-value" id="avgTemp">24.7°C</div>
<div class="stat-label">平均温度</div>
</div>
<div class="stat-card">
<div class="stat-value" id="maxWave">12.3m</div>
<div class="stat-label">最大波高</div>
</div>
<div class="stat-card">
<div class="stat-value" id="anomalyCount">8</div>
<div class="stat-label">异常区域数量</div>
</div>
<div class="stat-card">
<div class="stat-value" id="avgSalinity">35.2‰</div>
<div class="stat-label">平均盐度</div>
</div>
</div>
</div>
</div>
<!-- 剖面图 -->
<div class="dashboard-item profile-chart">
<div class="dashboard-item-header">
<div class="dashboard-item-title">温度/盐度垂直剖面</div>
<!-- <div class="dashboard-item-controls">
<button class="dashboard-item-control"><i class="fas fa-expand"></i></button>
</div> -->
</div>
<div class="dashboard-item-content" id="profileChart"></div>
</div>
<!-- 数据表格 -->
<div class="dashboard-item data-table">
<div class="dashboard-item-header">
<div class="dashboard-item-title">区域数据详情</div>
<!-- <div class="dashboard-item-controls">
<button class="dashboard-item-control"><i class="fas fa-download"></i></button>
</div> -->
</div>
<div class="dashboard-item-content">
<div class="data-table-content" id="dataTableContent">
<!-- 表格内容将通过JavaScript动态生成 -->
</div>
</div>
</div>
</div>
<script>
// 全局变量
let earthChart, timeSeriesChart, profileChart;
let animationInterval;
let currentDataType = 'temperature';
let currentTimeRange = '24h';
let currentRegion = 'global';
// 数据存储
let timeSeriesData = {
time: [],
temperature: [],
chlorophyll: [],
salinity: []
};
let profileData = {
depth: [],
temperature: [],
salinity: []
};
let tableData = [];
// 初始化函数
function init() {
// 更新当前时间
updateCurrentTime();
setInterval(updateCurrentTime, 1000);
// 初始化数据
initData();
// 初始化图表
initEarthGlobe();
initTimeSeriesChart();
initProfileChart();
initDataTable();
// 添加事件监听器
document.getElementById('dataTypeSelect').addEventListener('change', function (e) {
currentDataType = e.target.value;
updateAllCharts();
});
document.getElementById('timeRangeSelect').addEventListener('change', function (e) {
currentTimeRange = e.target.value;
updateAllCharts();
});
document.getElementById('regionSelect').addEventListener('change', function (e) {
currentRegion = e.target.value;
updateAllCharts();
});
document.getElementById('playAnimation').addEventListener('click', startAnimation);
document.getElementById('pauseAnimation').addEventListener('click', pauseAnimation);
document.getElementById('resetView').addEventListener('click', resetView);
// 启动数据更新定时器
setInterval(updateAllData, 3000);
}
// 初始化数据
function initData() {
// 初始化时序数据
const now = new Date();
for (let i = 23; i >= 0; i--) {
const time = new Date(now);
time.setHours(now.getHours() - i);
timeSeriesData.time.push(time.getHours() + ':00');
// 生成模拟数据
timeSeriesData.temperature.push(20 + Math.sin(i / 3) * 5 + Math.random() * 2);
timeSeriesData.chlorophyll.push(0.2 + Math.sin(i / 4) * 0.15 + Math.random() * 0.1);
timeSeriesData.salinity.push(34 + Math.cos(i / 5) * 1 + Math.random() * 0.5);
}
// 初始化剖面数据
for (let i = 0; i <= 1000; i += 50) {
profileData.depth.push(i);
// 生成模拟数据并保留两位小数
const temp = parseFloat(25 - (i / 1000) * 20 + Math.random() * 2).toFixed(2);
const salinity = parseFloat(34 + (i / 1000) * 1 + Math.random() * 0.5).toFixed(2);
profileData.temperature.push(parseFloat(temp)); // 转换回数字用于计算
profileData.salinity.push(parseFloat(salinity)); // 转换回数字用于计算
}
// 初始化表格数据
tableData = [
{ region: '南海', temperature: 28.5, salinity: 34.1, chlorophyll: 0.32 },
{ region: '东海', temperature: 22.3, salinity: 33.8, chlorophyll: 0.45 },
{ region: '黄海', temperature: 18.7, salinity: 32.5, chlorophyll: 0.28 },
{ region: '渤海', temperature: 16.2, salinity: 31.2, chlorophyll: 0.51 },
{ region: '西太平洋', temperature: 26.8, salinity: 35.1, chlorophyll: 0.18 }
];
}
// 更新当前时间
function updateCurrentTime() {
const now = new Date();
const dateStr = `${now.getFullYear()}年${now.getMonth() + 1}月${now.getDate()}日`;
const timeStr = now.toLocaleTimeString('zh-CN', { hour12: false });
document.getElementById('current-date').textContent = dateStr;
document.getElementById('current-time').textContent = timeStr;
}
// 初始化3D地球
function initEarthGlobe() {
const chartContainer = document.getElementById('earthGlobe');
earthChart = echarts.init(chartContainer, null, { renderer: 'canvas' });
// 配置项
const option = {
backgroundColor: 'transparent',
globe: {
baseTexture: "/assets/img/earth-topology.png",
heightTexture: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAyNCIgaGVpZ2h0PSI1MTIiIHZpZXdCb3g9IjAgMCAxMDI0IDUxMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjEwMjQiIGhlaWdodD0iNTEyIiBmaWxsPSIjMGMxYTJkIi8+CjxwYXRoIGQ9Ik01MTIgMjU2QzUxMiAzOTcuODgzIDM5Ny44ODMgNTEyIDI1NiA1MTIgMTA0LjExNyA1MTIgMCAzOTcuODgzIDAgMjU2QzAgMTE0LjExNyAxMDQuMTE3IDAgMjU2IDBDMzk3Ljg4MyAwIDUxMiAxMTQuMTE3IDUxMiAyNTZaIiBmaWxsPSIjMWIzZDc5Ii8+Cjwvc3ZnPg==",
displacementScale: 0.04,
displacementQuality: 'medium',
environment: "/assets/img/night-sky.png",
globeRadius: 100,
globeOuterRadius: 120,
shading: 'realistic',
realisticMaterial: {
roughness: 0.8,
metalness: 0
},
postEffect: {
enable: true,
bloom: {
enable: true,
intensity: 0.1
}
},
light: {
main: {
intensity: 2,
shadow: true
},
ambientCubemap: {
texture: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAyNCIgaGVpZ2h0PSI1MTIiIHZpZXdCb3g9IjAgMCAxMDI0IDUxMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjEwMjQiIGhlaWdodD0iNTEyIiBmaWxsPSIjMGMxYTJkIi8+Cjwvc3ZnPg=='
}
},
viewControl: {
autoRotate: true,
autoRotateSpeed: 1,
// autoRotateDirection: 'left', // 固定向左旋转
targetCoord: [105, 35], // 默认视角对准中国 (经度105, 纬度35)
zoom: 1.2 // 适当的缩放级别
},
layers: [
{
type: 'blend',
blendTo: 'emission',
texture: "/assets/img/earth-night.jpg",
intensity: 1.5
}
]
},
series: [] // 初始时系列为空
};
earthChart.setOption(option);
// 添加温度数据层
updateEarthData();
}
function updateEarthData() {
const temperatureData = [];
// 生成模拟温度数据
for (let i = 0; i < 1000; i++) {
const lat = Math.random() * 180 - 90;
const lng = Math.random() * 360 - 180;
const temp = 10 + Math.random() * 25; // 10-35°C
// 使用4个值[经度, 纬度, 高度(设为0使数据贴合地球表面), 温度值]
temperatureData.push([lng, lat, 0, temp]);
}
// 只更新系列数据,使用 replaceMerge 确保不重置其他配置
const option = {
series: [{
type: 'scatter3D',
coordinateSystem: 'globe',
blendMode: 'lighter',
symbolSize: 2,
itemStyle: {
color: function (params) {
const temp = params.value[3]; // 温度值现在是第4个元素
if (temp < 15) return '#4A90E2'; //
if (temp < 20) return '#7ED321'; //
if (temp < 25) return '#F5A623'; //
return '#D0021B'; // 热色
},
opacity: 0.8
},
data: temperatureData
}]
};
// 使用 replaceMerge 方式更新,避免重置视图
earthChart.setOption(option, {
replaceMerge: ['series']
});
}
// 初始化时序图
function initTimeSeriesChart() {
const chartContainer = document.getElementById('timeSeriesChart');
timeSeriesChart = echarts.init(chartContainer, null, { renderer: 'canvas' });
updateTimeSeriesChart();
}
function updateTimeSeriesChart() {
const option = {
backgroundColor: 'transparent',
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
formatter: function (params) {
let result = params[0].name + '<br/>';
params.forEach(param => {
// 保留两位小数
const value = parseFloat(param.value).toFixed(2);
result += `${param.seriesName}: ${value}${param.seriesName.includes('温度') ? '°C' : param.seriesName.includes('叶绿素') ? 'mg/m³' : '‰'}<br/>`;
});
return result;
}
},
legend: {
data: ['温度', '叶绿素', '盐度'],
textStyle: {
color: '#e0f0ff'
},
top: 10
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
top: '20%',
containLabel: true
},
xAxis: {
type: 'category',
data: timeSeriesData.time,
axisLine: {
lineStyle: {
color: '#4fc3f7'
}
},
axisLabel: {
color: '#a0d2ff'
}
},
yAxis: [
{
type: 'value',
name: '温度(°C)',
position: 'left',
axisLine: {
lineStyle: {
color: '#ff7043'
}
},
axisLabel: {
formatter: '{value} °C',
color: '#a0d2ff'
},
splitLine: {
lineStyle: {
color: 'rgba(255, 255, 255, 0.1)'
}
}
},
{
type: 'value',
name: '叶绿素(mg/m³)',
position: 'right',
axisLine: {
lineStyle: {
color: '#66bb6a'
}
},
axisLabel: {
formatter: '{value} mg/m³',
color: '#a0d2ff'
},
splitLine: {
show: false
}
}
],
series: [
{
name: '温度',
type: 'line',
smooth: true,
lineStyle: {
width: 3,
color: '#ff7043'
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [{
offset: 0, color: 'rgba(255, 112, 67, 0.5)'
}, {
offset: 1, color: 'rgba(255, 112, 67, 0.1)'
}]
}
},
data: timeSeriesData.temperature.map(value => parseFloat(value).toFixed(2))
},
{
name: '叶绿素',
type: 'line',
smooth: true,
yAxisIndex: 1,
lineStyle: {
width: 3,
color: '#66bb6a'
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [{
offset: 0, color: 'rgba(102, 187, 106, 0.5)'
}, {
offset: 1, color: 'rgba(102, 187, 106, 0.1)'
}]
}
},
data: timeSeriesData.chlorophyll.map(value => parseFloat(value).toFixed(2))
},
{
name: '盐度',
type: 'line',
smooth: true,
lineStyle: {
width: 3,
color: '#4fc3f7'
},
data: timeSeriesData.salinity.map(value => parseFloat(value).toFixed(2))
}
]
};
timeSeriesChart.setOption(option);
}
// 初始化剖面图
function initProfileChart() {
const chartContainer = document.getElementById('profileChart');
profileChart = echarts.init(chartContainer, null, { renderer: 'canvas' });
updateProfileChart();
}
// 修改 updateProfileChart 函数中的 tooltip formatter 部分
function updateProfileChart() {
const option = {
backgroundColor: 'transparent',
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
},
formatter: function (params) {
// 自定义提示框内容,只显示需要的信息,并保留两位小数
let result = '';
params.forEach((param, index) => {
if (index === 0) {
result += `深度: ${param.data[1]}m<br/>`;
}
// 保留两位小数
const value = parseFloat(param.data[0]).toFixed(2);
result += `${param.seriesName}: ${value}${param.seriesName.includes('温度') ? '°C' : '‰'}<br/>`;
});
return result;
}
},
legend: {
data: ['温度', '盐度'],
textStyle: {
color: '#e0f0ff'
},
top: 10
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
top: '15%',
containLabel: true
},
xAxis: [
{
type: 'value',
name: '温度(°C)',
position: 'bottom',
axisLine: {
lineStyle: {
color: '#ff7043'
}
},
axisLabel: {
formatter: '{value} °C',
color: '#a0d2ff'
},
splitLine: {
lineStyle: {
color: 'rgba(255, 255, 255, 0.1)'
}
}
},
{
type: 'value',
name: '盐度(‰)',
position: 'top',
axisLine: {
lineStyle: {
color: '#4fc3f7'
}
},
axisLabel: {
formatter: '{value} ‰',
color: '#a0d2ff'
},
splitLine: {
show: false
}
}
],
yAxis: {
type: 'value',
name: '深度(m)',
inverse: true,
axisLine: {
lineStyle: {
color: '#a0d2ff'
}
},
axisLabel: {
formatter: '{value} m',
color: '#a0d2ff'
},
splitLine: {
lineStyle: {
color: 'rgba(255, 255, 255, 0.1)'
}
}
},
series: [
{
name: '温度',
type: 'line',
xAxisIndex: 0,
smooth: true,
lineStyle: {
width: 3,
color: '#ff7043'
},
data: profileData.temperature.map((temp, index) => [parseFloat(temp).toFixed(2), profileData.depth[index]])
},
{
name: '盐度',
type: 'line',
xAxisIndex: 1,
smooth: true,
lineStyle: {
width: 3,
color: '#4fc3f7'
},
data: profileData.salinity.map((salinity, index) => [parseFloat(salinity).toFixed(2), profileData.depth[index]])
}
]
};
profileChart.setOption(option);
}
// 初始化数据表格
function initDataTable() {
updateDataTable();
}
// 更新数据表格
function updateDataTable() {
const container = document.getElementById('dataTableContent');
let html = `
<div class="data-table-row data-table-header">
<div class="data-table-cell">区域</div>
<div class="data-table-cell">温度(°C)</div>
<div class="data-table-cell">盐度(‰)</div>
<div class="data-table-cell">叶绿素(mg/m³)</div>
</div>
`;
tableData.forEach(item => {
html += `
<div class="data-table-row">
<div class="data-table-cell">${item.region}</div>
<div class="data-table-cell">${item.temperature.toFixed(1)}</div>
<div class="data-table-cell">${item.salinity.toFixed(1)}</div>
<div class="data-table-cell">${item.chlorophyll.toFixed(2)}</div>
</div>
`;
});
container.innerHTML = html;
}
// 修改 updateAllData 函数中的剖面数据更新部分
function updateAllData() {
// 更新时序数据
const now = new Date();
timeSeriesData.time.shift();
timeSeriesData.time.push(now.getHours() + ':' + now.getMinutes().toString().padStart(2, '0'));
timeSeriesData.temperature.shift();
timeSeriesData.temperature.push(20 + Math.sin(now.getHours() / 3) * 5 + Math.random() * 2);
timeSeriesData.chlorophyll.shift();
timeSeriesData.chlorophyll.push(0.2 + Math.sin(now.getHours() / 4) * 0.15 + Math.random() * 0.1);
timeSeriesData.salinity.shift();
timeSeriesData.salinity.push(34 + Math.cos(now.getHours() / 5) * 1 + Math.random() * 0.5);
// 更新剖面数据并保留两位小数
profileData.temperature = profileData.temperature.map(temp =>
parseFloat(Math.max(5, Math.min(30, temp + (Math.random() - 0.5) * 0.5)).toFixed(2))
);
profileData.salinity = profileData.salinity.map(salinity =>
parseFloat(Math.max(30, Math.min(38, salinity + (Math.random() - 0.5) * 0.2)).toFixed(2))
);
// 更新表格数据
tableData = tableData.map(item => ({
region: item.region,
temperature: Math.max(10, Math.min(35, item.temperature + (Math.random() - 0.5) * 0.3)),
salinity: Math.max(30, Math.min(38, item.salinity + (Math.random() - 0.5) * 0.1)),
chlorophyll: Math.max(0.1, Math.min(0.8, item.chlorophyll + (Math.random() - 0.5) * 0.05))
}));
// 更新统计卡片
updateStatsCards();
// 更新所有图表
updateAllCharts();
}
// 更新所有图表
function updateAllCharts() {
//updateEarthData();
updateTimeSeriesChart();
updateProfileChart();
updateDataTable();
}
// 更新统计卡片
function updateStatsCards() {
// 计算平均值
const avgTemp = timeSeriesData.temperature.reduce((a, b) => a + b, 0) / timeSeriesData.temperature.length;
const avgSalinity = timeSeriesData.salinity.reduce((a, b) => a + b, 0) / timeSeriesData.salinity.length;
// 更新卡片数据
document.getElementById('avgTemp').textContent = avgTemp.toFixed(1) + '°C';
document.getElementById('maxWave').textContent = (8 + Math.random() * 5).toFixed(1) + 'm';
document.getElementById('anomalyCount').textContent = Math.floor(3 + Math.random() * 8);
document.getElementById('avgSalinity').textContent = avgSalinity.toFixed(1) + '‰';
}
// 开始动画
function startAnimation() {
if (animationInterval) {
clearInterval(animationInterval);
}
animationInterval = setInterval(() => {
updateAllData();
}, 2000);
document.getElementById('playAnimation').disabled = true;
document.getElementById('pauseAnimation').disabled = false;
}
// 暂停动画
function pauseAnimation() {
if (animationInterval) {
clearInterval(animationInterval);
animationInterval = null;
}
document.getElementById('playAnimation').disabled = false;
document.getElementById('pauseAnimation').disabled = true;
}
// 重置视图
function resetView() {
// 重置所有选择器
document.getElementById('dataTypeSelect').value = 'temperature';
document.getElementById('timeRangeSelect').value = '24h';
document.getElementById('regionSelect').value = 'global';
// 更新当前选择
currentDataType = 'temperature';
currentTimeRange = '24h';
currentRegion = 'global';
// 重新初始化数据
initData();
// 更新图表
updateAllCharts();
// 暂停动画
pauseAnimation();
}
// 窗口大小改变时调整图表大小
window.addEventListener('resize', function () {
if (earthChart) earthChart.resize();
if (timeSeriesChart) timeSeriesChart.resize();
if (profileChart) profileChart.resize();
});
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', init);
</script>
</body>
</html>