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.

2437 lines
92 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>
<script src="https://cesium.com/downloads/cesiumjs/releases/1.100/Build/Cesium/Cesium.js"></script>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.100/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
<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: url('/assets/img/bg.png') no-repeat;
background-size: 100% 100%;
color: #e0f0ff;
overflow: hidden;
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;
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;
}
.container {
display: flex;
height: calc(100vh - 8vh - 20px);
position: relative;
margin-bottom: 20px;
}
/* 左侧信息面板 */
.info-panel {
width: 300px;
height: 100%;
padding: 20px;
background: rgba(12, 42, 73, 0.3);
backdrop-filter: blur(10px);
border: 1px solid rgba(64, 156, 255, 0.3);
box-shadow: 5px 0 15px rgba(0, 0, 0, 0.3);
z-index: 10;
border-radius: 8px;
position: absolute;
left: 15px;
top: 0;
display: flex;
flex-direction: column;
}
/* 右侧流程图面板 */
.process-panel {
width: 300px;
height: 100%;
padding: 20px;
background: rgba(12, 42, 73, 0.3);
backdrop-filter: blur(10px);
border: 1px solid rgba(64, 156, 255, 0.3);
box-shadow: -5px 0 15px rgba(0, 0, 0, 0.3);
z-index: 10;
border-radius: 8px;
position: absolute;
right: 15px;
top: 0;
display: flex;
flex-direction: column;
}
/* 卡片头部样式 */
.card-header {
height: 40px;
background: url('/assets/img/common/homeTitle2.png') no-repeat;
background-position: center;
background-size: 108% 120%;
display: flex;
justify-content: center;
align-items: center;
border-radius: 8px 8px 0 0 !important;
border-bottom: 1px solid var(--light-blue);
padding: 32px 20px;
position: relative;
margin: -20px -20px 0px -20px;
}
.card-header span {
font-size: 1.2rem;
letter-spacing: 1px;
font-family: 'YouSheBiaoTiHei', 'Microsoft YaHei';
font-weight: 400;
color: #ffffff;
}
/* 卡片头部样式 */
/* .card-header {
height: 40px;
background: linear-gradient(to right, #1a3a5f, #2a5a8f);
display: flex;
justify-content: center;
align-items: center;
border-radius: 8px 8px 0 0 !important;
border-bottom: 1px solid #4fc3f7;
padding: 12px 20px;
position: relative;
margin: -20px -20px 20px -20px;
}
.card-header span {
font-size: 1.2rem;
letter-spacing: 1px;
font-family: 'Microsoft YaHei';
font-weight: 400;
color: #ffffff;
} */
/* 数据类型列表 */
.data-types {
flex: 1;
overflow-y: auto;
margin-top: 10px;
padding-right: 5px;
}
.data-type-item {
background: rgba(25, 55, 95, 0.6);
border-radius: 8px;
padding: 15px;
border-left: 4px solid #4fc3f7;
margin-bottom: 15px;
cursor: pointer;
transition: all 0.3s ease;
}
.data-type-item:hover {
transform: translateX(5px);
background: rgba(40, 80, 130, 0.7);
}
.data-type-item.active {
border-left: 4px solid #ffd700;
background: rgba(40, 80, 130, 0.9);
}
.data-type-name {
font-size: 1.1rem;
font-weight: bold;
color: #4fc3f7;
margin-bottom: 8px;
}
.data-type-desc {
font-size: 0.9rem;
color: #a0d2ff;
margin-bottom: 10px;
}
.data-type-stats {
display: flex;
justify-content: space-between;
}
.stat-badge {
background: rgba(79, 195, 247, 0.2);
padding: 3px 8px;
border-radius: 12px;
font-size: 0.8rem;
}
/* 修改流程图区域样式 */
.process-container {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
/* 修改为flex-start */
height: 100%;
}
.process-flow {
position: relative;
width: 100%;
flex: 1;
/* 添加这一行,使流程图区域自适应高度 */
min-height: 0;
/* 添加这一行防止flex项目溢出 */
}
.process-node {
position: absolute;
width: 120px;
height: 80px;
background: rgba(25, 55, 95, 0.8);
border: 2px solid #4fc3f7;
border-radius: 10px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
}
.process-node:hover {
transform: scale(1.05);
border-color: #ffd700;
box-shadow: 0 0 20px rgba(255, 215, 0, 0.5);
}
.process-node.active {
border-color: #ffd700;
background: rgba(40, 80, 130, 0.9);
box-shadow: 0 0 25px rgba(255, 215, 0, 0.7);
}
.node-icon {
font-size: 24px;
margin-bottom: 8px;
color: #4fc3f7;
}
.node-title {
font-size: 0.9rem;
text-align: center;
color: #e0f0ff;
}
.node-connection {
position: absolute;
background: #4fc3f7;
height: 4px;
transform-origin: 0 0;
}
/* 中间主内容区 */
.main-content {
flex: 1;
height: 100%;
position: relative;
margin: 0 315px;
border-radius: 8px;
overflow: hidden;
display: flex;
flex-direction: column;
}
#processChart {
width: 100%;
height: 40%;
min-height: 200px;
}
/* 内容网格布局 */
.content-grid {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
gap: 15px;
padding: 15px;
height: 60%;
}
.grid-item {
background: rgba(12, 42, 73, 0.5);
border: 1px solid rgba(64, 156, 255, 0.3);
border-radius: 8px;
padding: 15px;
overflow: hidden;
}
.grid-item-header {
/* font-size: 1.1rem;
color: #4fc3f7;
margin-bottom: 10px;
padding-bottom: 5px;
border-bottom: 1px solid rgba(79, 195, 247, 0.3);
display: flex;
justify-content: space-between;
align-items: center; */
height: 40px;
background: url('/assets/img/common/homeTitle2.png') no-repeat;
background-position: center;
background-size: 108% 120%;
display: flex;
justify-content: center;
align-items: center;
border-radius: 8px 8px 0 0 !important;
border-bottom: 1px solid var(--light-blue);
padding: 32px 20px;
position: relative;
margin: -20px -20px 0px -20px;
}
/* 修改处理配置按钮样式 */
.config-button {
background: #4fc3f7;
color: #0c1a2d;
border: none;
border-radius: 4px;
padding: 6px 10px;
font-size: 0.8rem;
cursor: pointer;
display: none;
min-width: 70px;
height: 32px;
align-self: center;
margin-left: 10px;
}
.config-button.visible {
display: inline-block;
}
/* 任务概要 */
.task-summary {
display: flex;
flex-wrap: wrap;
height: calc(100% - 30px);
}
.task-item {
width: 50%;
padding: 8px 0;
}
.task-label {
font-size: 0.9rem;
color: #a0d2ff;
}
.task-value {
font-size: 1.2rem;
font-weight: bold;
color: #4fc3f7;
}
/* 资源监控 */
.resource-monitor {
display: flex;
flex-direction: column;
gap: 20px;
height: calc(100% - 30px);
}
.resource-item {
display: flex;
align-items: center;
}
.resource-name {
width: 80px;
font-size: 0.9rem;
color: #a0d2ff;
}
.resource-bar {
flex: 1;
height: 10px;
background: rgba(255, 255, 255, 0.1);
border-radius: 5px;
overflow: hidden;
}
.resource-fill {
height: 100%;
border-radius: 5px;
}
.resource-fill.cpu {
background: linear-gradient(90deg, #ff416c, #ff4b2b);
}
.resource-fill.memory {
background: linear-gradient(90deg, #2193b0, #6dd5ed);
}
.resource-fill.storage {
background: linear-gradient(90deg, #834d9b, #d04ed6);
}
.resource-percent {
width: 40px;
text-align: right;
font-size: 0.9rem;
color: #e0f0ff;
}
/* 产品数据库 */
.database-content {
height: calc(100% - 30px);
overflow-y: auto;
}
.database-item {
padding: 8px 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.database-item:last-child {
border-bottom: none;
}
.database-name {
font-size: 0.9rem;
color: #4fc3f7;
margin-bottom: 3px;
}
.database-info {
font-size: 0.8rem;
color: #a0d2ff;
display: flex;
justify-content: space-between;
}
/* 系统日志 */
.log-content {
height: calc(100% - 30px);
overflow-y: auto;
font-size: 0.85rem;
}
.log-entry {
padding: 5px 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.log-entry:last-child {
border-bottom: none;
}
.log-time {
color: #81d4fa;
margin-right: 10px;
}
.log-info {
color: #4fc3f7;
}
.log-warning {
color: #ffd54f;
}
.log-error {
color: #e57373;
}
/* 饼图容器 */
#taskChart {
width: 100%;
height: calc(100% - 30px);
}
/* 修改右侧流程节点样式 */
.process-step {
display: flex;
align-items: center;
padding: 12px 10px;
margin-bottom: 15px;
background: rgba(25, 55, 95, 0.6);
border-radius: 8px;
border-left: 4px solid #4fc3f7;
min-height: 90px;
/* 设置最小高度 */
}
.process-step.active {
border-left: 4px solid #ffd700;
background: rgba(40, 80, 130, 0.9);
}
.process-step-icon {
font-size: 24px;
margin-right: 12px;
color: #4fc3f7;
min-width: 30px;
}
.process-step-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
.process-step-name {
font-weight: bold;
color: #4fc3f7;
margin-bottom: 5px;
font-size: 1rem;
}
.process-step-desc {
font-size: 0.85rem;
color: #a0d2ff;
line-height: 1.4;
}
/* 配置模态框 */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
z-index: 1000;
justify-content: center;
align-items: center;
}
.modal-content {
background: linear-gradient(135deg, #1e3a8a 0%, #0f172a 100%);
border-radius: 16px;
padding: 24px;
width: 500px;
max-width: 90%;
border: 1px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5);
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.modal-title {
font-size: 1.5rem;
color: white;
margin: 0;
}
.modal-close {
background: none;
border: none;
color: #a0d2ff;
font-size: 1.5rem;
cursor: pointer;
}
.form-group {
margin-bottom: 15px;
}
.form-label {
display: block;
margin-bottom: 5px;
color: #a0d2ff;
}
/* 下拉框样式优化 */
.form-control {
width: 100%;
padding: 10px;
border-radius: 4px;
border: 1px solid #4fc3f7;
background: rgba(12, 42, 73, 0.7) !important;
/* 深蓝色背景 */
color: #e0f0ff !important;
/* 浅蓝色字体 */
appearance: menulist;
/* 保持默认下拉箭头 */
}
.form-control:focus {
outline: none;
border-color: #ffd700;
/* 聚焦时的边框颜色 */
box-shadow: 0 0 5px rgba(255, 215, 0, 0.5);
}
/* 优化选项样式 */
.form-control option {
background: rgba(12, 42, 73, 0.95) !important;
color: #e0f0ff !important;
padding: 5px;
}
/* 数字输入框样式 */
.form-control[type="number"] {
background: rgba(12, 42, 73, 0.7) !important;
color: #e0f0ff !important;
border: 1px solid #4fc3f7;
}
.form-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 20px;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
}
.btn-primary {
background: #4fc3f7;
color: #0c1a2d;
}
.btn-secondary {
background: #6c757d;
color: white;
}
/* 响应式调整 */
@media (max-width: 1200px) {
.info-panel,
.process-panel {
width: 260px;
}
.main-content {
margin: 0 275px;
}
}
@media (max-width: 900px) {
.info-panel,
.process-panel {
width: 220px;
padding: 15px;
}
.main-content {
margin: 0 235px;
}
}
/* 滚动条样式 */
::-webkit-scrollbar {
width: 4px;
height: 4px;
}
::-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);
}
/* 自定义多选下拉框样式 */
.custom-select-wrapper {
position: relative;
}
.custom-select {
position: relative;
background: rgba(12, 42, 73, 0.7);
border: 1px solid #4fc3f7;
border-radius: 4px;
cursor: pointer;
min-height: 38px;
}
.custom-select.open {
border-color: #ffd700;
box-shadow: 0 0 5px rgba(255, 215, 0, 0.5);
}
.select-trigger {
padding: 10px;
color: #e0f0ff;
display: flex;
justify-content: space-between;
align-items: center;
user-select: none;
}
.select-arrow {
font-size: 0.7rem;
color: #4fc3f7;
transition: transform 0.3s;
pointer-events: none;
}
.custom-select.open .select-arrow {
transform: rotate(180deg);
}
.select-dropdown {
position: absolute;
top: 100%;
left: 0;
right: 0;
background: rgba(12, 42, 73, 0.95);
border: 1px solid #4fc3f7;
border-top: none;
border-radius: 0 0 4px 4px;
max-height: 200px;
overflow-y: auto;
z-index: 1000;
display: none;
}
.custom-select.open .select-dropdown {
display: block;
}
.select-option {
padding: 8px 10px;
color: #e0f0ff;
cursor: pointer;
display: flex;
align-items: center;
}
.select-option:hover {
background: rgba(79, 195, 247, 0.2);
}
.select-option.selected {
background: rgba(79, 195, 247, 0.3);
}
.select-option::before {
content: "☐";
margin-right: 8px;
color: #a0d2ff;
width: 16px;
}
.select-option.selected::before {
content: "☑";
color: #4fc3f7;
}
.select-trigger-text {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex-grow: 1;
text-align: left;
}
</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="container">
<!-- 左侧数据类型面板 -->
<div class="info-panel">
<div class="card-header">
<span>数据管道</span>
</div>
<div class="data-types" id="dataTypesList">
<!-- 数据类型项将通过JavaScript动态添加 -->
</div>
</div>
<!-- 中间主内容区 -->
<div class="main-content">
<div id="processChart"></div>
<div class="content-grid">
<!-- 任务概要 (饼图) -->
<div class="grid-item">
<div class="grid-item-header">
<span>任务概要</span>
</div>
<div id="taskChart"></div>
</div>
<!-- 资源监控 -->
<div class="grid-item">
<div class="grid-item-header">
<span>资源监控</span>
</div>
<div class="resource-monitor" id="resourceMonitor">
<!-- 资源监控将通过JavaScript动态添加 -->
</div>
</div>
<!-- 产品数据库 -->
<div class="grid-item">
<div class="grid-item-header">
<span>产品数据库</span>
</div>
<div class="database-content" id="databaseContent">
<!-- 数据库内容将通过JavaScript动态添加 -->
</div>
</div>
<!-- 系统日志 -->
<div class="grid-item">
<div class="grid-item-header">
<span>系统日志</span>
</div>
<div class="log-content" id="logContent">
<!-- 日志内容将通过JavaScript动态添加 -->
</div>
</div>
</div>
</div>
<!-- 右侧流程图面板 -->
<div class="process-panel">
<div class="card-header">
<span>处理流程</span>
</div>
<div class="process-container">
<div class="process-flow" id="processFlow">
<!-- 流程节点将通过JavaScript动态添加 -->
</div>
</div>
</div>
</div>
<!-- 配置模态框 -->
<div class="modal" id="configModal">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title" id="modalTitle">处理配置</h2>
<button class="modal-close">&times;</button>
</div>
<form id="configForm">
<div class="form-group">
<label class="form-label" for="processingMode">处理模式</label>
<div class="custom-select-wrapper">
<div class="custom-select" id="processingModeSelect">
<div class="select-trigger">
<span class="select-trigger-text">请选择处理模式</span>
<span class="select-arrow"></span>
</div>
<div class="select-dropdown">
<div class="select-options" id="processingModeOptions">
<!-- 选项将通过JavaScript动态添加 -->
</div>
</div>
</div>
<input type="hidden" id="processingMode" name="processingMode" value="">
</div>
</div>
<div class="form-group">
<label class="form-label" for="batchSize">批处理大小</label>
<input type="number" class="form-control" id="batchSize" name="batchSize" min="1" value="100">
</div>
<div class="form-group">
<label class="form-label" for="qualityThreshold">质量阈值</label>
<input type="number" class="form-control" id="qualityThreshold" name="qualityThreshold" min="0"
max="100" step="0.1" value="95">
</div>
<!-- 修改特征提取算法为自定义多选下拉框 -->
<div class="form-group">
<label class="form-label" for="featureAlgorithm">特征提取算法</label>
<div class="custom-select-wrapper">
<div class="custom-select" id="featureAlgorithmSelect">
<div class="select-trigger">
<span class="select-trigger-text">请选择特征提取算法</span>
<span class="select-arrow"></span>
</div>
<div class="select-dropdown">
<div class="select-options" id="featureAlgorithmOptions">
<!-- 选项将通过JavaScript动态添加 -->
</div>
</div>
</div>
<input type="hidden" id="featureAlgorithm" name="featureAlgorithm" value="">
</div>
</div>
<div class="form-actions">
<button type="button" class="btn btn-secondary" id="cancelConfig">取消</button>
<button type="submit" class="btn btn-primary">保存配置</button>
</div>
</form>
</div>
</div>
<script>
// 全局变量
let chart;
let taskChart;
let processNodes = [];
let currentSelectedNode = null;
let currentDataType = 'temp';
// let completedSteps = ['input']; // 默认数据输入已完成
let dataTypeCompletedSteps = {}; // 为每种数据类型维护独立的完成状态
// 数据类型配置
const dataTypes = [
{
id: 'temp',
name: '遥感类数据',
description: '海表温度SST、海表高度异常SLA、海冰分布、叶绿素浓度、浊度',
count: 12450,
status: 'processing'
},
{
id: 'salinity',
name: '海洋气象类数据',
description: '气温、气压、湿度、风速、风向、降水、台风路径',
count: 9876,
status: 'processing'
},
{
id: 'chlorophyll',
name: '海洋水文类数据',
description: '潮位、波高、波周期、流速、流向、盐度、温度、密度、声速剖面',
count: 8765,
status: 'processing'
},
{
id: 'wave',
name: '海洋化学类数据',
description: '溶解氧、pH、营养盐硝酸盐、磷酸盐、硅酸盐、碱度',
count: 5432,
status: 'pending'
},
{
id: 'current',
name: '海洋化生物类数据',
description: '浮游植物、浮游动物、渔业资源、珊瑚覆盖率',
count: 7654,
status: 'processing'
},
{
id: '综合类数据',
name: '综合类数据',
description: '耦合模式输出、预报数据、数值实验结果',
count: 7654,
status: 'processing'
}
];
// 为每种数据类型定义不同的步骤描述
const stepDescriptions = {
'temp': {
'input': '接收遥感原始数据(卫星、无人机)',
'preprocessing': '原始影像几何校正、大气校正、辐射校正',
'processing': '反演模型计算物理量(温度、浓度等)',
'postprocessing': '数据质量标记、异常值剔除与填补',
'output': '生成标准网格化产品、时序产品、可视化专题图'
},
'salinity': {
'input': '接收海洋气象原始观测数据',
'preprocessing': '观测数据解码、坐标/时区统一、传感器异常检测',
'processing': '统计插值、风场/压场重构、台风路径拟合',
'postprocessing': '误差修正、多源数据融合',
'output': '生成格点气象场、台风路径Shapefile、预报驱动场'
},
'chlorophyll': {
'input': '接收海洋水文原始观测数据',
'preprocessing': '原始传感器数据质量检查、坏点剔除、漂移校正',
'processing': '插值/重采样、剖面插值至标准深度层、潮汐/波浪分解',
'postprocessing': '异常值填补、标准化元数据添加',
'output': '生成剖面数据、波浪场/潮流场可视化、耦合模式输入边界条件'
},
'wave': {
'input': '接收海洋化学原始观测数据',
'preprocessing': '样品实验室测定值录入与编码、仪器漂移修正',
'processing': '数据标准化、剖面或水平插值、与温盐数据融合',
'postprocessing': '异常点校验、增加采样元信息',
'output': '生成剖面/断面分布图、化学要素数据库、指标报表'
},
'current': {
'input': '接收海洋生物原始观测数据',
'preprocessing': '样品分类鉴定与计数、遥感数据物种分类训练集构建',
'processing': '生物量估算、时空分布建模',
'postprocessing': '异常生物量检测、多源融合',
'output': '生成生物分布图、种群动态数据库、生态风险评估报告'
},
'综合类数据': {
'input': '接收综合类原始数据(模式输出、预报数据等)',
'preprocessing': '模式输入准备、格式转换',
'processing': '数值积分运算、多模式集合平均',
'postprocessing': '结果对比观测资料、模式偏差订正',
'output': '生成预报场、数字孪生可视化接口、预警信息'
}
};
// 处理流程节点配置(横向布局)
const processSteps = [
{
id: 'input',
name: '数据输入',
icon: 'fas fa-download',
description: '接收原始海洋观测数据',
x: 100,
y: 300
},
{
id: 'preprocessing',
name: '预处理',
icon: 'fas fa-filter',
description: '数据清洗和预处理',
x: 300,
y: 300
},
{
id: 'processing',
name: '数据处理',
icon: 'fas fa-cogs',
description: '核心数据处理',
x: 500,
y: 300
},
{
id: 'postprocessing',
name: '后处理',
icon: 'fas fa-microchip',
description: '数据后处理和优化',
x: 700,
y: 300
},
{
id: 'output',
name: '数据输出',
icon: 'fas fa-database',
description: '生成标准格式产品数据',
x: 900,
y: 300
}
];
// 右侧流程步骤配置(垂直布局)
const rightPanelSteps = [
{
id: 'input',
name: '数据输入',
icon: 'fas fa-download',
description: '接收原始海洋观测数据'
},
{
id: 'preprocessing',
name: '预处理',
icon: 'fas fa-filter',
description: '数据清洗和预处理'
},
{
id: 'processing',
name: '数据处理',
icon: 'fas fa-cogs',
description: '核心数据处理'
},
{
id: 'postprocessing',
name: '后处理',
icon: 'fas fa-microchip',
description: '数据后处理和优化'
},
{
id: 'output',
name: '数据输出',
icon: 'fas fa-database',
description: '生成标准格式产品数据'
}
];
// 修改处理模式配置,根据不同数据类型和处理步骤提供不同的选项
// 修改处理模式配置,为海洋气象类数据添加完整的处理模式选项
const processingModes = {
'temp': {
'preprocessing': [
'数据清洗',
'原始影像几何校正',
'大气校正',
'辐射校正',
'掩膜处理(云、陆地)',
'投影/重采样',
'异常数据筛查并剔除'
],
'processing': [
'反演模型计算物理量(温度、浓度等)',
'时间序列拼接',
'格网化/空间插值'
],
'postprocessing': [
'数据质量标记QC flag',
'异常值剔除与填补'
],
'output': [
'标准网格化产品NetCDF/GeoTIFF',
'时序产品CSV/NetCDF',
'可视化专题图'
]
},
'salinity': {
'preprocessing': [
'观测数据解码GRIB、BUFR → 通用格式)',
'坐标/时区统一',
'传感器异常检测'
],
'processing': [
'统计插值(逐时、逐日)',
'风场/压场重构',
'台风路径拟合'
],
'postprocessing': [
'误差修正(模式同化)',
'多源数据融合(地面+卫星+模式)'
],
'output': [
'格点气象场NetCDF',
'台风路径Shapefile',
'预报驱动场(耦合模式输入)'
]
},
'chlorophyll': {
'preprocessing': [
'原始传感器数据质量检查',
'坏点剔除、漂移校正',
'统一单位、坐标'
],
'processing': [
'插值/重采样',
'剖面插值至标准深度层',
'潮汐/波浪分解'
],
'postprocessing': [
'异常值填补(气象驱动)',
'标准化元数据添加'
],
'output': [
'剖面数据NetCDF、ODV格式',
'波浪场/潮流场可视化',
'耦合模式输入边界条件'
]
},
'wave': {
'preprocessing': [
'样品实验室测定值录入与编码',
'仪器漂移修正'
],
'processing': [
'数据标准化至国际单位制',
'剖面或水平插值',
'与温盐数据融合'
],
'postprocessing': [
'异常点校验(基于经验上下限)',
'增加采样元信息(采样船只、时间、深度)'
],
'output': [
'剖面/断面分布图',
'化学要素数据库NetCDF/CSV',
'指标报表(富营养化等级)'
]
},
'current': {
'preprocessing': [
'样品分类鉴定与计数',
'遥感数据物种分类训练集构建'
],
'processing': [
'生物量估算(如叶绿素浓度 → 初级生产力)',
'时空分布建模'
],
'postprocessing': [
'异常生物量检测(赤潮预警)',
'多源融合(化学+物理环境)'
],
'output': [
'生物分布图',
'种群动态数据库',
'生态风险评估报告'
]
},
'综合类数据': {
'preprocessing': [
'模式输入准备(气象、水文、边界条件)',
'格式转换GRIB → NetCDF'
],
'processing': [
'数值积分运算',
'多模式集合平均'
],
'postprocessing': [
'结果对比观测资料(同化验证)',
'模式偏差订正'
],
'output': [
'预报场未来N天的流场、温度场等',
'数字孪生可视化接口API',
'预警信息(红/黄/蓝警报)'
]
}
};
// 任务统计数据
const taskStats = [
{ label: '总任务数', value: 24 },
{ label: '进行中', value: 8 },
{ label: '已完成', value: 12 },
{ label: '待处理', value: 4 }
];
// 资源监控数据
const resourceData = [
{ name: 'CPU使用率', value: 65, class: 'cpu' },
{ name: '内存使用率', value: 42, class: 'memory' },
{ name: '存储使用率', value: 78, class: 'storage' },
{ name: '网络流量', value: 33, class: 'cpu' }
];
// 产品数据库示例数据
// 产品数据库示例数据 - 为每种数据类型提供详细的数据库内容
const databaseProducts = {
'temp': {
'input': [
{ name: 'MODIS SST原始数据集', size: '4.2GB', time: '2025-09-17 14:30', description: '卫星遥感海表温度原始数据' },
{ name: 'AVHRR LAC数据', size: '3.8GB', time: '2025-09-17 13:45', description: '高分辨率卫星数据' },
{ name: '无人机红外影像', size: '2.1GB', time: '2025-09-17 12:15', description: '近海区域高精度影像' }
],
'preprocessing': [
{ name: '几何校正后SST数据', size: '3.9GB', time: '2025-09-17 15:30', description: '完成几何校正的海表温度数据' },
{ name: '大气校正后数据', size: '3.5GB', time: '2025-09-17 15:20', description: '完成大气校正处理' },
{ name: '云掩膜处理数据', size: '3.2GB', time: '2025-09-17 15:45', description: '去除云层影响的数据' }
],
'processing': [
{ name: 'SST反演产品', size: '1.8GB', time: '2025-09-17 16:30', description: '基于多光谱数据反演的海表温度' },
{ name: '海冰密集度产品', size: '1.2GB', time: '2025-09-17 16:45', description: '海冰覆盖度分析结果' },
{ name: '叶绿素浓度产品', size: '1.5GB', time: '2025-09-17 16:50', description: '基于海洋光学反演的叶绿素' }
],
'postprocessing': [
{ name: '质量标记SST数据', size: '1.7GB', time: '2025-09-17 17:00', description: '带质量控制标记的数据' },
{ name: '异常值剔除产品', size: '1.6GB', time: '2025-09-17 17:10', description: '已完成异常值处理' }
],
'output': [
{ name: 'L3级SST产品 (NetCDF)', size: '850MB', time: '2025-09-17 17:30', description: '标准网格化海表温度产品' },
{ name: 'SST时序数据 (CSV)', size: '620MB', time: '2025-09-17 17:35', description: '时间序列统计产品' },
{ name: '海表温度专题图 (GeoTIFF)', size: '920MB', time: '2025-09-17 17:40', description: '可视化栅格产品' },
{ name: '海冰分布图', size: '480MB', time: '2025-09-17 17:45', description: '海冰密集度空间分布' }
]
},
'salinity': {
'input': [
{ name: '气象站气压数据', size: '1.4GB', time: '2025-09-17 14:20', description: '地面气象观测站气压记录' },
{ name: '风场观测数据', size: '2.1GB', time: '2025-09-17 14:10', description: '风速风向观测数据' },
{ name: '卫星降水产品', size: '850MB', time: '2025-09-17 13:50', description: '卫星反演降水数据' }
],
'preprocessing': [
{ name: 'GRIB格式解码数据', size: '1.9GB', time: '2025-09-17 15:15', description: '完成格式解码的气象数据' },
{ name: '时间统一处理数据', size: '1.7GB', time: '2025-09-17 15:25', description: '统一时间基准的数据' }
],
'processing': [
{ name: '统计插值气压场', size: '980MB', time: '2025-09-17 16:20', description: '插值后的气压场数据' },
{ name: '风场重构产品', size: '1.1GB', time: '2025-09-17 16:35', description: '重构的风场矢量数据' },
{ name: '台风路径分析', size: '250MB', time: '2025-09-17 16:40', description: '台风路径追踪结果' }
],
'postprocessing': [
{ name: '模式同化修正数据', size: '1.2GB', time: '2025-09-17 17:05', description: '经过模式同化修正' },
{ name: '多源融合气象场', size: '1.4GB', time: '2025-09-17 17:15', description: '地面+卫星+模式融合产品' }
],
'output': [
{ name: '格点气象场 (NetCDF)', size: '720MB', time: '2025-09-17 17:35', description: '标准格式气象场产品' },
{ name: '台风路径Shapefile', size: '15MB', time: '2025-09-17 17:40', description: '台风路径矢量数据' },
{ name: '预报驱动场', size: '2.1GB', time: '2025-09-17 17:50', description: '海洋模式输入驱动场' }
]
},
'chlorophyll': {
'input': [
{ name: 'CTD原始温盐数据', size: '2.4GB', time: '2025-09-17 14:30', description: '温盐深仪观测原始数据' },
{ name: 'ADCP流速数据', size: '1.8GB', time: '2025-09-17 13:45', description: '海流剖面观测数据' },
{ name: '潮位站观测数据', size: '950MB', time: '2025-09-17 12:15', description: '潮位观测记录' }
],
'preprocessing': [
{ name: '传感器质量检查数据', size: '2.1GB', time: '2025-09-17 15:20', description: '完成质量检查的传感器数据' },
{ name: '坏点剔除温盐数据', size: '1.9GB', time: '2025-09-17 15:35', description: '剔除异常值后的数据' }
],
'processing': [
{ name: '插值温盐剖面', size: '1.6GB', time: '2025-09-17 16:30', description: '重采样后的标准深度层数据' },
{ name: '潮汐调和分析结果', size: '850MB', time: '2025-09-17 16:45', description: '潮汐成分分析' },
{ name: '波浪谱分解数据', size: '720MB', time: '2025-09-17 16:50', description: '波浪能量谱分解' }
],
'postprocessing': [
{ name: '气象驱动填补数据', size: '1.5GB', time: '2025-09-17 17:10', description: '利用气象数据填补缺失值' },
{ name: '标准化元数据产品', size: '1.4GB', time: '2025-09-17 17:20', description: '带完整元数据信息' }
],
'output': [
{ name: '温盐剖面数据库 (NetCDF)', size: '920MB', time: '2025-09-17 17:40', description: '标准格式剖面数据' },
{ name: 'ODV格式数据集', size: '780MB', time: '2025-09-17 17:45', description: '海洋数据查看器格式' },
{ name: '潮流场可视化产品', size: '650MB', time: '2025-09-17 17:50', description: '潮流场可视化结果' },
{ name: '耦合模式边界条件', size: '1.2GB', time: '2025-09-17 17:55', description: '模式输入边界条件' }
]
},
'wave': {
'input': [
{ name: '实验室营养盐测定', size: '750MB', time: '2025-09-17 14:10', description: '海水样品化学分析数据' },
{ name: 'pH现场测量数据', size: '520MB', time: '2025-09-17 13:55', description: '现场pH值测量记录' },
{ name: '溶解氧观测数据', size: '680MB', time: '2025-09-17 13:40', description: '溶解氧浓度观测' }
],
'preprocessing': [
{ name: '测定值编码数据', size: '690MB', time: '2025-09-17 15:10', description: '标准化编码的化学数据' },
{ name: '仪器漂移修正数据', size: '650MB', time: '2025-09-17 15:25', description: '完成仪器校正的数据' }
],
'processing': [
{ name: '标准化化学要素', size: '720MB', time: '2025-09-17 16:20', description: '统一标准的化学参数' },
{ name: '剖面插值产品', size: '820MB', time: '2025-09-17 16:35', description: '垂直剖面插值结果' },
{ name: '温盐融合化学场', size: '950MB', time: '2025-09-17 16:45', description: '与物理海洋数据融合' }
],
'postprocessing': [
{ name: '异常点校验数据', size: '780MB', time: '2025-09-17 17:05', description: '基于经验范围校验' },
{ name: '采样元信息产品', size: '810MB', time: '2025-09-17 17:15', description: '含完整采样信息' }
],
'output': [
{ name: '剖面分布图 (NetCDF)', size: '580MB', time: '2025-09-17 17:35', description: '化学要素垂直分布' },
{ name: '断面分布图', size: '620MB', time: '2025-09-17 17:40', description: '水平断面分布图' },
{ name: '化学要素数据库 (CSV)', size: '420MB', time: '2025-09-17 17:45', description: '表格化化学数据' },
{ name: '富营养化等级报表', size: '180MB', time: '2025-09-17 17:50', description: '海域富营养化评估' }
]
},
'current': {
'input': [
{ name: '浮游植物样品数据', size: '1.2GB', time: '2025-09-17 14:25', description: '浮游植物分类鉴定数据' },
{ name: '浮游动物计数数据', size: '980MB', time: '2025-09-17 14:10', description: '浮游动物数量统计' },
{ name: '遥感物种识别训练集', size: '2.4GB', time: '2025-09-17 13:30', description: '用于物种识别的训练数据' }
],
'preprocessing': [
{ name: '样品分类鉴定结果', size: '1.1GB', time: '2025-09-17 15:25', description: '完成物种分类的数据' },
{ name: '训练集预处理数据', size: '2.2GB', time: '2025-09-17 15:40', description: '预处理后的训练样本' }
],
'processing': [
{ name: '初级生产力估算', size: '850MB', time: '2025-09-17 16:35', description: '基于叶绿素的生产力计算' },
{ name: '时空分布模型', size: '1.4GB', time: '2025-09-17 16:50', description: '生物量时空分布模型' }
],
'postprocessing': [
{ name: '异常生物量检测', size: '780MB', time: '2025-09-17 17:10', description: '赤潮等异常现象检测' },
{ name: '多源融合生物场', size: '1.2GB', time: '2025-09-17 17:25', description: '化学+物理环境融合' }
],
'output': [
{ name: '生物分布图', size: '680MB', time: '2025-09-17 17:40', description: '海洋生物空间分布图' },
{ name: '种群动态数据库', size: '920MB', time: '2025-09-17 17:45', description: '生物种群时间序列' },
{ name: '生态风险评估报告', size: '320MB', time: '2025-09-17 17:55', description: '生态系统健康评估' }
]
},
'综合类数据': {
'input': [
{ name: '模式输入气象场', size: '3.2GB', time: '2025-09-17 14:35', description: '数值模式驱动气象场' },
{ name: '预报初始场数据', size: '2.8GB', time: '2025-09-17 14:20', description: '预报模式初始条件' },
{ name: '数值实验设置', size: '150MB', time: '2025-09-17 13:50', description: '数值实验配置参数' }
],
'preprocessing': [
{ name: '模式输入准备数据', size: '3.1GB', time: '2025-09-17 15:30', description: '准备好的模式输入' },
{ name: 'GRIB转NetCDF数据', size: '2.9GB', time: '2025-09-17 15:45', description: '完成格式转换' }
],
'processing': [
{ name: '数值积分运算结果', size: '4.2GB', time: '2025-09-17 16:40', description: '模式积分计算结果' },
{ name: '多模式集合平均', size: '3.8GB', time: '2025-09-17 16:55', description: '多模式集成预测' }
],
'postprocessing': [
{ name: '同化验证结果', size: '2.1GB', time: '2025-09-17 17:15', description: '与观测资料对比' },
{ name: '模式偏差订正产品', size: '1.9GB', time: '2025-09-17 17:30', description: '偏差订正后产品' }
],
'output': [
{ name: '预报场产品', size: '3.2GB', time: '2025-09-17 17:50', description: '未来N天气预报场' },
{ name: '数字孪生API接口', size: '120MB', time: '2025-09-17 17:55', description: '可视化接口数据' },
{ name: '预警信息产品', size: '280MB', time: '2025-09-17 18:00', description: '红黄蓝警报信息' }
]
}
};
// 系统日志数据
const systemLogs = [
{ time: '17:45:23', level: 'info', message: '开始处理CTD温度数据' },
{ time: '17:42:15', level: 'warning', message: 'ADCP数据存在缺失值' },
{ time: '17:40:08', level: 'info', message: '完成海表温度产品生成' },
{ time: '17:35:42', level: 'error', message: '传感器校准参数异常' },
{ time: '17:32:17', level: 'info', message: '接收卫星遥感数据包' },
{ time: '17:30:55', level: 'info', message: '启动数据处理任务队列' }
];
// 日志更新间隔ID
let logUpdateInterval = null;
// 为不同数据类型定义特征提取算法选项
const featureAlgorithms = {
'temp': [
// 'NDVI植被指数计算',
'SST海表温度反演',
'海冰密集度分析',
'叶绿素浓度反演',
'大气校正算法',
'SST/SSHA梯度'
],
'salinity': [
'风速风向分解算法',
'气压场重构算法',
'台风路径预测算法',
'气象要素统计分析',
'数值模式插值算法'
],
'chlorophyll': [
'潮汐调和分析',
'波浪谱分析',
'流场矢量分解',
'剖面数据平滑',
'水文要素相关性分析'
],
'wave': [
'营养盐浓度反演',
'pH值计算模型',
'溶解氧饱和度算法',
'化学要素分布拟合',
'水质评价指数计算'
],
'current': [
'浮游植物分类识别',
'生物量估算模型',
'种群动态分析',
'生态位模型构建',
'生物多样性指数计算'
],
'综合类数据': [
'多模式集成算法',
'数据同化算法',
'耦合模型计算',
'预报误差订正',
'敏感性分析算法'
]
};
// 修改 updateFeatureAlgorithms 函数
function updateFeatureAlgorithms() {
const algorithmOptionsContainer = document.getElementById('featureAlgorithmOptions');
if (!algorithmOptionsContainer) return;
algorithmOptionsContainer.innerHTML = '';
// 获取当前数据类型对应的算法选项
const algorithms = featureAlgorithms[currentDataType] || [
'默认算法1',
'默认算法2',
'默认算法3'
];
// 获取当前节点已保存的选项(如果有的话)
const savedSelections = getSavedSelections(currentDataType, currentSelectedNode, 'featureAlgorithm');
algorithms.forEach(algorithm => {
const option = document.createElement('div');
option.className = 'select-option';
option.dataset.value = algorithm;
option.textContent = algorithm;
// 检查是否已保存选中
if (savedSelections.includes(algorithm)) {
option.classList.add('selected');
}
option.addEventListener('click', function (e) {
e.stopPropagation();
this.classList.toggle('selected');
updateHiddenInput('featureAlgorithm');
updateTriggerText('featureAlgorithmSelect', 'featureAlgorithm');
});
algorithmOptionsContainer.appendChild(option);
});
// 更新触发器文本
updateTriggerText('featureAlgorithmSelect', 'featureAlgorithm');
}
// 添加保存选择的函数
function saveSelections(dataType, stepId, selectId) {
const hiddenInput = document.getElementById(selectId);
if (!hiddenInput) return;
const selectedValues = hiddenInput.value ? hiddenInput.value.split(',') : [];
// 为每个数据类型和步骤保存选择
if (!localStorage.selections) {
localStorage.selections = JSON.stringify({});
}
const selections = JSON.parse(localStorage.selections);
if (!selections[dataType]) {
selections[dataType] = {};
}
if (!selections[dataType][stepId]) {
selections[dataType][stepId] = {};
}
selections[dataType][stepId][selectId] = selectedValues;
localStorage.selections = JSON.stringify(selections);
}
// 在初始化时为每种数据类型初始化完成状态
function init() {
// 为每种数据类型初始化完成状态
dataTypes.forEach(type => {
dataTypeCompletedSteps[type.id] = ['input']; // 每种数据类型默认只有input完成
});
// 更新当前时间
updateCurrentTime();
setInterval(updateCurrentTime, 1000);
// 初始化自定义下拉框
initCustomSelects();
// 初始化各面板内容
initDataTypes();
initProcessFlow();
initRightPanelFlow();
initResourceMonitor();
initDatabaseContent('preprocessing'); // 默认显示预处理节点的数据
initSystemLogs(); // 初始化系统日志
// 初始化处理流程图
initProcessFlow();
// 初始化任务概要饼图
initTaskChart();
// 初始化配置按钮事件
initConfigButtons();
// 初始化模态框事件
initModal();
// 默认选中预处理节点
setTimeout(() => {
// 设置右侧面板默认选中预处理
document.querySelectorAll('.process-step').forEach(step => {
step.classList.remove('active');
if (step.dataset.id === 'preprocessing') {
step.classList.add('active');
}
});
// 设置中间流程图默认选中预处理
updateProcessChartHighlight('preprocessing');
// 更新当前选中节点
currentSelectedNode = 'preprocessing';
}, 100);
}
// 更新当前时间
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;
}
// 修改 initDataTypes 函数,在切换数据类型时确保正确更新按钮状态
function initDataTypes() {
const container = document.getElementById('dataTypesList');
container.innerHTML = '';
dataTypes.forEach((type, index) => {
const item = document.createElement('div');
item.className = 'data-type-item';
item.dataset.id = type.id;
// 默认第一个选中
if (index === 0) {
item.classList.add('active');
currentDataType = type.id;
}
item.innerHTML = `
<div class="data-type-name">${type.name}</div>
<div class="data-type-desc">${type.description}</div>
<div class="data-type-stats">
<span class="stat-badge">数据量: ${type.count.toLocaleString()}</span>
<span class="stat-badge">状态: ${getStatusText(type.status)}</span>
</div>
`;
item.addEventListener('click', function () {
// 移除其他项的选中状态
document.querySelectorAll('.data-type-item').forEach(el => {
el.classList.remove('active');
});
// 添加当前项的选中状态
this.classList.add('active');
// 更新当前数据类型
currentDataType = type.id;
// 重新初始化右侧流程面板以更新步骤描述
initRightPanelFlow();
// 更新处理模式下拉框
updateProcessingModes();
// 更新特征提取算法下拉框
updateFeatureAlgorithms();
// 更新配置按钮可见性
updateConfigButtonVisibility();
});
container.appendChild(item);
});
}
// 获取状态文本
function getStatusText(status) {
const statusMap = {
'processing': '处理中',
'completed': '已完成',
'pending': '待处理'
};
return statusMap[status] || status;
}
// 初始化中间处理流程(横向布局)
function initProcessFlow() {
const container = document.getElementById('processChart');
// 创建ECharts实例
chart = echarts.init(container, null, { renderer: 'canvas' });
// 配置项
const option = {
backgroundColor: 'rgba(12, 26, 45, 0.1)', // 设置为半透明背景
tooltip: {
trigger: 'item',
triggerOn: 'mousemove'
},
series: [
{
type: 'graph',
layout: 'none',
symbolSize: 120,
roam: true,
label: {
show: true,
position: 'inside',
formatter: '{b}',
fontSize: 14,
color: '#ffffff' // 默认字体颜色为白色
},
edgeSymbol: ['circle', 'arrow'],
edgeSymbolSize: [4, 10],
edgeLabel: {
fontSize: 20
},
data: processSteps.map(step => ({
name: step.name,
x: step.x,
y: step.y,
symbolSize: 80,
itemStyle: {
color: step.id === 'preprocessing' ? '#ffd700' : '#1e3a8a' // 默认预处理高亮
},
// 添加标签颜色设置
label: {
color: step.id === 'preprocessing' ? '#000000' : '#ffffff' // 选中时为黑色,未选中时为白色
}
})),
links: processSteps.slice(0, -1).map((step, index) => ({
source: index,
target: index + 1
})),
lineStyle: {
opacity: 0.9,
width: 2,
curveness: 0
}
}
]
};
// 设置配置项
chart.setOption(option);
// 添加点击事件
chart.on('click', function (params) {
if (params.dataType === 'node') {
const stepId = processSteps[params.dataIndex].id;
// 更新右侧流程节点选中状态
document.querySelectorAll('.process-step').forEach(step => {
step.classList.remove('active');
if (step.dataset.id === stepId) {
step.classList.add('active');
}
});
// 更新当前选中节点
currentSelectedNode = stepId;
// 更新产品数据库内容
initDatabaseContent(stepId);
}
});
}
// 修改 initRightPanelFlow 函数,确保在初始化时正确设置按钮可见性
function initRightPanelFlow() {
const container = document.getElementById('processFlow');
container.innerHTML = '';
rightPanelSteps.forEach((step, index) => {
const stepElement = document.createElement('div');
stepElement.className = 'process-step';
stepElement.dataset.id = step.id;
// 默认预处理节点选中
if (step.id === 'preprocessing') {
stepElement.classList.add('active');
}
// 根据当前数据类型获取对应的步骤描述
const description = stepDescriptions[currentDataType] && stepDescriptions[currentDataType][step.id]
? stepDescriptions[currentDataType][step.id]
: step.description;
stepElement.innerHTML = `
<div class="process-step-icon">
<i class="${step.icon}"></i>
</div>
<div class="process-step-info">
<div class="process-step-name">${step.name}</div>
<div class="process-step-desc">${description}</div>
</div>
${step.id !== 'input' ? `<button class="config-button" data-step="${step.id}">处理配置</button>` : ''}
`;
stepElement.addEventListener('click', function () {
// 移除其他节点的选中状态
document.querySelectorAll('.process-step').forEach(el => {
el.classList.remove('active');
});
// 添加当前节点的选中状态
this.classList.add('active');
// 更新当前选中节点
currentSelectedNode = step.id;
// 更新产品数据库内容
initDatabaseContent(step.id);
// 更新中间流程图高亮
updateProcessChartHighlight(step.id);
});
container.appendChild(stepElement);
});
// 重新初始化配置按钮事件监听器
setTimeout(() => {
initConfigButtons();
// 初始化配置按钮显示逻辑
updateConfigButtonVisibility();
// 只有预处理步骤在初始状态下显示配置按钮(如果它未完成)
const completedSteps = dataTypeCompletedSteps[currentDataType] || ['input'];
const preprocessingButton = document.querySelector('.config-button[data-step="preprocessing"]');
if (preprocessingButton && !completedSteps.includes('preprocessing')) {
preprocessingButton.classList.add('visible');
}
}, 0);
}
// 更新中间流程图高亮
function updateProcessChartHighlight(selectedId) {
const option = chart.getOption();
option.series[0].data = processSteps.map(step => ({
name: step.name,
x: step.x,
y: step.y,
symbolSize: 80,
itemStyle: {
color: step.id === selectedId ? '#ffd700' : '#1e3a8a'
},
// 添加标签颜色设置,选中时为黑色,未选中时为白色
label: {
color: step.id === selectedId ? '#000000' : '#ffffff'
}
}));
chart.setOption(option);
}
// 初始化资源监控
function initResourceMonitor() {
const container = document.getElementById('resourceMonitor');
container.innerHTML = '';
resourceData.forEach(resource => {
const item = document.createElement('div');
item.className = 'resource-item';
item.innerHTML = `
<div class="resource-name">${resource.name}</div>
<div class="resource-bar">
<div class="resource-fill ${resource.class}" style="width: ${resource.value}%"></div>
</div>
<div class="resource-percent">${resource.value}%</div>
`;
container.appendChild(item);
});
}
// 修改初始化产品数据库内容函数
function initDatabaseContent(stepId) {
const container = document.getElementById('databaseContent');
container.innerHTML = '';
// 根据当前数据类型获取对应的产品数据
const products = databaseProducts[currentDataType] && databaseProducts[currentDataType][stepId]
? databaseProducts[currentDataType][stepId]
: [];
if (products.length === 0) {
container.innerHTML = '<div style="color: #a0d2ff; text-align: center; padding: 20px;">暂无数据</div>';
return;
}
products.forEach(product => {
const item = document.createElement('div');
item.className = 'database-item';
item.innerHTML = `
<div class="database-name">${product.name}</div>
<div class="database-info">
<span>${product.size}</span>
<span>${product.time}</span>
</div>
<div class="database-description" style="font-size: 0.75rem; color: #81d4fa; margin-top: 5px;">
${product.description}
</div>
`;
container.appendChild(item);
});
}
// 初始化系统日志
function initSystemLogs() {
const container = document.getElementById('logContent');
container.innerHTML = '';
// 添加初始日志
systemLogs.forEach(log => {
const entry = document.createElement('div');
entry.className = `log-entry log-${log.level}`;
entry.innerHTML = `
<span class="log-time">${log.time}</span>
<span class="log-${log.level}">${log.message}</span>
`;
container.appendChild(entry);
});
// 滚动到底部
container.scrollTop = container.scrollHeight;
// 启动动态日志更新
startLogUpdates();
}
// 启动日志更新
function startLogUpdates() {
// 如果已有更新任务,先清除
if (logUpdateInterval) {
clearInterval(logUpdateInterval);
}
// 定期添加新日志
logUpdateInterval = setInterval(() => {
addRandomLogEntry();
}, 5000); // 每5秒添加一条新日志
// 初始日志在1秒后添加
setTimeout(() => {
addLogEntry('开始执行遥感数据预处理任务', 'info');
}, 1000);
}
// 添加随机日志条目
function addRandomLogEntry() {
const logMessages = [
{ msg: '数据块 #A7B2 处理完成', type: 'info' },
{ msg: '三维插值算法执行成功', type: 'info' },
{ msg: '叶绿素反演计算完成', type: 'info' },
{ msg: '警告: 数据源 浮标-003 信号弱', type: 'warning' },
{ msg: '产品数据已存储到数据库', type: 'info' },
{ msg: '质量检验通过率: 98.5%', type: 'info' },
{ msg: '开始处理海洋气象类数据', type: 'info' },
{ msg: '潮位数据校准完成', type: 'info' },
{ msg: '波浪谱分析完成', type: 'info' },
{ msg: '注意: 盐度数据存在异常波动', type: 'warning' },
{ msg: '错误: 浮游植物计数设备离线', type: 'error' },
{ msg: '数据融合算法执行中...', type: 'info' },
{ msg: '模式输出数据接收正常', type: 'info' },
{ msg: '预报场生成完成', type: 'info' }
];
const randomMessage = logMessages[Math.floor(Math.random() * logMessages.length)];
addLogEntry(randomMessage.msg, randomMessage.type);
}
// 添加日志条目
function addLogEntry(message, type = 'info') {
const container = document.getElementById('logContent');
if (!container) return;
const now = new Date();
const timeString = now.toLocaleTimeString('zh-CN', {
hour12: false,
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
const logEntry = document.createElement('div');
logEntry.className = `log-entry log-${type}`;
logEntry.innerHTML = `
<span class="log-time">${timeString}</span>
<span class="log-${type}">${message}</span>
`;
// 添加到容器顶部
container.insertBefore(logEntry, container.firstChild);
// 限制日志条数为20条
while (container.children.length > 20) {
container.removeChild(container.lastChild);
}
// 滚动到顶部显示最新日志
container.scrollTop = 0;
}
// 修改 initTaskChart 函数,将饼图替换为柱状图
function initTaskChart() {
const chartContainer = document.getElementById('taskChart');
// 创建ECharts实例
taskChart = echarts.init(chartContainer, null, { renderer: 'canvas' });
// 配置项
const option = {
backgroundColor: 'transparent',
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
grid: {
top: '8%',
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
data: ['总任务数', '进行中', '已完成', '待处理'],
axisTick: {
alignWithLabel: true
},
axisLabel: {
color: '#e0f0ff'
},
axisLine: {
lineStyle: {
color: 'rgba(79, 195, 247, 0.5)'
}
}
},
yAxis: {
type: 'value',
axisLabel: {
color: '#e0f0ff'
},
axisLine: {
lineStyle: {
color: 'rgba(79, 195, 247, 0.5)'
}
},
splitLine: {
lineStyle: {
color: 'rgba(79, 195, 247, 0.2)'
}
}
},
series: [{
type: 'bar',
barWidth: '30%',
data: [
{
value: 24,
itemStyle: { color: '#4fc3f7' }
},
{
value: 8,
itemStyle: { color: '#ffd54f' }
},
{
value: 12,
itemStyle: { color: '#66bb6a' }
},
{
value: 4,
itemStyle: { color: '#ff7043' }
}
],
label: {
show: true,
position: 'top',
color: '#e0f0ff',
fontWeight: 'bold'
},
emphasis: {
focus: 'series'
},
itemStyle: {
borderRadius: [4, 4, 0, 0]
}
}]
};
// 设置配置项
taskChart.setOption(option);
}
// 初始化配置按钮
function initConfigButtons() {
// 为每个配置按钮添加事件监听器
document.querySelectorAll('.config-button').forEach(button => {
button.addEventListener('click', function () {
const stepId = this.dataset.step;
openConfigModal(stepId);
});
});
}
// 修改 updateConfigButtonVisibility 函数,使用当前数据类型的完成状态
function updateConfigButtonVisibility() {
// 获取当前数据类型的完成步骤
const completedSteps = dataTypeCompletedSteps[currentDataType] || ['input'];
// 获取所有步骤ID
const allSteps = rightPanelSteps.map(step => step.id);
// 遍历每个步骤,检查是否应该显示配置按钮
allSteps.forEach((stepId, index) => {
// 跳过数据输入步骤(不需要配置按钮)
if (stepId === 'input') return;
const button = document.querySelector(`.config-button[data-step="${stepId}"]`);
if (!button) return;
// 如果当前步骤已完成,则隐藏按钮
if (completedSteps.includes(stepId)) {
button.classList.remove('visible');
return;
}
// 获取前一个步骤
const prevStepId = allSteps[index - 1];
// 如果前一个步骤已完成,则显示当前步骤的配置按钮
if (completedSteps.includes(prevStepId)) {
button.classList.add('visible');
} else {
button.classList.remove('visible');
}
});
}
// 修改 openConfigModal 函数,确保每次打开都重置选择
function openConfigModal(stepId) {
const modal = document.getElementById('configModal');
const title = document.getElementById('modalTitle');
// 设置标题
const step = rightPanelSteps.find(s => s.id === stepId);
if (step) {
title.textContent = `${step.name}配置`;
}
// 更新当前选中的节点
currentSelectedNode = stepId;
// 重置隐藏输入框的值
document.getElementById('processingMode').value = '';
document.getElementById('featureAlgorithm').value = '';
// 更新处理模式下拉框
updateProcessingModes();
// 更新特征提取算法下拉框
updateFeatureAlgorithms();
// 更新触发器文本显示
updateTriggerText('processingModeSelect', 'processingMode');
updateTriggerText('featureAlgorithmSelect', 'featureAlgorithm');
// 显示模态框
modal.style.display = 'flex';
}
// 更新处理模式选项
function updateProcessingModes() {
const modeOptionsContainer = document.getElementById('processingModeOptions');
if (!modeOptionsContainer) return;
modeOptionsContainer.innerHTML = '';
// 根据当前数据类型和处理步骤获取对应的处理模式
const modes = processingModes[currentDataType] && processingModes[currentDataType][currentSelectedNode]
? processingModes[currentDataType][currentSelectedNode]
: [];
// 获取当前节点已保存的选项(如果有的话)
const savedSelections = getSavedSelections(currentDataType, currentSelectedNode, 'processingMode');
modes.forEach(mode => {
const option = document.createElement('div');
option.className = 'select-option';
option.dataset.value = mode;
option.textContent = mode;
// 检查是否已保存选中
if (savedSelections.includes(mode)) {
option.classList.add('selected');
}
option.addEventListener('click', function (e) {
e.stopPropagation();
this.classList.toggle('selected');
updateHiddenInput('processingMode');
updateTriggerText('processingModeSelect', 'processingMode');
});
modeOptionsContainer.appendChild(option);
});
// 更新触发器文本
updateTriggerText('processingModeSelect', 'processingMode');
}
// 添加获取已保存选择的函数
function getSavedSelections(dataType, stepId, selectId) {
try {
if (!localStorage.selections) return [];
const selections = JSON.parse(localStorage.selections);
if (selections[dataType] && selections[dataType][stepId] && selections[dataType][stepId][selectId]) {
return selections[dataType][stepId][selectId];
}
} catch (e) {
console.error('Error reading saved selections:', e);
}
return [];
}
// 更新隐藏输入框的值
function updateHiddenInput(selectId) {
const selectElement = document.getElementById(selectId + 'Select');
const hiddenInput = document.getElementById(selectId);
if (!selectElement || !hiddenInput) return;
const selectedOptions = selectElement.querySelectorAll('.select-option.selected');
const selectedValues = Array.from(selectedOptions).map(option => option.dataset.value);
hiddenInput.value = selectedValues.join(',');
}
// 更新触发器显示文本
function updateTriggerText(selectElementId, hiddenInputId) {
const selectElement = document.getElementById(selectElementId);
const trigger = selectElement ? selectElement.querySelector('.select-trigger-text') : null;
const hiddenInput = document.getElementById(hiddenInputId);
if (!trigger || !hiddenInput) return;
const selectedValues = hiddenInput.value ? hiddenInput.value.split(',') : [];
if (selectedValues.length === 0) {
trigger.textContent = '请选择';
} else if (selectedValues.length === 1) {
trigger.textContent = selectedValues[0];
} else {
trigger.textContent = `${selectedValues.length} 项已选择`;
}
}
// 修改 initModal 函数中的表单提交处理
function initModal() {
const modal = document.getElementById('configModal');
const closeBtn = document.querySelector('.modal-close');
const cancelBtn = document.getElementById('cancelConfig');
const form = document.getElementById('configForm');
// 关闭模态框
function closeModal() {
modal.style.display = 'none';
}
// 关闭按钮事件
closeBtn.addEventListener('click', closeModal);
cancelBtn.addEventListener('click', closeModal);
// 点击背景关闭
modal.addEventListener('click', function (e) {
if (e.target === modal) {
closeModal();
}
});
// 修改表单提交事件处理函数
form.addEventListener('submit', function (e) {
e.preventDefault();
// 保存当前选择
saveSelections(currentDataType, currentSelectedNode, 'processingMode');
saveSelections(currentDataType, currentSelectedNode, 'featureAlgorithm');
// 获取选中的处理模式
const processingModeValue = document.getElementById('processingMode').value;
const selectedModes = processingModeValue ? processingModeValue.split(',') : [];
// 获取选中的特征提取算法
const featureAlgorithmValue = document.getElementById('featureAlgorithm').value;
const selectedAlgorithms = featureAlgorithmValue ? featureAlgorithmValue.split(',') : [];
console.log('选中的处理模式:', selectedModes);
console.log('选中的特征提取算法:', selectedAlgorithms);
// 这里可以添加保存配置的逻辑
console.log('配置已保存');
// 获取当前数据类型的完成步骤
let completedSteps = dataTypeCompletedSteps[currentDataType] || ['input'];
// 模拟完成当前步骤
if (currentSelectedNode && !completedSteps.includes(currentSelectedNode)) {
completedSteps.push(currentSelectedNode);
// 更新当前数据类型的完成步骤
dataTypeCompletedSteps[currentDataType] = completedSteps;
// 自动切换到下一个节点
const currentIndex = rightPanelSteps.findIndex(step => step.id === currentSelectedNode);
if (currentIndex < rightPanelSteps.length - 1) {
const nextStep = rightPanelSteps[currentIndex + 1];
// 更新右侧面板选中状态
document.querySelectorAll('.process-step').forEach(step => {
step.classList.remove('active');
if (step.dataset.id === nextStep.id) {
step.classList.add('active');
}
});
// 更新当前选中节点
currentSelectedNode = nextStep.id;
// 更新产品数据库内容
initDatabaseContent(nextStep.id);
// 更新中间流程图高亮
updateProcessChartHighlight(nextStep.id);
} else {
// 如果当前是最后一个步骤(数据输出),则更新左侧数据类型状态为已完成
updateDataTypeStatusToCompleted();
}
}
// 更新配置按钮可见性(必须在 closeModal 之前调用)
updateConfigButtonVisibility();
closeModal();
});
}
// 添加更新数据类型状态为已完成的函数
function updateDataTypeStatusToCompleted() {
// 更新数据类型状态
const dataType = dataTypes.find(type => type.id === currentDataType);
if (dataType) {
dataType.status = 'completed';
// 更新左侧数据类型面板的显示
const dataTypeElement = document.querySelector(`.data-type-item[data-id="${currentDataType}"]`);
if (dataTypeElement) {
const statusBadge = dataTypeElement.querySelector('.stat-badge:last-child');
if (statusBadge) {
statusBadge.textContent = `状态: 已完成`;
}
}
}
}
// 初始化自定义多选下拉框
function initCustomSelects() {
// 处理模式下拉框
const processingModeSelect = document.getElementById('processingModeSelect');
if (processingModeSelect) {
const processingModeTrigger = processingModeSelect.querySelector('.select-trigger');
const processingModeDropdown = processingModeSelect.querySelector('.select-dropdown');
processingModeTrigger.addEventListener('click', function (e) {
e.stopPropagation();
// 关闭其他下拉框
const featureAlgorithmSelect = document.getElementById('featureAlgorithmSelect');
if (featureAlgorithmSelect && featureAlgorithmSelect.classList.contains('open')) {
featureAlgorithmSelect.classList.remove('open');
}
// 切换当前下拉框
processingModeSelect.classList.toggle('open');
});
// 阻止下拉框内容区域的点击事件冒泡
processingModeDropdown.addEventListener('click', function (e) {
e.stopPropagation();
});
}
// 特征提取算法下拉框
const featureAlgorithmSelect = document.getElementById('featureAlgorithmSelect');
if (featureAlgorithmSelect) {
const featureAlgorithmTrigger = featureAlgorithmSelect.querySelector('.select-trigger');
const featureAlgorithmDropdown = featureAlgorithmSelect.querySelector('.select-dropdown');
featureAlgorithmTrigger.addEventListener('click', function (e) {
e.stopPropagation();
// 关闭其他下拉框
const processingModeSelect = document.getElementById('processingModeSelect');
if (processingModeSelect && processingModeSelect.classList.contains('open')) {
processingModeSelect.classList.remove('open');
}
// 切换当前下拉框
featureAlgorithmSelect.classList.toggle('open');
});
// 阻止下拉框内容区域的点击事件冒泡
featureAlgorithmDropdown.addEventListener('click', function (e) {
e.stopPropagation();
});
}
// 点击页面其他地方关闭下拉框
document.addEventListener('click', function () {
if (processingModeSelect) {
processingModeSelect.classList.remove('open');
}
if (featureAlgorithmSelect) {
featureAlgorithmSelect.classList.remove('open');
}
});
}
// 页面加载完成后初始化
window.addEventListener('DOMContentLoaded', init);
</script>
</body>
</html>