foreign-admin-ui/src/api/index.ts

185 lines
5.3 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.

import axios from 'axios'
import type { AxiosResponse, AxiosError, InternalAxiosRequestConfig } from 'axios'
import type { ApiResponse } from '@/api/types'
import { ElLoading, ElNotification } from 'element-plus'
import { getStorage, setStorage, removeStorage } from '@/utils/storage'
import router from '../router'
// 定义loading实例类型用于控制loading的显示和关闭
let loadingInstance: ReturnType<typeof ElLoading.service> | null = null
// 记录当前请求数量,防止多个请求重复显示/关闭loading
let requestCount = 0
// 显示loading的方法
const showLoading = () => {
if (requestCount === 0 && !loadingInstance) {
loadingInstance = ElLoading.service({
lock: true, // 锁定屏幕滚动
fullscreen: true, // 全屏显示
})
}
requestCount++
}
// 关闭loading的方法
const hideLoading = () => {
requestCount--
if (requestCount <= 0) {
if (loadingInstance) {
loadingInstance.close()
loadingInstance = null
}
requestCount = 0 // 重置计数,防止负数
}
}
// 1. 环境自动识别 + 配置读取核心修改适配Vite环境变量
const env = import.meta.env
const baseURL = env.VITE_API_BASE_URL || ''
const timeout = Number(env.VITE_REQUEST_TIMEOUT) || 10000
// 2. 创建axios实例
const service = axios.create({
baseURL,
timeout,
headers: { 'Content-Type': 'application/json;charset=utf-8' },
})
// 3. 请求拦截器:直接使用 axios 内置类型
const requestInterceptor = (config: InternalAxiosRequestConfig) => {
console.log('请求配置', config)
// 显示loading可通过config配置项控制是否显示比如某些请求不需要loading
if (config.headers?.showLoading !== false) {
showLoading()
}
// 添加token到请求头
const token = getStorage('token')
if (token) {
config.headers.token = token
}
return config
}
const requestErrorInterceptor = (error: AxiosError) => {
// 请求拦截器出错时也要关闭loading
ElNotification({
message: error.message || '未知的请求错误!',
type: 'error',
duration: 2000,
})
hideLoading()
return Promise.reject(error)
}
service.interceptors.request.use(requestInterceptor, requestErrorInterceptor)
// 4. 响应拦截器
const responseInterceptor = <T = unknown>(response: AxiosResponse<ApiResponse<T>>) => {
//关闭loading无论成功失败都要关
hideLoading()
if (response.headers['new_token'] !== undefined) {
setStorage('token', response.headers['new_token'] as string)
}
console.log('响应response', response)
const { data } = response
console.log('响应数据data', data)
// 判断业务状态码
if (data.code == 200 || data.code == 0) {
// 仅当业务成功时,才弹成功提示
ElNotification({
message: data.msg || '操作成功!',
type: 'success',
duration: 2000,
customClass: 'global-notification',
})
return data.data
} else {
switch (data.code) {
case 1:
if (!data.msg) {
data.msg = '未知的错误!'
}
break
case -101:
data.msg = 'token错误'
removeStorage('token')
router.push({ name: 'Login' })
break
default:
break;
}
// 其他业务失败弹错误提示并拒绝Promise
ElNotification({
message: data.msg || '未知的错误!',
type: 'error',
duration: 2000,
customClass: 'global-notification',
})
return Promise.reject(data)
}
}
const responseErrorInterceptor = (error: AxiosError) => {
// 响应失败关闭loading
hideLoading()
// 分类处理不同类型的网络错误
let errMsg = ''
if (error.message === 'Network Error') {
errMsg = '网络异常,请检查您的网络连接!'
} else if (error.code === 'ECONNABORTED') {
errMsg = `请求超时!请求耗时超过 ${timeout / 1000}`
} else if (error.response) {
// 有响应状态码的错误
const status = error.response.status
switch (status) {
case 401:
errMsg = '登录状态过期,请重新登录!'
// 可在此处添加跳转到登录页的逻辑
break
case 403:
errMsg = '您没有权限访问该资源!'
break
case 404:
errMsg = '请求的资源不存在!'
break
case 500:
errMsg = '服务器内部错误,请稍后重试!'
break
default:
errMsg = (error.response?.data as ApiResponse)?.msg || `请求失败:${status}`
}
} else {
errMsg = error.message || '服务器错误'
}
ElNotification({
message: errMsg,
type: 'error',
duration: 2000,
customClass: 'global-notification',
})
return Promise.reject(error)
}
service.interceptors.response.use(responseInterceptor, responseErrorInterceptor)
// 5. 封装通用请求方法
export const request = {
get<T = unknown>(url: string, params?: unknown, config?: InternalAxiosRequestConfig): Promise<T> {
return service.get<ApiResponse<T>>(url, { params, ...config }).then((res) => {
return res as T
})
},
post<T = unknown>(url: string, data?: unknown, config?: InternalAxiosRequestConfig): Promise<T> {
return service.post<ApiResponse<T>>(url, data, config).then((res) => {
return res as T
})
},
}
export default service