完善验证页面和后端接口的链接
> 1. 配置了开发环境的端口转发 -> localhost:8080\
> 2. 完成了注册,登录,忘记密码页的功能
> 3. 为项目配置了vitest测试框架
> 4. 对这三个页面进行了测试

> 重写了/test/setup.ts

Change-Id: I46c600ce06d698dae6953b2e1e3ff4a98b0f3de4
diff --git a/src/api/authApi.ts b/src/api/authApi.ts
index 3a8eb17..4a08c2d 100644
--- a/src/api/authApi.ts
+++ b/src/api/authApi.ts
@@ -1,78 +1,42 @@
-import axios from 'axios';
+import axios, { type AxiosResponse } from 'axios';
+import type { RejisterRequest , CommonResponse, ResetPasswordRequest} from './type';
+import type{ LoginRequest } from './type';
 
+class authAPI {
 
- class authAPI {
-
-    // static getUserById(userId) {
-    //     // 例如 GET http://localhost:8080/123
-    //     return axios.get(`/${userId}`);
-    // }
-
-
-    // static updateUser(userId, data) {
-    //     // 例如 PUT http://localhost:8080/123  Body: { username: 'xxx', ... }
-    //     return axios.put(`/${userId}`, data);
-    // }
-    //
-    //
-    // static deleteUser(userId:string) {
-    //     // 例如 DELETE http://localhost:8080/123
-    //     return axios.delete(`/${userId}`);
-    // }
-
-
-    static sendVerificationCode(email: string) {
-        // Body: { email: 'xxx@yyy.com'}
+    static sendVerificationCode(email: string): Promise<AxiosResponse<CommonResponse>> {
         return axios.post('/api/sendVerification', { email });
     }
 
+    static register(request: RejisterRequest): Promise<AxiosResponse<CommonResponse>> {
+        return axios.post('/api/register', request);
+    }
 
-    static sendResetCode(email: string) {
-        // Body: { email: 'xxx@yyy.com' }
+    static sendResetCode(email: string):Promise<AxiosResponse<CommonResponse>> {
         return axios.post('/api/sendResetCode', { email });
     }
 
-    //
-    // static resetPassword({ email, code, newPassword }) {
-    //     // Body: { email, code, newPassword }
-    //     return axios.post('/resetPassword', { email, code, newPassword });
-    // }
-    //
-    //
-    // static register({ username, email, verificationCode, password }) {
-    //     // Body: { username, email, verificationCode, password, identificationNumber? }
-    //     const body = {
-    //         username,
-    //         email,
-    //         verificationCode,
-    //         password,
-    //     };
-    //     return axios.post('/register', body);
-    // }
-    //
-    // /**
-    //  * 刷新 JWT Token(POST /refreshToken)
-    //  * @param {string} oldToken 旧的 JWT(放在 header: token)
-    //  * @returns {Promise<AxiosResponse>}
-    //  */
-    // static refreshToken(oldToken : string) {
-    //     // 因为后端是从 header 中读取旧 token,这里直接把 token 放进 headers
-    //     return axios.post(
-    //         '/refreshToken',
-    //         {}, // 请求体空
-    //         {
-    //             headers: {
-    //                 token: oldToken,
-    //             },
-    //         }
-    //     );
-    // }
-    //
-    //
-    // static login({ email, password } : {email: string, password:string}) {
-    //     // Body: { email, password }
-    //     return axios.post('/login', { email, password });
-    // }
+    static resetPassword( request: ResetPasswordRequest ):Promise<AxiosResponse<CommonResponse>> {
+        return axios.post('/api/resetPassword', request);
+    }
+    
+
+    static refreshToken(oldToken : string): Promise<AxiosResponse<CommonResponse<string>>> {
+        return axios.post(
+            '/api/refreshToken',
+            {}, // 请求体空
+            {
+                headers: {
+                    token: oldToken,
+                },
+            }
+        );
+    }
+    
+    
+    static login(loginRequest: LoginRequest): Promise<AxiosResponse<CommonResponse<string>>> {
+        return axios.post('/api/login', loginRequest);
+    }
 
 }
 
diff --git a/src/api/interceptors.ts b/src/api/interceptors.ts
index f56a8d4..3945bc3 100644
--- a/src/api/interceptors.ts
+++ b/src/api/interceptors.ts
@@ -1,4 +1,4 @@
-import axios from "axios";
+import axios, { type AxiosResponse } from "axios";
 
 // 为所有auth外请求添加token头
 axios.interceptors.request.use((config) => {
@@ -14,4 +14,33 @@
   return error;
 } );
 
+
+// 统一响应拦截器
+axios.interceptors.response.use(
+  (response: AxiosResponse) => {
+    const { code, msg, data } = response.data;
+
+    return {
+      ...response, // 保留原本的响应信息
+      data: {
+        code,
+        message: msg,
+        data,
+        success: code === 0, // 根据 code 判断请求是否成功
+      },
+    };
+  },
+  (error) => {
+    return {
+      ...error.response, // 保留原本的错误响应信息
+      data: {
+        code: -1,
+        message: error.message || '请求失败',
+        data: null,
+        success: false,
+      },
+    };
+  }
+);
+
 export default axios
\ No newline at end of file
diff --git a/src/api/type.ts b/src/api/type.ts
index a5dd1ca..c1acc22 100644
--- a/src/api/type.ts
+++ b/src/api/type.ts
@@ -2,9 +2,22 @@
     email: string;
     password: string;
 }
-  
-export interface LoginResponse {
-    user: string;
-    token: string;
-    refreshToken: string;
-}
\ No newline at end of file
+
+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;          
+  }
diff --git a/src/feature/auth/AuthLayout.tsx b/src/feature/auth/AuthLayout.tsx
index cceaf2d..656d869 100644
--- a/src/feature/auth/AuthLayout.tsx
+++ b/src/feature/auth/AuthLayout.tsx
@@ -17,12 +17,9 @@
                     position: 'relative',
                 }}
             >
