所有模块添加路由守卫
This commit is contained in:
+11
-10
@@ -2,6 +2,7 @@ import { Routes, Route } from 'react-router-dom';
|
|||||||
import HomePage from '@/pages/HomePage';
|
import HomePage from '@/pages/HomePage';
|
||||||
import SSOLogin from '@/pages/SSOLogin';
|
import SSOLogin from '@/pages/SSOLogin';
|
||||||
import LoginPage from '@/pages/LoginPage';
|
import LoginPage from '@/pages/LoginPage';
|
||||||
|
import RequireAuth from '@/shared/components/RequireAuth';
|
||||||
import AIMCockpit from '@/modules/ai-matching/pages/AIMCockpit';
|
import AIMCockpit from '@/modules/ai-matching/pages/AIMCockpit';
|
||||||
import CostCockpit from '@/modules/cost-analysis/pages/CostCockpit';
|
import CostCockpit from '@/modules/cost-analysis/pages/CostCockpit';
|
||||||
import KnowledgeGraphHome from '@/modules/knowledge-graph/pages/KnowledgeGraphHome';
|
import KnowledgeGraphHome from '@/modules/knowledge-graph/pages/KnowledgeGraphHome';
|
||||||
@@ -15,18 +16,18 @@ import SanruiAIDesignHome from '@/modules/sanrui-ai-design/pages/SanruiAIDesignH
|
|||||||
export default function App() {
|
export default function App() {
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<HomePage />} />
|
|
||||||
<Route path="/login" element={<LoginPage />} />
|
<Route path="/login" element={<LoginPage />} />
|
||||||
<Route path="/sso-login" element={<SSOLogin />} />
|
<Route path="/sso-login" element={<SSOLogin />} />
|
||||||
<Route path="/ai-matching" element={<AIMCockpit />} />
|
<Route path="/" element={<RequireAuth><HomePage /></RequireAuth>} />
|
||||||
<Route path="/cost-analysis" element={<CostCockpit />} />
|
<Route path="/ai-matching" element={<RequireAuth><AIMCockpit /></RequireAuth>} />
|
||||||
<Route path="/knowledge-graph" element={<KnowledgeGraphHome />} />
|
<Route path="/cost-analysis" element={<RequireAuth><CostCockpit /></RequireAuth>} />
|
||||||
<Route path="/knowledge-graph/space" element={<SpaceCockpit />} />
|
<Route path="/knowledge-graph" element={<RequireAuth><KnowledgeGraphHome /></RequireAuth>} />
|
||||||
<Route path="/bomx-reuse-analyzer" element={<BomxReuseHome />} />
|
<Route path="/knowledge-graph/space" element={<RequireAuth><SpaceCockpit /></RequireAuth>} />
|
||||||
<Route path="/order-cost-analysis" element={<CostCockpitFull />} />
|
<Route path="/bomx-reuse-analyzer" element={<RequireAuth><BomxReuseHome /></RequireAuth>} />
|
||||||
<Route path="/order-cost-analysis/aim" element={<AIMCockpitFull />} />
|
<Route path="/order-cost-analysis" element={<RequireAuth><CostCockpitFull /></RequireAuth>} />
|
||||||
<Route path="/bexell-scheduling-ai/*" element={<BexellSchedulingApp />} />
|
<Route path="/order-cost-analysis/aim" element={<RequireAuth><AIMCockpitFull /></RequireAuth>} />
|
||||||
<Route path="/sanrui-ai-design" element={<SanruiAIDesignHome />} />
|
<Route path="/bexell-scheduling-ai/*" element={<RequireAuth><BexellSchedulingApp /></RequireAuth>} />
|
||||||
|
<Route path="/sanrui-ai-design" element={<RequireAuth><SanruiAIDesignHome /></RequireAuth>} />
|
||||||
</Routes>
|
</Routes>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,14 +24,6 @@ export default function HomePage() {
|
|||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const [userInfo, setUserInfo] = useState<SessionUserInfo | null>(() => getUserInfo());
|
const [userInfo, setUserInfo] = useState<SessionUserInfo | null>(() => getUserInfo());
|
||||||
|
|
||||||
// 登录守卫:未登录跳转 /login
|
|
||||||
useEffect(() => {
|
|
||||||
const token = sessionStorage.getItem('userToken');
|
|
||||||
if (!token) {
|
|
||||||
navigate('/login', { replace: true });
|
|
||||||
}
|
|
||||||
}, [navigate]);
|
|
||||||
|
|
||||||
// 首页模块多时需纵向滚动;离开后恢复全局 overflow:hidden(其他模块自管布局)
|
// 首页模块多时需纵向滚动;离开后恢复全局 overflow:hidden(其他模块自管布局)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.body.style.overflow = 'auto';
|
document.body.style.overflow = 'auto';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate, useLocation } from 'react-router-dom';
|
||||||
import { User, Lock, LogIn, AlertCircle, Loader2 } from 'lucide-react';
|
import { User, Lock, LogIn, AlertCircle, Loader2 } from 'lucide-react';
|
||||||
import StarBackground from '@/shared/components/StarBackground';
|
import StarBackground from '@/shared/components/StarBackground';
|
||||||
import { iamLogin, saveSession } from '@/shared/services/iamAuth';
|
import { iamLogin, saveSession } from '@/shared/services/iamAuth';
|
||||||
@@ -28,6 +28,7 @@ const iconStyle = (isDark: boolean): React.CSSProperties => ({
|
|||||||
|
|
||||||
export default function LoginPage() {
|
export default function LoginPage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const location = useLocation();
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
const isDark = theme === 'dark';
|
const isDark = theme === 'dark';
|
||||||
|
|
||||||
@@ -52,7 +53,9 @@ export default function LoginPage() {
|
|||||||
try {
|
try {
|
||||||
const result = await iamLogin({ userId: userId.trim(), password });
|
const result = await iamLogin({ userId: userId.trim(), password });
|
||||||
saveSession(result.token, result.userInfo);
|
saveSession(result.token, result.userInfo);
|
||||||
navigate('/', { replace: true });
|
// 回跳原页面(RequireAuth 记录的 from),否则去首页
|
||||||
|
const from = (location.state as { from?: string })?.from ?? '/';
|
||||||
|
navigate(from, { replace: true });
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
setError(ex instanceof Error ? ex.message : '登录失败,请稍后重试');
|
setError(ex instanceof Error ? ex.message : '登录失败,请稍后重试');
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { ReactNode, useEffect, useState } from 'react';
|
||||||
|
import { useLocation, useNavigate } from 'react-router-dom';
|
||||||
|
import { Spin } from 'antd';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录守卫:未登录跳转 /login
|
||||||
|
*
|
||||||
|
* 检查 sessionStorage 中的 userToken,若不存在则跳转登录页。
|
||||||
|
* 登录后会带上 from state,便于登录成功后回跳原页面。
|
||||||
|
*/
|
||||||
|
export default function RequireAuth({ children }: { children: ReactNode }) {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const location = useLocation();
|
||||||
|
const [checked, setChecked] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const token = sessionStorage.getItem('userToken');
|
||||||
|
if (!token) {
|
||||||
|
// 记录原路径,登录后可回跳
|
||||||
|
navigate('/login', { replace: true, state: { from: location.pathname + location.search } });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setChecked(true);
|
||||||
|
}, [navigate, location]);
|
||||||
|
|
||||||
|
if (!checked) {
|
||||||
|
return (
|
||||||
|
<div style={{
|
||||||
|
width: '100vw', height: '100vh',
|
||||||
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||||
|
background: '#0a1628',
|
||||||
|
}}>
|
||||||
|
<Spin size="large" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <>{children}</>;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user