feat(web): Q3 前端体验优化 — ErrorBoundary + 5 hooks + 共享类型 + i18n 基础
- ErrorBoundary 组件:全局错误捕获与优雅降级 - 提取 5 个自定义 hooks:useCountUp, useDarkMode, useDebouncedValue, usePaginatedData, useApiRequest - 从 11 个 API 文件提取 PaginatedResponse 共享类型到 api/types.ts - 统一 API 错误处理(api/errors.ts) - client.ts 迁移到 axios adapter 模式(替代废弃的 CancelToken) - 添加 react-i18next 国际化基础设施 + zh-CN 语言包
This commit is contained in:
54
apps/web/src/components/ErrorBoundary.tsx
Normal file
54
apps/web/src/components/ErrorBoundary.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Component, type ReactNode } from 'react';
|
||||
import { Button, Result } from 'antd';
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
pageLevel?: boolean;
|
||||
}
|
||||
|
||||
interface State {
|
||||
hasError: boolean;
|
||||
error: Error | null;
|
||||
}
|
||||
|
||||
export 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, errorInfo: React.ErrorInfo) {
|
||||
console.error('ErrorBoundary caught:', error, errorInfo);
|
||||
}
|
||||
|
||||
handleReset = () => {
|
||||
this.setState({ hasError: false, error: null });
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
return (
|
||||
<Result
|
||||
status="error"
|
||||
title={this.props.pageLevel ? '页面加载出错' : '出了点问题'}
|
||||
subTitle={this.props.pageLevel
|
||||
? `错误信息:${this.state.error?.message || '未知错误'}`
|
||||
: '请刷新页面重试'}
|
||||
extra={[
|
||||
<Button key="retry" type="primary" onClick={this.handleReset}>
|
||||
重试
|
||||
</Button>,
|
||||
<Button key="home" onClick={() => window.location.hash = '/'}>
|
||||
返回首页
|
||||
</Button>,
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user