-                {/* <h1>
-                    登录创驿
-                </h1>
-                <p>
-                    与众多用户和创作者一起交流
-                </p> */}
+                <Card style={{ padding: 0, margin: 0, background: 'rgba(255,255,255,0)', border: 'none' }}>
+                    <img src={slogan} width="100%" />
+                </Card>
             </div>
             <Flex
                 style={{ width: 400, height: '80vh' }}
@@ -33,9 +30,7 @@
                 <Card>
                     <Outlet></Outlet>
                 </Card>
-                <Card style={{ padding: 0, margin: 0 }}>
-                    <img src={slogan} width="100%" />
-                </Card>
+
             </Flex>
         </Flex>
     );
diff --git a/src/feature/auth/Forget.tsx b/src/feature/auth/Forget.tsx
index 25bf6dc..a98ffcf 100644
--- a/src/feature/auth/Forget.tsx
+++ b/src/feature/auth/Forget.tsx
@@ -1,16 +1,74 @@
 import { MailOutlined, LockOutlined } from '@ant-design/icons';
-import { Button, Form, Input, message, Row, Col } from 'antd';
-import { NavLink } from 'react-router';
+import { Button, Form, Input, message, Row, Col, Modal } from 'antd';
+import { NavLink, useNavigate } from 'react-router';
 import { useState, useEffect } from 'react';
