Repository navigation
Expand file tree
/
Copy pathmonitor.html
More file actions
221 lines (196 loc) · 12.9 KB
/
Copy pathmonitor.html
File metadata and controls
221 lines (196 loc) · 12.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
<!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://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
<style>
body, html { margin: 0; padding: 0; width: 100%; height: 100%; background-color: #010a15; font-family: 'Microsoft YaHei', sans-serif; color: #fff; overflow: hidden; }
.header { height: 60px; background: linear-gradient(to right, rgba(0, 34, 62, 0.9), rgba(0, 89, 136, 0.9)); border-bottom: 2px solid #00f0ff; display: flex; align-items: center; justify-content: space-between; padding: 0 30px; box-shadow: 0 5px 15px rgba(0, 240, 255, 0.2); }
.header-title { font-size: 24px; font-weight: bold; color: #fff; text-shadow: 0 0 10px #00f0ff; }
.nav-btn { background: rgba(0, 240, 255, 0.2); border: 1px solid #00f0ff; color: #fff; padding: 8px 20px; border-radius: 4px; cursor: pointer; text-decoration: none; font-size: 14px;}
.nav-btn:hover { background: #00f0ff; color: #000; }
.main-content { display: flex; width: 100%; height: calc(100% - 60px); padding: 15px; box-sizing: border-box; gap: 15px; }
/* 左侧面板:完美适配你画圈的位置,并增加溢出滚动保护 */
.panel { background: rgba(5, 20, 35, 0.8); border: 1px solid #007bb5; border-radius: 8px; padding: 15px; display: flex; flex-direction: column; gap: 15px; }
.left-panel { width: 25%; overflow-y: auto; }
.right-panel { width: 35%; }
/* 中心区域:保留完整的 3D 大地图 */
.center-panel { flex-grow: 1; position: relative; background-image: url('factory-bg.png'); background-size: cover; background-position: center; border: 1px solid #007bb5; border-radius: 8px; overflow: hidden; }
.center-panel::after { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(1, 10, 21, 0.6); pointer-events: none; }
.section-title { font-size: 16px; color: #00f0ff; border-left: 4px solid #00f0ff; padding-left: 10px; margin-bottom: 10px; font-weight: bold; }
.info-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.info-card { background: rgba(0, 240, 255, 0.1); border: 1px solid rgba(0, 240, 255, 0.3); padding: 10px; border-radius: 4px; text-align: center; }
.info-card span { display: block; font-size: 12px; color: #94a3b8; margin-bottom: 5px; }
.info-card strong { font-size: 16px; color: #00f0ff; }
/* ================= 新增:左下角迷你监控网格样式 ================= */
.cctv-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 5px;}
.cctv-item { background: #000; border: 1px solid rgba(0, 240, 255, 0.3); border-radius: 4px; position: relative; overflow: hidden; aspect-ratio: 16 / 9; /* 强制16:9比例,不会被照片原尺寸撑爆 */ }
.cctv-item img { width: 100%; height: 100%; object-fit: cover; opacity: 0.75; transition: all 0.3s; filter: contrast(1.1) brightness(0.9);}
.cctv-item:hover img { opacity: 1; transform: scale(1.05); }
.cam-label { position: absolute; top: 4px; left: 4px; font-size: 9px; color: #0f0; background: rgba(0,0,0,0.7); padding: 2px 5px; border-radius: 2px; font-family: monospace; z-index: 10;}
.cam-rec { position: absolute; top: 6px; right: 6px; width: 6px; height: 6px; background: red; border-radius: 50%; animation: blink 1s infinite; z-index: 10;}
@keyframes blink { 50% { opacity: 0; } }
/* ============================================================== */
.data-table-container { flex: 1; overflow-y: auto; position: relative;}
table { width: 100%; border-collapse: collapse; font-size: 12px; text-align: center; }
th { color: #00f0ff; padding: 8px 5px; background: #051a2e; position: sticky; top: 0; z-index: 2;}
td { color: #ccc; padding: 8px 5px; border-bottom: 1px solid rgba(0, 123, 181, 0.3); }
#trend-chart { width: 100%; height: 250px; }
.map-dot { position: absolute; width: 16px; height: 16px; background: #00f0ff; border-radius: 50%; box-shadow: 0 0 15px #00f0ff; z-index: 2; transform: translate(-50%, -50%); cursor: pointer; transition: all 0.3s;}
.map-dot.active { background: #ffaa00; box-shadow: 0 0 20px #ffaa00; transform: translate(-50%, -50%) scale(1.3); }
.map-label { position: absolute; top: -30px; left: 50%; transform: translateX(-50%); background: rgba(0,0,0,0.8); border: 1px solid #00f0ff; padding: 4px 10px; border-radius: 4px; font-size: 12px; white-space: nowrap; color: #fff;}
.map-dot.active .map-label { border-color: #ffaa00; color: #ffaa00; }
</style>
</head>
<body>
<div class="header">
<div class="header-title">物联网水质实时监测系统</div>
<a href="index.html" class="nav-btn">返回数字孪生主页</a>
</div>
<div class="main-content">
<div class="panel left-panel">
<div class="section-title">物联网感知网络状态</div>
<div class="info-grid">
<div class="info-card"><span>在线传感器数</span><strong>12 / 12</strong></div>
<div class="info-card"><span>边缘计算网关</span><strong style="color:#0f0;">正常运行</strong></div>
<div class="info-card"><span>数据采集频率</span><strong>高频实时 (1s)</strong></div>
<div class="info-card"><span>视频监控在线</span><strong>4 / 4</strong></div>
</div>
<div class="section-title" id="left-title" style="margin-top: 5px;">当前选中节点:生化段</div>
<div class="info-grid" style="grid-template-columns: 1fr;">
<div class="info-card" style="text-align: left; padding: 12px;">
<span style="color:#00f0ff; font-size:13px; margin-bottom:5px;">多参数传感器阵列运行中...</span>
<div style="color:#ccc; font-size:12px; line-height: 1.6;">
• 在线监测仪: 实时读数<br>
• 视频码流: RTSP 解析正常<br>
• 数据源正在动态监听。
</div>
</div>
</div>
<div class="section-title" style="margin-top: 5px;">现场监控 (CCTV)</div>
<div class="cctv-grid">
<div class="cctv-item">
<img src="cam-1.jpg" alt="监测间">
<div class="cam-label">CAM 1 监测间</div>
<div class="cam-rec"></div>
</div>
<div class="cctv-item">
<img src="cam-2.jpg" alt="泵房">
<div class="cam-label">CAM 2 进水泵房</div>
<div class="cam-rec"></div>
</div>
<div class="cctv-item">
<img src="cam-3.jpg" alt="曝气池">
<div class="cam-label">CAM 3 曝气池</div>
<div class="cam-rec"></div>
</div>
<div class="cctv-item">
<img src="cam-4.jpg" alt="沉淀池">
<div class="cam-label">CAM 4 二沉池</div>
<div class="cam-rec"></div>
</div>
</div>
</div>
<div class="center-panel">
<div class="map-dot" id="dot-inlet" style="top: 55%; left: 75%;" onclick="switchNode('inlet')">
<div class="map-label">进水口</div>
</div>
<div class="map-dot active" id="dot-biochemical" style="top: 45%; left: 18%;" onclick="switchNode('biochemical')">
<div class="map-label">生化段</div>
</div>
<div class="map-dot" id="dot-outlet" style="top: 70%; left: 38%;" onclick="switchNode('outlet')">
<div class="map-label">出水口</div>
</div>
</div>
<div class="panel right-panel">
<div class="section-title" id="right-title">实时规范数据集 (生化段)</div>
<div class="data-table-container">
<table id="data-table">
<thead>
<tr><th>采集时间</th><th>COD (mg/L)</th><th>氨氮 (mg/L)</th><th>总氮 (mg/L)</th><th>总磷 (mg/L)</th><th>pH值</th></tr>
</thead>
<tbody id="table-body"></tbody>
</table>
</div>
<div class="section-title" style="margin-top: 10px;">历史数据趋势 (COD & 氨氮)</div>
<div id="trend-chart"></div>
</div>
</div>
<script>
const nodeConfig = {
'inlet': { name: '进水口', file: 'inlet-data.json' },
'biochemical': { name: '生化段', file: 'water-data.json' },
'outlet': { name: '出水口', file: 'outlet-data.json' }
};
let myChart = null;
function switchNode(nodeKey) {
const config = nodeConfig[nodeKey];
document.getElementById('left-title').innerText = `当前选中节点:${config.name}`;
document.getElementById('right-title').innerText = `实时规范数据集 (${config.name})`;
document.querySelectorAll('.map-dot').forEach(dot => dot.classList.remove('active'));
document.getElementById(`dot-${nodeKey}`).classList.add('active');
fetchWaterData(config.file);
}
function fetchWaterData(fileName) {
fetch(fileName)
.then(response => {
if (!response.ok) { throw new Error('找不到文件'); }
return response.json();
})
.then(realData => {
renderTable(realData);
renderChart(realData);
})
.catch(error => {
console.error("获取数据失败:", error);
document.getElementById('table-body').innerHTML = `<tr><td colspan="6" style="color:#ffaa00;">数据加载失败,请确保您已开启本地服务器并存在 ${fileName}</td></tr>`;
});
}
function renderTable(data) {
const tbody = document.getElementById('table-body');
tbody.innerHTML = '';
data.forEach(item => {
const tr = document.createElement('tr');
const codColor = item.cod > 40 ? '#ef4444' : '#ffaa00';
tr.innerHTML = `
<td>${item.time}</td>
<td style="color:${codColor}; font-weight:bold;">${item.cod}</td>
<td>${item.nh3n}</td>
<td>${item.tn}</td>
<td>${item.tp}</td>
<td style="color:#00f0ff;">${item.ph}</td>
`;
tbody.appendChild(tr);
});
}
function renderChart(data) {
const xTimes = data.map(item => item.time).reverse();
const yCodData = data.map(item => item.cod).reverse();
const yNh3nData = data.map(item => item.nh3n).reverse();
const chartDom = document.getElementById('trend-chart');
if (!myChart) {
myChart = echarts.init(chartDom);
window.addEventListener('resize', () => myChart.resize());
}
const option = {
grid: { left: '5%', right: '5%', bottom: '5%', top: '15%', containLabel: true },
tooltip: { trigger: 'axis' },
legend: { data: ['COD', '氨氮'], textStyle: { color: '#fff' } },
xAxis: { type: 'category', data: xTimes, axisLine: { lineStyle: { color: '#007bb5' } }, axisLabel: { color: '#94a3b8' } },
yAxis: [
{ type: 'value', name: 'COD', splitLine: { lineStyle: { color: 'rgba(0, 123, 181, 0.2)' } }, axisLabel: { color: '#94a3b8' } },
{ type: 'value', name: '氨氮', splitLine: { show: false }, axisLabel: { color: '#94a3b8' } }
],
series: [
{ name: 'COD', data: yCodData, type: 'line', smooth: true, yAxisIndex: 0, itemStyle: { color: '#ffaa00' }, areaStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: 'rgba(255, 170, 0, 0.4)' }, { offset: 1, color: 'rgba(255, 170, 0, 0)' }]) } },
{ name: '氨氮', data: yNh3nData, type: 'line', smooth: true, yAxisIndex: 1, itemStyle: { color: '#00f0ff' } }
]
};
myChart.setOption(option, true);
}
switchNode('biochemical');
</script>
</body>
</html>