feat: 优化前端页面
This commit is contained in:
parent
c931eb6af5
commit
f8fe5bd1e1
@ -29,8 +29,8 @@
|
|||||||
|
|
||||||
.controls {
|
.controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 15px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
@ -38,16 +38,25 @@
|
|||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.control-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
.control-group {
|
.control-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.station-input-group {
|
.station-input-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#stationInput {
|
#stationInput {
|
||||||
@ -113,12 +122,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.map-control-btn {
|
.map-control-btn {
|
||||||
padding: 5px 10px;
|
padding: 3px 8px;
|
||||||
background-color: rgba(255, 255, 255, 0.9);
|
background-color: rgba(255, 255, 255, 0.9);
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ddd;
|
||||||
border-radius: 4px;
|
border-radius: 3px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 12px;
|
font-size: 11px;
|
||||||
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
.map-control-btn:hover {
|
.map-control-btn:hover {
|
||||||
@ -195,6 +205,119 @@
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 设备列表样式 */
|
||||||
|
.device-modal {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.3);
|
||||||
|
z-index: 2000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-modal-content {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: #fff;
|
||||||
|
height: 40vh;
|
||||||
|
width: 100%;
|
||||||
|
border-top-left-radius: 5px;
|
||||||
|
border-top-right-radius: 5px;
|
||||||
|
box-shadow: 0 -2px 10px rgba(0,0,0,0.1);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-list-header {
|
||||||
|
padding: 15px 20px;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
position: relative;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-list {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 8px 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-list-footer {
|
||||||
|
padding: 10px;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-btn {
|
||||||
|
padding: 4px 8px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-btn:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-item {
|
||||||
|
padding: 12px 20px;
|
||||||
|
border-bottom: 1px solid #f5f5f5;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-item:hover {
|
||||||
|
background-color: #fafafa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-modal {
|
||||||
|
position: absolute;
|
||||||
|
right: 20px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #666;
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-modal:hover {
|
||||||
|
background-color: #eee;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
.error {
|
.error {
|
||||||
color: #dc3545;
|
color: #dc3545;
|
||||||
background-color: #f8d7da;
|
background-color: #f8d7da;
|
||||||
@ -269,50 +392,75 @@
|
|||||||
<h1>{{.Title}}</h1>
|
<h1>{{.Title}}</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 设备列表 -->
|
||||||
|
<div id="deviceModal" class="device-modal">
|
||||||
|
<div class="device-modal-content">
|
||||||
|
<div class="device-list-header">
|
||||||
|
设备列表
|
||||||
|
<span class="close-modal">×</span>
|
||||||
|
</div>
|
||||||
|
<div id="deviceList" class="device-list">
|
||||||
|
<!-- 设备列表将通过JavaScript动态填充 -->
|
||||||
|
</div>
|
||||||
|
<div class="device-list-footer">
|
||||||
|
<div class="pagination">
|
||||||
|
<button class="pagination-btn" id="prevPage" disabled>< 上一页</button>
|
||||||
|
<span>第 <span id="currentPage">1</span> 页,共 <span id="totalPages">1</span> 页</span>
|
||||||
|
<button class="pagination-btn" id="nextPage" disabled>下一页 ></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<!-- 系统信息 -->
|
<!-- 系统信息 -->
|
||||||
<div class="system-info">
|
<div class="system-info">
|
||||||
<strong>在线设备: </strong> <span id="onlineDevices">{{.OnlineDevices}}</span> 个
|
<strong>在线设备: </strong> <span id="onlineDevices">{{.OnlineDevices}}</span> 个 |
|
||||||
|
<strong>总设备: </strong> <a href="#" id="showDeviceList" style="color: #007bff; text-decoration: none;"><span id="wh65lpCount">0</span> 个</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 控制面板 -->
|
<!-- 控制面板 -->
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<div class="station-input-group">
|
<div class="control-row">
|
||||||
<label for="stationInput">站点编号:</label>
|
<div class="station-input-group">
|
||||||
<input type="text" id="stationInput" placeholder="">
|
<label for="stationInput">站点编号:</label>
|
||||||
|
<input type="text" id="stationInput" placeholder="">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control-group">
|
||||||
|
<label for="mapType">地图类型:</label>
|
||||||
|
<select id="mapType" onchange="switchLayer(this.value)">
|
||||||
|
<option value="satellite">卫星图</option>
|
||||||
|
<option value="vector">矢量图</option>
|
||||||
|
<option value="terrain">地形图</option>
|
||||||
|
<option value="hybrid">混合地形图</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-row">
|
||||||
<label for="mapType">地图类型:</label>
|
<div class="control-group">
|
||||||
<select id="mapType" onchange="switchLayer(this.value)">
|
<label for="interval">数据粒度:</label>
|
||||||
<option value="satellite">卫星图</option>
|
<select id="interval">
|
||||||
<option value="vector">矢量图</option>
|
<option value="10min">10分钟</option>
|
||||||
<option value="terrain">地形图</option>
|
<option value="30min">30分钟</option>
|
||||||
<option value="hybrid">混合地形图</option>
|
<option value="1hour" selected>1小时</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label for="interval">数据粒度:</label>
|
<label for="startDate">开始时间:</label>
|
||||||
<select id="interval">
|
<input type="datetime-local" id="startDate">
|
||||||
<option value="10min">10分钟</option>
|
</div>
|
||||||
<option value="30min">30分钟</option>
|
|
||||||
<option value="1hour" selected>1小时</option>
|
<div class="control-group">
|
||||||
</select>
|
<label for="endDate">结束时间:</label>
|
||||||
</div>
|
<input type="datetime-local" id="endDate">
|
||||||
|
</div>
|
||||||
<div class="control-group">
|
|
||||||
<label for="startDate">开始时间:</label>
|
<div class="control-group">
|
||||||
<input type="datetime-local" id="startDate">
|
<button onclick="queryHistoryData()" id="queryBtn">查看历史数据</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control-group">
|
|
||||||
<label for="endDate">结束时间:</label>
|
|
||||||
<input type="datetime-local" id="endDate">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="control-group">
|
|
||||||
<button onclick="queryHistoryData()" id="queryBtn">查看历史数据</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -448,8 +596,78 @@
|
|||||||
// 添加输入框事件监听
|
// 添加输入框事件监听
|
||||||
const stationInput = document.getElementById('stationInput');
|
const stationInput = document.getElementById('stationInput');
|
||||||
stationInput.addEventListener('input', function(e) {
|
stationInput.addEventListener('input', function(e) {
|
||||||
// 移除非数字字符
|
// 允许输入数字和十六进制字符
|
||||||
this.value = this.value.replace(/[^0-9]/g, '');
|
this.value = this.value.toUpperCase().replace(/[^0-9A-F]/g, '');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 设备列表模态框相关事件
|
||||||
|
const modal = document.getElementById('deviceModal');
|
||||||
|
const showDeviceListBtn = document.getElementById('showDeviceList');
|
||||||
|
const closeBtn = document.querySelector('.close-modal');
|
||||||
|
const prevPageBtn = document.getElementById('prevPage');
|
||||||
|
const nextPageBtn = document.getElementById('nextPage');
|
||||||
|
|
||||||
|
// 点击显示设备列表
|
||||||
|
showDeviceListBtn.addEventListener('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
modal.style.display = 'block';
|
||||||
|
updateDeviceList(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 分页按钮事件
|
||||||
|
prevPageBtn.addEventListener('click', function() {
|
||||||
|
if (currentPage > 1) {
|
||||||
|
updateDeviceList(currentPage - 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
nextPageBtn.addEventListener('click', function() {
|
||||||
|
const totalPages = Math.ceil(filteredDevices.length / itemsPerPage);
|
||||||
|
if (currentPage < totalPages) {
|
||||||
|
updateDeviceList(currentPage + 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 处理ID输入
|
||||||
|
stationInput.addEventListener('change', function() {
|
||||||
|
const value = this.value.trim();
|
||||||
|
if (value) {
|
||||||
|
if (isHexString(value)) {
|
||||||
|
// 如果是十六进制,转换为十进制
|
||||||
|
if (value.length <= 6) {
|
||||||
|
this.value = hexToDecimal(value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果是十进制,保持不变
|
||||||
|
const num = parseInt(value);
|
||||||
|
if (!isNaN(num)) {
|
||||||
|
this.value = num.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 点击关闭按钮
|
||||||
|
closeBtn.addEventListener('click', function() {
|
||||||
|
modal.style.display = 'none';
|
||||||
|
});
|
||||||
|
|
||||||
|
// 点击模态框外部关闭
|
||||||
|
window.addEventListener('click', function(e) {
|
||||||
|
if (e.target === modal) {
|
||||||
|
modal.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 点击设备项自动填充并查询
|
||||||
|
document.getElementById('deviceList').addEventListener('click', function(e) {
|
||||||
|
const deviceItem = e.target.closest('.device-item');
|
||||||
|
if (deviceItem) {
|
||||||
|
const decimalId = deviceItem.getAttribute('data-decimal-id');
|
||||||
|
document.getElementById('stationInput').value = decimalId;
|
||||||
|
modal.style.display = 'none';
|
||||||
|
queryHistoryData();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -663,12 +881,86 @@
|
|||||||
const response = await fetch('/api/stations');
|
const response = await fetch('/api/stations');
|
||||||
stations = await response.json();
|
stations = await response.json();
|
||||||
|
|
||||||
|
// 更新WH65LP设备数量
|
||||||
|
const wh65lpDevices = stations.filter(station => station.device_type === 'WH65LP');
|
||||||
|
document.getElementById('wh65lpCount').textContent = wh65lpDevices.length;
|
||||||
|
|
||||||
displayStationsOnMap();
|
displayStationsOnMap();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载站点失败:', error);
|
console.error('加载站点失败:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 分页相关变量
|
||||||
|
let currentPage = 1;
|
||||||
|
const itemsPerPage = 10;
|
||||||
|
let filteredDevices = [];
|
||||||
|
|
||||||
|
// 更新设备列表
|
||||||
|
function updateDeviceList(page = 1) {
|
||||||
|
const deviceListContainer = document.getElementById('deviceList');
|
||||||
|
deviceListContainer.innerHTML = '';
|
||||||
|
|
||||||
|
// 筛选WH65LP设备并按在线状态排序
|
||||||
|
filteredDevices = stations
|
||||||
|
.filter(station => station.device_type === 'WH65LP')
|
||||||
|
.sort((a, b) => {
|
||||||
|
const aOnline = new Date(a.last_update) > new Date(Date.now() - 5*60*1000);
|
||||||
|
const bOnline = new Date(b.last_update) > new Date(Date.now() - 5*60*1000);
|
||||||
|
if (aOnline === bOnline) return 0;
|
||||||
|
return aOnline ? -1 : 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 计算分页
|
||||||
|
const totalPages = Math.ceil(filteredDevices.length / itemsPerPage);
|
||||||
|
currentPage = Math.min(Math.max(1, page), totalPages);
|
||||||
|
|
||||||
|
// 更新分页按钮状态
|
||||||
|
document.getElementById('currentPage').textContent = currentPage;
|
||||||
|
document.getElementById('totalPages').textContent = totalPages;
|
||||||
|
document.getElementById('prevPage').disabled = currentPage <= 1;
|
||||||
|
document.getElementById('nextPage').disabled = currentPage >= totalPages;
|
||||||
|
|
||||||
|
// 获取当前页的设备
|
||||||
|
const startIndex = (currentPage - 1) * itemsPerPage;
|
||||||
|
const endIndex = startIndex + itemsPerPage;
|
||||||
|
const currentDevices = filteredDevices.slice(startIndex, endIndex);
|
||||||
|
|
||||||
|
currentDevices.forEach(device => {
|
||||||
|
const isOnline = new Date(device.last_update) > new Date(Date.now() - 5*60*1000);
|
||||||
|
const deviceItem = document.createElement('div');
|
||||||
|
deviceItem.className = 'device-item';
|
||||||
|
deviceItem.setAttribute('data-decimal-id', device.decimal_id);
|
||||||
|
|
||||||
|
deviceItem.innerHTML = `
|
||||||
|
<div style="font-size: 13px; color: #444">
|
||||||
|
${device.decimal_id} | ${device.name} | ${device.location || '未知位置'}
|
||||||
|
</div>
|
||||||
|
<span style="color: ${isOnline ? '#28a745' : '#dc3545'}; font-size: 12px; padding: 2px 6px; background: ${isOnline ? '#f0f9f1' : '#fef5f5'}; border-radius: 3px">${isOnline ? '在线' : '离线'}</span>
|
||||||
|
`;
|
||||||
|
|
||||||
|
deviceListContainer.appendChild(deviceItem);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (filteredDevices.length === 0) {
|
||||||
|
deviceListContainer.innerHTML = '<div style="padding: 20px; text-align: center; color: #666;">暂无WH65LP设备</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 十六进制和十进制转换函数
|
||||||
|
function isHexString(str) {
|
||||||
|
return /^[0-9A-F]+$/i.test(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hexToDecimal(hex) {
|
||||||
|
return parseInt(hex, 16).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
function decimalToHex(decimal) {
|
||||||
|
const hex = parseInt(decimal).toString(16).toUpperCase();
|
||||||
|
return '0'.repeat(Math.max(0, 6 - hex.length)) + hex;
|
||||||
|
}
|
||||||
|
|
||||||
// 更新集群距离
|
// 更新集群距离
|
||||||
function updateClusterDistance(zoom) {
|
function updateClusterDistance(zoom) {
|
||||||
// 动态调整聚合距离,让低缩放更容易聚合
|
// 动态调整聚合距离,让低缩放更容易聚合
|
||||||
@ -893,8 +1185,17 @@
|
|||||||
displayTable(data);
|
displayTable(data);
|
||||||
|
|
||||||
// 显示图表和表格
|
// 显示图表和表格
|
||||||
document.getElementById('chartContainer').classList.add('show');
|
const chartContainer = document.getElementById('chartContainer');
|
||||||
document.getElementById('tableContainer').classList.add('show');
|
const tableContainer = document.getElementById('tableContainer');
|
||||||
|
|
||||||
|
chartContainer.classList.add('show');
|
||||||
|
tableContainer.classList.add('show');
|
||||||
|
|
||||||
|
// 平滑滚动到图表位置
|
||||||
|
chartContainer.scrollIntoView({
|
||||||
|
behavior: 'smooth',
|
||||||
|
block: 'start'
|
||||||
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('查询历史数据失败:', error);
|
console.error('查询历史数据失败:', error);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user