完成顶部导航条
> 添加左侧logo
> 添加右侧用户信息展示
> 修复一些登录注册的跳转问题
> 修复axios拦截器错误的头设置
> 修复authApi错误的接口路径
> 组织api文件结构

Change-Id: Ifaec7e9a78ad6862ce7d0ce76be5181185186edd
diff --git a/src/AppLayout.tsx b/src/AppLayout.tsx
index 47dc382..99765e6 100644
--- a/src/AppLayout.tsx
+++ b/src/AppLayout.tsx
@@ -1,12 +1,28 @@
 import { Outlet, useLocation, useNavigate } from 'react-router';
-import { Layout, Menu } from 'antd';
-import { HomeOutlined, AppstoreOutlined } from '@ant-design/icons';
-
+import { Layout, Menu, Dropdown, Button, Flex } from 'antd';
+import { HomeOutlined, AppstoreOutlined, DownOutlined } from '@ant-design/icons';
+import { useEffect, useMemo } from 'react';
+import logo from "./assets/logo.png";
+import { useAppDispatch, useAppSelector } from './store/hooks';
+import { getUserInfo } from './feature/user/userSlice';
+import { logout } from './feature/auth/authSlice';
 const { Header } = Layout;
 
 const AppLayout = () => {
     const location = useLocation();
     const navigate = useNavigate();
+    const userState = useAppSelector(state => state.user);
+    const dispatch = useAppDispatch();
+
+    useEffect(() => {
+        dispatch(getUserInfo())
+    }, [dispatch])
+
+    // 判断是否在登录、注册或找回密码页面
+    const isAuthPage = useMemo(() => {
+        return ['/login', '/register', '/forget'].includes(location.pathname);
+    }, [location.pathname]);
+
     // 导航项配置
     const menuItems = [
         {
@@ -23,28 +39,77 @@
         },
     ];
 
+    // 处理登出逻辑
+    const handleLogout = () => {
+        dispatch(logout())
+        navigate('/login');  // 重定向到登录页
+    };
+
+    // 下拉菜单内容
+    const dropdownMenuItems = [
+        {
+            key: 'profile',
+            label: '个人中心',
+            onClick: () => navigate('/profile'),
+        },
+        {
+            key: 'logout',
+            label: '登出',
+            onClick: handleLogout,
+        },
+    ];
+
     return (
         <Layout style={{ minHeight: '100vh', width: '100%' }}>
-            <Header className="header" style={{ display: 'flex', alignItems: 'center' }}>
-                <div className="logo" color='white'>创驿</div>
+            <Header className="header" style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
+                {/* logo */}
+                <Flex justify='center' align='center'>
+                    <img src={logo} alt="Logo" height='48px' />
+                    <div style={{ color: 'white', marginLeft: '10px' }}>
+                        创驿
+                    </div>
+                </Flex>
+
+                <div style={{
+                    height: '30px',
+                    width: '1px',
+                    backgroundColor: 'white',
+                    margin: '0 20px',
+                }}></div>
+
+                {/* 中间导航菜单 */}
                 <Menu
                     mode="horizontal"
-                    theme='dark'
+                    theme="dark"
                     selectedKeys={[location.pathname === '/' ? 'home' : location.pathname.slice(1)]}
                     items={menuItems.map(item => ({
                         ...item,
                         onClick: () => navigate(item.path),
                     }))}
+                    style={{ flex: 1 }}
                 />
+
+                {/* 右侧用户名和下拉菜单 */}
+                {!isAuthPage && (
+                    <div style={{ display: 'flex', alignItems: 'center', color: 'white' }}>
+                        <span>{userState.username}</span>
+                        <Dropdown
+                            trigger={['click']}
+                            menu={{ items: dropdownMenuItems }}
+                        >
+                            <Button type="text" icon={<DownOutlined />} style={{ marginLeft: '10px', color: 'white' }} />
+                        </Dropdown>
+                    </div>
+                )}
             </Header>
             <Layout.Content style={{ padding: '24px' }}>
                 <Outlet />
             </Layout.Content>
-            <Layout.Footer>
-                © 2025 创驿 - 创作路上的同行者
+            <Layout.Footer style={{ textAlign: 'center' }}>
+                © 2025 创驿 - 愿做你创作路上的同行者
             </Layout.Footer>
         </Layout>
     );
 };
 
