智慧教务系统
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

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()