+import { useForm } from 'antd/es/form/Form';
+import authApi from '../../api/authApi';
+
+// 定义表单值的类型
+interface FormValues {
+    email: string;
+    code: string;
+    password: string;
+    confirmPassword: string;
+}
+
 
 function Forget() {
     const [countdown, setCountdown] = useState(0);
-    const [emailSent,] = useState(false);
+    const [emailSent, setEmailSent] = useState(false); // 是否已发送验证码
+    const [form] = useForm<FormValues>();
+    const emailValue = Form.useWatch('email', form);
+    const [messageApi, contextHolder] = message.useMessage();
+    const nav = useNavigate(); // 页面跳转
 
-    const onFinish = async () => {
+    // 校验邮箱格式
+    function isValidEmail(email: string): boolean {
+        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+        console.log(emailRegex.test(email))
+        console.log(email)
+        return emailRegex.test(email);
+    }
 
+    // 发送验证码
+    const sendResetCode = async () => {
+        if (!isValidEmail(emailValue)) {
+            form.validateFields(['email']);
+            return;
+        }
+
+        await authApi.sendResetCode(emailValue).then(() => {
+            setEmailSent(true);
+            setCountdown(60);
+        }).catch((error) => {
+            messageApi.error(error?.message || '验证码发送失败');
+        });
     };
 
+    const [modal, modalContext] = Modal.useModal();
+
+    const countDownNav = (onOk: () => void) => {
+        let secondsToGo = 5;
+        const instance = modal.success({
+            title: '重置成功',
+            content: `系统将在 ${secondsToGo} 后跳转到登陆页面.`,
+            okText: "立即跳转",
+            onOk
+        });
+
+        const timer = setInterval(() => {
+            secondsToGo -= 1;
+            instance.update({
+                content: `将在 ${secondsToGo} 后回到登陆页面.`,
+            });
+        }, 1000);
+
+        setTimeout(() => {
+            clearInterval(timer);
+            instance.destroy();
+        }, secondsToGo * 1000);
+    };
+    // 倒计时发送
     useEffect(() => {
         let countdownTimer = null;
 
@@ -27,92 +85,117 @@
         };
     }, [countdown]);
 
+    // 重新发送验证码
     const resendCode = () => {
         if (countdown > 0) return;
         setCountdown(60);
-        message.info('验证码已重新发送');
+        sendResetCode();  // 重新发送验证码
+    };
+
+    // 表单提交处理
+    const onFinish = (values: FormValues) => {
+        if (!emailSent) {
+            sendResetCode();
+        } else {
+            console.log(values);
+            authApi.resetPassword({
+                email: values.email,
+                code: values.code,
+                newPassword: values.password,
+            }).then((response) => {
+                if (response.data.code == 0) {
+                    countDownNav(() => nav('/login'))
+                } else {
+                    messageApi.error(response.data.message);
+                }
+            })
+        }
     };
 
     return (
-        <Form
-            name="forget"
-            initialValues={{ remember: true }}
-            style={{ maxWidth: 360 }}
-            onFinish={onFinish}
-        >
-            <h2>重置密码</h2>
-            <p>请输入您注册时使用的邮箱地址</p>
-
-            <Form.Item
-                name="email"
-                rules={[
-                    { required: true, message: '请输入您的邮箱!' },
-                    { type: 'email', message: '请输入正确的邮箱格式' }
-                ]}
+        <>
+            <Form
+                form={form}
+                name="forget"
+                initialValues={{ remember: false }}
+                onFinish={onFinish}
             >
-                <Input prefix={<MailOutlined />} placeholder="注册邮箱" />
-            </Form.Item>
+                {contextHolder}
+                <h2>重置密码</h2>
+                <p>请输入您注册时使用的邮箱地址</p>
 
-            {emailSent && (
-                <>
-                    <Form.Item
-                        name="code"
-                        rules={[{ required: true, message: '请输入验证码!' }]}
-                    >
-                        <Row gutter={8}>
-                            <Col span={16}>
-                                <Input placeholder="验证码" />
-                            </Col>
-                            <Col span={8}>
-                                <Button
-                                    disabled={countdown > 0}
-                                    onClick={resendCode}
-                                    style={{ width: '100%' }}
-                                >
-                                    {countdown > 0 ? `${countdown}s后重试` : '重新发送'}
-                                </Button>
-                            </Col>
-                        </Row>
-                    </Form.Item>
+                <Form.Item
+                    name="email"
+                    rules={[
+                        { required: true, message: '请输入您的邮箱!' },
+                        { type: 'email', message: '请输入正确的邮箱格式' }
+                    ]}
+                >
+                    <Input prefix={<MailOutlined />} placeholder="注册邮箱" />
+                </Form.Item>
 
-                    <Form.Item
-                        name="password"
-                        rules={[
-                            { required: true, message: '请设置新密码!' },
-                            { min: 6, message: '密码长度至少为6位' }
-                        ]}
-                    >
-                        <Input.Password prefix={<LockOutlined />} placeholder="新密码" />
-                    </Form.Item>
+                {emailSent && (
+                    <>
+                        <Form.Item
+                            name="code"
+                            rules={[{ required: true, message: '请输入验证码!' }]}
+                        >
+                            <Row gutter={8}>
+                                <Col span={16}>
+                                    <Input placeholder="验证码" />
+                                </Col>
+                                <Col span={8}>
+                                    <Button
+                                        disabled={countdown > 0}
+                                        onClick={resendCode}
+                                        style={{ width: '100%' }}
+                                    >
+                                        {countdown > 0 ? `${countdown}s后重试` : '重新发送'}
+                                    </Button>
+                                </Col>
+                            </Row>
+                        </Form.Item>
 
-                    <Form.Item
-                        name="confirmPassword"
-                        dependencies={['password']}
-                        rules={[
-                            { required: true, message: '请确认新密码!' },
-                            ({ getFieldValue }) => ({
-                                validator(_, value) {
-                                    if (!value || getFieldValue('password') === value) {
-                                        return Promise.resolve();
-                                    }
-                                    return Promise.reject(new Error('两次输入的密码不一致!'));
-                                },
-                            }),
-                        ]}
-                    >
-                        <Input.Password prefix={<LockOutlined />} placeholder="确认新密码" />
-                    </Form.Item>
-                </>
-            )}
+                        <Form.Item
+                            name="password"
+                            rules={[
+                                { required: true, message: '请设置新密码!' },
+                                { min: 6, message: '密码长度至少为6位' }
+                            ]}
+                        >
+                            <Input.Password prefix={<LockOutlined />} placeholder="新密码" />
+                        </Form.Item>
 
-            <Form.Item>
-                <Button block type="primary" htmlType="submit">
-                    {emailSent ? '确认重置' : '获取验证码'}
-                </Button>
-                或 <NavLink to='/login'>返回登录</NavLink>
-            </Form.Item>
-        </Form>
+                        <Form.Item
+                            name="confirmPassword"
+                            dependencies={['password']}
+                            rules={[
+                                { required: true, message: '请确认新密码!' },
+                                ({ getFieldValue }) => ({
+                                    validator(_, value) {
+                                        if (!value || getFieldValue('password') === value) {
+                                            return Promise.resolve();
+                                        }
+                                        return Promise.reject(new Error('两次输入的密码不一致!'));
+                                    },
+                                }),
+                            ]}
+                        >
+                            <Input.Password prefix={<LockOutlined />} placeholder="确认新密码" />
+                        </Form.Item>
+                    </>
+                )}
+
+                <Form.Item>
+                    <Button block type="primary" htmlType="submit">
+                        {emailSent ? '确认重置' : '获取验证码'}
+                    </Button>
+                    或 <NavLink to='/login'>返回登录</NavLink>
+                </Form.Item>
+            </Form>
+            {modalContext}
+        </>
     );
 }
 
