import type { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios' import axios from 'axios' import { ContentTypeEnum, RequestEnum } from '~#/http-enum' import { STORAGE_AUTHORIZE_KEY, useAuthorization } from '~/composables/authorization' import router from '~/router' import { AxiosLoading } from './loading' export interface ResponseBody { code: number data?: T msg: string } export interface RequestConfigExtra { token?: boolean customDev?: boolean loading?: boolean } const instance: AxiosInstance = axios.create({ baseURL: import.meta.env.VITE_APP_BASE_API ?? '/', timeout: 60000, headers: { 'Content-Type': ContentTypeEnum.JSON }, }) const axiosLoading = new AxiosLoading() async function requestHandler(config: InternalAxiosRequestConfig & RequestConfigExtra): Promise { // 处理请求前的url if ( import.meta.env.DEV && import.meta.env.VITE_APP_BASE_API_DEV && import.meta.env.VITE_APP_BASE_URL_DEV ) { // 开发环境统一使用 /api 前缀(由 vite 代理重写为后端真实前缀) config.baseURL = import.meta.env.VITE_APP_BASE_API_DEV } // console.log('Request URL:', config.baseURL) const token = useAuthorization() if (token.value && config.token !== false) config.headers.set(STORAGE_AUTHORIZE_KEY, token.value) // 增加多语言的配置 const { locale } = useI18nLocale() config.headers.set('Accept-Language', locale.value ?? 'zh-CN') if (config.loading) axiosLoading.addLoading() return config } function responseHandler(response: any): ResponseBody | AxiosResponse | Promise | any { return response.data } function errorHandler(error: AxiosError): Promise { const token = useAuthorization() const notification = useNotification() if (error.response) { const { data, status, statusText } = error.response as AxiosResponse if (status === 401) { notification?.error({ message: '401', description: data?.msg || statusText, duration: 3, }) /** * 这里处理清空用户信息和token的逻辑,后续扩展 */ token.value = null const { fullPath, query: { redirect } } = router.currentRoute.value router .push({ path: '/login', query: { redirect: redirect || fullPath, }, }) .then(() => { }) } else if (status === 403) { notification?.error({ message: '403', description: data?.msg || statusText, duration: 3, }) } else if (status === 500) { notification?.error({ message: '500', description: data?.msg || statusText, duration: 3, }) } else { notification?.error({ message: '服务错误', description: data?.msg || statusText, duration: 3, }) } } return Promise.reject(error) } interface AxiosOptions { url: string params?: T data?: T } instance.interceptors.request.use(requestHandler) instance.interceptors.response.use(responseHandler, errorHandler) export default instance function instancePromise(options: AxiosOptions & RequestConfigExtra): Promise> { const { loading } = options return new Promise((resolve, reject) => { instance.request(options).then((res) => { resolve(res as any) }).catch((e: Error | AxiosError) => { reject(e) }).finally(() => { if (loading) axiosLoading.closeLoading() }) }) } export function useGet(url: string, params?: T, config?: AxiosRequestConfig & RequestConfigExtra): Promise> { const options = { url, params, method: RequestEnum.GET, ...config, } return instancePromise(options) } export function usePost(url: string, data?: T, config?: AxiosRequestConfig & RequestConfigExtra): Promise> { const options = { url, data, method: RequestEnum.POST, ...config, } return instancePromise(options) } export function usePut(url: string, data?: T, config?: AxiosRequestConfig & RequestConfigExtra): Promise> { const options = { url, data, method: RequestEnum.PUT, ...config, } return instancePromise(options) } export function useDelete(url: string, data?: T, config?: AxiosRequestConfig & RequestConfigExtra): Promise> { const options = { url, data, method: RequestEnum.DELETE, ...config, } return instancePromise(options) }