angle_dtu/templates/index.html
2025-05-24 13:13:57 +08:00

668 lines
23 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;
}
canvas {
width: 100%;
max-height: 400px;
}
.table-container {
overflow-x: auto;
margin-top: 20px;
border: 1px solid #ddd;
border-radius: 4px;
background-color: #fff;
padding: 15px;
}
h2{
text-align:center;
color: #333;
margin-top: 0;
margin-bottom: 15px;
}
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;
}
.footer {
text-align: center;
padding: 10px;
border-top: 1px solid #ddd;
}
@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="sensorSelect">选择探头:</label>
<select id="sensorSelect">
<option value="all">所有探头</option>
</select>
</div>
<div class="control-group">
<label for="limitSelect">显示记录数:</label>
<select id="limitSelect">
<option value="10">10</option>
<option value="50">50</option>
<option value="100">100</option>
<option value="200">200</option>
<option value="500" selected>500</option>
<option value="1000">1000</option>
<option value="2000">2000</option>
<option value="5000">5000</option>
<option value="50000">50000</option>
<option value="all">全部</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 id="queryBtn">查询数据</button>
<button id="resetBtn">重置筛选</button>
</div>
<div class="control-group">
<label>
<input type="checkbox" id="autoRefresh">
自动刷新 (10分钟周期)
</label>
</div>
</div>
<!-- 图表区域 -->
<div class="chart-container">
<h2>传感器数据图表</h2>
<canvas id="sensorChart"></canvas>
</div>
<!-- 数据表格 -->
<div class="table-container">
<h2>传感器数据表格</h2>
<button id="exportBtn">导出CSV</button>
<table id="dataTable">
<thead>
<tr>
<th>数据编号</th>
<th>探头地址</th>
<th>时间</th>
<th>X</th>
<th>Y</th>
<th>Z</th>
<th>温度(°C)</th>
</tr>
</thead>
<tbody id="tableBody">
<!-- 数据将通过JavaScript动态添加 -->
</tbody>
</table>
</div>
</div>
<!-- 内联JavaScript -->
<script>
// 全局变量
let sensorChart = null;
let refreshInterval = null;
let allSensors = [];
let currentSensorData = [];
// 页面加载完成后执行
document.addEventListener('DOMContentLoaded', function() {
// 初始化日期选择器为今天
initializeDatePickers();
// 加载所有传感器ID
loadSensors();
// 添加事件监听器
setupEventListeners();
// 设置自动刷新
setupAutoRefresh();
});
// 初始化日期选择器
function initializeDatePickers() {
// 获取当前时间(使用本地时间)
const now = new Date();
// 格式化日期和时间为HTML datetime-local输入格式 YYYY-MM-DDThh:mm
const year = now.getFullYear();
const month = (now.getMonth() + 1).toString().padStart(2, '0'); // 月份从0开始
const day = now.getDate().toString().padStart(2, '0');
const hours = now.getHours().toString().padStart(2, '0');
const minutes = now.getMinutes().toString().padStart(2, '0');
const today = `${year}-${month}-${day}`;
const currentTime = `${hours}:${minutes}`;
// 设置默认的开始时间为当天00:00
document.getElementById('startDate').value = `${today}T00:00`;
// 设置默认的结束时间为当前时间
document.getElementById('endDate').value = `${today}T${currentTime}`;
console.log("当前设置的开始时间:", document.getElementById('startDate').value);
console.log("当前设置的结束时间:", document.getElementById('endDate').value);
}
// 设置事件监听器
function setupEventListeners() {
// 查询按钮
document.getElementById('queryBtn').addEventListener('click', function() {
loadData();
});
// 重置按钮
document.getElementById('resetBtn').addEventListener('click', function() {
resetFilters();
});
// 传感器选择变化
document.getElementById('sensorSelect').addEventListener('change', function() {
loadData();
});
// 记录数限制变化
document.getElementById('limitSelect').addEventListener('change', function() {
loadData();
});
// 导出CSV按钮
document.getElementById('exportBtn').addEventListener('click', function() {
exportToCSV();
});
}
// 设置自动刷新
function setupAutoRefresh() {
const autoRefreshCheckbox = document.getElementById('autoRefresh');
// 初始化自动刷新
if (autoRefreshCheckbox.checked) {
startAutoRefresh();
}
// 监听复选框变化
autoRefreshCheckbox.addEventListener('change', function() {
if (this.checked) {
startAutoRefresh();
} else {
stopAutoRefresh();
}
});
}
// 开始自动刷新
function startAutoRefresh() {
if (refreshInterval) {
clearInterval(refreshInterval);
}
refreshInterval = setInterval(loadData, 1000 * 60 * 10);
}
// 停止自动刷新
function stopAutoRefresh() {
if (refreshInterval) {
clearInterval(refreshInterval);
refreshInterval = null;
}
}
// 重置筛选条件
function resetFilters() {
initializeDatePickers();
document.getElementById('sensorSelect').value = 'all';
document.getElementById('limitSelect').value = '500';
loadData();
}
// 加载所有传感器ID
function loadSensors() {
fetch('/api/sensors')
.then(response => {
if (!response.ok) {
throw new Error('获取传感器列表失败');
}
return response.json();
})
.then(data => {
allSensors = data;
updateSensorSelect(data);
// 加载数据
loadData();
})
.catch(error => {
console.error('加载传感器列表出错:', error);
alert('加载传感器列表出错: ' + error.message);
});
}
// 更新传感器选择下拉框
function updateSensorSelect(sensors) {
const select = document.getElementById('sensorSelect');
// 保留"所有传感器"选项
const allOption = select.querySelector('option[value="all"]');
select.innerHTML = '';
select.appendChild(allOption);
if (sensors.length === 0) {
const option = document.createElement('option');
option.value = '';
option.textContent = '没有可用的传感器';
select.appendChild(option);
return;
}
sensors.forEach(id => {
const option = document.createElement('option');
option.value = id;
option.textContent = `探头 ${id}`;
select.appendChild(option);
});
}
// 加载传感器数据
function loadData() {
const sensorID = document.getElementById('sensorSelect').value;
const limit = document.getElementById('limitSelect').value;
const startDate = document.getElementById('startDate').value;
const endDate = document.getElementById('endDate').value;
let url = '/api/data?';
let params = [];
// 添加查询参数
if (sensorID !== 'all') {
params.push(`sensor_id=${sensorID}`);
}
// 始终发送limit参数即使是'all'
params.push(`limit=${limit}`);
if (startDate) {
params.push(`start_date=${encodeURIComponent(startDate)}`);
}
if (endDate) {
params.push(`end_date=${encodeURIComponent(endDate)}`);
}
url += params.join('&');
// 显示加载状态
document.getElementById('queryBtn').textContent = '加载中...';
fetch(url)
.then(response => {
if (!response.ok) {
throw new Error('获取传感器数据失败');
}
return response.json();
})
.then(data => {
currentSensorData = data;
updateTable(data);
updateChart(data);
document.getElementById('queryBtn').textContent = '查询数据';
})
.catch(error => {
console.error('加载数据出错:', error);
alert('加载数据出错: ' + error.message);
document.getElementById('queryBtn').textContent = '查询数据';
});
}
// 更新数据表格
function updateTable(data) {
const tableBody = document.getElementById('tableBody');
tableBody.innerHTML = '';
if (data.length === 0) {
const row = document.createElement('tr');
row.innerHTML = '<td colspan="7" style="text-align: center;">没有数据</td>';
tableBody.appendChild(row);
return;
}
data.forEach(item => {
const row = document.createElement('tr');
// 解析时间并调整为中国时间UTC+8
const date = new Date(item.timestamp);
// 减去8小时因为数据库时间似乎比实际时间早了8小时
date.setHours(date.getHours() - 8);
// 格式化为中文日期时间格式
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>' + item.id + '</td>' +
'<td>' + item.sensor_id + '</td>' +
'<td>' + formattedDate + '</td>' +
'<td>' + item.x.toFixed(3) + '</td>' +
'<td>' + item.y.toFixed(3) + '</td>' +
'<td>' + item.z.toFixed(3) + '</td>' +
'<td>' + item.temperature.toFixed(1) + '</td>';
tableBody.appendChild(row);
});
}
// 更新图表
function updateChart(data) {
// 准备图表数据
const chartData = prepareChartData(data);
// 如果图表已经存在,销毁它
if (sensorChart) {
sensorChart.destroy();
}
// 获取图表Canvas
const ctx = document.getElementById('sensorChart').getContext('2d');
// 创建新图表
sensorChart = new Chart(ctx, {
type: 'line',
data: chartData,
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: '历史数据'
},
tooltip: {
callbacks: {
label: function(context) {
let label = context.dataset.label || '';
if (label) {
label += ': ';
}
if (context.parsed.y !== null) {
label += context.parsed.y.toFixed(3);
}
return label;
}
}
}
},
scales: {
x: {
title: {
display: true,
text: '时间'
}
},
y: {
title: {
display: true,
text: '值'
}
}
}
}
});
}
// 准备图表数据
function prepareChartData(data) {
// 如果没有数据,返回空数据集
if (data.length === 0) {
return {
labels: [],
datasets: []
};
}
// 反转数据以便按时间先后顺序显示
const sortedData = [...data].sort((a, b) => {
return new Date(a.timestamp) - new Date(b.timestamp);
});
// 获取所有传感器ID
let sensorIDs = [...new Set(sortedData.map(item => item.sensor_id))];
// 按传感器ID分组数据
let datasets = [];
let labels = [];
// 准备时间标签(使用第一个传感器的数据)
if (sensorIDs.length > 0) {
const firstSensorData = sortedData.filter(item => item.sensor_id === sensorIDs[0]);
labels = firstSensorData.map(item => {
const date = new Date(item.timestamp);
date.setHours(date.getHours() - 8); // 调整时区
return date.toLocaleString('zh-CN', {
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
});
}
// 定义颜色
const colors = [
'rgb(75, 192, 192)',
'rgb(255, 99, 132)',
'rgb(54, 162, 235)',
'rgb(255, 205, 86)',
'rgb(153, 102, 255)',
'rgb(255, 159, 64)'
];
// 为X, Y, Z, 温度创建不同的数据集
const dataTypes = [
{ key: 'x', label: 'X' },
{ key: 'y', label: 'Y' },
{ key: 'z', label: 'Z' },
{ key: 'temperature', label: '温度(°C)' }
];
sensorIDs.forEach((sensorID, sensorIndex) => {
const sensorData = sortedData.filter(item => item.sensor_id === sensorID);
dataTypes.forEach((type, typeIndex) => {
const colorIndex = (sensorIndex * dataTypes.length + typeIndex) % colors.length;
datasets.push({
label: `探头${sensorID} - ${type.label}`,
data: sensorData.map(item => item[type.key]),
fill: false,
borderColor: colors[colorIndex],
tension: 0.1
});
});
});
return {
labels: labels,
datasets: datasets
};
}
// 导出到CSV文件
function exportToCSV() {
if (currentSensorData.length === 0) {
alert('没有数据可导出');
return;
}
// 准备CSV内容
let csvContent = "数据编号,探头地址编号,X,Y,Z,温度,时间\n";
currentSensorData.forEach(item => {
// 解析时间并调整为中国时间
const date = new Date(item.timestamp);
date.setHours(date.getHours() - 8);
// 格式化日期
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');
csvContent += item.id + "," +
item.sensor_id + "," +
item.x.toFixed(3) + "," +
item.y.toFixed(3) + "," +
item.z.toFixed(3) + "," +
item.temperature.toFixed(1) + "," +
formattedDate + "\n";
});
// 创建Blob对象
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
// 创建下载链接
const link = document.createElement("a");
const url = URL.createObjectURL(blob);
// 设置下载属性
link.setAttribute("href", url);
link.setAttribute("download", "sensor_data.csv");
// 添加到文档并点击
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
}
</script>
</body>
</html>