-export default AppLayout;    
\ No newline at end of file
+export default AppLayout;
diff --git a/src/api/authApi.ts b/src/api/Auth/AuthApi.ts
similarity index 63%
rename from src/api/authApi.ts
rename to src/api/Auth/AuthApi.ts
index 4a08c2d..9eae3fb 100644
--- a/src/api/authApi.ts
+++ b/src/api/Auth/AuthApi.ts
@@ -1,29 +1,30 @@
 import axios, { type AxiosResponse } from 'axios';
-import type { RejisterRequest , CommonResponse, ResetPasswordRequest} from './type';
+import type { RejisterRequest , ResetPasswordRequest} from './type';
 import type{ LoginRequest } from './type';
+import type { CommonResponse } from '../type';
 
-class authAPI {
+class AuthAPI {
 
     static sendVerificationCode(email: string): Promise<AxiosResponse<CommonResponse>> {
-        return axios.post('/api/sendVerification', { email });
+        return axios.post('/api/auth/sendVerification', { email });
     }
 
     static register(request: RejisterRequest): Promise<AxiosResponse<CommonResponse>> {
-        return axios.post('/api/register', request);
+        return axios.post('/api/auth/register', request);
     }
 
     static sendResetCode(email: string):Promise<AxiosResponse<CommonResponse>> {
-        return axios.post('/api/sendResetCode', { email });
+        return axios.post('/api/auth/sendResetCode', { email });
     }
 
     static resetPassword( request: ResetPasswordRequest ):Promise<AxiosResponse<CommonResponse>> {
-        return axios.post('/api/resetPassword', request);
+        return axios.post('/api/auth/resetPassword', request);
     }
     
 
     static refreshToken(oldToken : string): Promise<AxiosResponse<CommonResponse<string>>> {
         return axios.post(
-            '/api/refreshToken',
+            '/api/auth/refreshToken',
             {}, // 请求体空
             {
                 headers: {
@@ -35,10 +36,10 @@
     
     
     static login(loginRequest: LoginRequest): Promise<AxiosResponse<CommonResponse<string>>> {
-        return axios.post('/api/login', loginRequest);
+        return axios.post('/api/auth/login', loginRequest);
     }
 
 }
 
-export default authAPI;
+export default AuthAPI;
 
diff --git a/src/api/Auth/type.ts b/src/api/Auth/type.ts
new file mode 100644
index 0000000..fd6f87d
--- /dev/null
+++ b/src/api/Auth/type.ts
@@ -0,0 +1,19 @@
+export interface LoginRequest {
+    email: string;
+    password: string;
+}
+
+export interface RejisterRequest {
+    username: string,
+    email: string,
+    verificationCode: string,
+    password: string,
+}
+
+export interface ResetPasswordRequest {
+    email: string,
+    code: string,
+    newPassword: string,
+}
+
+
diff --git a/src/api/User/UserApi.ts b/src/api/User/UserApi.ts
new file mode 100644
index 0000000..44a0ae9
--- /dev/null
+++ b/src/api/User/UserApi.ts
@@ -0,0 +1,12 @@
+import type { AxiosResponse } from "axios";
+import axios from "axios";
+import type { UserInfo } from "./type";
+import type { CommonResponse } from "../type";
+
+class UserAPi {
+    static getMe() :Promise<AxiosResponse<CommonResponse<UserInfo>>> {
+        return axios.get('/api/me');
+    }
+}
+
+export default UserAPi;
\ No newline at end of file
diff --git a/src/api/User/type.ts b/src/api/User/type.ts
new file mode 100644
index 0000000..a7a1503
--- /dev/null
+++ b/src/api/User/type.ts
@@ -0,0 +1,10 @@
+export interface UserInfo {
+    userid: string,
+    username: string
+}
+
+export interface UserDetailInfo {
+    userid: string,
+    username: string,
+    // ...
+}
\ No newline at end of file
diff --git a/src/api/interceptors.ts b/src/api/interceptors.ts
index 3945bc3..bc2e566 100644
--- a/src/api/interceptors.ts
+++ b/src/api/interceptors.ts
@@ -7,7 +7,8 @@
     config.url = requestUrl.replace("/auth/","/");
   } else {
     const token = localStorage.getItem('token');
-    config.headers['Authorization'] = `Bearer ${token}`;
+    console.log(token);
+    config.headers['token'] = `${token}`;
   }
   return config;
 }, (error) => {
diff --git a/src/api/type.ts b/src/api/type.ts
index c1acc22..8714ae7 100644
--- a/src/api/type.ts
+++ b/src/api/type.ts
@@ -1,23 +1,5 @@
-export interface LoginRequest {
-    email: string;
-    password: string;
-}
-
-export interface RejisterRequest {
-    username: string,
-    email: string,
-    verificationCode: string,
-    password: string,
-}
-
-export interface ResetPasswordRequest {
-    email: string,
-    code: string,
-    newPassword: string,
-}
-
 export interface CommonResponse<T= null> {
     code: number;       
     message: string;     
     data: T;          
-  }
+}
\ No newline at end of file
diff --git a/src/assets/logo.png b/src/assets/logo.png
new file mode 100644
index 0000000..781c155
--- /dev/null
+++ b/src/assets/logo.png
Binary files differ
diff --git a/src/feature/auth/AuthLayout.tsx b/src/feature/auth/AuthLayout.tsx
index 656d869..382001c 100644
--- a/src/feature/auth/AuthLayout.tsx
+++ b/src/feature/auth/AuthLayout.tsx
@@ -2,6 +2,7 @@
 import { Outlet } from "react-router";
 import auth_background from "../../assets/auth_background.png"
 import slogan from "../../assets/slogan.png"
+
 function AuthLayout() {
 
     return (
diff --git a/src/feature/auth/Forget.tsx b/src/feature/auth/Forget.tsx
index a98ffcf..00c7793 100644
--- a/src/feature/auth/Forget.tsx
+++ b/src/feature/auth/Forget.tsx
@@ -3,7 +3,7 @@
 import { NavLink, useNavigate } from 'react-router';
 import { useState, useEffect } from 'react';
 import { useForm } from 'antd/es/form/Form';
-import authApi from '../../api/authApi';
+import authApi from '../../api/Auth/AuthApi';
 
 // 定义表单值的类型
 interface FormValues {
diff --git a/src/feature/auth/Login.tsx b/src/feature/auth/Login.tsx
index 8043007..1ae6c85 100644
--- a/src/feature/auth/Login.tsx
+++ b/src/feature/auth/Login.tsx
@@ -3,7 +3,7 @@
 import { NavLink, useNavigate } from 'react-router';
 import { useAppDispatch, useAppSelector } from '../../store/hooks';
 import { loginUser } from './authSlice';
-import { useEffect, useRef } from 'react';
+import { useEffect } from 'react';
 import useMessage from 'antd/es/message/useMessage';
 
 // 定义 Form 表单的字段类型
@@ -17,16 +17,17 @@
     const dispatch = useAppDispatch();
     const auth = useAppSelector(state => (state.auth));
     const [messageApi, Message] = useMessage()
-    const nav = useRef(useNavigate())
+    const nav = useNavigate()
 
     useEffect(() => {
         if (auth.isAuth) {
-            nav.current('/');
+            nav('/', { replace: true });
         }
         if (!auth.loading && auth.error) {
             messageApi.error(auth.error);
         }
     }, [auth, messageApi, nav])
+
     // 给 onFinish 参数添加类型
     const onFinish = async (values: FormValues) => {
         try {
diff --git a/src/feature/auth/Register.tsx b/src/feature/auth/Register.tsx
index 0023b71..251e1c8 100644
--- a/src/feature/auth/Register.tsx
+++ b/src/feature/auth/Register.tsx
@@ -2,8 +2,8 @@
 import { LockOutlined, MailOutlined, NumberOutlined, UserOutlined } from '@ant-design/icons';
 import { Button, Checkbox, Form, Input, message, Space } from 'antd';
 import { NavLink, useNavigate } from 'react-router';
-import authApi from "../../api/authApi";
-import type { RejisterRequest } from "../../api/type";
+import authApi from "../../api/Auth/AuthApi";
+import type { RejisterRequest } from "../../api/Auth/type";
 import type { AxiosResponse } from 'axios';
 
 // 定义表单字段的类型
diff --git a/src/feature/auth/authSlice.ts b/src/feature/auth/authSlice.ts
index f1ab2a0..607a6fd 100644
--- a/src/feature/auth/authSlice.ts
+++ b/src/feature/auth/authSlice.ts
@@ -1,11 +1,13 @@
 import { createAsyncThunk, createSlice, type PayloadAction } from "@reduxjs/toolkit";
 import type { AuthState } from "../../store/types";
-import type { LoginRequest } from "../../api/type";
-import authAPI from "../../api/authApi";
+import type { LoginRequest } from "../../api/Auth/type";
+import AuthAPI from "../../api/Auth/AuthApi";
 
+// 获取本地存储的 token
+const storedToken = localStorage.getItem('token');
 
 const initialState: AuthState = {
-  token: '',
+  token: storedToken || '',
   loading: false,
   isAuth: false,
   error: ''
@@ -19,7 +21,7 @@
   'auth/login',
   async (loginRequest: LoginRequest, { rejectWithValue }) => {
     try {
-      const response = await authAPI.login(loginRequest);
+      const response = await AuthAPI.login(loginRequest);
       if(response.data.code == 0) {
         return {token: response.data.data};
       }
@@ -40,7 +42,8 @@
   'auth/refresh',
   async (oldToken: string, { rejectWithValue }) => {
     try {
-      const response = await authAPI.refreshToken(oldToken);
+      const response = await AuthAPI.refreshToken(oldToken);
+      console.log(response);
       if(response.data.code == 0)
         return {token: response.data.data};
       else 
@@ -94,5 +97,6 @@
     },
     
 });
-  
+
+export const { logout } = authSlice.actions;
 export default authSlice.reducer;
\ No newline at end of file
diff --git a/src/feature/user/userSlice.ts b/src/feature/user/userSlice.ts
index e69de29..d6200af 100644
--- a/src/feature/user/userSlice.ts
+++ b/src/feature/user/userSlice.ts
@@ -0,0 +1,75 @@
+// src/store/userSlice.ts
+import { createSlice, createAsyncThunk, type PayloadAction } from '@reduxjs/toolkit';
+import type { UserInfo } from '../../api/User/type';
+import UserAPi from '../../api/User/UserApi';
+
+// 定义用户信息的类型
+interface UserState {
+    username: string;
+    userid: string;
+    email: string;
+    status: 'idle' | 'loading' | 'succeeded' | 'failed';
+    error: string | null;
+}
+
+// 定义初始状态
+const initialState: UserState = {
+    username: '',
+    userid: '',
+    email: '',
+    status: 'idle',
+    error: null,
+};
+
+
+// 创建异步 action,用于获取用户信息
+export const getUserInfo = createAsyncThunk<
+    UserInfo,
+    void,
+    {rejectValue:string}
+>(
+    'user/getUserInfo',
+    async (_, { rejectWithValue }) => {
+        const response = await UserAPi.getMe();
+        if (response.data.code == 0) {
+            console.log("xixi")
+            console.log(response)
+            return response.data.data;
+        } else {
+            console.log("buxixi")
+            console.log(response)
+            return rejectWithValue(response.data.message);
+        }
+    } 
+);
+
+// 创建 userSlice
+const userSlice = createSlice({
+    name: 'user',
+    initialState,
+    reducers: {
+        // 可以在这里处理同步操作,如修改用户名等
+        setUser: (state, action: PayloadAction<string>) => {
+            state.username = action.payload;
+        },
+    },
+    extraReducers: (builder) => {
+        builder
+            .addCase(getUserInfo.pending, (state) => {
+                state.status = 'loading';
+            })
+            .addCase(getUserInfo.fulfilled, (state, action: PayloadAction<UserInfo>) => {
+                state.status = 'succeeded';
+                state.username = action.payload.username;
+                state.userid = action.payload.userid;
+            })
+            .addCase(getUserInfo.rejected, (state, action) => {
+                state.status = 'failed';
+                state.error = action.error.message ?? 'Unknown error';
+            });
+    },
+});
+
+// 导出 actions 和 reducer
+export const { setUser } = userSlice.actions;
+export default userSlice.reducer;
diff --git a/src/main.tsx b/src/main.tsx
index 48b3777..22c365d 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -5,7 +5,7 @@
 import { store } from '../src/store/store.ts';
 import { Provider } from 'react-redux';
 // 路由 react-router
-import routes from './routes.ts';
+import routes from './routes/routes.ts';
 import { RouterProvider } from 'react-router';
 // 组件库 ant
 import '@ant-design/v5-patch-for-react-19';
diff --git a/src/routes.ts b/src/routes.ts
deleted file mode 100644
index e0dd020..0000000
--- a/src/routes.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { createBrowserRouter } from "react-router";
-import Home from "./feature/Home";
-import AuthLayout from "./feature/auth/AuthLayout";
-import Login from "./feature/auth/Login";
-import Register from "./feature/auth/Register";
-import AppLayout from "./AppLayout";
-import Forget from "./feature/auth/Forget";
-export default createBrowserRouter([{
-    Component: AppLayout,
-    children: [
-      {
-        path: "/",
-        Component: Home,
-      },
-      {
-          Component: AuthLayout,
-          children: [
-            { path: "login", Component: Login },
-            { path: "register", Component: Register },
-            { path: "forget", Component: Forget },
-          ],
-      }, 
-    ]
-}]);
-  
\ No newline at end of file
diff --git a/src/routes/ProtectedRoute.tsx b/src/routes/ProtectedRoute.tsx
new file mode 100644
index 0000000..238fdf4
--- /dev/null
+++ b/src/routes/ProtectedRoute.tsx
@@ -0,0 +1,36 @@
+import React, { useEffect } from 'react';
+import { useAppSelector, useAppDispatch } from '../store/hooks'; // 导入 hooks
+import { useNavigate } from 'react-router';
+import { refreshToken } from '../feature/auth/authSlice'; // 导入刷新 token 的 thunk
+
+interface ProtectedRouteProps {
+  children: React.ReactNode;
+}
+
+const ProtectedRoute: React.FC<ProtectedRouteProps> = ({ children }) => {
+  const { isAuth, token } = useAppSelector((state) => state.auth);
+  const navigate = useNavigate();
+  const dispatch = useAppDispatch();
+
+  useEffect(() => {
+    const tryRefreshToken = async () => {
+      if (!token) {
+        navigate('/login'); // 如果没有 token,跳转到登录页
+      }
+      if (!isAuth && token) {
+        try {
+          await dispatch(refreshToken(token)).unwrap(); // 尝试刷新 token
+        } catch {
+          navigate('/login');
+        }
+      }
+    };
+
+    tryRefreshToken(); // 执行 token 刷新
+  }, [isAuth, token, dispatch, navigate]);
+
+  // 如果已认证则渲染 children,否则返回 null
+  return isAuth ? <>{children}</> : null;
+};
+
+export default ProtectedRoute;
diff --git a/src/routes/routes.ts b/src/routes/routes.ts
new file mode 100644
index 0000000..585b1f6
--- /dev/null
+++ b/src/routes/routes.ts
@@ -0,0 +1,29 @@
+import { createBrowserRouter } from "react-router";
+import Home from "../feature/Home";
+import AuthLayout from "../feature/auth/AuthLayout";
+import Login from "../feature/auth/Login";
+import Register from "../feature/auth/Register";
+import Forget from "../feature/auth/Forget";
+import AppLayout from "../AppLayout";
+import withProtect from "./withProtect";
+
+export default createBrowserRouter([
+  {
+    Component: AppLayout,
+    children: [
+      {
+        path: "/",
+        // 使用 ProtectedRoute 来包裹需要保护的页面
+        element: withProtect(Home),
+      },
+      {
+        Component: AuthLayout,
+        children: [
+          { path: "login", Component: Login },
+          { path: "register", Component: Register },
+          { path: "forget", Component: Forget },
+        ],
+      },
+    ],
+  },
+]);
diff --git a/src/routes/withProtect.ts b/src/routes/withProtect.ts
new file mode 100644
index 0000000..bcde943
--- /dev/null
+++ b/src/routes/withProtect.ts
@@ -0,0 +1,10 @@
+import React from 'react';
+import ProtectedRoute from './ProtectedRoute'; 
+
+const withProtect = (Component: React.ComponentType) => {
+  return React.createElement(ProtectedRoute, {
+    children: React.createElement(Component)
+  });
+};
+
+export default withProtect;
\ No newline at end of file
diff --git a/src/store/store.ts b/src/store/store.ts
index 5f3841e..4921218 100644
--- a/src/store/store.ts
+++ b/src/store/store.ts
@@ -1,8 +1,10 @@
 import { configureStore } from '@reduxjs/toolkit'
 import authReducer from "../feature/auth/authSlice"
+import userReducer from "../feature/user/userSlice"
 export const store = configureStore({
   reducer: {
       auth: authReducer,
+      user: userReducer,
   },
   middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(),
 })