import config from './config.js'
|
|
// 创建请求实例
|
const service = {
|
// 默认配置
|
defaults: {
|
headers: {
|
'Content-Type': 'application/json;charset=utf-8',
|
// 'token':uni.getStorageSync('token')
|
},
|
timeout: 100000
|
},
|
|
// 基础URL
|
baseURL: config.apiBaseUrl || ''
|
}
|
|
// 请求拦截器
|
const requestInterceptors = []
|
|
// 响应拦截器
|
const responseInterceptors = []
|
|
addRequestInterceptor(async (cfg) => {
|
const needToken = !(cfg.headers && cfg.headers.isToken === false)
|
if (needToken) {
|
const token = uni.getStorageSync('token')
|
if (!token) {
|
try { uni.reLaunch({ url: '/subpackage/login/login' }) } catch (e) { }
|
return Promise.reject({ message: 'NO_TOKEN' })
|
}
|
cfg.headers = {
|
...(cfg.headers || {}),
|
'Authorization': `Bearer ${token}`
|
}
|
} else {
|
const headers = { ...(cfg.headers || {}) }
|
if ('Authorization' in headers) {
|
delete headers.Authorization
|
}
|
cfg.headers = headers
|
}
|
|
return cfg
|
})
|
let isRedirecting = false;
|
addResponseInterceptor(async (res) => {
|
// 1. 判断HTTP状态码是否为401(核心判断)
|
const is401 = (res.statusCode === 200 && res.data.code === 401 );
|
// 2. 可选:判断业务码是否为401(根据你的后端返回格式调整,比如 res.data.code === 401)
|
// const isBiz401 = res.data && res.data.code === 401;
|
|
if (is401) {
|
// 防重复跳转:如果正在跳转,直接拒绝Promise
|
if (isRedirecting) {
|
return Promise.reject({ message: '已在跳转登录页', statusCode: 401 });
|
}
|
|
isRedirecting = true;
|
|
try {
|
// 清除本地无效Token
|
uni.removeStorageSync('token');
|
// 跳转到登录页(使用reLaunch关闭所有页面,避免返回原页面)
|
await uni.reLaunch({ url: '/subpackage/login/login' });
|
} catch (e) {
|
console.error('401跳转登录失败:', e);
|
} finally {
|
// 重置跳转标识
|
isRedirecting = false;
|
}
|
|
// 拒绝Promise,阻止后续业务逻辑执行
|
return Promise.reject({ message: 'Token无效或已过期,请重新登录', statusCode: 401 });
|
}
|
|
// 非401响应,直接返回原响应
|
return res;
|
})
|
|
/**
|
* 添加请求拦截器
|
* @param {Function} fn 拦截器函数
|
*/
|
function addRequestInterceptor(fn) {
|
requestInterceptors.push(fn)
|
}
|
|
/**
|
* 添加响应拦截器
|
* @param {Function} fn 拦截器函数
|
*/
|
function addResponseInterceptor(fn) {
|
responseInterceptors.push(fn)
|
}
|
|
/**
|
* 处理请求拦截
|
* @param {Object} config 请求配置
|
* @returns {Object} 处理后的配置
|
*/
|
async function handleRequestInterceptors(config) {
|
let handledConfig = { ...config }
|
|
for (const interceptor of requestInterceptors) {
|
handledConfig = await interceptor(handledConfig)
|
}
|
|
return handledConfig
|
}
|
|
/**
|
* 处理响应拦截
|
* @param {Object} response 响应数据
|
* @returns {Object} 处理后的响应
|
*/
|
async function handleResponseInterceptors(response) {
|
let handledResponse = { ...response }
|
|
for (const interceptor of responseInterceptors) {
|
handledResponse = await interceptor(handledResponse)
|
}
|
|
return handledResponse
|
}
|
|
/**
|
* 转换请求参数为查询字符串
|
* @param {Object} params 参数对象
|
* @returns {String} 查询字符串
|
*/
|
function tansParams(params) {
|
let result = ''
|
for (const propName of Object.keys(params)) {
|
const value = params[propName]
|
var part = encodeURIComponent(propName) + "="
|
if (value !== null && value !== "" && typeof (value) !== "undefined") {
|
if (typeof value === 'object') {
|
for (const key of Object.keys(value)) {
|
if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== "undefined") {
|
let params = propName + "[" + key + "]"
|
var subPart = encodeURIComponent(params) + "="
|
result += subPart + encodeURIComponent(value[key]) + "&"
|
}
|
}
|
} else {
|
result += part + encodeURIComponent(value) + "&"
|
}
|
}
|
}
|
return result
|
}
|
|
/**
|
* 通用请求方法
|
* @param {Object} options 请求配置
|
* @returns {Promise}
|
*/
|
function request(options) {
|
// 合并默认配置
|
const config = {
|
method: 'GET',
|
headers: { ...service.defaults.headers },
|
timeout: service.defaults.timeout,
|
baseURL: service.baseURL,
|
...options
|
}
|
|
// 执行请求拦截器
|
return handleRequestInterceptors(config)
|
.then(processedConfig => {
|
// 构建完整URL
|
let url = processedConfig.baseURL + processedConfig.url
|
|
// 处理GET请求参数
|
if (processedConfig.method.toUpperCase() === 'GET' && processedConfig.params) {
|
const paramsStr = tansParams(processedConfig.params)
|
if (paramsStr) {
|
url += (url.includes('?') ? '&' : '?') + paramsStr.slice(0, -1)
|
}
|
}
|
|
// 构建uni.request参数
|
const requestOptions = {
|
url: url,
|
method: processedConfig.method.toUpperCase(),
|
header: processedConfig.headers,
|
timeout: processedConfig.timeout
|
}
|
|
// 添加请求体(非GET请求)
|
if (processedConfig.method.toUpperCase() !== 'GET' && processedConfig.data) {
|
requestOptions.data = processedConfig.data
|
}
|
|
// 发起请求
|
return new Promise((resolve, reject) => {
|
uni.request({
|
...requestOptions,
|
success: (res) => {
|
// 处理响应拦截器
|
handleResponseInterceptors(res)
|
.then(processedRes => {
|
resolve(processedRes)
|
})
|
.catch(error => {
|
reject(error)
|
})
|
},
|
fail: (err) => {
|
reject(err)
|
}
|
})
|
})
|
})
|
.catch(error => {
|
console.error('Request interceptor error:', error)
|
return Promise.reject(error)
|
})
|
}
|
|
/**
|
* GET请求
|
* @param {String} url 请求地址
|
* @param {Object} params 请求参数
|
* @param {Object} config 请求配置
|
* @returns {Promise}
|
*/
|
function get(url, params = {}, config = {}) {
|
return request({
|
method: 'GET',
|
url,
|
params,
|
...config
|
})
|
}
|
|
/**
|
* POST请求
|
* @param {String} url 请求地址
|
* @param {Object} data 请求数据
|
* @param {Object} config 请求配置
|
* @returns {Promise}
|
*/
|
function post(url, data = {}, config = {}) {
|
return request({
|
method: 'POST',
|
url,
|
data,
|
...config
|
})
|
}
|
|
/**
|
* PUT请求
|
* @param {String} url 请求地址
|
* @param {Object} data 请求数据
|
* @param {Object} config 请求配置
|
* @returns {Promise}
|
*/
|
function put(url, data = {}, config = {}) {
|
return request({
|
method: 'PUT',
|
url,
|
data,
|
...config
|
})
|
}
|
|
/**
|
* DELETE请求
|
* @param {String} url 请求地址
|
* @param {Object} params 请求参数
|
* @param {Object} config 请求配置
|
* @returns {Promise}
|
*/
|
function del(url, params = {}, config = {}) {
|
return request({
|
method: 'DELETE',
|
url,
|
params,
|
...config
|
})
|
}
|
|
/**
|
* 通用上传文件方法
|
* @param {Object} options 上传配置
|
* @returns {Promise}
|
*/
|
function uploadFile(options) {
|
const config = {
|
headers: { ...service.defaults.headers },
|
timeout: service.defaults.timeout,
|
baseURL: service.baseURL,
|
...options
|
}
|
|
return handleRequestInterceptors(config)
|
.then(processedConfig => {
|
return new Promise((resolve, reject) => {
|
uni.uploadFile({
|
url: processedConfig.baseURL + processedConfig.url,
|
filePath: processedConfig.filePath,
|
name: processedConfig.name || 'file',
|
header: processedConfig.headers,
|
formData: processedConfig.formData,
|
success: (res) => {
|
// uni.uploadFile 的 data 是字符串,尝试解析为对象
|
if (typeof res.data === 'string') {
|
try {
|
res.data = JSON.parse(res.data)
|
} catch (e) {
|
// 解析失败则保持原样
|
}
|
}
|
handleResponseInterceptors(res)
|
.then(processedRes => {
|
resolve(processedRes)
|
})
|
.catch(error => {
|
reject(error)
|
})
|
},
|
fail: (err) => {
|
reject(err)
|
}
|
})
|
})
|
})
|
}
|
|
// 导出方法
|
export default {
|
request,
|
uploadFile,
|
get,
|
post,
|
put,
|
delete: del,
|
addRequestInterceptor,
|
addResponseInterceptor,
|
service
|
}
|