From d9b9ff495e6ee14a3eb42caa8db8716d7524f8e4 Mon Sep 17 00:00:00 2001 From: kkfluous Date: Tue, 28 Apr 2026 12:05:11 +0800 Subject: [PATCH] =?UTF-8?q?feat(energy):=20electric=20view=20=E2=80=94=20d?= =?UTF-8?q?aily=20bar=20chart=20+=20anomaly=20tint=20+=20mobile=20?= =?UTF-8?q?=E7=8E=AF=E6=AF=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New 本月每日充电 bar chart (蓝青 gradient) sits between KPI row and table, fixing the previous "wall of numbers" feel - Day rows now tint emerald/red when |chainPct| >= 30% (matches hydrogen) - 环比 pill column now also shows on mobile (was desktop-only) - Today KPI: pill moves to second line alongside kwh via justify-between so it no longer gets clipped on narrow viewports - Day labels in table trimmed to MM-DD (parent month row already shows year) Co-Authored-By: Claude Opus 4.7 (1M context) --- src/modules/energy/ElectricView.tsx | 99 ++++++++++++++++++++++------- 1 file changed, 76 insertions(+), 23 deletions(-) diff --git a/src/modules/energy/ElectricView.tsx b/src/modules/energy/ElectricView.tsx index 9bef3f3..6efa802 100644 --- a/src/modules/energy/ElectricView.tsx +++ b/src/modules/energy/ElectricView.tsx @@ -1,6 +1,7 @@ import { useMemo, useState } from 'react'; import { ChevronRight, Wallet, BatteryCharging, CalendarClock } from 'lucide-react'; import { motion, AnimatePresence } from 'motion/react'; +import { BarChart, Bar, XAxis, YAxis, ResponsiveContainer, Cell, Tooltip } from 'recharts'; import TrendBadge from './TrendBadge'; import { ELECTRIC_KPI, ELECTRIC_MONTHLY } from './mock'; import type { CustomerType } from './types'; @@ -22,6 +23,13 @@ export default function ElectricView() { return ELECTRIC_MONTHLY; }, [customer]); + // 本月每日数据(按日期升序,便于柱图按时间从左到右展示) + const trendData = useMemo(() => { + const first = ELECTRIC_MONTHLY[0]; + if (!first) return []; + return [...first.rows].sort((a, b) => a.date.localeCompare(b.date)); + }, []); + const k = ELECTRIC_KPI; const toggleMonth = (m: string) => setOpenMonths(prev => { @@ -56,14 +64,53 @@ export default function ElectricView() {
今日
-
-
{fmtYuan(k.todayFee)}
+
{fmtYuan(k.todayFee)}
+
+ {fmtKwh(k.todayKwh)}
-
{fmtKwh(k.todayKwh)}
+ {/* 本月每日充电柱图 */} +
+
+ 本月每日充电 + 单位 元 +
+ + + 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)' }} + /> + + {trendData.map((_, i) => ( + + ))} + + + + + + + + + +
+ {/* 客户类型 */}
{(['external', 'lingniu'] as const).map(c => ( @@ -81,12 +128,12 @@ export default function ElectricView() { {/* 月份分组表 */}
-
+
月份 / 日期 充电电量 充电费用(元) - 环比 + 环比
{months.map(m => { const open = openMonths.has(m.month); @@ -94,7 +141,7 @@ export default function ElectricView() {
{open && ( @@ -117,23 +164,29 @@ export default function ElectricView() { animate={{ height: 'auto', opacity: 1 }} exit={{ height: 0, opacity: 0 }} transition={{ duration: 0.15 }} - className="overflow-hidden bg-slate-50/50" + className="overflow-hidden" > - {m.rows.map(d => ( -
- {d.date} - - {d.kwh.toLocaleString('zh-CN', { maximumFractionDigits: 2 })} - - - {d.fee.toLocaleString('zh-CN', { maximumFractionDigits: 2 })} - - -
- ))} + {m.rows.map(d => { + const isAbnormal = Math.abs(d.chainPct) >= 0.3; + const abnormalBg = isAbnormal + ? d.chainPct > 0 ? 'bg-emerald-50/40' : 'bg-red-50/40' + : 'bg-slate-50/50'; + return ( +
+ {d.date.slice(5)} + + {d.kwh.toLocaleString('zh-CN', { maximumFractionDigits: 2 })} + + + {d.fee.toLocaleString('zh-CN', { maximumFractionDigits: 2 })} + + +
+ ); + })} )}