Initial commit: OmniAI Web Frontend
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
import { Component, type ReactNode } from "react";
|
||||
import { reportError } from "../utils/errorReporting";
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
interface State {
|
||||
hasError: boolean;
|
||||
error: Error | null;
|
||||
}
|
||||
|
||||
class ErrorBoundary extends Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = { hasError: false, error: null };
|
||||
}
|
||||
|
||||
static getDerivedStateFromError(error: Error): State {
|
||||
return { hasError: true, error };
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, info: React.ErrorInfo) {
|
||||
console.error("[ErrorBoundary] Uncaught error:", error, info.componentStack);
|
||||
reportError(error, "boundary");
|
||||
}
|
||||
|
||||
handleReset = () => {
|
||||
this.setState({ hasError: false, error: null });
|
||||
};
|
||||
|
||||
handleReload = () => {
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
render() {
|
||||
if (!this.state.hasError) {
|
||||
return this.props.children;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
minHeight: "100vh",
|
||||
background: "var(--bg-base, #f5f5f5)",
|
||||
padding: 24,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
maxWidth: 480,
|
||||
padding: "48px 40px",
|
||||
borderRadius: 16,
|
||||
background: "var(--surface-panel, #fff)",
|
||||
boxShadow: "0 4px 24px rgba(0,0,0,0.08)",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
<div style={{ fontSize: 48, marginBottom: 16 }}>⚠️</div>
|
||||
<h2
|
||||
style={{
|
||||
margin: "0 0 12px",
|
||||
fontSize: 20,
|
||||
fontWeight: 600,
|
||||
color: "var(--text-primary, #1a1a1a)",
|
||||
}}
|
||||
>
|
||||
页面出错了
|
||||
</h2>
|
||||
<p
|
||||
style={{
|
||||
margin: "0 0 24px",
|
||||
fontSize: 14,
|
||||
color: "var(--text-secondary, #666)",
|
||||
lineHeight: 1.6,
|
||||
}}
|
||||
>
|
||||
应用遇到了意外错误,请尝试刷新页面。
|
||||
<br />
|
||||
如果问题持续出现,请联系技术支持。
|
||||
</p>
|
||||
{this.state.error && (
|
||||
<details
|
||||
style={{
|
||||
marginBottom: 24,
|
||||
padding: 12,
|
||||
borderRadius: 8,
|
||||
background: "var(--bg-elevated, #f0f0f0)",
|
||||
textAlign: "left",
|
||||
fontSize: 12,
|
||||
color: "var(--text-secondary, #666)",
|
||||
}}
|
||||
>
|
||||
<summary style={{ cursor: "pointer", fontWeight: 500 }}>
|
||||
错误详情
|
||||
</summary>
|
||||
<pre
|
||||
style={{
|
||||
marginTop: 8,
|
||||
whiteSpace: "pre-wrap",
|
||||
wordBreak: "break-word",
|
||||
fontFamily: "monospace",
|
||||
fontSize: 11,
|
||||
}}
|
||||
>
|
||||
{this.state.error.message}
|
||||
</pre>
|
||||
</details>
|
||||
)}
|
||||
<div style={{ display: "flex", gap: 12, justifyContent: "center" }}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={this.handleReset}
|
||||
style={{
|
||||
padding: "10px 24px",
|
||||
borderRadius: 8,
|
||||
border: "1px solid var(--border-normal, #ddd)",
|
||||
background: "transparent",
|
||||
color: "var(--text-primary, #1a1a1a)",
|
||||
fontSize: 14,
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
重试
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={this.handleReload}
|
||||
style={{
|
||||
padding: "10px 24px",
|
||||
borderRadius: 8,
|
||||
border: "none",
|
||||
background: "var(--accent, #0d9488)",
|
||||
color: "#fff",
|
||||
fontSize: 14,
|
||||
fontWeight: 500,
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
刷新页面
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ErrorBoundary;
|
||||
Reference in New Issue
Block a user