feat(energy): connect to real DB (lingniu_prod)
Replace front-end mock data with live API backed by: - tab_energy_hydrogen_bill (66.5K rows) joined with tab_hydrogen_site (internal stations) and tab_outside_hydrogen_site (external stations, joined via inner_site_id) - tab_energy_electricity_bill (4.4K rows, all 龙王路充电站) New server routes (src/server/routes/energy/): - GET /api/energy/hydrogen/overview → KPI + Top5 站点 + 区域占比 - GET /api/energy/hydrogen/daily?range=&customer= → 日级 + 站点级下钻 - GET /api/energy/electric/overview → KPI + 本月柱图 (fallback to last available month if current month has no data) - GET /api/energy/electric/monthly?customer= → 6 个月分组日级表 Business rules encoded server-side: - 客户类型: customer_id IS NULL = 羚牛承担, NOT NULL = 外部 - 时区: DATETIME 列字面值是 UTC,分组前 +8h 转成 CST - 数据清理: hydrogen_time >= 2024-01-01 (排除 1900 年脏数据) - 站点名 fallback: short_name → name → fixed_station_name → station_name → '未知站点' - 区域归一化: SUBSTRING_INDEX(city, '-', -1) 取最后一段,去掉 '省'/'市' 让 '四川省-成都市' 和 '成都市' 合并为 '成都' Component changes: - All 4 components (HydrogenOverview, HydrogenDaily, ElectricOverview, ElectricDaily) now use useEffect + fetch with loading/error states - HydrogenDaily filtering moved to server (range + customer params) → drops client-side TODAY constant + isInPick switch - ElectricOverview chart title is dynamic: shows 'YYYY-MM 每日充电' when fallback kicks in (current month has no data) - mock.ts deleted Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Wallet, BatteryCharging, CalendarClock } from 'lucide-react';
|
||||
import { BarChart, Bar, XAxis, YAxis, ResponsiveContainer, Cell, Tooltip } from 'recharts';
|
||||
import TrendBadge from './TrendBadge';
|
||||
import { ELECTRIC_KPI, ELECTRIC_MONTHLY } from './mock';
|
||||
import { fetchElectricOverview, type ElectricOverviewResponse } from './api';
|
||||
|
||||
function fmtYuan(yuan: number) {
|
||||
return `¥${yuan.toLocaleString('zh-CN', { maximumFractionDigits: 2 })}`;
|
||||
@@ -12,15 +12,32 @@ function fmtKwh(kwh: number) {
|
||||
}
|
||||
|
||||
export default function ElectricOverview() {
|
||||
const k = ELECTRIC_KPI;
|
||||
const [data, setData] = useState<ElectricOverviewResponse | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// 本月每日数据(按日期升序,便于柱图按时间从左到右展示)
|
||||
const trendData = useMemo(() => {
|
||||
const first = ELECTRIC_MONTHLY[0];
|
||||
if (!first) return [];
|
||||
return [...first.rows].sort((a, b) => a.date.localeCompare(b.date));
|
||||
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 <div className="bg-red-50 text-red-600 rounded-2xl border border-red-100 p-4 text-sm">加载失败:{error}</div>;
|
||||
}
|
||||
if (!data) {
|
||||
return <div className="bg-white rounded-2xl border border-slate-100 shadow-sm p-6 text-center text-slate-400 text-sm">加载中…</div>;
|
||||
}
|
||||
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} 每日充电`
|
||||
: '本月每日充电';
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-3">
|
||||
{/* 横向 mini KPI 头 */}
|
||||
@@ -52,7 +69,7 @@ export default function ElectricOverview() {
|
||||
{/* 本月每日充电柱图 */}
|
||||
<div className="bg-white rounded-2xl border border-slate-100 shadow-sm p-4">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-sm font-bold text-slate-700">本月每日充电</span>
|
||||
<span className="text-sm font-bold text-slate-700">{chartTitle}</span>
|
||||
<span className="text-[11px] text-slate-400 font-bold">单位 元</span>
|
||||
</div>
|
||||
<ResponsiveContainer width="100%" height={160}>
|
||||
|
||||
Reference in New Issue
Block a user