diff --git a/docs/bi-refactor-roadmap.md b/docs/bi-refactor-roadmap.md index 5125962..9b4141e 100644 --- a/docs/bi-refactor-roadmap.md +++ b/docs/bi-refactor-roadmap.md @@ -150,6 +150,7 @@ SUM(fee) / NULLIF(SUM(kwh), 0) - 已完成版本化指标目录和四模块入口。 - 已完成里程、电能、氢能主要 URL 状态。 - 已完成 ETC 记录/账单视图、日期、搜索和分页 URL 状态,以及超范围页码自动纠正。 +- 已统一共享加载、空数据和故障组件的顶层卡片与明细内联边界,消除能源明细中的嵌套状态卡片。 - 已完成并发同参 GET 去重。 ### 阶段 B:核心下钻闭环 diff --git a/src/components/ui/surface.tsx b/src/components/ui/surface.tsx index 01e1b12..e16eba2 100644 --- a/src/components/ui/surface.tsx +++ b/src/components/ui/surface.tsx @@ -233,9 +233,20 @@ export function SkeletonBlock({ className }: { className?: string }) { return
; } -export function LoadingState({ label = '数据加载中' }: { label?: string }) { +type StateVariant = 'card' | 'inline'; + +export function LoadingState({ + label = '数据加载中', + variant = 'card', +}: { + label?: string; + variant?: StateVariant; +}) { return ( -
+
{label}
@@ -245,12 +256,17 @@ export function LoadingState({ label = '数据加载中' }: { label?: string }) export function EmptyState({ title = '暂无数据', description = '换个筛选条件或稍后再试', + variant = 'card', }: { title?: string; description?: string; + variant?: StateVariant; }) { return ( -
+
{title}
{description}
@@ -263,14 +279,19 @@ export function ErrorState({ title = '加载失败', onRetry, retrying = false, + variant = 'card', }: { message: string; title?: string; onRetry?: () => void; retrying?: boolean; + variant?: StateVariant; }) { return ( -
+
diff --git a/src/modules/energy/ETCDetails.tsx b/src/modules/energy/ETCDetails.tsx index 3aaa23d..1ada1ce 100644 --- a/src/modules/energy/ETCDetails.tsx +++ b/src/modules/energy/ETCDetails.tsx @@ -1,6 +1,7 @@ import { useEffect, useState } from 'react'; -import { ChevronLeft, ChevronRight, Loader2, ReceiptText, Route, Search } from 'lucide-react'; +import { ChevronLeft, ChevronRight, ReceiptText, Route, Search } from 'lucide-react'; import Blur from '../../components/Blur'; +import { ErrorState, LoadingState } from '../../components/ui/surface'; import { fetchEtcBills, fetchEtcRecords } from './api'; import type { EtcBillResponse, EtcTollRecordResponse } from './types'; import type { EtcDetailView, EtcDrillContext } from './etc-drill-context'; @@ -165,11 +166,9 @@ export default function ETCDetails({ ) : null} {error ? ( -
{error}
+ ) : loading && !activeData ? ( -
- 正在加载 ETC 明细 -
+ ) : view === 'records' && records ? ( records.items.length === 0 ? (
diff --git a/src/modules/energy/ElectricDaily.tsx b/src/modules/energy/ElectricDaily.tsx index dc91047..0579055 100644 --- a/src/modules/energy/ElectricDaily.tsx +++ b/src/modules/energy/ElectricDaily.tsx @@ -311,11 +311,11 @@ export default function ElectricDaily() { 环比
{error ? ( -
+ ) : months === null ? ( -
+ ) : months.length === 0 ? ( -
+ ) : months.map(m => { const open = openMonths.has(m.month); return ( @@ -369,11 +369,11 @@ export default function ElectricDaily() { {selectedDate === d.date && (
{ordersError ? ( - + ) : !orders || orders.date !== d.date ? ( - + ) : orders.items.length === 0 ? ( - + ) : (
diff --git a/src/modules/energy/HydrogenDaily.tsx b/src/modules/energy/HydrogenDaily.tsx index 5df637d..8c9378b 100644 --- a/src/modules/energy/HydrogenDaily.tsx +++ b/src/modules/energy/HydrogenDaily.tsx @@ -472,9 +472,9 @@ export default function HydrogenDaily() {
{/* 主行 + 子行 */} {rows === null ? ( -
+ ) : rows.length === 0 ? ( -
+ ) : rows.map(r => { const open = expanded.has(r.date); const isAbnormal = Math.abs(r.chainPct) >= 0.3; diff --git a/src/modules/energy/HydrogenOrders.tsx b/src/modules/energy/HydrogenOrders.tsx index e9fdd06..ad4f480 100644 --- a/src/modules/energy/HydrogenOrders.tsx +++ b/src/modules/energy/HydrogenOrders.tsx @@ -1,6 +1,7 @@ import { useEffect, useState } from 'react'; -import { ChevronLeft, ChevronRight, Loader2, ReceiptText, RefreshCw, X } from 'lucide-react'; +import { ChevronLeft, ChevronRight, ReceiptText, X } from 'lucide-react'; import Blur from '../../components/Blur'; +import { EmptyState, ErrorState, LoadingState } from '../../components/ui/surface'; import { fetchHydrogenOrders } from './api'; import type { CustomerType, HydrogenOrderResponse } from './types'; @@ -81,23 +82,16 @@ export default function HydrogenOrders({ ) : null} {error ? ( -
-
订单数据暂不可用
-
{error}
- -
+ setRetryKey(key => key + 1)} + variant="inline" + /> ) : loading && !data ? ( -
- 正在加载加氢订单 -
+ ) : data?.items.length === 0 ? ( -
当前筛选下无加氢订单
+ ) : data ? ( <>