| // src/routes/ProtectedRoute.tsx |
| |
| import React, { useEffect, useState } from 'react'; |
| import { useAppSelector, useAppDispatch } from '../store/hooks'; |
| import { useNavigate } from 'react-router'; |
| import { refreshToken } from '../feature/auth/authSlice'; |
| import { Spin } from 'antd'; |
| |
| interface ProtectedRouteProps { |
| children: React.ReactNode; |
| } |
| |
| const ProtectedRoute: React.FC<ProtectedRouteProps> = ({ children }) => { |
| const { isAuth, token } = useAppSelector((state) => state.auth); |
| const navigate = useNavigate(); |
| const dispatch = useAppDispatch(); |
| const [isLoading, setIsLoading] = useState(false); |
| |
| useEffect(() => { |
| const tryRefreshToken = async () => { |
| // 如果没有token,直接跳转到登录页 |
| if (!token) { |
| navigate('/login', { replace: true }); |
| return; |
| } |
| |
| // 如果有token但未认证,尝试刷新token |
| if (!isAuth && token) { |
| setIsLoading(true); |
| try { |
| await dispatch(refreshToken(token)).unwrap(); |
| } catch (error) { |
| console.error('Token refresh failed:', error); |
| navigate('/login', { replace: true }); |
| } finally { |
| setIsLoading(false); |
| } |
| } |
| }; |
| |
| tryRefreshToken(); |
| }, [isAuth, token, dispatch, navigate]); |
| |
| // 显示加载状态 |
| if (isLoading) { |
| return ( |
| <div style={{ |
| display: 'flex', |
| justifyContent: 'center', |
| alignItems: 'center', |
| height: '100vh' |
| }}> |
| <Spin size="large" tip="验证身份中..." /> |
| </div> |
| ); |
| } |
| |
| // 如果没有token,返回null(会触发导航) |
| if (!token) { |
| return null; |
| } |
| |
| // 如果已认证,渲染children |
| if (isAuth) { |
| return <>{children}</>; |
| } |
| |
| // 如果有token但还在验证中,显示加载状态 |
| return ( |
| <div style={{ |
| display: 'flex', |
| justifyContent: 'center', |
| alignItems: 'center', |
| height: '100vh' |
| }}> |
| <Spin size="large" tip="验证身份中..." /> |
| </div> |
| ); |
| }; |
| |
| export default ProtectedRoute; |