HomeLease/utils/request.js

584 lines
14 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 { getTempToken, shouldUseTempToken, getAppId } from '@/config/dev.js'
import {
showLoading,
hideLoading,
forceHideLoading,
initGlobalLoadingManager,
config as loadingConfig,
getLoadingStatus,
setLoadingConfig,
getLoadingConfig,
showLoadingWithDelay,
hideLoadingWithDelay,
AutoLoadingManager,
} from './loading-manager.js'
// 环境配置
const ENV_CONFIG = {
develop: {
// 开发环境
// baseUrl: 'http://192.168.2.136:4501',
baseUrl: 'http://192.168.2.143:4601',
appId: 1, // TODO: 根据实际后端配置调整
},
trial: {
// 体验版
baseUrl: 'http://192.168.2.143:4601',
appId: 1, // TODO: 根据实际后端配置调整
},
release: {
// 正式版
baseUrl: 'http://192.168.2.143:4601',
appId: 1, // TODO: 根据实际后端配置调整
},
}
/**
* 清理token移除无效字符
* @param {string} token - 原始token
* @returns {string} 清理后的token
*/
function cleanToken(token) {
if (!token) return ''
// 移除换行符、空格等无效字符
return token.trim().replace(/[\r\n\s]/g, '')
}
// 获取当前环境配置
const getCurrentConfig = () => {
try {
const { envVersion } = wx.getAccountInfoSync().miniProgram
console.log('当前环境:', envVersion)
const envConfig = ENV_CONFIG[envVersion] || ENV_CONFIG.release
// 确保配置对象包含所有必要属性
return {
baseUrl: envConfig.baseUrl,
appId: envConfig.appId || 1, // 确保appId有默认值
}
} catch (error) {
console.warn('获取环境失败,默认使用正式环境:', error)
const fallbackConfig = ENV_CONFIG.release
return {
baseUrl: fallbackConfig.baseUrl,
appId: fallbackConfig.appId || 1, // 确保appId有默认值
}
}
}
const config = getCurrentConfig()
const BASE_URL = config.baseUrl
// 调试信息
console.log('HTTP配置:', {
baseUrl: BASE_URL,
config: config,
})
/**
* 获取请求头
* @param {Object} customHeader - 自定义请求头
* @returns {Object} 请求头对象
*/
function getRequestHeaders(customHeader = {}) {
let token = uni.getStorageSync('token')
// 开发环境使用临时token
if (shouldUseTempToken() && !token) {
token = getTempToken()
}
// 清理token
token = cleanToken(token)
let authorization = ''
if (token) {
// 平台差异化处理
// #ifdef H5
authorization = `Bearer ${token}`
// #endif
// #ifndef H5
authorization = token
// #endif
}
const headers = {
'Content-Type': 'application/json;charset=UTF-8',
...customHeader,
}
// 只有在有token时才添加Authorization头部
if (authorization) {
headers.Authorization = authorization
}
return headers
}
/**
* 处理响应错误
* @param {Object} res - 响应对象
* @param {Function} reject - Promise reject函数
*/
function handleResponseError(res, reject, options = {}) {
// 先清除loading状态
if (options.showLoading !== false) {
hideLoading()
}
const errorMap = {
401: {
title: '登录已过期,请重新登录',
action: () => {
setTimeout(() => {
uni.reLaunch({
url: '/pages/login/login',
})
}, 1500)
},
},
403: {
title: '权限不足',
action: () => {},
},
404: {
title: '请求的资源不存在',
action: () => {},
},
500: {
title: '服务器错误',
action: () => {},
},
}
const error = errorMap[res.statusCode] || {
title: res.data?.msg || '请求失败',
action: () => {},
}
// 显示错误提示
uni.showToast({
title: error.title,
icon: 'none',
duration: 2000,
})
// 执行错误处理动作
error.action()
reject(new Error(error.title))
}
// Loading相关函数已从loading-manager.js导入
/**
* 统一请求方法
* @param {Object} options - 请求配置
* @param {string} options.url - 请求地址
* @param {string} options.method - 请求方法
* @param {Object} options.params - 查询参数
* @param {Object} options.data - 请求体数据
* @param {Object} options.header - 请求头
* @param {number} options.timeout - 超时时间
* @param {boolean} options.showLoading - 是否显示加载状态默认true
* @param {string} options.loadingText - 加载提示文字
* @param {boolean} options.noToken - 是否需要token
* @returns {Promise} 返回请求结果
*/
export function request(options = {}) {
return new Promise((resolve, reject) => {
// 获取token优先使用本地存储的token如果没有则使用临时token
const localToken = uni.getStorageSync('token')
let token = localToken
// 如果本地没有token且启用了临时token则使用临时token
if (!token && shouldUseTempToken() && !options.noToken) {
token = getTempToken()
console.log('使用临时token进行开发测试')
}
// 验证URL格式
if (!options.url || typeof options.url !== 'string') {
reject(new Error('无效的URL'))
return
}
// 确保URL以/开头
const url = options.url.startsWith('/') ? options.url : '/' + options.url
// 构建请求配置
const requestOptions = {
url: BASE_URL + url,
method: options.method || 'GET',
header: getRequestHeaders(options.header),
timeout: options.timeout || 60000, // 默认60秒超时
success: res => {
// 隐藏加载状态
if (options.showLoading !== false) {
hideLoading()
}
// 请求成功处理
if (res.statusCode === 200) {
resolve(res.data)
} else {
// 处理错误响应
handleResponseError(res, reject, options)
}
},
fail: err => {
// 隐藏加载状态
if (options.showLoading !== false) {
hideLoading()
}
// 请求失败处理
console.error('请求失败:', {
error: err,
url: requestOptions.url,
method: requestOptions.method,
baseUrl: BASE_URL,
originalUrl: options.url,
})
// 网络错误处理
let errorMessage = '网络错误'
if (err.errMsg) {
if (err.errMsg.includes('timeout')) {
errorMessage = '请求超时'
} else if (err.errMsg.includes('fail')) {
errorMessage = '网络连接失败'
}
}
uni.showToast({
title: errorMessage,
icon: 'none',
duration: 2000,
})
reject(err)
},
}
// 特殊接口处理不需要token的接口
const noTokenUrls = ['/wxLogin', '/user/login']
if (noTokenUrls.includes(url) || options.noToken) {
delete requestOptions.header.Authorization
console.log('跳过token验证的接口:', url)
}
// 处理请求参数
if (options.params && Object.keys(options.params).length > 0) {
// 对于GET请求或明确指定使用查询参数的请求params作为查询参数
if (requestOptions.method === 'GET' || options.useQueryParams) {
const queryString = Object.keys(options.params)
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(options.params[key])}`)
.join('&')
requestOptions.url += (requestOptions.url.includes('?') ? '&' : '?') + queryString
} else {
// 对于其他请求方法params作为请求体数据
requestOptions.data = { ...options.params }
}
} else if (options.data && Object.keys(options.data).length > 0) {
requestOptions.data = { ...options.data }
} else {
// 如果既没有params也没有data初始化为空对象
requestOptions.data = {}
}
// 自动添加appId到所有请求参数中除非明确指定不添加
try {
if (!options.noAppId && requestOptions.data && !requestOptions.data.appId) {
const appId = getCurrentAppId()
requestOptions.data.appId = appId
console.log('自动添加appId:', appId, '到请求:', requestOptions.url)
}
} catch (error) {
console.error('添加appId时出错:', error)
// 确保即使出错也有appId
if (requestOptions.data) {
requestOptions.data.appId = 1
}
}
// 发起请求
console.log('发起请求:', {
url: requestOptions.url,
method: requestOptions.method,
header: requestOptions.header,
data: requestOptions.data,
timeout: requestOptions.timeout,
baseUrl: BASE_URL,
useQueryParams: options.useQueryParams,
hasParams: !!(options.params && Object.keys(options.params).length > 0),
})
// 显示loading默认显示但减少延迟
if (options.showLoading !== false) {
showLoading(options.loadingText || loadingConfig.loadingText)
}
uni.request(requestOptions)
})
}
/**
* GET请求
* @param {string} url - 请求地址
* @param {Object} params - 查询参数
* @param {Object} options - 请求配置
* @returns {Promise} 返回请求结果
*/
export function get(url, params = {}, options = {}) {
return request({
url,
method: 'GET',
params,
...options,
})
}
/**
* POST请求
* @param {string} url - 请求地址
* @param {Object} data - 请求体数据
* @param {Object} options - 请求配置
* @returns {Promise} 返回请求结果
*/
export function post(url, data = {}, options = {}) {
return request({
url,
method: 'POST',
data,
...options,
})
}
/**
* PUT请求
* @param {string} url - 请求地址
* @param {Object} data - 请求体数据
* @param {Object} options - 请求配置
* @returns {Promise} 返回请求结果
*/
export function put(url, data = {}, options = {}) {
return request({
url,
method: 'PUT',
data,
...options,
})
}
/**
* DELETE请求
* @param {string} url - 请求地址
* @param {Object} options - 请求配置
* @returns {Promise} 返回请求结果
*/
export function del(url, options = {}) {
return request({
url,
method: 'DELETE',
...options,
})
}
/**
* 获取当前环境的appId
* @returns {number} 当前环境的appId
*/
export function getCurrentAppId() {
try {
return config.appId || getAppId() || 1
} catch (error) {
console.error('获取appId失败使用默认值:', error)
return 1
}
}
/**
* 设置请求配置
* @param {Object} newConfig - 新的配置
*/
export function setRequestConfig(newConfig) {
Object.assign(config, newConfig)
console.log('更新请求配置:', config)
}
/**
* 获取请求配置
* @returns {Object} 当前配置
*/
export function getRequestConfig() {
return { ...config }
}
/**
* 清除token
*/
export function clearToken() {
uni.removeStorageSync('token')
console.log('Token已清除')
}
/**
* 设置token
* @param {string} token - token值
*/
export function setToken(token) {
uni.setStorageSync('token', token)
console.log('Token已设置')
}
/**
* 获取token
* @returns {string} token值
*/
export function getToken() {
return uni.getStorageSync('token')
}
// 导出loading相关函数作为统一入口
export {
// 基础loading函数
showLoading,
hideLoading,
forceHideLoading,
// 高级loading函数
showLoadingWithDelay,
hideLoadingWithDelay,
// 状态和配置管理
getLoadingStatus,
setLoadingConfig,
getLoadingConfig,
// 全局初始化
initGlobalLoadingManager,
// 自动loading管理器类
AutoLoadingManager,
}
/**
* 文件上传
* @param {string} url - 上传地址
* @param {string} filePath - 文件路径
* @param {string} name - 文件字段名
* @param {Object} formData - 额外的表单数据
* @param {Object} options - 上传配置
* @returns {Promise} 返回上传结果
*/
export function uploadFile(url, filePath, name = 'file', formData = {}, options = {}) {
return new Promise((resolve, reject) => {
// 获取token
let token = uni.getStorageSync('token')
// 检查token是否存在
if (!token && !options.noToken) {
token = getTempToken()
console.log('使用临时token进行开发测试')
}
// 清理token
token = cleanToken(token)
// 确保URL以/开头
const uploadUrl = url.startsWith('/') ? url : '/' + url
// 构建请求头
let authorization = ''
if (token) {
// #ifdef H5
authorization = `Bearer ${token}`
// #endif
// #ifndef H5
authorization = token
// #endif
}
const header = {
...options.header
}
// 只有在有token时才添加Authorization头部
if (authorization) {
header.Authorization = authorization
}
console.log('开始文件上传:', {
url: BASE_URL + uploadUrl,
filePath,
name,
hasToken: !!token,
tokenLength: token ? token.length : 0,
header: header
})
uni.uploadFile({
url: BASE_URL + uploadUrl,
filePath: filePath,
name: name,
formData: formData,
header: header,
timeout: options.timeout || 60000,
success: (res) => {
console.log('文件上传响应:', {
statusCode: res.statusCode,
data: res.data,
header: res.header
})
try {
// 解析响应数据
const data = JSON.parse(res.data)
console.log('解析后的响应数据:', data)
if (res.statusCode === 200 && data.code === 200) {
console.log('文件上传成功:', data)
resolve(data)
} else {
// 服务器返回错误
const errorMsg = data.msg || data.message || '上传失败'
console.error('文件上传失败 - 服务器错误:', {
statusCode: res.statusCode,
code: data.code,
message: errorMsg,
data: data
})
uni.showToast({
title: errorMsg,
icon: 'none',
duration: 3000
})
reject(new Error(errorMsg))
}
} catch (parseError) {
console.error('解析响应数据失败:', parseError)
reject(new Error('响应数据格式错误'))
}
},
fail: (err) => {
console.error('文件上传失败:', err)
uni.showToast({
title: errorMessage,
icon: 'none',
duration: 3000
})
reject(err)
}
})
})
}
// Loading管理相关函数已从loading-manager.js导入
// 默认导出request函数方便API文件导入
export default request