import { useEffect, useState } from 'react'; import { BatteryCharging, Gauge, Wallet, CalendarClock } from 'lucide-react'; import { BarChart, Bar, XAxis, YAxis, ResponsiveContainer, Cell, Tooltip, ReferenceLine } from 'recharts'; import { fetchElectricOverview, type ElectricOverviewResponse } from './api'; import RotatingFooterHint from '../../components/RotatingFooterHint'; import { ErrorState, LoadingState, MetricTile, SurfaceCard } from '../../components/ui/surface'; function fmtYuan(yuan: number) { return `¥${yuan.toLocaleString('zh-CN', { maximumFractionDigits: 2 })}`; } function fmtKwh(kwh: number) { return `${kwh.toLocaleString('zh-CN', { maximumFractionDigits: 2 })} 度`; } export default function ElectricOverview() { const [data, setData] = useState(null); const [error, setError] = useState(null); useEffect(() => { let cancelled = false; fetchElectricOverview() .then(d => { if (!cancelled) setData(d); }) .catch(e => { if (!cancelled) setError(e instanceof Error ? e.message : String(e)); }); return () => { cancelled = true; }; }, []); if (error) { return ; } if (!data) { return ; } const k = data.kpi; const trendData = data.trend; // 当电能数据滞后(本月无数据走 fallback)时,柱图标题显示实际月份 const trendMonthLabel = trendData[0]?.date.slice(0, 7); const currentMonth = new Date().toISOString().slice(0, 7); const chartTitle = trendMonthLabel && trendMonthLabel !== currentMonth ? `${trendMonthLabel} 每日充电` : '本月每日充电'; const activeDays = trendData.filter(item => item.kwh > 0).length; const avgDailyKwh = activeDays > 0 ? trendData.reduce((sum, item) => sum + item.kwh, 0) / activeDays : 0; const avgDailyFee = activeDays > 0 ? trendData.reduce((sum, item) => sum + item.fee, 0) / activeDays : 0; const peakDay = trendData.reduce((best, item) => (!best || item.kwh > best.kwh ? item : best), null); const avgPrice = k.totalKwh > 0 ? k.totalFee / k.totalKwh : 0; const monthPrice = k.monthKwh > 0 ? k.monthFee / k.monthKwh : 0; return (
龙王路停车场充电站,期初 2025-01-01,手工导入每日更新
{/* 横向 mini KPI 头 */}
= 0 ? '+' : ''}${(k.todayChainPct * 100).toFixed(1)}%`} tone={Math.abs(k.todayChainPct) >= 0.3 ? 'rose' : 'slate'} />
有效充电日
{activeDays}
日均 {avgDailyKwh.toLocaleString('zh-CN', { maximumFractionDigits: 1 })} 度
峰值日
{peakDay ? peakDay.date.slice(5) : '—'}
{peakDay ? `${peakDay.kwh.toLocaleString('zh-CN', { maximumFractionDigits: 1 })} 度 · ${fmtYuan(peakDay.fee)}` : '暂无数据'}
月度占比
{k.totalFee > 0 ? (k.monthFee / k.totalFee * 100).toFixed(1) : '0.0'}%
本月费用 / 累计费用
{/* 本月每日充电柱图 */}
{chartTitle} 时间单位:日 · 单位 元 · 均线为日均费用
v.slice(5)} tick={{ fontSize: 10, fill: '#94a3b8' }} tickLine={false} axisLine={false} interval="preserveStartEnd" minTickGap={8} /> [`¥${Number(v ?? 0).toLocaleString('zh-CN', { maximumFractionDigits: 2 })}`, '充电费用']} labelFormatter={(d) => `日期 ${d}`} contentStyle={{ borderRadius: 12, fontSize: 12 }} cursor={{ fill: 'rgba(59, 130, 246, 0.06)' }} /> {avgDailyFee > 0 && ( )} {trendData.map((_, i) => ( ))}
); }