22301014 | 4ce0587 | 2025-06-08 22:33:28 +0800 | [diff] [blame^] | 1 | // src/routes/ProtectedRoute.tsx |
| 2 | |
| 3 | import React, { useEffect, useState } from 'react'; |
| 4 | import { useAppSelector, useAppDispatch } from '../store/hooks'; |
22301014 | b1477f7 | 2025-06-07 22:54:40 +0800 | [diff] [blame] | 5 | import { useNavigate } from 'react-router'; |
22301014 | 4ce0587 | 2025-06-08 22:33:28 +0800 | [diff] [blame^] | 6 | import { refreshToken } from '../feature/auth/authSlice'; |
| 7 | import { Spin } from 'antd'; |
22301014 | b1477f7 | 2025-06-07 22:54:40 +0800 | [diff] [blame] | 8 | |
| 9 | interface ProtectedRouteProps { |
| 10 | children: React.ReactNode; |
| 11 | } |
| 12 | |
| 13 | const ProtectedRoute: React.FC<ProtectedRouteProps> = ({ children }) => { |
| 14 | const { isAuth, token } = useAppSelector((state) => state.auth); |
| 15 | const navigate = useNavigate(); |
| 16 | const dispatch = useAppDispatch(); |
22301014 | 4ce0587 | 2025-06-08 22:33:28 +0800 | [diff] [blame^] | 17 | const [isLoading, setIsLoading] = useState(false); |
22301014 | b1477f7 | 2025-06-07 22:54:40 +0800 | [diff] [blame] | 18 | |
| 19 | useEffect(() => { |
| 20 | const tryRefreshToken = async () => { |
22301014 | 4ce0587 | 2025-06-08 22:33:28 +0800 | [diff] [blame^] | 21 | // 如果没有token,直接跳转到登录页 |
22301014 | b1477f7 | 2025-06-07 22:54:40 +0800 | [diff] [blame] | 22 | if (!token) { |
22301014 | 4ce0587 | 2025-06-08 22:33:28 +0800 | [diff] [blame^] | 23 | navigate('/login', { replace: true }); |
| 24 | return; |
22301014 | b1477f7 | 2025-06-07 22:54:40 +0800 | [diff] [blame] | 25 | } |
22301014 | 4ce0587 | 2025-06-08 22:33:28 +0800 | [diff] [blame^] | 26 | |
| 27 | // 如果有token但未认证,尝试刷新token |
22301014 | b1477f7 | 2025-06-07 22:54:40 +0800 | [diff] [blame] | 28 | if (!isAuth && token) { |
22301014 | 4ce0587 | 2025-06-08 22:33:28 +0800 | [diff] [blame^] | 29 | setIsLoading(true); |
22301014 | b1477f7 | 2025-06-07 22:54:40 +0800 | [diff] [blame] | 30 | try { |
22301014 | 4ce0587 | 2025-06-08 22:33:28 +0800 | [diff] [blame^] | 31 | await dispatch(refreshToken(token)).unwrap(); |
| 32 | } catch (error) { |
| 33 | console.error('Token refresh failed:', error); |
| 34 | navigate('/login', { replace: true }); |
| 35 | } finally { |
| 36 | setIsLoading(false); |
22301014 | b1477f7 | 2025-06-07 22:54:40 +0800 | [diff] [blame] | 37 | } |
| 38 | } |
| 39 | }; |
| 40 | |
22301014 | 4ce0587 | 2025-06-08 22:33:28 +0800 | [diff] [blame^] | 41 | tryRefreshToken(); |
22301014 | b1477f7 | 2025-06-07 22:54:40 +0800 | [diff] [blame] | 42 | }, [isAuth, token, dispatch, navigate]); |
| 43 | |
22301014 | 4ce0587 | 2025-06-08 22:33:28 +0800 | [diff] [blame^] | 44 | // 显示加载状态 |
| 45 | if (isLoading) { |
| 46 | return ( |
| 47 | <div style={{ |
| 48 | display: 'flex', |
| 49 | justifyContent: 'center', |
| 50 | alignItems: 'center', |
| 51 | height: '100vh' |
| 52 | }}> |
| 53 | <Spin size="large" tip="验证身份中..." /> |
| 54 | </div> |
| 55 | ); |
| 56 | } |
| 57 | |
| 58 | // 如果没有token,返回null(会触发导航) |
| 59 | if (!token) { |
| 60 | return null; |
| 61 | } |
| 62 | |
| 63 | // 如果已认证,渲染children |
| 64 | if (isAuth) { |
| 65 | return <>{children}</>; |
| 66 | } |
| 67 | |
| 68 | // 如果有token但还在验证中,显示加载状态 |
| 69 | return ( |
| 70 | <div style={{ |
| 71 | display: 'flex', |
| 72 | justifyContent: 'center', |
| 73 | alignItems: 'center', |
| 74 | height: '100vh' |
| 75 | }}> |
| 76 | <Spin size="large" tip="验证身份中..." /> |
| 77 | </div> |
| 78 | ); |
22301014 | b1477f7 | 2025-06-07 22:54:40 +0800 | [diff] [blame] | 79 | }; |
| 80 | |
22301014 | 4ce0587 | 2025-06-08 22:33:28 +0800 | [diff] [blame^] | 81 | export default ProtectedRoute; |