"use client" /** * Grades/Diagnostic 模块通用 Widget 边界组件。 * * 组合三个能力: * 1. Error Boundary — 隔离故障域,单个 Widget 抛错不影响其他区块 * 2. Suspense — 流式渲染时显示骨架屏,避免白屏等待 * 3. Skeleton — 与 Widget 尺寸匹配的占位 * * 用法: * ```tsx * * * * ``` */ import { Component, Suspense, type ReactNode } from "react" import { AlertCircle } from "lucide-react" import { Button } from "@/shared/components/ui/button" import { Skeleton } from "@/shared/components/ui/skeleton" interface WidgetBoundaryProps { children: ReactNode /** Widget 标题(用于错误提示和 aria-label) */ title?: string /** 骨架屏高度(默认 200px) */ skeletonHeight?: number /** 自定义错误描述 */ fallbackDescription?: string /** 重试按钮文案 */ retryLabel?: string } interface WidgetBoundaryState { hasError: boolean } class WidgetErrorBoundary extends Component< Pick< WidgetBoundaryProps, "title" | "fallbackDescription" | "retryLabel" | "children" >, WidgetBoundaryState > { constructor(props: WidgetBoundaryProps) { super(props) this.state = { hasError: false } } static getDerivedStateFromError(): WidgetBoundaryState { return { hasError: true } } handleReset = (): void => { this.setState({ hasError: false }) } render(): ReactNode { if (this.state.hasError) { const title = this.props.title ?? "区块" return (
) } return this.props.children } } function WidgetSkeleton({ height, title, }: { height: number title?: string }): ReactNode { return (
) } export function WidgetBoundary({ children, title, skeletonHeight = 200, fallbackDescription, retryLabel, }: WidgetBoundaryProps): ReactNode { return ( } > {children} ) }