feat(web): add login page, auth store, API client, and route guard

- API client with axios interceptors: JWT attach + 401 auto-refresh
- Auth store (Zustand): login/logout/loadFromStorage with localStorage
- Login page: gradient background, Ant Design form, error handling
- Home page: dashboard with statistics cards
- App.tsx: PrivateRoute guard, /login route, auth state restoration
- MainLayout: dynamic user display, logout dropdown, menu navigation
- Users API service: CRUD with pagination support
This commit is contained in:
iven
2026-04-11 03:38:29 +08:00
parent a7cdf67d17
commit 4a03a639a6
9 changed files with 421 additions and 27 deletions

View File

@@ -1,29 +1,53 @@
import { HashRouter, Routes, Route } from 'react-router-dom';
import { useEffect } from 'react';
import { HashRouter, Routes, Route, Navigate } from 'react-router-dom';
import { ConfigProvider, theme as antdTheme } from 'antd';
import zhCN from 'antd/locale/zh_CN';
import MainLayout from './layouts/MainLayout';
import Login from './pages/Login';
import Home from './pages/Home';
import { useAuthStore } from './stores/auth';
import { useAppStore } from './stores/app';
function HomePage() {
return <div> ERP </div>;
function PrivateRoute({ children }: { children: React.ReactNode }) {
const isAuthenticated = useAuthStore((s) => s.isAuthenticated);
return isAuthenticated ? <>{children}</> : <Navigate to="/login" replace />;
}
export default function App() {
const { theme: appTheme } = useAppStore();
const loadFromStorage = useAuthStore((s) => s.loadFromStorage);
const theme = useAppStore((s) => s.theme);
// Restore auth state from localStorage on app load
useEffect(() => {
loadFromStorage();
}, [loadFromStorage]);
return (
<ConfigProvider
locale={zhCN}
theme={{
algorithm: appTheme === 'dark' ? antdTheme.darkAlgorithm : antdTheme.defaultAlgorithm,
algorithm: theme === 'dark' ? antdTheme.darkAlgorithm : antdTheme.defaultAlgorithm,
}}
>
<HashRouter>
<MainLayout>
<Routes>
<Route path="/" element={<HomePage />} />
</Routes>
</MainLayout>
<Routes>
<Route path="/login" element={<Login />} />
<Route
path="/"
element={
<PrivateRoute>
<MainLayout>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/users" element={<div></div>} />
<Route path="/roles" element={<div></div>} />
<Route path="/settings" element={<div></div>} />
</Routes>
</MainLayout>
</PrivateRoute>
}
/>
</Routes>
</HashRouter>
</ConfigProvider>
);