| // src/feature/auth/authSlice.ts |
| |
| import { createAsyncThunk, createSlice, type PayloadAction } from "@reduxjs/toolkit"; |
| import type { AuthState } from "./types"; |
| import type { LoginRequest } from "../../api/Auth/type"; |
| import AuthAPI from "../../api/Auth/AuthApi"; |
| |
| // 获取本地存储的 token |
| const storedToken = localStorage.getItem('token'); |
| |
| const initialState: AuthState = { |
| token: storedToken || '', |
| loading: false, |
| isAuth: !!storedToken, // 如果有token就设为true |
| 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('登录失败'); |
| } |
| } |
| ); |
| |
| // 新增:从localStorage恢复登录状态 |
| export const loginFromLocalStorage = createAsyncThunk< |
| {token: string} | {empty: true}, |
| void, |
| { rejectValue: string } |
| >( |
| 'auth/loginFromLocalStorage', |
| async (_, { rejectWithValue, dispatch }) => { |
| try { |
| const token = localStorage.getItem('token'); |
| if (!token) { |
| // 返回空状态而不是错误 |
| return { empty: true }; |
| } |
| |
| // 直接使用refreshToken来验证token有效性 |
| const result = await dispatch(refreshToken(token)); |
| if (refreshToken.fulfilled.match(result)) { |
| // refresh成功,返回新的token |
| return { token: result.payload.token }; |
| } else { |
| // refresh失败,token无效 |
| localStorage.removeItem('token'); |
| return rejectWithValue('token已失效,需要重新登录'); |
| } |
| } catch { |
| localStorage.removeItem('token'); |
| 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: { |
| logout: (state) => { |
| state.token = ''; |
| state.isAuth = false; |
| localStorage.clear(); |
| }, |
| |
| // 清除错误信息 |
| clearError: (state) => { |
| state.error = ''; |
| }, |
| }, |
| extraReducers: (builder) => { |
| // 处理普通登录的异步操作 |
| builder |
| .addCase(loginUser.pending, (state) => { |
| state.loading = true; |
| state.error = ''; |
| }) |
| .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 : ''; |
| }); |
| |
| // 处理从localStorage恢复登录状态 |
| builder |
| .addCase(loginFromLocalStorage.pending, (state) => { |
| state.loading = true; |
| state.error = ''; |
| }) |
| .addCase(loginFromLocalStorage.fulfilled, (state, action) => { |
| state.loading = false; |
| if ('token' in action.payload) { |
| // 有token的情况 |
| state.token = action.payload.token; |
| state.isAuth = true; |
| } else { |
| // 空token的情况 |
| state.token = ''; |
| state.isAuth = false; |
| } |
| }) |
| .addCase(loginFromLocalStorage.rejected, (state, action) => { |
| state.loading = false; |
| state.token = ''; |
| state.isAuth = false; |
| state.error = action.payload ? action.payload : ''; |
| localStorage.removeItem('token'); |
| }); |
| |
| // 处理刷新 token 的异步操作 |
| builder |
| .addCase(refreshToken.pending, (state) => { |
| state.loading = true; |
| state.error = ''; |
| }) |
| .addCase(refreshToken.fulfilled, (state, action) => { |
| state.loading = false; |
| state.token = action.payload.token; |
| state.isAuth = true; |
| localStorage.setItem('token', state.token); |
| }) |
| .addCase(refreshToken.rejected, (state, action) => { |
| state.loading = false; |
| state.error = action.payload ? action.payload : ''; |
| state.isAuth = false; |
| }); |
| }, |
| }); |
| |
| export const { logout, clearError } = authSlice.actions; |
| export default authSlice.reducer; |