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

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!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>