import { ErrorOutlineRounded, RefreshRounded, BugReportRounded, } from '@mui/icons-material' import { Box, Typography, Button, Alert, Collapse } from '@mui/material' import React, { Component, ErrorInfo, ReactNode } from 'react' import { useTranslation } from 'react-i18next' interface Props { children: ReactNode fallbackComponent?: ReactNode onError?: (error: Error, errorInfo: ErrorInfo) => void } interface State { hasError: boolean error: Error | null errorInfo: ErrorInfo | null showDetails: boolean } export class TrafficErrorBoundary extends Component { private retryCount = 0 private maxRetries = 3 constructor(props: Props) { super(props) this.state = { hasError: false, error: null, errorInfo: null, showDetails: false, } } static getDerivedStateFromError(error: Error): Partial { return { hasError: true, error } } componentDidCatch(error: Error, errorInfo: ErrorInfo) { console.error('[TrafficErrorBoundary] 捕获到组件错误:', error, errorInfo) this.setState({ error, errorInfo, }) if (this.props.onError) { this.props.onError(error, errorInfo) } this.reportError(error, errorInfo) } private reportError = (error: Error, errorInfo: ErrorInfo) => { const errorReport = { message: error.message, stack: error.stack, componentStack: errorInfo.componentStack, timestamp: new Date().toISOString(), userAgent: navigator.userAgent, url: window.location.href, } console.error('[TrafficErrorBoundary] 错误报告:', errorReport) } private handleRetry = () => { if (this.retryCount < this.maxRetries) { this.retryCount++ console.log( `[TrafficErrorBoundary] 尝试重试 (${this.retryCount}/${this.maxRetries})`, ) this.setState({ hasError: false, error: null, errorInfo: null, showDetails: false, }) } else { console.warn('[TrafficErrorBoundary] 已达到最大重试次数') } } private handleRefresh = () => { window.location.reload() } private toggleDetails = () => { this.setState((prev) => ({ showDetails: !prev.showDetails })) } render() { if (this.state.hasError) { if (this.props.fallbackComponent) { return this.props.fallbackComponent } return ( ) } return this.props.children } } interface TrafficErrorFallbackProps { error: Error | null errorInfo: ErrorInfo | null showDetails: boolean canRetry: boolean retryCount: number maxRetries: number onRetry: () => void onRefresh: () => void onToggleDetails: () => void } const TrafficErrorFallback: React.FC = ({ error, errorInfo, showDetails, canRetry, retryCount, maxRetries, onRetry, onRefresh, onToggleDetails, }) => { const { t } = useTranslation() return ( {t('shared.feedback.errors.trafficStats')} {t('shared.feedback.errors.trafficStatsDescription')} {t('shared.feedback.errors.label')}:{' '} {error instanceof Error ? error.message : t('shared.feedback.errors.unknown')} {retryCount > 0 && ( {t('shared.labels.retryAttempts')}: {retryCount}/{maxRetries} )} {canRetry && ( )} {t('shared.feedback.errors.details')}: {error?.stack} {errorInfo?.componentStack && ( <> {t('shared.feedback.errors.componentStack')}: {errorInfo.componentStack} )} ) } export const LightweightTrafficErrorBoundary: React.FC<{ children: ReactNode }> = ({ children }) => { const { t } = useTranslation() return ( {t('shared.feedback.errors.trafficUnavailable')} } > {children} ) }