前端项目初始化,登录页支持暗色主题与禁止滑动
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
95
frontend/src/api/client.ts
Normal file
95
frontend/src/api/client.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import axios, { AxiosError, type AxiosInstance, type AxiosRequestConfig } from 'axios'
|
||||
import { Device, Language, type ApiRequest, type ApiResponse, type ErrorResponse } from '@/types/api'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
|
||||
// 根据环境选择基础地址
|
||||
// Docker 开发环境:Vite proxy 会将 /api 转发到后端
|
||||
// 生产环境:Nginx 代理 /api 到后端网关
|
||||
const baseURL = import.meta.env.DEV ? '/' : '/api'
|
||||
|
||||
const client: AxiosInstance = axios.create({
|
||||
baseURL,
|
||||
timeout: 30000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
// 请求拦截器:注入 JWT
|
||||
client.interceptors.request.use((config) => {
|
||||
const token = useAuthStore.getState().token
|
||||
if (token && config.headers) {
|
||||
config.headers.Authorization = `Bearer ${token}`
|
||||
}
|
||||
return config
|
||||
})
|
||||
|
||||
// 响应拦截器:统一错误处理
|
||||
client.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error: AxiosError<ErrorResponse>) => {
|
||||
const status = error.response?.status
|
||||
const message = error.response?.data?.message || error.message || '请求失败'
|
||||
|
||||
if (status === 401) {
|
||||
useAuthStore.getState().logout()
|
||||
window.location.href = '/login'
|
||||
}
|
||||
|
||||
return Promise.reject(new Error(message))
|
||||
}
|
||||
)
|
||||
|
||||
/** 包装业务请求(注册/业务类接口) */
|
||||
export async function apiPost<TReq, TRes>(
|
||||
url: string,
|
||||
data: TReq,
|
||||
config?: AxiosRequestConfig
|
||||
): Promise<TRes> {
|
||||
const body: ApiRequest<TReq> = {
|
||||
device: Device.Web,
|
||||
language: Language.SimplifiedChinese,
|
||||
data,
|
||||
}
|
||||
|
||||
const response = await client.post<ApiResponse<TRes>>(url, body, config)
|
||||
const result = response.data
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error(result.message)
|
||||
}
|
||||
|
||||
if (result.data === null) {
|
||||
throw new Error('接口返回数据为空')
|
||||
}
|
||||
|
||||
return result.data
|
||||
}
|
||||
|
||||
/** 原始 POST(登录/认证类接口,不包装) */
|
||||
export async function rawPost<TReq, TRes>(
|
||||
url: string,
|
||||
data: TReq,
|
||||
config?: AxiosRequestConfig
|
||||
): Promise<TRes> {
|
||||
const response = await client.post<TRes>(url, data, config)
|
||||
return response.data
|
||||
}
|
||||
|
||||
/** 通用 GET */
|
||||
export async function apiGet<TRes>(url: string, config?: AxiosRequestConfig): Promise<TRes> {
|
||||
const response = await client.get<ApiResponse<TRes>>(url, config)
|
||||
const result = response.data
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error(result.message)
|
||||
}
|
||||
|
||||
if (result.data === null) {
|
||||
throw new Error('接口返回数据为空')
|
||||
}
|
||||
|
||||
return result.data
|
||||
}
|
||||
|
||||
export default client
|
||||
Reference in New Issue
Block a user