370 lines
11 KiB
TypeScript
370 lines
11 KiB
TypeScript
import useUserStore from '../store/user'
|
||
import { getrefreshtoken } from '@/apis/longin'
|
||
import { getToken, setToken, removeToken } from '@/utils/auth'
|
||
import { goToPage, checkConfigFileExists, readConfigFile, createDefaultConfig } from '@/utils/util'
|
||
interface Response {
|
||
code: number
|
||
data: object
|
||
msg: string
|
||
}
|
||
// let appVersion = ''
|
||
// plus.runtime.getProperty(plus.runtime.appid, (info) => {
|
||
// appVersion = info.version
|
||
// })
|
||
// let newhttp = '' //请求ip
|
||
// let newpro = '' //请求端口
|
||
let newhttp = import.meta.env.VITE_APP_BASE_URL
|
||
let newpro = import.meta.env.VITE_APP_BASE_URLpro
|
||
let newapi = import.meta.env.VITE_APP_BASE_PRE //请求的api开头
|
||
let htpurl = '' //正式的加htp的
|
||
// #ifdef APP-PLUS
|
||
const configFileName = 'app_config.json'
|
||
//const configFilePath = `${plus.io.PRIVATE_DOC}/${configFileName}`; // 私有目录
|
||
// 或
|
||
const configFilePath = `${plus.io.PUBLIC_DOWNLOADS}/${configFileName}` // 公共目录
|
||
|
||
async function handleConfigFile() {
|
||
try {
|
||
const exists = await checkConfigFileExists(configFilePath, configFileName)
|
||
if (exists) {
|
||
const config = await readConfigFile(configFilePath, configFileName)
|
||
console.log('读取配置:', config)
|
||
newhttp = config.url
|
||
newpro = config.pro
|
||
const userStore = useUserStore()
|
||
userStore.seturlip(newhttp)
|
||
userStore.seturlpro(newpro)
|
||
userStore.setallurl(newhttp + ':' + newpro + newapi)
|
||
return config
|
||
} else {
|
||
newhttp = import.meta.env.VITE_APP_BASE_URL
|
||
newpro = import.meta.env.VITE_APP_BASE_URLpro
|
||
const userStore = useUserStore()
|
||
userStore.seturlip(newhttp)
|
||
userStore.seturlpro(newpro)
|
||
userStore.setallurl(newhttp + ':' + newpro + newapi)
|
||
const defaultConfig = await createDefaultConfig(configFilePath, configFileName, { url: newhttp, pro: newpro, Version: 'appVersion' })
|
||
console.log('生成默认配置:', defaultConfig)
|
||
return defaultConfig
|
||
}
|
||
} catch (err) {
|
||
newhttp = import.meta.env.VITE_APP_BASE_URL
|
||
newpro = import.meta.env.VITE_APP_BASE_URLpro
|
||
console.error('操作失败:', err)
|
||
throw err
|
||
}
|
||
}
|
||
// 调用示例
|
||
// handleConfigFile().then((config) => {
|
||
// // 使用配置数据
|
||
// //debugger
|
||
// })
|
||
// #endif
|
||
|
||
const requestObj = {
|
||
isLock: false,
|
||
methodstr: 'POST',
|
||
http({ url = '', data = {}, isPost = '0', isSinglePost = false, isToken = true, timeout = 20000 }) {
|
||
const _this = this
|
||
const userStore = useUserStore()
|
||
return new Promise(function (resolve, reject) {
|
||
if (isSinglePost && _this.isLock) {
|
||
reject({ message: '加载中' })
|
||
}
|
||
|
||
// debugger
|
||
_this.isLock = true
|
||
|
||
//debugger
|
||
// console.log('lsdfcs', userStore)
|
||
const header = {
|
||
'content-type': 'application/json',
|
||
// 'content-type': 'application/x-www-form-urlencoded',
|
||
// Authorization: ''
|
||
Bearerauth: ''
|
||
}
|
||
// if (isPost == '3') {
|
||
// header['content-type'] = 'application/x-www-form-urlencoded'
|
||
// }
|
||
if (getToken() && isToken) {
|
||
// header['Authorization'] = 'Bearer ' + userStore.token
|
||
header['Bearerauth'] = 'Bearer ' + getToken()
|
||
}
|
||
|
||
let htconfig = { url: url, data: data, isPost: isPost, isSinglePost: isSinglePost, isToken: isToken, timeout: timeout, header: header }
|
||
// #ifdef H5
|
||
// debugger
|
||
url = import.meta.env.VITE_APP_BASE_PRE + url
|
||
// #endif
|
||
// debugger
|
||
// #ifndef H5
|
||
// url = import.meta.env.VITE_APP_BASE_URL + ':' + import.meta.env.VITE_APP_BASE_URLpro + import.meta.env.VITE_APP_BASE_PRE + url
|
||
// url = import.meta.env.VITE_APP_BASE_URL + ':' + import.meta.env.VITE_APP_BASE_URLpro + newapi + url
|
||
// if (!userStore.allurl) {
|
||
// newhttp = import.meta.env.VITE_APP_BASE_URL
|
||
// newpro = import.meta.env.VITE_APP_BASE_URLpro
|
||
// userStore.seturlip(newhttp)
|
||
// userStore.seturlpro(newpro)
|
||
// userStore.setallurl(newhttp + ':' + newpro + newapi)
|
||
// }
|
||
|
||
userStore.seturlip(newhttp)
|
||
userStore.seturlpro(newpro)
|
||
userStore.setallurl(newhttp + ':' + newpro + newapi)
|
||
|
||
url = userStore.allurl + url
|
||
|
||
console.log('11111', url)
|
||
// #endif
|
||
|
||
// debugger
|
||
switch (isPost) {
|
||
case '0':
|
||
_this.methodstr = 'POST'
|
||
break
|
||
case '1':
|
||
_this.methodstr = 'GET'
|
||
break
|
||
case '2':
|
||
_this.methodstr = 'put'
|
||
break
|
||
default:
|
||
_this.methodstr = 'DELETE'
|
||
// 当 expression 不匹配任何 case 时执行的代码
|
||
}
|
||
// uni.showLoading({
|
||
// title: '加载中...',
|
||
// duration: 15000 // 可根据实用场景设置时间
|
||
// })
|
||
|
||
uni.request({
|
||
url,
|
||
header,
|
||
data,
|
||
// method: isPost ? 'POST' : 'GET',
|
||
method: _this.methodstr,
|
||
timeout: timeout,
|
||
success(res) {
|
||
// debugger
|
||
// uni.hideLoading()
|
||
// console.log('55dfdsfsd5', res.data)
|
||
res.data.code = res.data.code == 'ok' ? 200 : res.data.code
|
||
const data = res.data as Response
|
||
if (res.statusCode === 200) {
|
||
if (data.code == 200) {
|
||
// 成功
|
||
resolve(res.data)
|
||
} else if (data.code == 401) {
|
||
// uni.reLaunch({
|
||
// url: '/pages/login/login'
|
||
// })
|
||
} else if (data.code == 601) {
|
||
// userStore.setrefreshtoken()
|
||
// uni.showToast({ title: data.msg, icon: 'none' })
|
||
// setTimeout(() => {
|
||
// goToPage({
|
||
// url: 'pages/login/login',
|
||
// mode: 'navigateTo'
|
||
// })
|
||
// }, 1000)
|
||
return handleTokenRefresh(htconfig)
|
||
.then((result) => resolve(result))
|
||
.catch((error) => reject(error))
|
||
} else {
|
||
uni.showToast({ title: data.msg, icon: 'none' })
|
||
reject(res.data)
|
||
}
|
||
} else {
|
||
uni.showToast({ title: '响应错误', icon: 'none' })
|
||
reject({ message: '响应错误' })
|
||
}
|
||
},
|
||
fail(err) {
|
||
// uni.hideLoading()
|
||
console.log('fail', err)
|
||
uni.showToast({ title: '网络错误', icon: 'none' })
|
||
reject({ message: '网络错误' })
|
||
},
|
||
complete() {
|
||
// uni.hideLoading()
|
||
_this.isLock = false
|
||
}
|
||
})
|
||
})
|
||
}
|
||
}
|
||
|
||
// 请求队列与刷新状态管理
|
||
let requestQueue: any = []
|
||
let isRefreshing = false
|
||
let tokenRefreshPromise: any = null
|
||
|
||
/**
|
||
* 处理Token刷新和请求重试的核心函数
|
||
* @param {Object} config - 原始请求配置
|
||
* @returns {Promise} - 返回处理结果
|
||
*/
|
||
const handleTokenRefresh = (config: any) => {
|
||
const userStore = useUserStore()
|
||
return new Promise((resolve, reject) => {
|
||
// 将当前请求添加到队列
|
||
|
||
requestQueue.push({
|
||
resolve,
|
||
reject,
|
||
config
|
||
})
|
||
|
||
// 如果已经在刷新token,则不再重复刷新
|
||
if (tokenRefreshPromise) {
|
||
return
|
||
}
|
||
|
||
// 如果没有在刷新token,则开始刷新
|
||
if (!isRefreshing) {
|
||
isRefreshing = true
|
||
|
||
const rtoken = userStore.refreshtoken
|
||
if (!rtoken) {
|
||
// 没有刷新token,直接跳转登录
|
||
resetQueueAndRefreshState(new Error('未找到刷新token'))
|
||
showLoginConfirm()
|
||
reject(new Error('未找到刷新token'))
|
||
|
||
return
|
||
}
|
||
|
||
// 创建token刷新Promise并保存引用
|
||
tokenRefreshPromise = getrefreshtoken(rtoken)
|
||
.then((response: any) => {
|
||
// const { data = {} } = response;
|
||
// const { accessToken, refreshToken: newRefreshToken } = data;
|
||
// debugger
|
||
let accessToken = response.data
|
||
|
||
if (!accessToken) {
|
||
throw new Error('刷新token失败:未获取到新token')
|
||
}
|
||
|
||
// 更新存储的token
|
||
// setToken(accessToken);
|
||
// uni.setStorageSync(appConfig.cache_key.RefreshToken, newRefreshToken);
|
||
|
||
setToken(accessToken)
|
||
// userInfo.value.token = response
|
||
|
||
// 处理队列中的所有请求
|
||
return processQueue(accessToken)
|
||
})
|
||
.catch((error) => {
|
||
console.log('token刷新失败', error)
|
||
// 通知队列中所有请求失败
|
||
resetQueueAndRefreshState(error || new Error('无效的会话,或者会话已过期,请重新登录'))
|
||
// 显示登录确认对话框
|
||
showLoginConfirm()
|
||
throw error // 继续传递错误
|
||
})
|
||
.finally(() => {
|
||
// 重置刷新状态和Promise引用
|
||
tokenRefreshPromise = null
|
||
isRefreshing = false
|
||
})
|
||
}
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 处理队列中的所有请求
|
||
* @param {String} newToken - 新的访问令牌
|
||
* @returns {Promise} - 返回处理结果
|
||
*/
|
||
const processQueue = async (newToken) => {
|
||
const currentQueue = [...requestQueue] // 复制当前队列,防止处理过程中队列变化
|
||
requestQueue = [] // 清空队列
|
||
|
||
try {
|
||
// 确保token已完全保存后再处理请求
|
||
await new Promise((resolve) => setTimeout(resolve, 50))
|
||
|
||
// 依次处理队列中的请求
|
||
const results = await Promise.allSettled(
|
||
currentQueue.map(async ({ resolve, reject, config }) => {
|
||
console.log('222222222222', config)
|
||
|
||
try {
|
||
// 更新请求头中的token
|
||
config.header['Bearerauth'] = 'Bearer ' + newToken
|
||
|
||
// 重新发起请求
|
||
const result = await requestObj.http({
|
||
...config,
|
||
retryCount: (config.retryCount || 0) + 1
|
||
})
|
||
|
||
// 使用setTimeout确保异步操作完成
|
||
setTimeout(() => resolve(result), 0)
|
||
return result
|
||
} catch (error) {
|
||
console.error('重试请求失败:', error)
|
||
reject(error)
|
||
throw error
|
||
}
|
||
})
|
||
)
|
||
|
||
return results
|
||
} catch (error) {
|
||
console.error('队列处理失败:', error)
|
||
throw error
|
||
} finally {
|
||
isRefreshing = false
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 重置队列和刷新状态
|
||
* @param {Error} error - 错误信息
|
||
*/
|
||
const resetQueueAndRefreshState = (error: any) => {
|
||
// 通知队列中所有请求失败
|
||
requestQueue.forEach(({ reject }) => {
|
||
reject(error)
|
||
})
|
||
|
||
// 重置队列和刷新状态
|
||
requestQueue = []
|
||
isRefreshing = false
|
||
tokenRefreshPromise = null
|
||
}
|
||
|
||
/**
|
||
* 显示登录确认对话框
|
||
*/
|
||
const showLoginConfirm = () => {
|
||
// this.$modal.confirm('确定要退出系统吗?').then(() => {
|
||
// this.userStore.userlogOut();
|
||
// });
|
||
// showConfirmh('未登录或登录状态过期,您可以继续留在该页面,或者重新登录?').then((res: any) => {
|
||
// if (res.confirm) {
|
||
// uni.navigateTo({
|
||
// url: '/pages/login'
|
||
// })
|
||
// }
|
||
// })
|
||
|
||
uni.showModal({
|
||
title: '系统提示',
|
||
content: '登录状态过期,您可以继续留在该页面,或者重新登录?',
|
||
cancelText: '取消',
|
||
confirmText: '确定',
|
||
success: function (res) {
|
||
if (res.confirm) {
|
||
uni.reLaunch({
|
||
url: '/pages/login/login'
|
||
})
|
||
}
|
||
}
|
||
})
|
||
}
|
||
export const request = requestObj
|