147 lines
3.5 KiB
JavaScript
147 lines
3.5 KiB
JavaScript
import axios from 'axios'
|
||
import config from '@/config'
|
||
import { useUserStore } from '@/stores/modules/user'
|
||
import { ElMessage } from 'element-plus'
|
||
import router from '@/router'
|
||
|
||
const http = axios.create({
|
||
timeout: 30000,
|
||
baseURL: config.baseURL
|
||
})
|
||
|
||
// 是否正在刷新 token
|
||
let isRefreshing = false
|
||
// 存储待重试的请求
|
||
let requests = []
|
||
|
||
// 请求拦截器
|
||
http.interceptors.request.use(
|
||
(config) => {
|
||
const userStore = useUserStore()
|
||
const token = userStore.token
|
||
|
||
// 如果有 token,添加到请求头
|
||
if (token) {
|
||
config.headers['Authorization'] = `Bearer ${token}`
|
||
}
|
||
|
||
return config
|
||
},
|
||
(error) => {
|
||
return Promise.reject(error)
|
||
}
|
||
)
|
||
|
||
// 响应拦截器
|
||
http.interceptors.response.use(
|
||
(response) => {
|
||
// 根据后端返回的数据结构进行处理
|
||
// 假设后端返回格式为 { code, data, message }
|
||
const { code, data, message } = response.data
|
||
|
||
// 请求成功
|
||
if (code === 200 || code === 0) {
|
||
return data
|
||
}
|
||
|
||
// 其他错误码处理
|
||
ElMessage.error(message || '请求失败')
|
||
return Promise.reject(new Error(message || '请求失败'))
|
||
},
|
||
async (error) => {
|
||
const userStore = useUserStore()
|
||
const { response } = error
|
||
|
||
// 无响应(网络错误、超时等)
|
||
if (!response) {
|
||
ElMessage.error('网络错误,请检查网络连接')
|
||
return Promise.reject(error)
|
||
}
|
||
|
||
const { status, data } = response
|
||
|
||
// 401 未授权 - token 过期或无效
|
||
if (status === 401) {
|
||
// 如果正在刷新 token,将请求加入队列
|
||
if (isRefreshing) {
|
||
return new Promise((resolve) => {
|
||
requests.push((token) => {
|
||
// 重新设置请求头
|
||
error.config.headers['Authorization'] = `Bearer ${token}`
|
||
resolve(http(error.config))
|
||
})
|
||
})
|
||
}
|
||
|
||
// 标记正在刷新
|
||
isRefreshing = true
|
||
|
||
try {
|
||
// 尝试刷新 token
|
||
const newToken = await refreshToken()
|
||
|
||
// 刷新成功,更新 token
|
||
userStore.setToken(newToken)
|
||
|
||
// 执行队列中的所有请求
|
||
requests.forEach((callback) => callback(newToken))
|
||
requests = []
|
||
|
||
// 重新执行当前请求
|
||
error.config.headers['Authorization'] = `Bearer ${newToken}`
|
||
return http(error.config)
|
||
} catch (refreshError) {
|
||
// 刷新失败,清空队列并跳转登录页
|
||
requests = []
|
||
userStore.logout()
|
||
router.push('/login')
|
||
ElMessage.error('登录已过期,请重新登录')
|
||
return Promise.reject(refreshError)
|
||
} finally {
|
||
isRefreshing = false
|
||
}
|
||
}
|
||
|
||
// 403 禁止访问
|
||
if (status === 403) {
|
||
ElMessage.error('没有权限访问该资源')
|
||
return Promise.reject(error)
|
||
}
|
||
|
||
// 404 资源不存在
|
||
if (status === 404) {
|
||
ElMessage.error('请求的资源不存在')
|
||
return Promise.reject(error)
|
||
}
|
||
|
||
// 500 服务器错误
|
||
if (status >= 500) {
|
||
ElMessage.error('服务器错误,请稍后重试')
|
||
return Promise.reject(error)
|
||
}
|
||
|
||
// 其他错误
|
||
const errorMessage = data?.message || error.message || '请求失败'
|
||
ElMessage.error(errorMessage)
|
||
return Promise.reject(error)
|
||
}
|
||
)
|
||
|
||
// 刷新 token 的方法
|
||
async function refreshToken() {
|
||
// 这里需要根据实际的刷新 token 接口进行修改
|
||
// 假设刷新接口是 /auth/refresh
|
||
const refreshUrl = config.baseURL + '/auth/refresh'
|
||
const userStore = useUserStore()
|
||
const refreshTokenValue = userStore.refreshToken
|
||
|
||
const response = await axios.post(refreshUrl, {
|
||
refreshToken: refreshTokenValue
|
||
})
|
||
|
||
// 假设返回格式为 { code, data: { token, refreshToken } }
|
||
return response.data.data.token
|
||
}
|
||
|
||
export default http
|