|
|
@@ -10,93 +10,58 @@ const getBaseURL = () => {
|
|
|
}
|
|
|
|
|
|
const axiosInstance = axios.create({
|
|
|
- timeout: 15000,
|
|
|
+ timeout: 30000,
|
|
|
baseURL: getBaseURL(),
|
|
|
withCredentials: true,
|
|
|
- transformRequest: [
|
|
|
- (data) => {
|
|
|
- if (data && typeof data === 'object' && !(data instanceof FormData)) {
|
|
|
- return JSON.stringify(data)
|
|
|
- }
|
|
|
- return data
|
|
|
- }
|
|
|
- ],
|
|
|
validateStatus: (status) => status >= 200 && status < 300,
|
|
|
headers: {
|
|
|
- get: { 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8' },
|
|
|
post: { 'Content-Type': 'application/json;charset=utf-8' }
|
|
|
- },
|
|
|
- transformResponse: [
|
|
|
- (data, headers) => {
|
|
|
- // 如果 data 是 undefined 或 null,直接返回
|
|
|
- if (data === undefined || data === null) {
|
|
|
- return data
|
|
|
- }
|
|
|
-
|
|
|
- const contentType = headers['content-type']
|
|
|
- if (contentType && contentType.includes('application/json')) {
|
|
|
- try {
|
|
|
- // 如果 data 是字符串,尝试解析
|
|
|
- if (typeof data === 'string') {
|
|
|
- return JSON.parse(data)
|
|
|
- }
|
|
|
- // 如果已经是对象,直接返回
|
|
|
- return data
|
|
|
- } catch (error) {
|
|
|
- console.error('JSON parse error:', error, 'data:', data)
|
|
|
- return data
|
|
|
- }
|
|
|
- }
|
|
|
- return data
|
|
|
- }
|
|
|
- ]
|
|
|
+ }
|
|
|
})
|
|
|
|
|
|
-// 请求拦截器
|
|
|
axiosInstance.interceptors.request.use(
|
|
|
(config: InternalAxiosRequestConfig) => {
|
|
|
const userStore = useUserStore()
|
|
|
if (userStore.accessToken) {
|
|
|
config.headers.Authorization = `Bearer ${userStore.accessToken}`
|
|
|
}
|
|
|
-
|
|
|
- // 对于有 body 的请求(POST, PUT, PATCH),确保 Content-Type 正确
|
|
|
+
|
|
|
const method = config.method?.toUpperCase()
|
|
|
if (method === 'POST' || method === 'PUT' || method === 'PATCH') {
|
|
|
if (config.data && typeof config.data === 'object' && !(config.data instanceof FormData)) {
|
|
|
- // 如果 data 是对象,确保 Content-Type 是 application/json
|
|
|
config.headers['Content-Type'] = 'application/json;charset=utf-8'
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return config
|
|
|
},
|
|
|
- (error) => {
|
|
|
- return Promise.reject(error)
|
|
|
- }
|
|
|
+ (error) => Promise.reject(error)
|
|
|
)
|
|
|
|
|
|
-// 响应拦截器
|
|
|
+function redirectToLogin() {
|
|
|
+ setTimeout(() => {
|
|
|
+ window.location.hash = '#/login'
|
|
|
+ }, 1500)
|
|
|
+}
|
|
|
+
|
|
|
axiosInstance.interceptors.response.use(
|
|
|
(response: AxiosResponse) => {
|
|
|
- // 如果 response.data 是 undefined,直接返回
|
|
|
- if (!response.data) {
|
|
|
+ if (response.data === undefined || response.data === null) {
|
|
|
return response.data
|
|
|
}
|
|
|
-
|
|
|
- // 如果响应数据没有 code 字段,说明后端直接返回数据,直接返回
|
|
|
+
|
|
|
+ // 无 code 字段:直接返回(兼容直出 JSON)
|
|
|
if (typeof response.data !== 'object' || !('code' in response.data)) {
|
|
|
return response.data
|
|
|
}
|
|
|
-
|
|
|
- const { code, message } = response.data
|
|
|
|
|
|
- // 成功响应
|
|
|
+ const { code, message, data } = response.data
|
|
|
+
|
|
|
if (code === 200 || code === 0) {
|
|
|
- return response.data
|
|
|
+ // 门户信封:解包 data
|
|
|
+ return data
|
|
|
}
|
|
|
|
|
|
- // 401 未授权,清除 token 并跳转登录
|
|
|
if (code === 401) {
|
|
|
const userStore = useUserStore()
|
|
|
userStore.logOut()
|
|
|
@@ -104,13 +69,10 @@ axiosInstance.interceptors.response.use(
|
|
|
type: 'fail',
|
|
|
message: message || '登录已过期,请重新登录'
|
|
|
})
|
|
|
- setTimeout(() => {
|
|
|
- window.location.href = '/login'
|
|
|
- }, 1500)
|
|
|
+ redirectToLogin()
|
|
|
return Promise.reject(new Error(message || 'Unauthorized'))
|
|
|
}
|
|
|
|
|
|
- // 其他错误
|
|
|
showToast({
|
|
|
type: 'fail',
|
|
|
message: message || '请求失败'
|
|
|
@@ -125,16 +87,16 @@ axiosInstance.interceptors.response.use(
|
|
|
userStore.logOut()
|
|
|
showToast({
|
|
|
type: 'fail',
|
|
|
- message: '登录已过期,请重新登录'
|
|
|
+ message: data?.message || data?.detail || '登录已过期,请重新登录'
|
|
|
})
|
|
|
- setTimeout(() => {
|
|
|
- window.location.href = '/login'
|
|
|
- }, 1500)
|
|
|
+ redirectToLogin()
|
|
|
} else {
|
|
|
- showToast({
|
|
|
- type: 'fail',
|
|
|
- message: data?.message || `请求失败: ${status}`
|
|
|
- })
|
|
|
+ const detail = data?.detail
|
|
|
+ const msg =
|
|
|
+ typeof detail === 'string'
|
|
|
+ ? detail
|
|
|
+ : data?.message || `请求失败: ${status}`
|
|
|
+ showToast({ type: 'fail', message: msg })
|
|
|
}
|
|
|
} else if (error.request) {
|
|
|
showToast({
|
|
|
@@ -151,7 +113,6 @@ axiosInstance.interceptors.response.use(
|
|
|
}
|
|
|
)
|
|
|
|
|
|
-// API 方法封装
|
|
|
const api = {
|
|
|
get<T = any>(config: AxiosRequestConfig): Promise<T> {
|
|
|
return axiosInstance.request({ ...config, method: 'GET' })
|