|
|
<!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">×</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> |