From fcc5d118040572a1c3de2f3ff2e03f30fdb6ced6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=A6=82=E5=A8=81?= Date: Thu, 28 Aug 2025 11:12:03 +0800 Subject: [PATCH] feat: layout --- frontend/src/api/base.js | 1 + frontend/src/api/user.js | 7 ++- frontend/src/context/AuthContext.jsx | 22 ++++++--- frontend/src/main.jsx | 10 +++-- frontend/src/pages/Layout.jsx | 67 ++++++++++++++++++++++++++++ frontend/src/pages/Loading.jsx | 16 +++++++ frontend/src/pages/Login.jsx | 8 ++-- frontend/src/pages/User.jsx | 5 +++ frontend/src/routes.jsx | 18 ++++++-- 9 files changed, 137 insertions(+), 17 deletions(-) create mode 100644 frontend/src/pages/Layout.jsx create mode 100644 frontend/src/pages/Loading.jsx create mode 100644 frontend/src/pages/User.jsx diff --git a/frontend/src/api/base.js b/frontend/src/api/base.js index 5afe591..40ab3d1 100644 --- a/frontend/src/api/base.js +++ b/frontend/src/api/base.js @@ -3,6 +3,7 @@ export async function request(url, method, data) { method, headers: { "Content-Type": "application/json", + "Authorization": "Bearer " + localStorage.getItem('_token') || '' }, }; diff --git a/frontend/src/api/user.js b/frontend/src/api/user.js index 186b0d0..d7804a1 100644 --- a/frontend/src/api/user.js +++ b/frontend/src/api/user.js @@ -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") } + diff --git a/frontend/src/context/AuthContext.jsx b/frontend/src/context/AuthContext.jsx index ab67744..143b213 100644 --- a/frontend/src/context/AuthContext.jsx +++ b/frontend/src/context/AuthContext.jsx @@ -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 ( - + {children} ); diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index b21bc76..d3ef1e1 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -9,15 +9,19 @@ import { ConfigProvider } from "antd"; import { ProConfigProvider } from "@ant-design/pro-components"; createRoot(document.getElementById('root')).render( - - + - , ) diff --git a/frontend/src/pages/Layout.jsx b/frontend/src/pages/Layout.jsx new file mode 100644 index 0000000..730f319 --- /dev/null +++ b/frontend/src/pages/Layout.jsx @@ -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: , label: "首页", path: "/welcome" }, + { key: "2", icon: , label: "用户", path: "/user" }, + ]; + + const selectedKey = menuItems.find(item => location.pathname.startsWith(item.path))?.key || "1"; + + return ( + + +
+ { + const item = menuItems.find(i => i.key === key); + if (item) navigate(item.path); + }} + /> + + +
+ +
+ + {/* 渲染子路由 */} + +
+ + ); +} diff --git a/frontend/src/pages/Loading.jsx b/frontend/src/pages/Loading.jsx new file mode 100644 index 0000000..6b1c232 --- /dev/null +++ b/frontend/src/pages/Loading.jsx @@ -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 <> +} \ No newline at end of file diff --git a/frontend/src/pages/Login.jsx b/frontend/src/pages/Login.jsx index 338718f..bbbeb79 100644 --- a/frontend/src/pages/Login.jsx +++ b/frontend/src/pages/Login.jsx @@ -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
{ onFinish={async (values) => { const res = await login(values?.username, values?.password); if (res?.success) { - + navigate('/') } else { message.error(res?.message); } diff --git a/frontend/src/pages/User.jsx b/frontend/src/pages/User.jsx new file mode 100644 index 0000000..a1e407c --- /dev/null +++ b/frontend/src/pages/User.jsx @@ -0,0 +1,5 @@ +export default () => { + return <> +

User

+ +} \ No newline at end of file diff --git a/frontend/src/routes.jsx b/frontend/src/routes.jsx index db5220f..5fa7418 100644 --- a/frontend/src/routes.jsx +++ b/frontend/src/routes.jsx @@ -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 ? : ; + const { user, loading } = useAuth(); + return (user || loading) ? : ; } const routes = [ // 登录页公开访问 + { path: "/", element: }, { path: "/login", element: }, // 需要登录才能访问的路由 { element: , children: [ - { path: "/", element: }, // 默认首页 + { + element: , + children: [ + { path: "/welcome", element: }, + { path: "/User", element: }, + ] + } ], },