feat: layout

This commit is contained in:
李如威 2025-08-28 11:12:03 +08:00
parent 8fc61a98c6
commit fcc5d11804
9 changed files with 137 additions and 17 deletions

View File

@ -3,6 +3,7 @@ export async function request(url, method, data) {
method,
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + localStorage.getItem('_token') || ''
},
};

View File

@ -1,7 +1,12 @@
import { request } from "./base";
export const login = async (username, password) => {
return request("/api/login", "POST", {username, password})
return request("/api/login", "POST", { username, password })
}
export const currentUser = async () => {
return request("/api/current_user", "GET")
}

View File

@ -1,20 +1,28 @@
import { createContext, useContext, useEffect, useState } from "react";
import { login as loginToServer } from "../api/user";
import { login as loginToServer, currentUser } from "../api/user";
const AuthContext = createContext(null);
export function AuthProvider({ children }) {
const token = localStorage.getItem('_token')
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(token?.length > 0);
const currentUser = async () => {
const getUserInfoFromToken = async () => {
const res = await currentUser();
if (res?.success) {
setUser({
token: token,
...res?.data
})
}
setLoading(false);
}
const login = async (username, password) => {
const res = await loginToServer(username, password);
if (res?.success) {
localStorage.setItem('_token', res.data);
localStorage.setItem('_token', res.data?.token);
setUser({
token: res?.data?.token,
...res?.data?.user,
@ -28,8 +36,12 @@ export function AuthProvider({ children }) {
setUser(null);
};
useEffect(() => {
token && getUserInfoFromToken();
}, [token])
return (
<AuthContext.Provider value={{ user, login, logout }}>
<AuthContext.Provider value={{ user, login, logout, loading }}>
{children}
</AuthContext.Provider>
);

View File

@ -9,15 +9,19 @@ import { ConfigProvider } from "antd";
import { ProConfigProvider } from "@ant-design/pro-components";
createRoot(document.getElementById('root')).render(
<StrictMode>
<BrowserRouter>
<AuthProvider>
<ConfigProvider locale={zhCN}>
<ConfigProvider locale={zhCN} theme={{
components: {
Button: {
algorithm: true
}
}
}}>
<ProConfigProvider>
<App />
</ProConfigProvider>
</ConfigProvider>
</AuthProvider>
</BrowserRouter>
</StrictMode>,
)

View File

@ -0,0 +1,67 @@
import React, { useState } from "react";
import { Outlet, useNavigate, useLocation } from "react-router-dom";
import { Layout, Menu, Button, theme, Flex } from "antd";
import { MenuFoldOutlined, MenuUnfoldOutlined, UserOutlined, VideoCameraOutlined, UploadOutlined, LoginOutlined } from "@ant-design/icons";
import { useAuth } from "../context/AuthContext";
const { Header, Sider, Content } = Layout;
export default function LayoutPage() {
const { logout } = useAuth();
const [collapsed, setCollapsed] = useState(false);
const navigate = useNavigate();
const location = useLocation();
const menuItems = [
{ key: "1", icon: <UserOutlined />, label: "首页", path: "/welcome" },
{ key: "2", icon: <VideoCameraOutlined />, label: "用户", path: "/user" },
];
const selectedKey = menuItems.find(item => location.pathname.startsWith(item.path))?.key || "1";
return (
<Layout style={{ height: '100vh', width: '100vw' }}>
<Sider trigger={null} collapsible collapsed={collapsed} width={200}>
<div style={{ height: 40, width: 'calc(100% - 16px)', backgroundColor: 'lightgray', margin: '8px 8px' }} />
<Menu
theme="dark"
mode="inline"
selectedKeys={[selectedKey]}
items={menuItems}
onClick={({ key }) => {
const item = menuItems.find(i => i.key === key);
if (item) navigate(item.path);
}}
/>
</Sider>
<Layout style={{ backgroundColor: '#f2f3f5' }}>
<Header style={{ padding: 0, backgroundColor: 'white', width: "100%" }}>
<Flex align="center" justify="space-between" style={{
height: '100%',
padding: '0 8px',
boxSizing: 'border-box'
}}>
<Button
type='text'
icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
onClick={() => setCollapsed(!collapsed)}
/>
<Button type="text"
icon={<LoginOutlined />}
onClick={() => logout()}
/>
</Flex>
</Header>
<Content
style={{
margin: "24px 16px",
minHeight: 280,
}}
>
<Outlet /> {/* 渲染子路由 */}
</Content>
</Layout>
</Layout>
);
}

View File

@ -0,0 +1,16 @@
import { useEffect } from "react";
import { useAuth } from "../context/AuthContext";
import { useNavigate } from 'react-router-dom';
export default () => {
const { user, loading } = useAuth();
const navigate = useNavigate();
useEffect(() => {
user && navigate("/welcome")
!user && !loading && navigate("/login")
}, [user])
return <></>
}

View File

@ -3,12 +3,12 @@ import { LoginFormPage, ProForm, ProFormText } from '@ant-design/pro-components'
import { message } from 'antd';
import { useAuth } from '../context/AuthContext';
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
const REQUIRED = [{required: true}]
export default () => {
const { login, user } = useAuth();
console.log(user);
const navigate = useNavigate();
const { login } = useAuth();
return <div style={{ width: '100vw', height: '100vh'}}>
<LoginFormPage
@ -17,7 +17,7 @@ export default () => {
onFinish={async (values) => {
const res = await login(values?.username, values?.password);
if (res?.success) {
navigate('/')
} else {
message.error(res?.message);
}

View File

@ -0,0 +1,5 @@
export default () => {
return <>
<h1>User</h1>
</>
}

View File

@ -1,24 +1,34 @@
import { Navigate, Outlet } from "react-router-dom";
import { useAuth } from "./context/AuthContext";
import Loading from './pages/Loading';
import Home from "./pages/Home";
import Login from "./pages/Login";
import User from "./pages/User";
import LayoutPage from "./pages/Layout";
//
function PrivateRoute() {
const { user } = useAuth();
console.log(user);
return user ? <Outlet /> : <Navigate to="/login" replace />;
const { user, loading } = useAuth();
return (user || loading) ? <Outlet /> : <Navigate to="/login" replace />;
}
const routes = [
// 访
{ path: "/", element: <Loading />},
{ path: "/login", element: <Login /> },
// 访
{
element: <PrivateRoute />,
children: [
{ path: "/", element: <Home /> }, //
{
element: <LayoutPage />,
children: [
{ path: "/welcome", element: <Home /> },
{ path: "/User", element: <User /> },
]
}
],
},