261 lines
11 KiB
JavaScript
261 lines
11 KiB
JavaScript
// 应用主控制器
|
|
const WeatherApp = {
|
|
cachedHistoryData: [],
|
|
cachedForecastData: [],
|
|
currentPage: 1,
|
|
itemsPerPage: 10,
|
|
filteredDevices: [],
|
|
|
|
init() {
|
|
// 初始化日期
|
|
WeatherUtils.initializeDateInputs();
|
|
// 初始化地图
|
|
WeatherMap.init(window.TIANDITU_KEY || '');
|
|
WeatherMap.loadStations();
|
|
// 定时刷新在线设备数
|
|
setInterval(() => this.updateOnlineDevices(), 30000);
|
|
// 绑定 UI
|
|
this.bindUI();
|
|
// 监听地图请求查询事件
|
|
window.addEventListener('query-history-data', () => this.queryHistoryData());
|
|
},
|
|
|
|
bindUI() {
|
|
const stationInput = document.getElementById('stationInput');
|
|
if (stationInput) {
|
|
stationInput.addEventListener('input', function() {
|
|
this.value = this.value.toUpperCase().replace(/[^0-9A-F]/g, '');
|
|
});
|
|
stationInput.addEventListener('change', function() {
|
|
const value = this.value.trim();
|
|
if (!value) return;
|
|
if (/^[0-9A-F]+$/i.test(value)) {
|
|
if (value.length <= 6) this.value = WeatherUtils.hexToDecimal(value);
|
|
} else {
|
|
const num = parseInt(value);
|
|
if (!isNaN(num)) this.value = num.toString();
|
|
}
|
|
});
|
|
}
|
|
|
|
const showDeviceListBtn = document.getElementById('showDeviceList');
|
|
const modal = document.getElementById('deviceModal');
|
|
const closeBtn = document.querySelector('.close-modal');
|
|
const prevPageBtn = document.getElementById('prevPage');
|
|
const nextPageBtn = document.getElementById('nextPage');
|
|
|
|
// 由 Alpine 管理设备列表弹窗开关与分页,不再绑定以下事件
|
|
// if (showDeviceListBtn && modal) {
|
|
// showDeviceListBtn.addEventListener('click', (e) => {
|
|
// e.preventDefault();
|
|
// modal.style.display = 'block';
|
|
// this.updateDeviceList(1);
|
|
// });
|
|
// }
|
|
// if (prevPageBtn) {
|
|
// prevPageBtn.addEventListener('click', () => {
|
|
// if (this.currentPage > 1) this.updateDeviceList(this.currentPage - 1);
|
|
// });
|
|
// }
|
|
// if (nextPageBtn) {
|
|
// nextPageBtn.addEventListener('click', () => {
|
|
// const totalPages = Math.ceil(this.filteredDevices.length / this.itemsPerPage);
|
|
// if (this.currentPage < totalPages) this.updateDeviceList(this.currentPage + 1);
|
|
// });
|
|
// }
|
|
// if (closeBtn && modal) {
|
|
// closeBtn.addEventListener('click', () => modal.style.display = 'none');
|
|
// window.addEventListener('click', (e) => { if (e.target === modal) modal.style.display = 'none'; });
|
|
// }
|
|
|
|
const deviceListEl = document.getElementById('deviceList');
|
|
if (deviceListEl && modal) {
|
|
deviceListEl.addEventListener('click', (e) => {
|
|
const deviceItem = e.target.closest('.device-item');
|
|
if (!deviceItem) return;
|
|
const decimalId = deviceItem.getAttribute('data-decimal-id');
|
|
const input = document.getElementById('stationInput');
|
|
if (input) input.value = decimalId;
|
|
// 关闭交给 Alpine: deviceModalOpen = false
|
|
window.dispatchEvent(new CustomEvent('close-device-modal'));
|
|
this.queryHistoryData();
|
|
});
|
|
}
|
|
|
|
const showPastForecast = document.getElementById('showPastForecast');
|
|
if (showPastForecast) {
|
|
showPastForecast.addEventListener('change', () => {
|
|
WeatherTable.display(this.cachedHistoryData, this.cachedForecastData);
|
|
});
|
|
}
|
|
|
|
// 提供全局函数以兼容现有 HTML on* 绑定
|
|
window.switchLayer = (type) => WeatherMap.switchLayer(type);
|
|
window.toggleMap = () => WeatherMap.toggleMap();
|
|
window.queryHistoryData = () => this.queryHistoryData();
|
|
},
|
|
|
|
// 更新设备列表
|
|
updateDeviceList(page = 1) {
|
|
const deviceListContainer = document.getElementById('deviceList');
|
|
if (!deviceListContainer) return;
|
|
deviceListContainer.innerHTML = '';
|
|
|
|
this.filteredDevices = (WeatherMap.stations || [])
|
|
.filter(station => station.device_type === 'WH65LP')
|
|
.sort((a, b) => {
|
|
const aOnline = WeatherUtils.isDeviceOnline(a.last_update);
|
|
const bOnline = WeatherUtils.isDeviceOnline(b.last_update);
|
|
if (aOnline === bOnline) return 0;
|
|
return aOnline ? -1 : 1;
|
|
});
|
|
|
|
const totalPages = Math.ceil(this.filteredDevices.length / this.itemsPerPage) || 1;
|
|
this.currentPage = Math.min(Math.max(1, page), totalPages);
|
|
|
|
const currentPageEl = document.getElementById('currentPage');
|
|
const totalPagesEl = document.getElementById('totalPages');
|
|
const prevBtn = document.getElementById('prevPage');
|
|
const nextBtn = document.getElementById('nextPage');
|
|
if (currentPageEl) currentPageEl.textContent = this.currentPage;
|
|
if (totalPagesEl) totalPagesEl.textContent = totalPages;
|
|
if (prevBtn) prevBtn.disabled = this.currentPage <= 1;
|
|
if (nextBtn) nextBtn.disabled = this.currentPage >= totalPages;
|
|
|
|
const startIndex = (this.currentPage - 1) * this.itemsPerPage;
|
|
const endIndex = startIndex + this.itemsPerPage;
|
|
const currentDevices = this.filteredDevices.slice(startIndex, endIndex);
|
|
|
|
currentDevices.forEach(device => {
|
|
const isOnline = WeatherUtils.isDeviceOnline(device.last_update);
|
|
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 (this.filteredDevices.length === 0) {
|
|
deviceListContainer.innerHTML = '<div style="padding: 20px; text-align: center; color: #666;">暂无WH65LP设备</div>';
|
|
}
|
|
},
|
|
|
|
async updateOnlineDevices() {
|
|
try {
|
|
const response = await fetch('/api/system/status');
|
|
const data = await response.json();
|
|
const onlineEl = document.getElementById('onlineDevices');
|
|
if (onlineEl) onlineEl.textContent = data.online_devices;
|
|
} catch (error) {
|
|
console.error('更新在线设备数量失败:', error);
|
|
}
|
|
},
|
|
|
|
async queryHistoryData() {
|
|
const decimalId = (document.getElementById('stationInput')?.value || '').trim();
|
|
if (!decimalId) {
|
|
alert('请输入站点编号');
|
|
return;
|
|
}
|
|
if (!/^\d+$/.test(decimalId)) {
|
|
alert('请输入有效的十进制编号');
|
|
return;
|
|
}
|
|
|
|
const startTime = document.getElementById('startDate').value;
|
|
const endTime = document.getElementById('endDate').value;
|
|
const interval = document.getElementById('interval').value;
|
|
const forecastProvider = document.getElementById('forecastProvider').value;
|
|
if (!startTime || !endTime) {
|
|
alert('请选择开始和结束时间');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const historyParams = new URLSearchParams({
|
|
decimal_id: decimalId,
|
|
start_time: startTime.replace('T', ' ') + ':00',
|
|
end_time: endTime.replace('T', ' ') + ':00',
|
|
interval: interval
|
|
});
|
|
const historyResponse = await fetch(`/api/data?${historyParams}`);
|
|
if (!historyResponse.ok) throw new Error('查询历史数据失败');
|
|
const responseData = await historyResponse.json();
|
|
const historyData = Array.isArray(responseData) ? responseData : [];
|
|
|
|
let forecastData = [];
|
|
if (forecastProvider && interval === '1hour') {
|
|
try {
|
|
const hexID = WeatherUtils.decimalToHex(decimalId);
|
|
const stationID = `RS485-${hexID}`;
|
|
const forecastParams = new URLSearchParams({
|
|
station_id: stationID,
|
|
from: startTime.replace('T', ' ') + ':00',
|
|
to: endTime.replace('T', ' ') + ':00',
|
|
provider: forecastProvider,
|
|
versions: '3'
|
|
});
|
|
const forecastResponse = await fetch(`/api/forecast?${forecastParams}`);
|
|
if (forecastResponse.ok) {
|
|
const responseData = await forecastResponse.json();
|
|
forecastData = Array.isArray(responseData) ? responseData : [];
|
|
console.log(`查询到 ${forecastData.length} 条预报数据`);
|
|
}
|
|
} catch (e) {
|
|
console.warn('查询预报数据失败:', e);
|
|
}
|
|
}
|
|
|
|
this.cachedHistoryData = historyData;
|
|
this.cachedForecastData = forecastData;
|
|
|
|
if (historyData.length === 0 && forecastData.length === 0) {
|
|
alert('该时间段内无数据');
|
|
return;
|
|
}
|
|
|
|
const station = (WeatherMap.stations || []).find(s => s.decimal_id == decimalId);
|
|
const stationInfoTitle = document.getElementById('stationInfoTitle');
|
|
if (stationInfoTitle) {
|
|
if (station) {
|
|
stationInfoTitle.innerHTML = `
|
|
<strong>
|
|
${station.location || '未知位置'} ·
|
|
编号 ${decimalId} ·
|
|
坐标 ${station.latitude ? station.latitude.toFixed(6) : '未知'}, ${station.longitude ? station.longitude.toFixed(6) : '未知'}
|
|
</strong>
|
|
`;
|
|
} else {
|
|
stationInfoTitle.innerHTML = `编号 ${decimalId}`;
|
|
}
|
|
}
|
|
|
|
if (!WeatherMap.isMapCollapsed) WeatherMap.toggleMap();
|
|
|
|
WeatherChart.display(historyData, forecastData);
|
|
WeatherTable.display(historyData, forecastData);
|
|
|
|
const chartContainer = document.getElementById('chartContainer');
|
|
const tableContainer = document.getElementById('tableContainer');
|
|
if (chartContainer) chartContainer.classList.add('show');
|
|
if (tableContainer) tableContainer.classList.add('show');
|
|
setTimeout(() => {
|
|
chartContainer?.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
}, 300);
|
|
} catch (error) {
|
|
console.error('查询数据失败:', error);
|
|
alert('查询数据失败: ' + error.message);
|
|
}
|
|
}
|
|
};
|
|
|
|
window.WeatherApp = WeatherApp;
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
WeatherApp.init();
|
|
});
|