diff --git a/software/web/src/pages/energy/index.tsx b/software/web/src/pages/energy/index.tsx index b3c7176..cf10584 100644 --- a/software/web/src/pages/energy/index.tsx +++ b/software/web/src/pages/energy/index.tsx @@ -15,6 +15,7 @@ import { Space, Select, Grid, + Alert, Spin, Message } from '@arco-design/web-react'; import { IconDownload } from '@arco-design/web-react/icon'; @@ -77,6 +78,61 @@ interface TrendData { list: TrendPoint[]; } +/** 柜子下拉选项 */ +interface CabinetOption { + id: number; + name?: string; + abstract_id?: string; +} + +/** + * 电表口径日用电量行 + * + * ⛔ 与页面其它区块**不是一套数据**:其它区块是 `energy-stats`(订单电量口径, + * 平台按 V×I×Δt 积分的估算值);这里是 `meter-usage`(**电表读数**口径, + * 硬件计量、含非充电损耗)。两者数值必然不一致,谁也不替代谁。 + */ +interface MeterUsageDay { + day: string; + end_reading: number; + /** 当日用电量(Wh)。`undefined` = 无对比基准(该柜第一条读数) */ + usage_wh?: number; + /** 数据质量标记,前端据此决定怎么展示 */ + flags: MeterUsageFlag[]; +} + +/** 数据质量标记(与后端 `UsageFlag` 枚举一一对应) */ +type MeterUsageFlag = + | 'no_baseline' + | 'covers_multiple_days' + | 'meter_reset_or_replaced'; + +/** 电表口径汇总 */ +interface MeterUsageSummary { + total_wh: number; + counted_days: number; + days_with_data: number; +} + +/** 电表口径响应 */ +interface MeterUsageData { + cabinet_id: number; + start: string; + end: string; + days: MeterUsageDay[]; + summary: MeterUsageSummary; +} + +/** flags → 人类可读说明 + 展示色调 */ +const METER_FLAG_META: Record = { + // 该柜第一条读数,无对比基准 ⇒ 不是 0 度,是"不知道" + no_baseline: { text: '无基准', color: 'var(--color-text-3)' }, + // 柜子离线了几天,本行差值覆盖多天 + covers_multiple_days: { text: '覆盖多日', color: 'var(--color-warning-6)' }, + // 读数变小 = 换表 / 清零,差值不是用电量 + meter_reset_or_replaced: { text: '电表更换/清零', color: 'var(--color-danger-6)' }, +}; + export default function Energy() { // 总览 const [summary, setSummary] = useState(null); @@ -87,6 +143,11 @@ export default function Energy() { // 趋势 const [trendData, setTrendData] = useState([]); const [granularity, setGranularity] = useState('day'); + // 电表口径(整柜日用电量) + const [cabinetOptions, setCabinetOptions] = useState([]); + const [meterCabinetId, setMeterCabinetId] = useState(); + const [meterData, setMeterData] = useState(null); + const [meterLoading, setMeterLoading] = useState(false); // 通用 const [loading, setLoading] = useState(true); const [downloadVisible, setDownloadVisible] = useState(false); @@ -146,6 +207,27 @@ export default function Energy() { fetchTrend(); }, [fetchTrend]); + /** 加载柜子下拉(电表口径区块用) */ + useEffect(() => { + api.get('/cabinets/options') + .then((res) => setCabinetOptions(res.data || [])) + .catch(() => Message.error('加载柜子列表失败')); + }, []); + + /** 选中柜子变化时加载电表口径日用电量 */ + useEffect(() => { + if (!meterCabinetId) { + setMeterData(null); + return; + } + setMeterLoading(true); + api + .get(`/cabinets/${meterCabinetId}/meter-usage`) + .then((res) => setMeterData(res.data)) + .catch(() => Message.error('加载电表口径用电量失败')) + .finally(() => setMeterLoading(false)); + }, [meterCabinetId]); + /** 导出(后端要求 energy:export) */ const handleExport = async () => { try { @@ -235,6 +317,53 @@ export default function Energy() { padding: '8px 12px', }; + /** 电表口径日用电量列定义 */ + const meterUsageColumns = [ + { + title: '日期', + dataIndex: 'day', + width: 140, + defaultSortOrder: 'descend' as const, + sortable: true, + sorter: (a: MeterUsageDay, b: MeterUsageDay) => a.day.localeCompare(b.day), + }, + { + title: '电表末读数(kWh)', + dataIndex: 'end_reading', + width: 160, + render: (v: number) => fmt(v / 1000), + }, + { + title: '当日用电(kWh)', + dataIndex: 'usage_wh', + width: 150, + render: (v: number | undefined, record: MeterUsageDay) => { + // ⛔ 无基准 ⇒ 不是 0 度,必须显示"-"(显示 0 等于告诉用户这天没用一度电) + if (v == null) return '-'; + // 换表/清零 ⇒ 差值是负数,绝不能呈现成"负用电量" + if (record.flags.includes('meter_reset_or_replaced')) return '-'; + return fmt(v / 1000); + }, + }, + { + title: '数据质量', + dataIndex: 'flags', + width: 220, + render: (flags: MeterUsageFlag[]) => + flags.length === 0 ? ( + 正常 + ) : ( + + {flags.map((f) => ( + + {METER_FLAG_META[f].text} + + ))} + + ), + }, + ]; + return ( {/* 顶部数据卡片 */} @@ -446,6 +575,77 @@ export default function Energy() { + {/* 整柜日用电量(电表读数口径)*/} + + + 默认最近 30 天 + +