heritage-ui/src/utils/request.ts
Leo 093cb7c1fd feat: 添加API服务层和请求封装
- 新增authApi.ts实现用户认证相关接口
- 新增commentApi.ts实现评论功能接口
- 新增eventApi.ts实现活动管理接口
- 新增favoriteApi.ts实现收藏功能接口
- 新增heritageApi.ts实现非遗项目接口
- 新增inheritorApi.ts实现传承人接口
- 新增likeApi.ts实现点赞功能接口
- 新增newsApi.ts实现资讯接口
- 新增userApi.ts实现用户中心接口
- 优化request.ts请求拦截器,统一处理认证和错误
2025-10-13 21:40:21 +08:00

197 lines
5.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Axios HTTP 请求封装
* 统一处理请求、响应、错误和Token
*/
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios'
import { message } from 'antd'
import type { ApiResponse } from '@/types'
// 创建axios实例
const request: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL || 'http://localhost:18099',
timeout: 30000,
headers: {
'Content-Type': 'application/json',
},
})
// Token存储键统一使用 hrt-token与后端约定一致
const TOKEN_KEY = 'hrt-token'
// 获取Token
export const getToken = (): string | null => {
return localStorage.getItem(TOKEN_KEY)
}
// 设置Token
export const setToken = (token: string): void => {
localStorage.setItem(TOKEN_KEY, token)
}
// 移除Token
export const removeToken = (): void => {
localStorage.removeItem(TOKEN_KEY)
}
// 请求拦截器
request.interceptors.request.use(
(config) => {
// 自动添加Token到请求头前台用户使用hrt-token
const token = getToken()
if (token && config.headers) {
config.headers['hrt-token'] = token
}
return config
},
(error: AxiosError) => {
console.error('请求错误:', error)
return Promise.reject(error)
}
)
// 响应拦截器
request.interceptors.response.use(
(response: AxiosResponse<any>) => {
const { data } = response
// 后端使用 mica 框架的 R 类包装响应
// R.success() 格式:{ code: 1, success: true, data: ..., msg: "操作成功" }
// R.error() 格式:{ code: xxx, success: false, data: null, msg: "错误信息" }
if (data && typeof data === 'object') {
// 检查 code 字段mica 框架使用 code
if ('code' in data) {
// 成功响应判断(支持多种成功标识)
// 1. success === true最可靠的成功标志
// 2. code === 1后端实际使用 - 重要!)
// 3. code === 0部分框架默认
// 4. code === 200HTTP标准
const isSuccess =
data.success === true ||
data.success === 'true' ||
data.code === 1 || // ✅ 后端实际使用 code: 1 表示成功
data.code === 0 ||
data.code === 200
if (isSuccess) {
return data.data
}
// 业务错误
const errorMessage = data.msg || data.message || '请求失败'
// 🔥 识别未登录相关的错误,自动跳转到登录页
const needLoginKeywords = [
'未提供token',
'未提供Token',
'token无效',
'Token无效',
'未登录',
'请登录',
'登录已过期',
'身份验证失败',
'未授权'
]
const needLogin = needLoginKeywords.some(keyword =>
errorMessage.toLowerCase().includes(keyword.toLowerCase())
)
if (needLogin) {
message.warning('请先登录')
removeToken()
// 延迟跳转,让用户看到提示
setTimeout(() => {
window.location.href = '/login'
}, 500)
}
return Promise.reject(new Error(errorMessage))
}
// 兼容其他格式:{ status, msg, data }
if ('status' in data) {
if (data.status === 200 || data.status === 0) {
return data.data
}
const errorMessage = data.msg || data.message || '请求失败'
return Promise.reject(new Error(errorMessage))
}
}
// 如果后端直接返回数据(非标准格式)
return data
},
(error: AxiosError<any>) => {
// HTTP错误处理
if (error.response) {
const { status, data } = error.response
let errorMessage = '请求失败'
// 优先从 data.msg 或 data.message 提取错误信息
if (data && typeof data === 'object') {
errorMessage = data.msg || data.message || errorMessage
}
// 针对特定 HTTP 状态码的处理
switch (status) {
case 400:
errorMessage = errorMessage || '请求参数错误'
break
case 401:
errorMessage = '未授权,请重新登录'
removeToken()
window.location.href = '/login'
break
case 403:
errorMessage = errorMessage || '没有权限访问'
break
case 404:
errorMessage = '请求的资源不存在'
break
case 500:
errorMessage = errorMessage || '服务器错误'
break
}
// 将错误信息附加到error对象上
const enhancedError = new Error(errorMessage) as any
enhancedError.status = status
enhancedError.response = error.response
return Promise.reject(enhancedError)
} else if (error.request) {
// 请求已发出但没有收到响应
return Promise.reject(new Error('网络连接失败,请检查网络'))
} else {
// 请求配置出错
return Promise.reject(new Error('请求配置错误'))
}
}
)
// 导出请求方法
export default request
// 常用请求方法封装
export const http = {
get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
return request.get(url, config)
},
post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
return request.post(url, data, config)
},
put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
return request.put(url, data, config)
},
delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
return request.delete(url, config)
},
patch<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
return request.patch(url, data, config)
},
}