refactor(energy): visual polish + KPI/table self-consistency

- mock: derive ELECTRIC_KPI month/today from APR_DAYS so card and table
  totals always agree (previously ¥8,437 vs ¥9,151 mismatch)
- overview: Top5 bar chart now shows rank badges (1-5) and inline value
  labels at bar ends — readable without hover
- overview: donut "年合计 362.43T" moves into the chart center
  (previously below as a separate line, defeating the donut hole)
- daily: rows with |chainPct| ≥ 30% get a tinted background
  (green for spikes, red for drops) for at-a-glance abnormal-day spotting

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
kkfluous
2026-04-28 12:01:13 +08:00
parent 2a92d991b0
commit bdd039a2c4
3 changed files with 79 additions and 35 deletions

View File

@@ -1,7 +1,28 @@
import { Fuel, Wallet, Coins, CalendarClock } from 'lucide-react';
import { BarChart, Bar, XAxis, YAxis, ResponsiveContainer, Cell, PieChart, Pie, Tooltip } from 'recharts';
import { BarChart, Bar, XAxis, YAxis, ResponsiveContainer, Cell, PieChart, Pie, Tooltip, LabelList } from 'recharts';
import { HYDROGEN_KPI, HYDROGEN_STATIONS_TOP5, HYDROGEN_REGION_SHARE } from './mock';
interface YAxisTickProps {
x?: number;
y?: number;
index?: number;
payload?: { value: string };
}
function RankYAxisTick({ x = 0, y = 0, index = 0, payload }: YAxisTickProps) {
return (
<g transform={`translate(${x},${y})`}>
<circle cx={-158} cy={0} r={9} fill="#3b82f6" />
<text x={-158} y={3} textAnchor="middle" fontSize={10} fontWeight={700} fill="#fff">
{index + 1}
</text>
<text x={-144} y={4} textAnchor="start" fontSize={11} fill="#475569">
{payload?.value}
</text>
</g>
);
}
const REGION_COLORS = [
'#3b82f6', '#22d3ee', '#a855f7', '#f59e0b',
'#10b981', '#ef4444', '#6366f1', '#14b8a6',
@@ -84,14 +105,14 @@ export default function HydrogenOverview() {
<span className="text-sm font-bold text-slate-700"> Top5</span>
<span className="text-[11px] text-slate-400 font-bold"> Kg</span>
</div>
<ResponsiveContainer width="100%" height={240}>
<BarChart data={HYDROGEN_STATIONS_TOP5} layout="vertical" margin={{ top: 0, right: 60, bottom: 0, left: 0 }}>
<ResponsiveContainer width="100%" height={260}>
<BarChart data={HYDROGEN_STATIONS_TOP5} layout="vertical" margin={{ top: 4, right: 80, bottom: 4, left: 0 }}>
<XAxis type="number" hide />
<YAxis
type="category"
dataKey="name"
width={150}
tick={{ fontSize: 11, fill: '#475569' }}
width={170}
tick={<RankYAxisTick />}
tickLine={false}
axisLine={false}
/>
@@ -103,6 +124,14 @@ export default function HydrogenOverview() {
{HYDROGEN_STATIONS_TOP5.map((_, i) => (
<Cell key={i} fill={`url(#topBarGrad)`} />
))}
<LabelList
dataKey="kg"
position="right"
formatter={(v) => `${Number(v ?? 0).toLocaleString('zh-CN', { maximumFractionDigits: 0 })}`}
fill="#475569"
fontSize={11}
fontWeight={700}
/>
</Bar>
<defs>
<linearGradient id="topBarGrad" x1="0" x2="1" y1="0" y2="0">
@@ -117,23 +146,29 @@ export default function HydrogenOverview() {
<div className="bg-white rounded-2xl border border-slate-100 shadow-sm p-4 flex flex-col gap-2">
<span className="text-sm font-bold text-slate-700"></span>
<div className="flex items-center gap-2">
<ResponsiveContainer width="50%" height={200}>
<PieChart>
<Pie
data={HYDROGEN_REGION_SHARE}
dataKey="kg"
nameKey="region"
innerRadius={48}
outerRadius={80}
paddingAngle={1}
>
{HYDROGEN_REGION_SHARE.map((_, i) => (
<Cell key={i} fill={REGION_COLORS[i % REGION_COLORS.length]} />
))}
</Pie>
<Tooltip formatter={(v) => `${(Number(v ?? 0) / 1000).toFixed(2)}T`} contentStyle={{ borderRadius: 12, fontSize: 12 }} />
</PieChart>
</ResponsiveContainer>
<div className="relative w-1/2 h-[200px]">
<ResponsiveContainer width="100%" height="100%">
<PieChart>
<Pie
data={HYDROGEN_REGION_SHARE}
dataKey="kg"
nameKey="region"
innerRadius={48}
outerRadius={80}
paddingAngle={1}
>
{HYDROGEN_REGION_SHARE.map((_, i) => (
<Cell key={i} fill={REGION_COLORS[i % REGION_COLORS.length]} />
))}
</Pie>
<Tooltip formatter={(v) => `${(Number(v ?? 0) / 1000).toFixed(2)}T`} contentStyle={{ borderRadius: 12, fontSize: 12 }} />
</PieChart>
</ResponsiveContainer>
<div className="absolute inset-0 flex flex-col items-center justify-center pointer-events-none">
<div className="text-[10px] text-slate-400 font-bold"></div>
<div className="text-base font-bold text-slate-700 leading-tight">{(HYDROGEN_KPI.yearKg / 1000).toFixed(2)}T</div>
</div>
</div>
<div className="flex-1 grid grid-cols-1 md:grid-cols-2 gap-x-3 gap-y-1 text-[11px]">
{HYDROGEN_REGION_SHARE.map((r, i) => (
<div key={r.region} className="flex items-center gap-1.5">
@@ -144,7 +179,6 @@ export default function HydrogenOverview() {
))}
</div>
</div>
<div className="text-center text-[11px] text-slate-400 font-bold pt-1"> {(HYDROGEN_KPI.yearKg / 1000).toFixed(2)}T</div>
</div>
</div>
</div>