// 宿舍电费监控 - 前端逻辑 const POLL_INTERVAL = 30000; // 自动刷新间隔 30s let usageChart = null; let availableMonths = []; // 后端返回的可用月份列表 let currentMonth = ""; // 当前选中的月份 (YYYY-MM) const $ = (id) => document.getElementById(id); // ---------- 工具 ---------- function formatTime(ts) { if (!ts) return "--"; const d = new Date(ts * 1000); return d.toLocaleString("zh-CN", { hour12: false }); } function formatDateStr(dateStr) { const d = new Date(dateStr); return d.toLocaleDateString("zh-CN", { month: "numeric", day: "numeric" }); } function setBadge(state, text) { const el = $("statusBadge"); el.className = "status-badge " + state; el.textContent = text; } // ---------- 当前电费 ---------- async function fetchCurrent() { setBadge("loading", "查询中..."); try { const resp = await fetch("/api/electric"); const data = await resp.json(); if (data.ok) { $("surplusValue").textContent = data.surplus.toFixed(2); $("amountValue").textContent = data.amount.toFixed(2); $("updateTime").textContent = formatTime(data.time); $("roomInfo").textContent = "🏠 " + data.room; setBadge("ok", "实时"); } else { setBadge("err", "查询失败"); $("roomInfo").textContent = "⚠️ " + (data.error || "查询失败"); } } catch (e) { setBadge("err", "网络错误"); } } // ---------- 月度汇总 ---------- function renderSummary(s) { if (!s || !s.record_count) { const empty = "--"; ["sRecordCount", "sTotalUsage", "sAvgUsage", "sRecharge", "sRechargeKwh", "sRechargeMoney", "sStartSurplus", "sEndSurplus"].forEach((id) => { $(id).textContent = empty; }); return; } $("sRecordCount").textContent = s.record_count + " 天"; $("sTotalUsage").textContent = s.total_usage != null ? s.total_usage.toFixed(2) : "--"; $("sAvgUsage").textContent = s.avg_usage != null ? s.avg_usage.toFixed(2) : "--"; $("sRecharge").textContent = s.recharge_days != null ? s.recharge_days + " 次" : "--"; $("sRechargeKwh").textContent = s.recharge_kwh != null ? s.recharge_kwh.toFixed(2) : "--"; $("sRechargeMoney").textContent = s.recharge_money != null ? s.recharge_money.toFixed(2) : "--"; $("sStartSurplus").textContent = s.start_surplus != null ? s.start_surplus.toFixed(2) : "--"; $("sEndSurplus").textContent = s.end_surplus != null ? s.end_surplus.toFixed(2) : "--"; } // ---------- 历史记录 ---------- function renderChart(daily) { $("chartEmpty").style.display = daily.length ? "none" : "block"; if (!daily.length) return; const labels = daily.map((d) => formatDateStr(d.date)); const usage = daily.map((d) => d.usage); const surplus = daily.map((d) => d.surplus); const ctx = $("usageChart").getContext("2d"); if (usageChart) usageChart.destroy(); usageChart = new Chart(ctx, { data: { labels, datasets: [ { type: "bar", label: "日耗电(度)", data: usage, backgroundColor: "rgba(255, 167, 38, 0.75)", borderRadius: 6, yAxisID: "y", }, { type: "line", label: "剩余电量(度)", data: surplus, borderColor: "#4fc3f7", backgroundColor: "rgba(79, 195, 247, 0.15)", tension: 0.3, pointRadius: 4, yAxisID: "y1", }, ], }, options: { responsive: true, interaction: { mode: "index", intersect: false }, plugins: { legend: { labels: { color: "#e8edf2", usePointStyle: true }, }, }, scales: { x: { ticks: { color: "#9fb3c8" }, grid: { color: "rgba(255,255,255,0.05)" } }, y: { position: "left", title: { display: true, text: "耗电(度)", color: "#ffa726" }, ticks: { color: "#9fb3c8" }, grid: { color: "rgba(255,255,255,0.05)" }, }, y1: { position: "right", title: { display: true, text: "剩余(度)", color: "#4fc3f7" }, ticks: { color: "#9fb3c8" }, grid: { drawOnChartArea: false }, }, }, }, }); } function renderHistory(daily) { const body = $("historyBody"); if (!daily.length) { body.innerHTML = '