RequireAuth.tsx 1.16 KB
// REQ-USR-004: 受保护路由守卫(BR1)。三态:unauthenticated / authResolving / ready。
import { Navigate, Outlet, useLocation } from 'react-router-dom';
import { Spin } from 'antd';
import { useAppSelector } from '../store/hooks';

/**
 * 布局守卫:
 * - 无 token → 重定向 /login,携带 state.from(来源路径,登录后可回跳,BR1)
 * - 有 token 但 user 未就绪 → 渲染 Spin 占位(authResolving,token 已存在但用户信息尚未拉取)
 * - token + user 均就绪 → 放行(ready),渲染 <Outlet/>
 */
export default function RequireAuth() {
  const { token, user } = useAppSelector((s) => s.auth);
  const location = useLocation();

  if (!token) {
    return <Navigate to="/login" replace state={{ from: location.pathname }} />;
  }

  if (!user) {
    return (
      <div
        data-testid="auth-resolving"
        style={{
          height: '100vh',
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
        }}
      >
        <Spin size="large" tip="加载中…">
          <div style={{ padding: 24 }} />
        </Spin>
      </div>
    );
  }

  return <Outlet />;
}