go_rain_dtu/static/index.html
2025-05-15 18:03:51 +08:00

401 lines
14 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>
<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">
<label for="adaptiveScale">自适应范围:</label>
<input type="checkbox" id="adaptiveScale">
</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>大气压(hPa)</th>
<th>太阳辐射(W/m²)</th>
</tr>
</thead>
<tbody id="tableBody"></tbody>
</table>
</div>
</div>
<script>
let mainChart = null;
// 初始化日期选择器
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 = document.getElementById('adaptiveScale').checked;
// 计算降雨量和温度的范围
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
}
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 = '';
// 数据在updateChart中已经排序这里不需要重复排序
data.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');
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(1)}</td>
<td>${item.atm_pressure ? (item.atm_pressure/10).toFixed(1) : 'N/A'}</td>
<td>${item.solar_radiation ? (item.solar_radiation/10).toFixed(1) : 'N/A'}</td>
`;
tbody.appendChild(row);
});
}
// 导出数据
function exportData() {
// 从表格中获取完整数据
const tableRows = document.querySelectorAll('#tableBody tr');
let csv = '时间,降雨量(mm),温度(℃),湿度(%),风速(m/s),大气压(hPa),太阳辐射(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();
});
</script>
</body>
</html>