diff --git a/web/src/components/common/ErrorBoundary.jsx b/web/src/components/common/ErrorBoundary.jsx
new file mode 100644
index 00000000..3827969a
--- /dev/null
+++ b/web/src/components/common/ErrorBoundary.jsx
@@ -0,0 +1,52 @@
+import React from 'react';
+import { Empty, Button } from '@douyinfe/semi-ui';
+import {
+ IllustrationFailure,
+ IllustrationFailureDark,
+} from '@douyinfe/semi-illustrations';
+import { withTranslation } from 'react-i18next';
+
+class ErrorBoundary extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = { hasError: false };
+ }
+
+ static getDerivedStateFromError() {
+ return { hasError: true };
+ }
+
+ componentDidCatch(error, errorInfo) {
+ console.error('[ErrorBoundary]', error, errorInfo);
+ }
+
+ render() {
+ if (this.state.hasError) {
+ const { t } = this.props;
+ return (
+
+
+ }
+ darkModeImage={
+
+ }
+ description={t('页面渲染出错,请刷新页面重试')}
+ />
+
+
+ );
+ }
+ return this.props.children;
+ }
+}
+
+export default withTranslation()(ErrorBoundary);
diff --git a/web/src/components/layout/PageLayout.jsx b/web/src/components/layout/PageLayout.jsx
index 51666b5e..ca38ed50 100644
--- a/web/src/components/layout/PageLayout.jsx
+++ b/web/src/components/layout/PageLayout.jsx
@@ -23,6 +23,7 @@ import SiderBar from './SiderBar';
import App from '../../App';
import FooterBar from './Footer';
import { ToastContainer } from 'react-toastify';
+import ErrorBoundary from '../common/ErrorBoundary';
import React, { useContext, useEffect, useState } from 'react';
import { useIsMobile } from '../../hooks/common/useIsMobile';
import { useSidebarCollapsed } from '../../hooks/common/useSidebarCollapsed';
@@ -216,7 +217,9 @@ const PageLayout = () => {
position: 'relative',
}}
>
-
+
+
+
{!shouldHideFooter && (