go_rain_dtu/static/index.html
2025-05-15 18:17:20 +08:00

442 lines
16 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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/chart.js"></script>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
.header {
padding: 10px;
text-align: center;
border-bottom: 1px solid #ddd;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 15px;
}
.controls {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 20px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
background-color: #fff;
}
.control-group {
display: flex;
align-items: center;
gap: 5px;
}
select, input, button {
padding: 5px 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
button {
background-color: #007bff;
color: white;
border: none;
cursor: pointer;
transition: background-color 0.3s;
}
button:hover {
background-color: #0056b3;
}
.chart-container {
margin-bottom: 20px;
border: 1px solid #ddd;
border-radius: 4px;
padding: 15px;
background-color: #fff;
}
.table-container {
overflow-x: auto;
margin-top: 20px;
border: 1px solid #ddd;
border-radius: 4px;
background-color: #fff;
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
border: 1px solid #ddd;
padding: 12px 8px;
text-align: left;
}
th {
background-color: #f8f9fa;
font-weight: bold;
}
tr:nth-child(even) {
background-color: #f9f9f9;
}
tr:hover {
background-color: #f5f5f5;
}
@media (max-width: 768px) {
.controls {
flex-direction: column;
}
.control-group {
width: 100%;
}
select, input {
width: 100%;
}
}
</style>
</head>
<body>
<div class="header">
<h1>雨量计数据</h1>
<div id="connectionStatus" style="display: inline-block; padding: 5px 10px; border-radius: 4px; margin-left: 10px; background-color: red; color: white;">
未连接
</div>
</div>
<div class="container">
<div class="controls">
<div class="control-group">
<label for="interval">数据粒度:</label>
<select id="interval">
<option value="5min">5分钟</option>
<option value="30min">30分钟</option>
<option value="1hour" selected>1小时</option>
</select>
</div>
<div class="control-group">
<label for="startDate">开始时间:</label>
<input type="datetime-local" id="startDate">
</div>
<div class="control-group">
<label for="endDate">结束时间:</label>
<input type="datetime-local" id="endDate">
</div>
<div class="control-group">
<button onclick="queryData()">查询</button>
<button onclick="exportData()">导出数据</button>
</div>
</div>
<div class="chart-container">
<canvas id="mainChart"></canvas>
</div>
<div class="table-container">
<table>
<thead>
<tr>
<th>时间</th>
<th>降雨量(mm)</th>
<th>温度(℃)</th>
<th>湿度(%)</th>
<th>风速(m/s)</th>
<th>大气压(kPa)</th>
<th>太阳辐射(W/m²)</th>
</tr>
</thead>
<tbody id="tableBody"></tbody>
</table>
</div>
</div>
<script>
let mainChart = null;
let connectionCheckTimer = null;
// 检查连接状态
function checkConnectionStatus() {
fetch('/api/status')
.then(response => response.json())
.then(data => {
const statusElem = document.getElementById('connectionStatus');
if (data.connected) {
statusElem.style.backgroundColor = 'green';
statusElem.textContent = `已连接: ${data.ip}:${data.port}`;
} else {
statusElem.style.backgroundColor = 'red';
statusElem.textContent = '未连接';
}
})
.catch(error => {
console.error('获取连接状态失败:', error);
const statusElem = document.getElementById('connectionStatus');
statusElem.style.backgroundColor = 'red';
statusElem.textContent = '状态未知';
});
}
// 初始化日期选择器
function initDatePickers() {
// 获取当前北京时间UTC+8
const now = new Date();
// 设置开始时间为当天的0点北京时间
const today = new Date(now);
today.setHours(0, 0, 0, 0);
document.getElementById('startDate').value = formatDateTime(today);
document.getElementById('endDate').value = formatDateTime(now);
}
// 格式化日期时间为中国时区UTC+8
function formatDateTime(date) {
// 转换为ISO字符串但不使用Z表示UTC而是使用+08:00表示中国时区
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
const hours = date.getHours().toString().padStart(2, '0');
const minutes = date.getMinutes().toString().padStart(2, '0');
return `${year}-${month}-${day}T${hours}:${minutes}`;
}
// 查询数据
function queryData() {
const interval = document.getElementById('interval').value;
const startDate = document.getElementById('startDate').value;
const endDate = document.getElementById('endDate').value;
// 确保时间格式符合后端要求,添加本地时区信息
const startDateTime = new Date(startDate).toISOString();
const endDateTime = new Date(endDate).toISOString();
fetch(`/api/data?interval=${interval}&start=${startDateTime}&end=${endDateTime}`)
.then(response => response.json())
.then(data => {
updateChart(data);
updateTable(data);
})
.catch(error => {
console.error('Error:', error);
alert('获取数据失败,请检查网络连接');
});
}
// 更新图表
function updateChart(data) {
const ctx = document.getElementById('mainChart').getContext('2d');
if (mainChart) {
mainChart.destroy();
}
// 按时间正序排列数据(从早到晚)
data.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp));
const labels = data.map(item => {
// 解析时间字符串为本地时间
const date = new Date(item.timestamp);
// 格式化为中文日期时间格式
return date.getFullYear() + '/' +
(date.getMonth() + 1).toString().padStart(2, '0') + '/' +
date.getDate().toString().padStart(2, '0') + ' ' +
date.getHours().toString().padStart(2, '0') + ':' +
date.getMinutes().toString().padStart(2, '0');
});
// 是否使用自适应范围
const useAdaptiveScale = 1;
// 计算降雨量和温度的范围
let rainfallMin = 0;
let rainfallMax = 50;
let tempMin = -10;
let tempMax = 40;
if (useAdaptiveScale && data.length > 0) {
// 找出温度的最小值和最大值,并添加一些边距
const temps = data.map(item => item.avg_temperature);
tempMin = Math.floor(Math.min(...temps) - 5);
tempMax = Math.ceil(Math.max(...temps) + 5);
// 找出降雨量的最大值,并添加一些边距
const rainfalls = data.map(item => item.rainfall);
rainfallMax = Math.ceil(Math.max(...rainfalls) * 1.2) || 10; // 如果最大值是0则默认为10
}
// 数据已经在DAO层正确转换了单位
// 不需要再做额外的单位转换
mainChart = new Chart(ctx, {
data: {
labels: labels,
datasets: [
{
type: 'bar',
label: '降雨量(mm)',
data: data.map(item => item.rainfall),
backgroundColor: 'rgba(54, 162, 235, 0.5)',
borderColor: 'rgba(54, 162, 235, 1)',
borderWidth: 1,
yAxisID: 'y-rainfall',
},
{
type: 'line',
label: '温度(℃)',
data: data.map(item => item.avg_temperature),
borderColor: 'rgb(255, 99, 132)',
backgroundColor: 'rgba(255, 99, 132, 0.5)',
tension: 0.1,
yAxisID: 'y-temp',
}
]
},
options: {
responsive: true,
interaction: {
mode: 'index',
intersect: false,
},
scales: {
'y-rainfall': {
type: 'linear',
position: 'left',
title: {
display: true,
text: '降雨量(mm)'
},
grid: {
drawOnChartArea: false
},
// 设置降雨量的范围
min: rainfallMin,
max: rainfallMax,
suggestedMax: Math.min(10, rainfallMax)
},
'y-temp': {
type: 'linear',
position: 'right',
title: {
display: true,
text: '温度(℃)'
},
// 设置温度的范围
min: tempMin,
max: tempMax,
suggestedMin: Math.max(0, tempMin),
suggestedMax: Math.min(30, tempMax)
}
}
}
});
}
// 更新表格
function updateTable(data) {
const tbody = document.getElementById('tableBody');
tbody.innerHTML = '';
// 按时间倒序排列数据(从晚到早),这样最新的数据在表格顶部
const sortedData = [...data].sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
sortedData.forEach(item => {
const row = document.createElement('tr');
// 解析时间字符串为本地时间
const date = new Date(item.timestamp);
const formattedDate =
date.getFullYear() + '/' +
(date.getMonth() + 1).toString().padStart(2, '0') + '/' +
date.getDate().toString().padStart(2, '0') + ' ' +
date.getHours().toString().padStart(2, '0') + ':' +
date.getMinutes().toString().padStart(2, '0') + ':' +
date.getSeconds().toString().padStart(2, '0');
// 根据原始TCP服务器中的单位换算
// 原始的风速是实际值的100倍
// 温度和湿度是实际值的10倍
// 大气压是实际值的10倍
// 太阳辐射是原始值
row.innerHTML = `
<td>${formattedDate}</td>
<td>${item.rainfall.toFixed(1)}</td>
<td>${item.avg_temperature.toFixed(1)}</td>
<td>${item.avg_humidity.toFixed(1)}</td>
<td>${item.avg_wind_speed.toFixed(2)}</td>
<td>${item.atm_pressure ? item.atm_pressure.toFixed(1) : 'N/A'}</td>
<td>${item.solar_radiation ? item.solar_radiation.toFixed(0) : 'N/A'}</td>
`;
tbody.appendChild(row);
});
}
// 导出数据
function exportData() {
// 从表格中获取完整数据
const tableRows = document.querySelectorAll('#tableBody tr');
let csv = '时间,降雨量(mm),温度(℃),湿度(%),风速(m/s),大气压(kPa),太阳辐射(W/m²)\n';
tableRows.forEach(row => {
const cells = row.querySelectorAll('td');
const rowData = [
cells[0].textContent, // 时间
cells[1].textContent, // 降雨量
cells[2].textContent, // 温度
cells[3].textContent, // 湿度
cells[4].textContent, // 风速
cells[5].textContent, // 大气压
cells[6].textContent // 太阳辐射
];
csv += rowData.join(',') + '\n';
});
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = '雨量计数据.csv';
link.click();
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {
initDatePickers();
queryData();
// 每30秒检查一次连接状态
checkConnectionStatus();
connectionCheckTimer = setInterval(checkConnectionStatus, 30000);
});
// 页面卸载时清除定时器
window.addEventListener('beforeunload', function() {
if (connectionCheckTimer) {
clearInterval(connectionCheckTimer);
}
});
</script>
</body>
</html>