feat: layout
This commit is contained in:
parent
8fc61a98c6
commit
fcc5d11804
|
|
@ -3,6 +3,7 @@ export async function request(url, method, data) {
|
|||
method,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": "Bearer " + localStorage.getItem('_token') || ''
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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 <></>
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
export default () => {
|
||||
return <>
|
||||
<h1>User</h1>
|
||||
</>
|
||||
}
|
||||
|
|
@ -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 /> },
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue