Files
ApiServer-Web-admin_dashboa…/src/utils/request.js
T
lin d650bfeb61
Build and Deploy Vue3 / build (push) Successful in 1m15s
Build and Deploy Vue3 / deploy (push) Successful in 54s
style: 对商品管理页面进行名称样式修改
2026-03-13 14:07:09 +08:00

282 lines
8.8 KiB
JavaScript

import axios from 'axios'
import { ElMessage } from 'element-plus'
import router from '@/router'
import {getRefreshToken,refreshAccessToken} from "@/api/login.js";
import { baseUrl, acsBaseUrl, noAuthUrls as noAuthUrlList, requestTimeout, acsRequestTimeout, TOKEN_KEY, TOKEN_EXPIRE_KEY, USER_INFO_KEY } from '@/config/env.js'
// 检查URL是否需要认证
const urlNeedAuth = (url) => {
return !noAuthUrlList.some(noAuthUrl => url.includes(noAuthUrl))
}
// 检查token是否过期
const isTokenExpired = () => {
const token = localStorage.getItem(TOKEN_KEY)
const expire = localStorage.getItem(TOKEN_EXPIRE_KEY)
if (!token) return true
// 检查过期时间
if (expire) {
const expireTime = parseInt(expire) * 1000 // 转换为毫秒
const now = Date.now()
return now >= expireTime
}
// 没有过期时间时,默认认为Token已过期(因为无法验证有效性)
return true
}
// 检查token是否即将过期(5分钟内)
const isTokenExpiringSoon = () => {
const expire = localStorage.getItem(TOKEN_EXPIRE_KEY)
if (!expire) return false
const expireTime = parseInt(expire) * 1000 // 转换为毫秒
const now = Date.now()
const fiveMinutes = 5 * 60 * 1000 // 5分钟
// 如果已过期,返回false(由isTokenExpired处理)
if (now >= expireTime) return false
// 如果在5分钟内过期,返回true
return (expireTime - now) <= fiveMinutes
}
// 正在刷新token的标志
let isRefreshing = false
// 等待刷新token的请求队列
let refreshSubscribers = []
// 添加请求到队列
const subscribeTokenRefresh = (callback) => {
refreshSubscribers.push(callback)
}
// 刷新token后执行队列中的请求
const onTokenRefreshed = (newToken) => {
refreshSubscribers.forEach(callback => callback(newToken))
refreshSubscribers = []
}
// 执行token刷新
const doRefreshToken = async () => {
try {
const domain = window.location.hostname
// 获取交换token
const refreshTokenRes = await getRefreshToken(domain,{
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`
}
})
if (refreshTokenRes.data?.code === 200 && refreshTokenRes.data?.data?.refresh_token) {
// 使用交换token获取新的access token
const newTokenRes = await refreshAccessToken(refreshTokenRes.data.data.refresh_token)
if (newTokenRes.data?.code === 200 && newTokenRes.data?.data?.token) {
const { token, expire } = newTokenRes.data.data
localStorage.setItem(TOKEN_KEY, token)
if (expire) {
localStorage.setItem(TOKEN_EXPIRE_KEY, expire.toString())
}
return token
}
}
// 刷新失败,触发登出逻辑
localStorage.removeItem(TOKEN_KEY)
localStorage.removeItem(TOKEN_EXPIRE_KEY)
localStorage.removeItem(USER_INFO_KEY)
ElMessage.warning('登录过期,请重新登录')
router.push('/login')
return null
} catch (error) {
console.error('Token刷新失败:', error)
// 刷新失败,触发登出逻辑
localStorage.removeItem(TOKEN_KEY)
localStorage.removeItem(TOKEN_EXPIRE_KEY)
localStorage.removeItem(USER_INFO_KEY)
ElMessage.warning('登录过期,请重新登录')
router.push('/login')
return null
}
}
class Request {
constructor(config = {}) {
// 创建 axios 实例
this.instance = axios.create({
baseURL: config.baseURL || '',
timeout: config.timeout || 5000,
headers: config.headers || {}
})
// 请求拦截器
this.instance.interceptors.request.use(
(config) => {
// 在发送请求之前做些什么
// 例如:添加 token
const token = localStorage.getItem(TOKEN_KEY)
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
(error) => {
return Promise.reject(error)
}
)
// 响应拦截器
this.instance.interceptors.response.use(
(response) => {
// 对响应数据做点什么
return response.data
},
(error) => {
// 对响应错误做点什么
// if (error.response) {
// switch (error.response.status) {
// case 401:
// // 未授权,可以在这里处理登出逻辑
// break
// case 403:
// // 禁止访问
// break
// case 404:
// // 未找到
// break
// case 500:
// // 服务器错误
// break
// }
// }
return error.response
}
)
}
// GET 请求
get(url, params = {}, config = {}) {
return this.instance.get(url, { params, ...config })
}
// POST 请求
post(url, data = {}, config = {}) {
return this.instance.post(url, data, config)
}
// PUT 请求
put(url, data = {}, config = {}) {
return this.instance.put(url, data, config)
}
// DELETE 请求
delete(url, config = {}) {
return this.instance.delete(url, config)
}
// PATCH 请求
patch(url, data = {}, config = {}) {
return this.instance.patch(url, data, config)
}
}
// 创建默认实例
const request = new Request({
baseURL: baseUrl,
timeout: requestTimeout,
headers: {
'Content-Type': 'multipart/form-data'
}
})
export const mainUrl = baseUrl + '/acs'
export const baseURL = baseUrl
export const http2 = axios.create({
baseURL: baseUrl,
timeout: acsRequestTimeout,
headers: {},
});
http2.interceptors.request.use(async config => {
const token = localStorage.getItem(TOKEN_KEY)
// 检查是否需要认证
if (urlNeedAuth(config.url)) {
// 检查token是否已过期
if (isTokenExpired()) {
if (token) {
localStorage.removeItem(TOKEN_KEY)
localStorage.removeItem(TOKEN_EXPIRE_KEY)
localStorage.removeItem(USER_INFO_KEY)
ElMessage.warning('登录过期,请重新登录')
}
router.push('/login')
return Promise.reject(new Error('Token已过期'))
}
// 检查token是否即将过期,进行无感刷新
if (isTokenExpiringSoon() && !isRefreshing) {
isRefreshing = true
try {
const newToken = await doRefreshToken()
if (newToken) {
console.log('Token已无感刷新')
onTokenRefreshed(newToken)
config.headers.Authorization = `Bearer ${newToken}`
} else {
// 刷新失败,doRefreshToken已处理登出逻辑,直接拒绝请求
return Promise.reject(new Error('Token刷新失败'))
}
} catch (error) {
console.error('Token刷新异常:', error)
// 刷新异常,doRefreshToken已处理登出逻辑,直接拒绝请求
return Promise.reject(error)
} finally {
isRefreshing = false
}
} else if (isRefreshing) {
// 正在刷新,等待刷新完成
return new Promise((resolve, reject) => {
subscribeTokenRefresh((newToken) => {
if (newToken) {
config.headers.Authorization = `Bearer ${newToken}`
// 重新发送原始请求
resolve(config)
} else {
reject(new Error('Token刷新失败'))
}
})
})
} else {
// 正常情况,直接使用token
config.headers.Authorization = `Bearer ${token}`
}
}
// 不需要认证的请求,不添加token
return config
})
http2.interceptors.response.use(
response => response, // 正常响应时直接返回
error => {
console.log('出现错误', error.response);
if (error.response == undefined) {
ElMessage.error("服务器错误,请稍后再试");
return Promise.reject(error);
}
const { status } = error.response;
if (status === 401) {
localStorage.removeItem(TOKEN_KEY);
ElMessage.warning('登陆过期,请重新登陆')
router.push('/login')
return Promise.reject();
}
// 其他错误处理(可选)
return Promise.reject(error);
}
);
export default request