refactor: 更新PageErrorBoundary的文案和样式以对齐shadcn主题

- 错误提示从“该页面加载失败”改为“该功能运行异常”
- 重试按钮文字从“重试”改为“重新尝试”
- 使用border-destructive, bg-destructive等语义化类名替换硬编码颜色
- 更新对应测试断言
This commit is contained in:
雨霖铃
2026-05-22 20:49:46 +08:00
parent 8406b03dd6
commit 2ced38769b
2 changed files with 41 additions and 23 deletions
+32 -14
View File
@@ -14,7 +14,7 @@ interface State {
/**
* 页面级错误边界组件:捕获子组件树中的 JavaScript 错误
* 与全局 ErrorBoundary 的区别:使用轻量内嵌卡片 UI,提供重试按钮
* 完美适配 shadcn/ui 语义化主题与暗黑模式
*/
export class PageErrorBoundary extends Component<Props, State> {
state: State = {
@@ -43,27 +43,45 @@ export class PageErrorBoundary extends Component<Props, State> {
render() {
if (this.state.hasError) {
return (
<div className="flex flex-col items-center justify-center flex-1 p-4 min-h-[200px]">
<div className="p-6 text-center rounded-xl border border-red-200 bg-red-50 max-w-md w-full">
<AlertCircle className="h-12 w-12 text-red-500 mx-auto mb-3" />
<h3 className="text-lg font-bold text-red-600 mb-2"></h3>
<p className="text-sm text-muted-foreground mb-4">
<div className="flex flex-col items-center justify-center flex-1 p-6 min-h-[300px] animate-in fade-in zoom-in-95 duration-200">
{/*
1. 适配暗黑模式的容器设计:
不再使用 border-red-200 / bg-red-50,改用标准的 border-destructive/20 和 bg-destructive/5
并在黑夜模式下会自动转为深红底色,绝不刺眼。
*/}
<div className="p-6 text-center rounded-xl border border-destructive/20 bg-destructive/5 max-w-md w-full shadow-sm">
{/* 2. 状态符号改用标准的 text-destructive 语义色 */}
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-destructive/10 text-destructive mx-auto mb-4">
<AlertCircle className="h-6 w-6" />
</div>
<h3 className="text-base font-semibold text-foreground mb-1.5"></h3>
<p className="text-xs text-muted-foreground mb-5">
</p>
{/* 3. 错误日志展示:使用与 shadcn 贴合的深色代码块包裹 */}
{this.state.error && (
<div className="mb-4 p-3 bg-muted rounded-lg text-left max-h-[160px] overflow-auto">
<pre className="font-mono text-xs whitespace-pre-wrap break-all text-red-700">
{this.state.error.toString()}
<div className="mb-5 p-3 rounded-lg bg-zinc-950 dark:bg-zinc-900 text-left max-h-40 overflow-y-auto border border-border/40">
<pre className="font-mono text-[11px] leading-relaxed whitespace-pre-wrap break-all text-zinc-200 selection:bg-zinc-700">
{this.state.error.stack || this.state.error.toString()}
</pre>
</div>
)}
{/*
4. 严谨调用 shadcn 原子 Button
去掉全部手动指定的红底白字类名,直接启用 variant="destructive"。
它会自动处理 hover 颜色变化、暗黑模式切换以及无障碍高亮边框。
*/}
<Button
variant="default"
variant="destructive"
size="sm"
onClick={this.handleRetry}
className="rounded-lg font-bold bg-red-600 hover:bg-red-700 text-white"
className="font-medium shadow-sm"
>
<RefreshCw className="mr-2 h-4 w-4" />
<RefreshCw className="mr-1.5 h-3.5 w-3.5" />
</Button>
</div>
</div>
@@ -34,7 +34,7 @@ describe('PageErrorBoundary', () => {
</PageErrorBoundary>,
);
expect(screen.getByText('该页面加载失败')).toBeInTheDocument();
expect(screen.getByText('该功能运行异常')).toBeInTheDocument();
expect(screen.getByText(/测试错误/)).toBeInTheDocument();
});
@@ -45,7 +45,7 @@ describe('PageErrorBoundary', () => {
</PageErrorBoundary>,
);
expect(screen.getByText('该页面加载失败')).toBeInTheDocument();
expect(screen.getByText('该功能运行异常')).toBeInTheDocument();
// 将子组件替换为正常组件,然后点击重试
rerender(
@@ -54,14 +54,14 @@ describe('PageErrorBoundary', () => {
</PageErrorBoundary>,
);
const retryButton = screen.getByRole('button', { name: /重试/ });
const retryButton = screen.getByRole('button', { name: /重新尝试/ });
retryButton.click();
await waitFor(() => {
expect(screen.getByTestId('normal-content')).toHaveTextContent('恢复后的内容');
});
expect(screen.queryByText('该页面加载失败')).not.toBeInTheDocument();
expect(screen.queryByText('该功能运行异常')).not.toBeInTheDocument();
});
it('resetKey 变化时自动重置错误状态', async () => {
@@ -71,7 +71,7 @@ describe('PageErrorBoundary', () => {
</PageErrorBoundary>,
);
expect(screen.getByText('该页面加载失败')).toBeInTheDocument();
expect(screen.getByText('该功能运行异常')).toBeInTheDocument();
// 切换 resetKey,同时提供正常子组件
rerender(
@@ -84,7 +84,7 @@ describe('PageErrorBoundary', () => {
expect(screen.getByTestId('normal-content')).toHaveTextContent('页面 B 内容');
});
expect(screen.queryByText('该页面加载失败')).not.toBeInTheDocument();
expect(screen.queryByText('该功能运行异常')).not.toBeInTheDocument();
});
it('resetKey 不变时保持错误状态', () => {
@@ -94,7 +94,7 @@ describe('PageErrorBoundary', () => {
</PageErrorBoundary>,
);
expect(screen.getByText('该页面加载失败')).toBeInTheDocument();
expect(screen.getByText('该功能运行异常')).toBeInTheDocument();
// 仅 children 变化,resetKey 不变,错误应保持
rerender(
@@ -103,7 +103,7 @@ describe('PageErrorBoundary', () => {
</PageErrorBoundary>,
);
expect(screen.getByText('该页面加载失败')).toBeInTheDocument();
expect(screen.getByText('该功能运行异常')).toBeInTheDocument();
});
it('错误 UI 包含重试按钮', () => {
@@ -113,7 +113,7 @@ describe('PageErrorBoundary', () => {
</PageErrorBoundary>,
);
const retryButton = screen.getByRole('button', { name: /重试/ });
const retryButton = screen.getByRole('button', { name: /重新尝试/ });
expect(retryButton).toBeInTheDocument();
});