141 lines
3.7 KiB
React
141 lines
3.7 KiB
React
import { useEffect } from 'react';
|
|
import { BrowserRouter, Routes, Route, Navigate, useNavigate, useLocation } from 'react-router-dom';
|
|
import { AnimatePresence, motion } from 'framer-motion';
|
|
import { Background } from './components/layout';
|
|
import Loader from './components/Loader';
|
|
import LoginPage from './pages/LoginPage';
|
|
import OnboardingPage from './pages/OnboardingPage';
|
|
import DashboardPage from './pages/DashboardPage';
|
|
import useStore from './store/useStore';
|
|
|
|
/**
|
|
* 路由守卫组件
|
|
* 根据登录状态和注册完成状态进行路由重定向
|
|
*/
|
|
const ProtectedRoute = ({ children, requireAuth = false, requireOnboarding = false }) => {
|
|
const { isLoggedIn, registrationData } = useStore();
|
|
const navigate = useNavigate();
|
|
|
|
// 检查是否完成入站流程
|
|
const hasCompletedOnboarding = registrationData.nickname && registrationData.future?.vision;
|
|
|
|
useEffect(() => {
|
|
if (requireAuth && !isLoggedIn) {
|
|
navigate('/', { replace: true });
|
|
} else if (requireOnboarding && !hasCompletedOnboarding) {
|
|
navigate('/onboarding', { replace: true });
|
|
}
|
|
}, [isLoggedIn, hasCompletedOnboarding, requireAuth, requireOnboarding, navigate]);
|
|
|
|
if (requireAuth && !isLoggedIn) {
|
|
return <Loader text="正在验证身份..." />;
|
|
}
|
|
|
|
if (requireOnboarding && !hasCompletedOnboarding) {
|
|
return <Loader text="正在加载..." />;
|
|
}
|
|
|
|
return children;
|
|
};
|
|
|
|
/**
|
|
* 页面过渡动画包装器
|
|
*/
|
|
const PageTransition = ({ children }) => {
|
|
return (
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
exit={{ opacity: 0, y: -20 }}
|
|
transition={{
|
|
duration: 0.4,
|
|
ease: [0.25, 0.46, 0.45, 0.94]
|
|
}}
|
|
className="w-full h-full"
|
|
>
|
|
{children}
|
|
</motion.div>
|
|
);
|
|
};
|
|
|
|
/**
|
|
* 动画路由组件
|
|
*/
|
|
const AnimatedRoutes = () => {
|
|
const location = useLocation();
|
|
const { isLoggedIn, registrationData } = useStore();
|
|
|
|
// 检查是否完成入站流程
|
|
const hasCompletedOnboarding = registrationData.nickname && registrationData.future?.vision;
|
|
|
|
return (
|
|
<AnimatePresence mode="wait">
|
|
<Routes location={location} key={location.pathname}>
|
|
{/* 登录页 */}
|
|
<Route
|
|
path="/"
|
|
element={
|
|
isLoggedIn ? (
|
|
hasCompletedOnboarding ? (
|
|
<Navigate to="/dashboard" replace />
|
|
) : (
|
|
<Navigate to="/onboarding" replace />
|
|
)
|
|
) : (
|
|
<PageTransition>
|
|
<LoginPage />
|
|
</PageTransition>
|
|
)
|
|
}
|
|
/>
|
|
|
|
{/* 入站流程页 */}
|
|
<Route
|
|
path="/onboarding"
|
|
element={
|
|
<ProtectedRoute requireAuth>
|
|
<PageTransition>
|
|
<OnboardingPage />
|
|
</PageTransition>
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
|
|
{/* 仪表盘页 */}
|
|
<Route
|
|
path="/dashboard"
|
|
element={
|
|
<ProtectedRoute requireAuth requireOnboarding>
|
|
<PageTransition>
|
|
<DashboardPage />
|
|
</PageTransition>
|
|
</ProtectedRoute>
|
|
}
|
|
/>
|
|
|
|
{/* 404 重定向 */}
|
|
<Route path="*" element={<Navigate to="/" replace />} />
|
|
</Routes>
|
|
</AnimatePresence>
|
|
);
|
|
};
|
|
|
|
/**
|
|
* App 主组件
|
|
*/
|
|
function App() {
|
|
return (
|
|
<BrowserRouter>
|
|
{/* 动态背景 */}
|
|
<Background />
|
|
|
|
{/* 主容器 */}
|
|
<main className="relative z-10 min-h-screen flex flex-col items-center justify-center p-4 md:p-8">
|
|
<AnimatedRoutes />
|
|
</main>
|
|
</BrowserRouter>
|
|
);
|
|
}
|
|
|
|
export default App;
|