Files
sionrui/frontend/api/axios/client.js
2026-02-22 20:34:02 +08:00

151 lines
3.5 KiB
JavaScript
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 tokenManager from '@gold/utils/token-manager'
// Token白名单
const WHITE_LIST = [
'/auth/login',
'/auth/send-sms-code',
'/auth/sms-login',
'/auth/validate-sms-code',
'/auth/register',
'/auth/reset-password',
'/auth/refresh-token',
]
function isInWhiteList(url) {
return !!(url && WHITE_LIST.some(path => url.includes(path)))
}
// Token刷新状态管理
let isRefreshing = false
let refreshSubscribers = []
function subscribeTokenRefresh(callback) {
isRefreshing ? refreshSubscribers.push(callback) : callback()
}
function onRefreshed() {
refreshSubscribers.forEach(callback => callback())
refreshSubscribers = []
}
/**
* 创建Axios实例
* @param {Object} options - 配置选项
* @param {string} options.baseURL - 基础URL
* @param {number} options.timeout - 超时时间
* @param {Function} options.on401 - 401错误处理函数
* @param {Function} options.on403 - 403错误处理函数
* @param {Function} options.refreshTokenFn - Token刷新函数
* @returns {AxiosInstance} Axios实例
*/
export default function createClientAxios(options = {}) {
const {
baseURL = '/',
timeout = 180000,
on401 = null,
on403 = null,
refreshTokenFn = null,
} = options
const client = axios.create({
baseURL,
timeout,
})
// 请求拦截器
client.interceptors.request.use((config) => {
const needToken = config.headers?.isToken !== false && !isInWhiteList(config.url || '')
if (!needToken) {
return config
}
const currentToken = tokenManager.getAccessToken()
if (!currentToken) {
return config
}
const isTokenExpired = tokenManager.isExpired(30 * 1000)
if (!isTokenExpired) {
const authHeader = tokenManager.getAuthHeader()
if (authHeader) {
config.headers.Authorization = authHeader
}
return config
}
if (!isRefreshing) {
isRefreshing = true
if (!refreshTokenFn || typeof refreshTokenFn !== 'function') {
isRefreshing = false
onRefreshed()
return config
}
refreshTokenFn().finally(() => {
isRefreshing = false
onRefreshed()
})
}
return new Promise((resolve, reject) => {
subscribeTokenRefresh(() => {
const token = tokenManager.getAccessToken()
if (!token) {
return reject({
message: 'Token已过期需要重新登录',
code: 401,
})
}
config.headers.Authorization = tokenManager.getAuthHeader()
resolve(config)
})
})
})
// 响应拦截器
client.interceptors.response.use(
(response) => {
const data = response.data
if (data && typeof data.code === 'number') {
if (data.code === 0 || data.code === 200) {
return data
}
const error = new Error(data?.message || data?.msg || '请求失败')
error.code = data?.code
error.data = data
if (data.code === 403 && typeof on403 === 'function') {
on403(error)
}
if (data.code === 401 && typeof on401 === 'function') {
on401(error)
}
return Promise.reject(error)
}
return data
},
(error) => {
const status = error.response?.status
const code = error.code
if ((status === 401 || code === 401) && typeof on401 === 'function') {
on401(error)
}
else if ((status === 403 || code === 403) && typeof on403 === 'function') {
on403(error)
}
return Promise.reject(error)
}
)
return client
}