-export default Forget;    
\ No newline at end of file
+export default Forget;
diff --git a/src/feature/auth/Login.tsx b/src/feature/auth/Login.tsx
index 14bf6c8..8043007 100644
--- a/src/feature/auth/Login.tsx
+++ b/src/feature/auth/Login.tsx
@@ -1,19 +1,48 @@
+import { Loading3QuartersOutlined, LockOutlined, MailOutlined } from '@ant-design/icons';
+import { Button, Checkbox, Flex, Form, Input } from 'antd';
+import { NavLink, useNavigate } from 'react-router';
+import { useAppDispatch, useAppSelector } from '../../store/hooks';
+import { loginUser } from './authSlice';
+import { useEffect, useRef } from 'react';
+import useMessage from 'antd/es/message/useMessage';
 
-import { LockOutlined, MailOutlined } from '@ant-design/icons';
-import { Button, Checkbox, Form, Input, Flex } from 'antd';
-import { NavLink } from 'react-router';
+// 定义 Form 表单的字段类型
+interface FormValues {
+    email: string;
+    password: string;
+    remember: boolean;
+}
+
 function Login() {
-    const onFinish = (values: unknown) => {
-        console.log('Received values of form: ', values);
+    const dispatch = useAppDispatch();
+    const auth = useAppSelector(state => (state.auth));
+    const [messageApi, Message] = useMessage()
+    const nav = useRef(useNavigate())
+
+    useEffect(() => {
+        if (auth.isAuth) {
+            nav.current('/');
+        }
+        if (!auth.loading && auth.error) {
+            messageApi.error(auth.error);
+        }
+    }, [auth, messageApi, nav])
+    // 给 onFinish 参数添加类型
+    const onFinish = async (values: FormValues) => {
+        try {
+            await dispatch(loginUser({ email: values.email, password: values.password }));
+        } catch (error) {
+            console.error('登录失败', error);
+        }
     };
 
     return (
         <Form
             name="login"
             initialValues={{ remember: true }}
-            style={{ maxWidth: 360 }}
             onFinish={onFinish}
         >
+            {Message}
             <h2>登录</h2>
             <Form.Item
                 name="email"
@@ -32,19 +61,23 @@
                     <Form.Item name="remember" valuePropName="checked" noStyle>
                         <Checkbox>自动登录</Checkbox>
                     </Form.Item>
-                    <NavLink to='/forget'> 忘记密码 </NavLink>
-
+                    <NavLink to="/forget"> 忘记密码 </NavLink>
                 </Flex>
             </Form.Item>
 
             <Form.Item>
                 <Button block type="primary" htmlType="submit">
-                    登录
+                    {auth.loading ? (
+                        <><Loading3QuartersOutlined /></>
+                    ) : (
+                        <>登录</>
+                    )
+                    }
                 </Button>
-                或 <NavLink to='/register'>注册</NavLink>
+                或 <NavLink to="/register">注册</NavLink>
             </Form.Item>
         </Form>
     );
-};
+}
 
