401 lines
14 KiB
HTML
401 lines
14 KiB
HTML
<!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> |