153 lines
4.4 KiB
TypeScript
153 lines
4.4 KiB
TypeScript
import axios, { type AxiosInstance, type AxiosRequestConfig, type AxiosResponse } from 'axios'
|
|
import NProgress from 'nprogress'
|
|
import CryptoJS from 'crypto-js'
|
|
import { useUserStore } from '~/stores/user'
|
|
import message from '~/utils/message'
|
|
import loading from '~/utils/loading'
|
|
import { InternalAxiosRequestConfig } from 'axios'
|
|
|
|
interface CustomRequestConfig extends InternalAxiosRequestConfig {
|
|
showLoading?: boolean,
|
|
showError?: boolean
|
|
}
|
|
|
|
interface RequestConfig extends AxiosRequestConfig {
|
|
showLoading?: boolean,
|
|
showError?: boolean
|
|
}
|
|
|
|
interface ApiResponse<T = any> {
|
|
code: number
|
|
message: string
|
|
data: T
|
|
}
|
|
|
|
class Request {
|
|
private instance: AxiosInstance
|
|
|
|
constructor(config: AxiosRequestConfig) {
|
|
this.instance = axios.create(config)
|
|
|
|
// Request interceptor
|
|
this.instance.interceptors.request.use(
|
|
(config: InternalAxiosRequestConfig & { showLoading?: boolean }) => {
|
|
const customConfig = config as CustomRequestConfig
|
|
|
|
// NProgress runs by default for visual feedback
|
|
NProgress.start()
|
|
|
|
// Full screen blocking loading controlled by showLoading parameter
|
|
if (customConfig.showLoading) {
|
|
loading.show()
|
|
}
|
|
|
|
const userStore = useUserStore()
|
|
customConfig.headers = customConfig.headers || {}
|
|
|
|
if (userStore.token) {
|
|
customConfig.headers['Authorization'] = 'Bearer ' + userStore.token
|
|
}
|
|
// Add Signature
|
|
const timestamp = Date.now().toString()
|
|
const secret = import.meta.env.VITE_API_SECRET || ''
|
|
const sign = CryptoJS.MD5(timestamp + secret).toString()
|
|
customConfig.headers['X-App-Sign'] = sign
|
|
customConfig.headers['X-App-Timestamp'] = timestamp
|
|
return customConfig
|
|
},
|
|
(error) => {
|
|
return Promise.reject(error)
|
|
}
|
|
)
|
|
|
|
// Response interceptor
|
|
this.instance.interceptors.response.use(
|
|
(response: AxiosResponse<ApiResponse>) => {
|
|
const config = response.config as CustomRequestConfig
|
|
|
|
NProgress.done()
|
|
|
|
if (config.showLoading) {
|
|
loading.hide()
|
|
}
|
|
|
|
const res = response.data
|
|
if (res.code === 200) {
|
|
return res.data
|
|
} else {
|
|
if (config.showError !== false) {
|
|
message.error(res.message || 'Error')
|
|
}
|
|
return Promise.reject(new Error(res.message || 'Error'))
|
|
}
|
|
},
|
|
(error) => {
|
|
const config = error.config as CustomRequestConfig
|
|
|
|
NProgress.done()
|
|
|
|
if (config?.showLoading) {
|
|
loading.hide()
|
|
}
|
|
|
|
let msg = 'Network Error'
|
|
if (error.response) {
|
|
// 优先使用后端返回的错误信息
|
|
const backendMsg = error.response.data?.message
|
|
|
|
switch (error.response.status) {
|
|
case 401:
|
|
msg = backendMsg || 'Unauthorized'
|
|
const userStore = useUserStore()
|
|
userStore.clearToken()
|
|
userStore.clearUserInfo()
|
|
window.location.href= '/'
|
|
// userStore.logout()
|
|
// router push login?
|
|
break
|
|
case 403:
|
|
msg = backendMsg || 'Forbidden'
|
|
break
|
|
case 404:
|
|
msg = backendMsg || 'Not Found'
|
|
break
|
|
case 429:
|
|
msg = backendMsg || 'Too Many Requests'
|
|
break
|
|
case 500:
|
|
msg = backendMsg || 'Internal Server Error'
|
|
break
|
|
default:
|
|
msg = backendMsg || `Error: ${error.response.status}`
|
|
}
|
|
} else if (error.request) {
|
|
msg = 'No response from server'
|
|
}
|
|
|
|
if (config?.showError !== false) {
|
|
message.error(msg)
|
|
}
|
|
|
|
return Promise.reject(error)
|
|
}
|
|
)
|
|
}
|
|
|
|
request<T = any>(config: RequestConfig): Promise<T> {
|
|
return this.instance.request(config)
|
|
}
|
|
|
|
get<T = any>(url: string, config?: RequestConfig): Promise<T> {
|
|
return this.instance.get(url, config)
|
|
}
|
|
|
|
post<T = any>(url: string, data?: any, config?: RequestConfig): Promise<T> {
|
|
return this.instance.post(url, data, config)
|
|
}
|
|
}
|
|
|
|
export default new Request({
|
|
baseURL: import.meta.env.VITE_API_BASE_URL,
|
|
timeout: 100000,
|
|
})
|