- API client with axios interceptors: JWT attach + 401 auto-refresh - Auth store (Zustand): login/logout/loadFromStorage with localStorage - Login page: gradient background, Ant Design form, error handling - Home page: dashboard with statistics cards - App.tsx: PrivateRoute guard, /login route, auth state restoration - MainLayout: dynamic user display, logout dropdown, menu navigation - Users API service: CRUD with pagination support
53 lines
1.1 KiB
TypeScript
53 lines
1.1 KiB
TypeScript
import client from './client';
|
|
|
|
export interface LoginRequest {
|
|
username: string;
|
|
password: string;
|
|
}
|
|
|
|
export interface UserInfo {
|
|
id: string;
|
|
username: string;
|
|
email?: string;
|
|
phone?: string;
|
|
display_name?: string;
|
|
avatar_url?: string;
|
|
status: string;
|
|
roles: RoleInfo[];
|
|
}
|
|
|
|
export interface RoleInfo {
|
|
id: string;
|
|
name: string;
|
|
code: string;
|
|
description?: string;
|
|
is_system: boolean;
|
|
}
|
|
|
|
export interface LoginResponse {
|
|
access_token: string;
|
|
refresh_token: string;
|
|
expires_in: number;
|
|
user: UserInfo;
|
|
}
|
|
|
|
export async function login(req: LoginRequest): Promise<LoginResponse> {
|
|
const { data } = await client.post<{ success: boolean; data: LoginResponse }>(
|
|
'/auth/login',
|
|
req
|
|
);
|
|
return data.data;
|
|
}
|
|
|
|
export async function refresh(refreshToken: string): Promise<LoginResponse> {
|
|
const { data } = await client.post<{ success: boolean; data: LoginResponse }>(
|
|
'/auth/refresh',
|
|
{ refresh_token: refreshToken }
|
|
);
|
|
return data.data;
|
|
}
|
|
|
|
export async function logout(): Promise<void> {
|
|
await client.post('/auth/logout');
|
|
}
|