Files
cunkebao_v3/Cunkebao/utils/request.js
2025-03-28 16:20:32 +08:00

185 lines
5.0 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 Auth from './auth';
// 服务器地址
const BASE_URL = process.env.VUE_APP_BASE_API || 'http://yishi.com';
// 请求超时时间
const TIMEOUT = 10000;
/**
* 请求拦截器
* @param {Object} config 请求配置
* @returns {Object} 处理后的请求配置
*/
function requestInterceptor(config) {
// 获取 token
const token = uni.getStorageSync('token');
// 如果有 token则带上请求头 Authorization: Bearer + token
if (token) {
config.header = {
...config.header,
'Authorization': `Bearer ${token}`
};
}
// 打印请求日志
console.log('请求地址:', `${config.baseURL || BASE_URL}${config.url}`);
return config;
}
/**
* 响应拦截器
* @param {Object} response 响应数据
* @returns {Object|Promise} 处理后的响应数据或Promise
*/
function responseInterceptor(response) {
// 未登录或token失效 - 取消登录拦截
if (response.data.code === 401) {
console.log('登录已过期,需要重新登录');
// 清除登录信息
Auth.removeToken();
Auth.removeUserInfo();
// 跳转到登录页
uni.reLaunch({
url: '/pages/login/index'
});
return Promise.reject(new Error('登录已过期,请重新登录'));
}
// token需要刷新 - 410 状态码
if (response.data.code === 410) {
// 尝试刷新 token
return Auth.refreshToken()
.then(res => {
if (res.code === 200) {
// 更新本地token
Auth.setToken(res.data.token, res.data.token_expired - Math.floor(Date.now() / 1000));
// 使用新token重试原请求
const config = response.config;
config.header.Authorization = `Bearer ${res.data.token}`;
// 重新发起请求
return request(config);
} else {
// 刷新失败,跳转到登录页
uni.reLaunch({
url: '/pages/login/index'
});
return Promise.reject(new Error('登录已过期,请重新登录'));
}
})
.catch(err => {
console.error('刷新token失败', err);
// 清除登录信息
Auth.removeToken();
Auth.removeUserInfo();
// 跳转到登录页
uni.reLaunch({
url: '/pages/login/index'
});
return Promise.reject(new Error('登录已过期,请重新登录'));
});
}
return response.data;
}
/**
* 构建完整的URL包括查询参数
* @param {string} baseUrl 基础URL
* @param {Object} params 查询参数
* @returns {string} 完整的URL
*/
function buildUrlWithParams(baseUrl, params) {
if (!params || Object.keys(params).length === 0) {
return baseUrl;
}
const queryString = Object.keys(params)
.filter(key => params[key] !== undefined && params[key] !== null)
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
.join('&');
return queryString ? `${baseUrl}${baseUrl.includes('?') ? '&' : '?'}${queryString}` : baseUrl;
}
/**
* 统一请求函数
* @param {Object} options 请求选项
* @returns {Promise} 请求结果
*/
function request(options) {
// 合并请求选项
const config = {
baseURL: BASE_URL,
timeout: TIMEOUT,
header: {
'Content-Type': 'application/json'
},
...options
};
// 请求拦截
const interceptedConfig = requestInterceptor(config);
// 处理GET请求参数
let url = `${interceptedConfig.baseURL}${interceptedConfig.url}`;
const method = interceptedConfig.method || 'GET';
// 如果是GET请求并且有params参数将其转换为URL查询字符串
if (method.toUpperCase() === 'GET' && interceptedConfig.params) {
url = buildUrlWithParams(url, interceptedConfig.params);
// 打印完整请求URL便于调试
console.log('完整请求URL:', url);
}
// 发起请求
return new Promise((resolve, reject) => {
uni.request({
url: url,
method: method,
data: method.toUpperCase() === 'GET' ? undefined : interceptedConfig.data,
header: interceptedConfig.header,
timeout: interceptedConfig.timeout,
success: (res) => {
try {
const result = responseInterceptor(res);
resolve(result);
} catch (error) {
reject(error);
}
},
fail: (err) => {
// 显示提示
uni.showToast({
title: '网络请求失败',
icon: 'none',
duration: 2000
});
// 增强错误对象,添加更多信息便于调试
const enhancedError = {
...err,
url: url,
method: method,
params: method.toUpperCase() === 'GET' ? interceptedConfig.params : undefined,
data: method.toUpperCase() === 'GET' ? undefined : interceptedConfig.data,
message: err.errMsg || '网络请求失败'
};
console.error('请求失败详情:', enhancedError);
reject(enhancedError);
}
});
});
}
export default request;