feat: 前端全面优化 — 懒加载/骨架屏/错误边界/面包屑/主题增强/Glass登录页/动画系统
- 新增: ErrorBoundary, PageTransition, Breadcrumb, SkeletonCard (4组件) - 重构: App.tsx (React.lazy懒加载所有页面), index.css (55+主题变量/动画/工具类) - 优化: MainLayout (CSS变量布局+Footer), Header (搜索增强+快捷键), Sidebar (折叠Tooltip+子菜单动画) - 优化: Dashboard骨架屏+级联动画, Stats/Charts/Recent主题感知颜色 - 优化: Login.tsx Glass毛玻璃效果+密码显隐图标+光球动画 - 图表Recharts主题化(CustomTooltip/chart-grid/chart-axis) - 减少动画偏好支持(prefers-reduced-motion)
This commit is contained in:
1 parent
3d7932c087
commit
722587ab84
14 files changed
+2026
-969
No files matched your search
@@ -0,0 +1,133 @@
|
||||
import { Component, type ErrorInfo, type ReactNode } from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { AlertTriangle, RotateCcw, Home, Bug } from 'lucide-react';
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
fallback?: ReactNode;
|
||||
}
|
||||
|
||||
interface State {
|
||||
hasError: boolean;
|
||||
error: Error | null;
|
||||
errorInfo: ErrorInfo | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* ErrorBoundary - 全局错误边界组件
|
||||
*
|
||||
* 捕获 React 组件树中的错误,防止整个应用崩溃
|
||||
* 提供友好的错误提示和恢复操作
|
||||
*/
|
||||
export class ErrorBoundary extends Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = { hasError: false, error: null, errorInfo: null };
|
||||
}
|
||||
|
||||
static getDerivedStateFromError(error: Error): State {
|
||||
return { hasError: true, error, errorInfo: null };
|
||||
}
|
||||
|
||||
override componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
||||
console.error('[ErrorBoundary] 捕获到错误:', error);
|
||||
console.error('[ErrorBoundary] 错误信息:', errorInfo.componentStack);
|
||||
this.setState({ error, errorInfo });
|
||||
}
|
||||
|
||||
handleRetry = () => {
|
||||
this.setState({ hasError: false, error: null, errorInfo: null });
|
||||
};
|
||||
|
||||
handleGoHome = () => {
|
||||
window.location.href = '/';
|
||||
};
|
||||
|
||||
handleReport = () => {
|
||||
const { error, errorInfo } = this.state;
|
||||
const body = `## 错误报告\n\n**时间:** ${new Date().toISOString()}\n**URL:** ${window.location.href}\n**用户代理:** ${navigator.userAgent}\n\n### 错误信息\n\n\`\`\`\n${error?.name}: ${error?.message}\n\`\`\`\n\n### 组件堆栈\n\n\`\`\`\n${errorInfo?.componentStack}\n\`\`\`\n`;
|
||||
navigator.clipboard.writeText(body).then(() => {
|
||||
alert('错误信息已复制到剪贴板,请粘贴到 Issue 中');
|
||||
});
|
||||
};
|
||||
|
||||
override render() {
|
||||
if (this.state.hasError) {
|
||||
if (this.props.fallback) {
|
||||
return this.props.fallback;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-[60vh] flex items-center justify-center p-6">
|
||||
<div className="dg-card max-w-lg w-full p-8 text-center space-y-6">
|
||||
{/* Error Icon */}
|
||||
<div className="mx-auto w-16 h-16 rounded-2xl bg-red-500/10 flex items-center justify-center">
|
||||
<AlertTriangle className="h-8 w-8 text-red-400" />
|
||||
</div>
|
||||
|
||||
{/* Title */}
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-[var(--dg-text-primary)]">
|
||||
页面出现错误
|
||||
</h2>
|
||||
<p className="text-sm text-[var(--dg-text-muted)] mt-2">
|
||||
抱歉,该页面遇到了意外错误。您可以尝试重新加载或返回首页。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Error Detail (collapsible) */}
|
||||
{this.state.error && (
|
||||
<div className="text-left">
|
||||
<details className="group">
|
||||
<summary className="cursor-pointer text-xs text-[var(--dg-text-secondary)] hover:text-[var(--dg-text-primary)] transition-colors flex items-center gap-1.5">
|
||||
<Bug className="h-3.5 w-3.5" />
|
||||
查看错误详情
|
||||
</summary>
|
||||
<div className="mt-2 p-3 rounded-lg bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] overflow-auto max-h-48">
|
||||
<code className="text-xs text-red-400 font-mono block whitespace-pre-wrap">
|
||||
{this.state.error.name}: {this.state.error.message}
|
||||
</code>
|
||||
{this.state.errorInfo && (
|
||||
<pre className="text-xs text-[var(--dg-text-muted)] mt-2 whitespace-pre-wrap">
|
||||
{this.state.errorInfo.componentStack}
|
||||
</pre>
|
||||
)}
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex items-center justify-center gap-3">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="dg-btn-secondary"
|
||||
onClick={this.handleGoHome}
|
||||
>
|
||||
<Home className="h-4 w-4 mr-1.5" />
|
||||
返回首页
|
||||
</Button>
|
||||
<Button
|
||||
className="dg-btn-primary"
|
||||
onClick={this.handleRetry}
|
||||
>
|
||||
<RotateCcw className="h-4 w-4 mr-1.5" />
|
||||
重新加载
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="dg-btn-ghost"
|
||||
onClick={this.handleReport}
|
||||
>
|
||||
<Bug className="h-4 w-4 mr-1.5" />
|
||||
复制错误
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user