From 379f1ce941cc1e0373c7623b459ffef7fe3d0319 Mon Sep 17 00:00:00 2001 From: lingniu Date: Thu, 16 Jul 2026 05:08:40 +0800 Subject: [PATCH] fix(web): recover route errors on scope change --- .../web/src/v2/routing/RouteBoundary.test.tsx | 25 +++++++++++++++++++ .../apps/web/src/v2/routing/RouteBoundary.tsx | 8 +++++- .../docs/frontend-production-readiness.md | 14 +++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/vehicle-data-platform/apps/web/src/v2/routing/RouteBoundary.test.tsx b/vehicle-data-platform/apps/web/src/v2/routing/RouteBoundary.test.tsx index 04ea6d4c..452a2c81 100644 --- a/vehicle-data-platform/apps/web/src/v2/routing/RouteBoundary.test.tsx +++ b/vehicle-data-platform/apps/web/src/v2/routing/RouteBoundary.test.tsx @@ -58,4 +58,29 @@ describe('route recovery boundary', () => { expect(screen.getByText('本地状态 1')).toBeInTheDocument(); expect(mounts).toBe(1); }); + + it('retries a failed route when its search scope changes without remounting healthy scopes', () => { + vi.spyOn(console, 'error').mockImplementation(() => undefined); + let healthyMounts = 0; + function ScopeSensitivePage() { + const [params] = useSearchParams(); + if (params.get('scope') === 'broken') throw new Error('invalid route scope'); + useEffect(() => { healthyMounts += 1; }, []); + return 新筛选范围已恢复; + } + function RecoveryHarness() { + const [, setSearchParams] = useSearchParams(); + return <>; + } + + render(); + expect(screen.getByRole('alert')).toHaveTextContent('当前模块暂时无法显示'); + expect(screen.getByText(/更换筛选条件会自动重试/)).toBeInTheDocument(); + + fireEvent.click(screen.getByRole('button', { name: '修正筛选条件' })); + + expect(screen.queryByRole('alert')).not.toBeInTheDocument(); + expect(screen.getByText('新筛选范围已恢复')).toBeInTheDocument(); + expect(healthyMounts).toBe(1); + }); }); diff --git a/vehicle-data-platform/apps/web/src/v2/routing/RouteBoundary.tsx b/vehicle-data-platform/apps/web/src/v2/routing/RouteBoundary.tsx index 289c02a6..d4be24fc 100644 --- a/vehicle-data-platform/apps/web/src/v2/routing/RouteBoundary.tsx +++ b/vehicle-data-platform/apps/web/src/v2/routing/RouteBoundary.tsx @@ -42,6 +42,12 @@ class RecoverableRouteBoundary extends Component<{ children: ReactNode; routeKey } } + componentDidUpdate(previous: Readonly<{ children: ReactNode; routeKey: string }>) { + if (this.state.error && previous.routeKey !== this.props.routeKey) { + this.setState({ error: undefined }); + } + } + render() { const { error } = this.state; if (!error) return this.props.children; @@ -51,7 +57,7 @@ class RecoverableRouteBoundary extends Component<{ children: ReactNode; routeKey
{chunkError ? '检测到页面版本更新' : '页面运行异常'}

{chunkError ? '正在等待加载最新页面资源' : '当前模块暂时无法显示'}

-

{chunkError ? '通常是发布后旧标签页仍引用上一版本资源。刷新后会恢复,不会丢失服务端数据。' : '页面已被安全隔离,侧栏和其他模块仍可使用。可刷新当前页面重试。'}

+

{chunkError ? '通常是发布后旧标签页仍引用上一版本资源。刷新后会恢复,不会丢失服务端数据。' : '页面已被安全隔离,侧栏和其他模块仍可使用。更换筛选条件会自动重试,也可刷新当前页面。'}

{error.message || error.name}