107 lines
4.2 KiB
JavaScript
Executable File
107 lines
4.2 KiB
JavaScript
Executable File
import React from 'react';
|
||
import {
|
||
Box,
|
||
Alert,
|
||
AlertIcon,
|
||
AlertTitle,
|
||
AlertDescription,
|
||
Button,
|
||
VStack,
|
||
Container
|
||
} from '@chakra-ui/react';
|
||
import { logger } from '../utils/logger';
|
||
|
||
class ErrorBoundary extends React.Component {
|
||
constructor(props) {
|
||
super(props);
|
||
this.state = { hasError: false, error: null, errorInfo: null };
|
||
}
|
||
|
||
static getDerivedStateFromError(error) {
|
||
// 所有环境都捕获错误,避免无限重渲染
|
||
return { hasError: true };
|
||
}
|
||
|
||
componentDidCatch(error, errorInfo) {
|
||
// 记录详细的错误日志
|
||
logger.error('ErrorBoundary', 'Component Error Caught', error, {
|
||
componentStack: errorInfo.componentStack,
|
||
errorName: error.name,
|
||
errorMessage: error.message,
|
||
environment: process.env.NODE_ENV,
|
||
stack: error.stack
|
||
});
|
||
|
||
// 保存错误信息到 state
|
||
this.setState({
|
||
error: error,
|
||
errorInfo: errorInfo
|
||
});
|
||
}
|
||
|
||
render() {
|
||
// 如果有错误,显示错误边界(所有环境)
|
||
if (this.state.hasError) {
|
||
return (
|
||
<Container maxW="lg" py={20}>
|
||
<VStack spacing={6}>
|
||
<Alert status="error" borderRadius="lg" p={6}>
|
||
<AlertIcon boxSize="24px" />
|
||
<Box>
|
||
<AlertTitle fontSize="lg" mb={2}>
|
||
页面出现错误!
|
||
</AlertTitle>
|
||
<AlertDescription>
|
||
{process.env.NODE_ENV === 'development'
|
||
? '组件渲染时发生错误,请查看下方详情和控制台日志。'
|
||
: '页面加载时发生了未预期的错误,请尝试刷新页面。'}
|
||
</AlertDescription>
|
||
</Box>
|
||
</Alert>
|
||
|
||
{/* 开发环境显示详细错误信息 */}
|
||
{process.env.NODE_ENV === 'development' && this.state.error && (
|
||
<Box
|
||
w="100%"
|
||
bg="red.50"
|
||
p={4}
|
||
borderRadius="lg"
|
||
fontSize="sm"
|
||
overflow="auto"
|
||
maxH="400px"
|
||
border="1px"
|
||
borderColor="red.200"
|
||
>
|
||
<Box fontWeight="bold" mb={2} color="red.700">错误详情:</Box>
|
||
<Box as="pre" whiteSpace="pre-wrap" color="red.900" fontSize="xs">
|
||
<Box fontWeight="bold" mb={1}>{this.state.error.name}: {this.state.error.message}</Box>
|
||
{this.state.error.stack && (
|
||
<Box mt={2} color="gray.700">{this.state.error.stack}</Box>
|
||
)}
|
||
{this.state.errorInfo && this.state.errorInfo.componentStack && (
|
||
<>
|
||
<Box fontWeight="bold" mt={3} mb={1} color="red.700">组件堆栈:</Box>
|
||
<Box color="gray.700">{this.state.errorInfo.componentStack}</Box>
|
||
</>
|
||
)}
|
||
</Box>
|
||
</Box>
|
||
)}
|
||
|
||
<Button
|
||
colorScheme="blue"
|
||
size="lg"
|
||
onClick={() => window.location.reload()}
|
||
>
|
||
重新加载页面
|
||
</Button>
|
||
</VStack>
|
||
</Container>
|
||
);
|
||
}
|
||
|
||
return this.props.children;
|
||
}
|
||
}
|
||
|
||
export default ErrorBoundary; |