- 新增authApi.ts实现用户认证相关接口 - 新增commentApi.ts实现评论功能接口 - 新增eventApi.ts实现活动管理接口 - 新增favoriteApi.ts实现收藏功能接口 - 新增heritageApi.ts实现非遗项目接口 - 新增inheritorApi.ts实现传承人接口 - 新增likeApi.ts实现点赞功能接口 - 新增newsApi.ts实现资讯接口 - 新增userApi.ts实现用户中心接口 - 优化request.ts请求拦截器,统一处理认证和错误
197 lines
5.7 KiB
TypeScript
197 lines
5.7 KiB
TypeScript
/**
|
||
* 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 === 200(HTTP标准)
|
||
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)
|
||
},
|
||
}
|