"use client" import { Component, type ReactNode, Suspense } from "react" import { AlertCircle } from "lucide-react" import { EmptyState } from "@/shared/components/ui/empty-state" import { Card, CardContent, CardHeader } from "@/shared/components/ui/card" import { Skeleton } from "@/shared/components/ui/skeleton" import { useTranslations } from "next-intl" /** * 仪表盘分区 Error Boundary * * 包裹每个独立数据区块,避免单个区块崩溃导致整页不可用。 * 与路由级 error.tsx 不同,此组件仅替换出错区块,其余区块继续渲染。 */ export class DashboardSectionErrorBoundary extends Component< { children: ReactNode }, { hasError: boolean } > { state: { hasError: boolean } = { hasError: false } static getDerivedStateFromError(): { hasError: boolean } { return { hasError: true } } handleRetry = (): void => { this.setState({ hasError: false }) } render(): ReactNode { if (this.state.hasError) { return } return this.props.children } } function DashboardSectionErrorFallback({ onRetry, }: { onRetry: () => void }): ReactNode { const t = useTranslations("dashboard.error") return ( ) } /** * 分区骨架屏变体 * * 不同数据区块使用不同骨架布局,提供更贴近真实内容的加载占位。 */ type SkeletonVariant = "stats" | "card" | "chart" | "table" | "list" export function DashboardSectionSkeleton({ variant = "card", }: { variant?: SkeletonVariant }): ReactNode { switch (variant) { case "stats": return (
{Array.from({ length: 4 }).map((_, i) => ( ))}
) case "chart": return ( ) case "table": return ( {Array.from({ length: 5 }).map((_, i) => ( ))} ) case "list": return ( {Array.from({ length: 4 }).map((_, i) => (
))}
) case "card": default: return ( ) } } /** * 仪表盘分区包装器 * * 组合 Error Boundary + Suspense + 骨架屏,包裹每个独立数据区块。 * 单个区块出错或加载中时,仅影响该区块,不波及整页。 * * @example * * * */ export function DashboardSection({ children, variant = "card", }: { children: ReactNode variant?: SkeletonVariant }): ReactNode { return ( }> {children} ) }