-export default Login;
\ No newline at end of file
+export default Login;
diff --git a/src/feature/auth/Register.tsx b/src/feature/auth/Register.tsx
index 08edc70..0023b71 100644
--- a/src/feature/auth/Register.tsx
+++ b/src/feature/auth/Register.tsx
@@ -1,9 +1,12 @@
 import { useEffect, useState } from 'react';
 import { LockOutlined, MailOutlined, NumberOutlined, UserOutlined } from '@ant-design/icons';
-import {Button, Checkbox, Form, Input, Space} from 'antd';
-import { NavLink } from 'react-router';
-import authApi from "../../api/authApi.ts";
+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 type { AxiosResponse } from 'axios';
 
+// 定义表单字段的类型
 interface FormValues {
     name: string;
     email: string;
@@ -16,42 +19,65 @@
 function Register() {
     const [countdown, setCountdown] = useState(0);
     const [form] = Form.useForm<FormValues>();
-    const emailValue = Form.useWatch('email', form)
+    const emailValue = Form.useWatch('email', form);
+    const [messageApi, contextHolder] = message.useMessage();
+    const nav = useNavigate(); // 用于页面跳转
 
-    // 
+    // 校验邮箱格式
     function isValidEmail(email: string): boolean {
         const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
-
         return emailRegex.test(email);
     }
 
+    // 发送验证码
     const sendVerificationCode = () => {
-        // 如果邮箱校验不通过,则触发表单校验提示,并中断
         if (!isValidEmail(emailValue)) {
             form.validateFields(['email']);
             return;
         }
 
-        // 发起 POST 请求到后端 /sendVerification
-        authApi.sendVerificationCode(emailValue).catch()
-        setCountdown(60);
+        authApi.sendVerificationCode(emailValue).then(() => {
+            setCountdown(60); // 开始倒计时
+        }).catch((error) => {
+            messageApi.error(error?.message || '验证码发送失败');
+        });
     };
 
-    // 发送表单倒计时
+    // 倒计时处理
     useEffect(() => {
-        if (countdown > 0) {
-            const timer = setTimeout(() => {
-                setCountdown(prev => prev - 1);
-            }, 1000);
+        if (countdown === 0) return;
+        const timer = setInterval(() => {
+            setCountdown(prev => {
+                if (prev === 1) clearInterval(timer); // 倒计时结束
+                return prev - 1;
+            });
+        }, 1000);
 
-            return () => clearTimeout(timer);
-        }
+        return () => clearInterval(timer);
     }, [countdown]);
 
-
     // 表单提交
     const onFinish = (values: FormValues) => {
-        console.log('注册成功:', values);
+        const registerRequest: RejisterRequest = {
+            username: values.name,
+            email: values.email,
+            verificationCode: values.verifyCode,
+            password: values.password,
+        };
+
+        authApi.register(registerRequest).then((response: AxiosResponse) => {
+            if (response.data.code === 200) {
+                messageApi.success("注册成功");
+                form.resetFields(); // 清空表单
+                setTimeout(() => {
+                    nav('/login'); // 注册成功后跳转到登录页面
+                }, 1500);
+            } else {
+                messageApi.error(response.data.message);
+            }
+        }).catch((error) => {
+            messageApi.error(error?.message || '注册失败,请重试');
+        });
     };
 
     return (
@@ -62,6 +88,7 @@
             scrollToFirstError
         >
             <h2>注册</h2>
+            {contextHolder}
 
             <Form.Item
                 name="name"
@@ -72,7 +99,6 @@
 
             <Form.Item
                 name="email"
-
                 rules={[{ required: true, message: '请输入邮箱' }, { type: 'email', message: '邮箱格式错误' }]}
             >
                 <Input
@@ -163,4 +189,4 @@
     );
 }
 
-export default Register;    
\ No newline at end of file
+export default Register;
diff --git a/src/feature/auth/authSlice.ts b/src/feature/auth/authSlice.ts
index 01cd4e5..f1ab2a0 100644
--- a/src/feature/auth/authSlice.ts
+++ b/src/feature/auth/authSlice.ts
@@ -1,17 +1,98 @@
-import { createSlice } from "@reduxjs/toolkit";
+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";
 
 
 const initialState: AuthState = {
   token: '',
   loading: false,
   isAuth: false,
+  error: ''
 }
 
+export const loginUser = createAsyncThunk<
+  {token: string},
+  LoginRequest,
+  { rejectValue: string }
+>(
+  'auth/login',
+  async (loginRequest: LoginRequest, { rejectWithValue }) => {
+    try {
+      const response = await authAPI.login(loginRequest);
+      if(response.data.code == 0) {
+        return {token: response.data.data};
+      }
+      else 
+        return rejectWithValue(response.data.message);
+    } catch {
+      return rejectWithValue('登录失败');
+    }
+  }
+);
+
+export const refreshToken = createAsyncThunk<
+  {token: string},
+  string,
+  { rejectValue: string }
+>(
+
+  'auth/refresh',
+  async (oldToken: string, { rejectWithValue }) => {
+    try {
+      const response = await authAPI.refreshToken(oldToken);
+      if(response.data.code == 0)
+        return {token: response.data.data};
+      else 
+        return rejectWithValue(response.data.message);
+    } catch {
+      return rejectWithValue('刷新失败');
+    }
+  }
+);
+
 const authSlice = createSlice({
   name: 'auth',
   initialState,
-  reducers: {},
+  reducers: {
+    logout: (state) => {
+      state.token = '';
+      state.isAuth = false;
+      localStorage.clear()
+    },
+  },extraReducers: (builder) => {
+      // 处理登录的异步操作
+      builder
+        .addCase(loginUser.pending, (state) => {
+          state.loading = true;
+        })
+        .addCase(loginUser.fulfilled, (state, action: PayloadAction<{token: string}>) => {
+          state.loading = false;
+          state.token = action.payload.token;
+          state.isAuth = true;
+
+          localStorage.setItem('token', state.token);
+        })
+        .addCase(loginUser.rejected, (state, action) => {
+          state.loading = false;
+          state.error = action.payload ? action.payload : '' // 错误处理
+        })
+
+        // 处理刷新 token 的异步操作
+        .addCase(refreshToken.pending, (state) => {
+          state.loading = true;
+        })
+        .addCase(refreshToken.fulfilled, (state, action) => {
+          state.loading = false;
+          state.token = action.payload.token; 
+          localStorage.setItem('token', state.token);
+        })
+        .addCase(refreshToken.rejected, (state, action) => {
+          state.loading = false;
+          state.error = action.payload ? action.payload : ''
+        });
+    },
+    
 });
   
 export default authSlice.reducer;
\ No newline at end of file
diff --git a/src/main.tsx b/src/main.tsx
index 31a6d39..48b3777 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -8,6 +8,7 @@
 import routes from './routes.ts';
 import { RouterProvider } from 'react-router';
 // 组件库 ant
+import '@ant-design/v5-patch-for-react-19';
 import { ConfigProvider } from 'antd';
 import zhCN from 'antd/locale/zh_CN';
 
diff --git a/src/store/store.ts b/src/store/store.ts
index ebf1408..5f3841e 100644
--- a/src/store/store.ts
+++ b/src/store/store.ts
@@ -1,9 +1,10 @@
 import { configureStore } from '@reduxjs/toolkit'
-
+import authReducer from "../feature/auth/authSlice"
 export const store = configureStore({
   reducer: {
-      
-  }
+      auth: authReducer,
+  },
+  middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(),
 })
 
 // 从 store 本身推断出 `RootState` 和 `AppDispatch` 类型
