const WeatherApp = {
cachedHistoryData: [],
cachedForecastData: [],
currentPage: 1,
itemsPerPage: 10,
filteredDevices: [],
init() {
WeatherUtils.initializeDateInputs();
WeatherMap.init(window.TIANDITU_KEY || '');
WeatherMap.loadStations();
setInterval(() => this.updateOnlineDevices(), 30000);
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');
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;
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);
});
}
const legendMode = document.getElementById('legendMode');
if (legendMode) {
legendMode.addEventListener('change', (e) => {
const mode = e.target.value;
if (window.WeatherChart && typeof window.WeatherChart.applyLegendMode === 'function') {
window.WeatherChart.applyLegendMode(mode);
}
});
}
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 = `
${device.decimal_id} | ${device.name} | ${device.location || '未知位置'}
${isOnline ? '在线' : '离线'}
`;
deviceListContainer.appendChild(deviceItem);
});
if (this.filteredDevices.length === 0) {
deviceListContainer.innerHTML = '暂无WH65LP设备
';
}
},
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}`;
// 将预报查询范围按小时对齐:from 向下取整到整点;to 向上取整到整点,再 +3h
const parseLocal = (s) => { try { return new Date(s); } catch { return null; } };
const floorHour = (d) => { const t = new Date(d); t.setMinutes(0,0,0); return t; };
const ceilHour = (d) => { const t = new Date(d); if (t.getMinutes()||t.getSeconds()||t.getMilliseconds()) { t.setHours(t.getHours()+1); } t.setMinutes(0,0,0); return t; };
const fmt = (d) => {
const y=d.getFullYear(); const m=String(d.getMonth()+1).padStart(2,'0'); const da=String(d.getDate()).padStart(2,'0');
const h=String(d.getHours()).padStart(2,'0'); const mi='00'; const s='00';
return `${y}-${m}-${da} ${h}:${mi}:${s}`;
};
const startD = parseLocal(startTime);
const endD = parseLocal(endTime);
const fromStr = startD && !isNaN(startD) ? fmt(floorHour(startD)) : startTime.replace('T',' ') + ':00';
const toStr = endD && !isNaN(endD) ? fmt(new Date(ceilHour(endD).getTime() + 3*60*60*1000)) : endTime.replace('T',' ') + ':00';
const forecastParams = new URLSearchParams({
station_id: stationID,
from: fromStr,
to: toStr,
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 = `
${station.location || '未知位置'} ·
编号 ${decimalId} ·
坐标 ${station.latitude ? station.latitude.toFixed(6) : '未知'}, ${station.longitude ? station.longitude.toFixed(6) : '未知'}
`;
} 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);
const legendMode = document.getElementById('legendMode');
if (legendMode) {
WeatherChart.applyLegendMode(legendMode.value);
}
} catch (error) {
console.error('查询数据失败:', error);
alert('查询数据失败: ' + error.message);
}
}
};
window.WeatherApp = WeatherApp;
document.addEventListener('DOMContentLoaded', () => {
WeatherApp.init();
});