You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
273 lines
7.1 KiB
273 lines
7.1 KiB
import axios, { HttpStatusCode } from 'axios'
|
|
import type {
|
|
AxiosInstance,
|
|
InternalAxiosRequestConfig,
|
|
AxiosResponse,
|
|
AxiosRequestConfig,
|
|
} from 'axios'
|
|
import { getToken, isUrl } from './common'
|
|
import { ElMessage } from 'element-plus'
|
|
import type { MessageParams } from 'element-plus'
|
|
import useUserStore from '@/stores/modules/user'
|
|
import storage from '@/utils/storage'
|
|
|
|
interface RequestConfig extends AxiosRequestConfig {
|
|
showErrorMessage?: boolean
|
|
showSuccessMessage?: boolean
|
|
}
|
|
|
|
interface InternalRequestConfig extends InternalAxiosRequestConfig {
|
|
showErrorMessage?: boolean
|
|
showSuccessMessage?: boolean
|
|
}
|
|
|
|
interface requestResponse extends AxiosResponse {
|
|
config: InternalRequestConfig
|
|
}
|
|
|
|
class Request {
|
|
private instance: AxiosInstance
|
|
|
|
constructor() {
|
|
this.instance = axios.create({
|
|
baseURL: typeof import.meta.env.VITE_APP_BASE_URL === 'string' && import.meta.env.VITE_APP_BASE_URL.length > 0
|
|
? (import.meta.env.VITE_APP_BASE_URL.substr(-1) == '/'
|
|
? import.meta.env.VITE_APP_BASE_URL
|
|
: `${import.meta.env.VITE_APP_BASE_URL}/`)
|
|
: '/',
|
|
timeout: 0,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
lang: storage.get('lang') ?? 'zh-cn',
|
|
},
|
|
})
|
|
|
|
// 全局请求拦截器
|
|
this.instance.interceptors.request.use(
|
|
(config: InternalRequestConfig) => {
|
|
// 携带token
|
|
if (getToken()) {
|
|
config.headers[import.meta.env.VITE_REQUEST_HEADER_TOKEN_KEY] =
|
|
getToken()
|
|
}
|
|
return config
|
|
},
|
|
(err: any) => {
|
|
return Promise.reject(err)
|
|
}
|
|
)
|
|
|
|
// 全局响应拦截器
|
|
this.instance.interceptors.response.use(
|
|
(response: requestResponse) => {
|
|
if (response.request.responseType != 'blob') {
|
|
const res = response.data
|
|
if (res.code != 1) {
|
|
this.handleAuthError(res.code)
|
|
if (res.code != 401 && response.config.showErrorMessage !== false)
|
|
this.showElMessage({
|
|
message: res.msg,
|
|
type: 'error',
|
|
dangerouslyUseHTMLString: true,
|
|
duration: 5000,
|
|
})
|
|
return Promise.reject(new Error(res.msg || 'Error'))
|
|
} else {
|
|
if (response.config.showSuccessMessage)
|
|
ElMessage({ message: res.msg, type: 'success' })
|
|
return res
|
|
}
|
|
}
|
|
return response.data
|
|
},
|
|
(err: any) => {
|
|
this.handleNetworkError(err)
|
|
return Promise.reject(err)
|
|
}
|
|
)
|
|
}
|
|
|
|
/**
|
|
* 发送get请求
|
|
* @param url
|
|
* @param config
|
|
* @returns
|
|
*/
|
|
public get<T = any, R = AxiosResponse<T>>(
|
|
url: string,
|
|
config?: RequestConfig
|
|
): Promise<R> {
|
|
if (!url || typeof url !== 'string') {
|
|
console.error('Invalid URL for GET request:', url)
|
|
return Promise.reject(new Error('Invalid URL'))
|
|
}
|
|
try {
|
|
return this.instance.get(url, config)
|
|
} catch (error) {
|
|
console.error('Error in GET request:', error)
|
|
return Promise.reject(error)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 发送post请求
|
|
* @param url
|
|
* @param data
|
|
* @param config
|
|
* @returns
|
|
*/
|
|
public post<T = any, R = AxiosResponse<T>, D = any>(
|
|
url: string,
|
|
data?: D,
|
|
config?: RequestConfig
|
|
): Promise<R> {
|
|
if (!url || typeof url !== 'string') {
|
|
console.error('Invalid URL for POST request:', url)
|
|
return Promise.reject(new Error('Invalid URL'))
|
|
}
|
|
try {
|
|
return this.instance.post(url, data, config)
|
|
} catch (error) {
|
|
console.error('Error in POST request:', error)
|
|
return Promise.reject(error)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 发送get请求
|
|
* @param url
|
|
* @param config
|
|
* @returns
|
|
*/
|
|
public put<T = any, R = AxiosResponse<T>, D = any>(
|
|
url: string,
|
|
data?: D,
|
|
config?: RequestConfig
|
|
): Promise<R> {
|
|
return this.instance.put(url, data, config)
|
|
}
|
|
|
|
/**
|
|
* 发送get请求
|
|
* @param url
|
|
* @param config
|
|
* @returns
|
|
*/
|
|
public delete<T = any, R = AxiosResponse<T>>(
|
|
url: string,
|
|
config?: RequestConfig
|
|
): Promise<R> {
|
|
return this.instance.delete(url, config)
|
|
}
|
|
|
|
/**
|
|
* 处理网络请求错误
|
|
* @param err
|
|
*/
|
|
private handleNetworkError(err: any) {
|
|
let errMessage = ''
|
|
|
|
if (err.response && err.response.status) {
|
|
const errStatus = err.response.status
|
|
switch (errStatus) {
|
|
case 400:
|
|
errMessage = '请求错误'
|
|
break
|
|
case 401:
|
|
errMessage = '未授权,请登录'
|
|
break
|
|
case 403:
|
|
errMessage = '拒绝访问'
|
|
break
|
|
case 404:
|
|
let baseURL = '';
|
|
try {
|
|
baseURL = err.response.config.baseURL && typeof err.response.config.baseURL === 'string'
|
|
? (isUrl(err.response.config.baseURL)
|
|
? err.response.config.baseURL
|
|
: `${location.origin}${err.response.config.baseURL}`)
|
|
: location.origin;
|
|
} catch (e) {
|
|
baseURL = location.origin;
|
|
}
|
|
errMessage = baseURL + '请求地址出错'
|
|
break
|
|
case 405:
|
|
errMessage = '请求方法未允许'
|
|
break
|
|
case 408:
|
|
errMessage = '请求超时'
|
|
break
|
|
case 409:
|
|
errMessage = '资源冲突'
|
|
break
|
|
case 500:
|
|
errMessage = '服务器内部错误'
|
|
break
|
|
case 501:
|
|
errMessage = '服务未实现'
|
|
break
|
|
case 502:
|
|
errMessage = '网关错误'
|
|
break
|
|
case 503:
|
|
errMessage = '服务不可用'
|
|
break
|
|
case 504:
|
|
errMessage = '网关超时'
|
|
break
|
|
case 505:
|
|
errMessage = 'HTTP版本不受支持'
|
|
break
|
|
}
|
|
}
|
|
err.message.includes('timeout') && (errMessage = '请求超时')
|
|
if (err.code == 'ERR_NETWORK') {
|
|
let baseURL = '';
|
|
try {
|
|
baseURL = err.config.baseURL && typeof err.config.baseURL === 'string'
|
|
? (isUrl(err.config.baseURL)
|
|
? err.config.baseURL
|
|
: `${location.origin}${err.config.baseURL}`)
|
|
: location.origin;
|
|
} catch (e) {
|
|
baseURL = location.origin;
|
|
}
|
|
errMessage = baseURL + '请求地址出错'
|
|
}
|
|
errMessage &&
|
|
this.showElMessage({
|
|
dangerouslyUseHTMLString: true,
|
|
duration: 5000,
|
|
message: errMessage,
|
|
type: 'error',
|
|
})
|
|
}
|
|
|
|
private handleAuthError(code: number) {
|
|
switch (code) {
|
|
case 401:
|
|
useUserStore().logout()
|
|
break
|
|
}
|
|
}
|
|
|
|
private messageCache = new Map()
|
|
|
|
private showElMessage(options: MessageParams) {
|
|
// 处理类型问题,安全地获取消息
|
|
const message = typeof options === 'string' ? options : (options as any).message;
|
|
if (!message) return;
|
|
|
|
const cacheKey = message;
|
|
const cachedMessage = this.messageCache.get(cacheKey)
|
|
|
|
if (!cachedMessage || Date.now() - cachedMessage.timestamp > 5000) {
|
|
// 5秒内重复内容不再弹出,可自定义过期时间
|
|
this.messageCache.set(cacheKey, { timestamp: Date.now() })
|
|
ElMessage(options)
|
|
}
|
|
}
|
|
}
|
|
|
|
export default new Request()
|
|
|