diff --git a/src/store/types.ts b/src/store/types.ts
index 372c089..3e2d796 100644
--- a/src/store/types.ts
+++ b/src/store/types.ts
@@ -12,4 +12,5 @@
     token: string;
     loading: boolean;
     isAuth: boolean;
+    error: string
 }
\ No newline at end of file
diff --git a/src/test/auth/Forget.test.tsx b/src/test/auth/Forget.test.tsx
new file mode 100644
index 0000000..b2ba158
--- /dev/null
+++ b/src/test/auth/Forget.test.tsx
@@ -0,0 +1,24 @@
+import { render, screen } from '@testing-library/react'
+import Forget from '../../feature/auth/Forget'
+import { Provider } from 'react-redux'
+import { store } from '../../store/store'
+import { MemoryRouter } from 'react-router'
+
+describe('Forget Password Page', () => {
+    it('renders forget password form', () => {
+        render(
+            <MemoryRouter>
+                <Provider store={store}>
+                    <Forget />
+                </Provider>
+            </MemoryRouter>
+
+        )
+
+        const emailInput = screen.getByPlaceholderText('注册邮箱')
+        const getCodeButton = screen.getByText('获取验证码')
+
+        expect(emailInput).toBeInTheDocument()
+        expect(getCodeButton).toBeInTheDocument()
+    })
+})
\ No newline at end of file
diff --git a/src/test/auth/Login.test.tsx b/src/test/auth/Login.test.tsx
new file mode 100644
index 0000000..ade54e8
--- /dev/null
+++ b/src/test/auth/Login.test.tsx
@@ -0,0 +1,27 @@
+import { render, screen } from '@testing-library/react'
+import Login from '../../feature/auth/Login'
+import { Provider } from 'react-redux'
+import { store } from '../../store/store'
+import { MemoryRouter } from 'react-router'
+
+describe('Login Page', () => {
+  it('renders login form', () => {
+    render(
+      <MemoryRouter>
+        <Provider store={store}>
+          <Login />
+        </Provider>
+      </MemoryRouter>
+
+    )
+
+    const emailInput = screen.getByPlaceholderText('账号(注册邮箱)')
+    const passwordInput = screen.getByPlaceholderText('密码')
+    const loginButton = screen.getByRole('button', { name: /登录/i })
+
+
+    expect(emailInput).toBeInTheDocument()
+    expect(passwordInput).toBeInTheDocument()
+    expect(loginButton).toBeInTheDocument()
+  })
+})
\ No newline at end of file
diff --git a/src/test/auth/Register.test.tsx b/src/test/auth/Register.test.tsx
new file mode 100644
index 0000000..c118a8b
--- /dev/null
+++ b/src/test/auth/Register.test.tsx
@@ -0,0 +1,32 @@
+import { render, screen } from '@testing-library/react'
+import Register from '../../feature/auth/Register'
+import { Provider } from 'react-redux'
+import { store } from '../../store/store'
+import { MemoryRouter } from 'react-router'
+
+describe('Register Page', () => {
+    it('renders register form', () => {
+        render(
+            <MemoryRouter>
+                <Provider store={store}>
+                    <Register />
+                </Provider>
+            </MemoryRouter>
+
+        )
+
+        const nameInput = screen.getByPlaceholderText('请输入用户名')
+        const emailInput = screen.getByPlaceholderText('请输入邮箱')
+        const verifyCodeInput = screen.getByPlaceholderText('请输入验证码')
+        const passwordInput = screen.getByPlaceholderText('请输入密码')
+        const confirmPasswordInput = screen.getByPlaceholderText('请确认密码')
+        const registerButton = screen.getByText('注册')
+
+        expect(nameInput).toBeInTheDocument()
+        expect(emailInput).toBeInTheDocument()
+        expect(verifyCodeInput).toBeInTheDocument()
+        expect(passwordInput).toBeInTheDocument()
+        expect(confirmPasswordInput).toBeInTheDocument()
+        expect(registerButton).toBeInTheDocument()
+    })
+})
\ No newline at end of file
diff --git a/src/test/setup.ts b/src/test/setup.ts
new file mode 100644
index 0000000..bb15cf4
--- /dev/null
+++ b/src/test/setup.ts
@@ -0,0 +1,9 @@
+import '@testing-library/jest-dom';
+
+globalThis.matchMedia = globalThis.matchMedia || function() {
+    return {
+      matches: false,  // 模拟返回值
+      addListener: () => {},  // 不执行任何操作
+      removeListener: () => {}  // 不执行任何操作
+    };
+  };
\ No newline at end of file