// 统一请求工具 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