// uni-app x UTS 标准请求封装 | APP 直接访问,WEB 需代理跨域 // 修复:URL 格式错误、Header 类型错误、平台判断错误 // 定义请求配置类型 export type RequestOptions = { url : string method ?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' params ?: any | null data ?: any | null showLoading ?: boolean loadingMsg ?: string } // 平台判断工具(运行期判断,兼容所有平台) class PlatformUtil { // 获取平台信息(使用 uniPlatform,uni-app x 推荐方式) static getPlatform() : string { try { const appBaseInfo = uni.getAppBaseInfo() if (appBaseInfo.uniPlatform != null && appBaseInfo.uniPlatform.length > 0) { return appBaseInfo.uniPlatform } // 降级方案:尝试 getSystemInfoSync const systemInfo = uni.getSystemInfoSync() if (systemInfo.platform != null && systemInfo.platform.length > 0) { return systemInfo.platform } return '' } catch (e) { return '' } } // 是否为 App 端(Android 或 iOS) static isApp() : boolean { const platform = this.getPlatform() return platform === 'app' || platform === 'android' || platform === 'ios' } // 是否为 Web 端(H5) static isH5() : boolean { const platform = this.getPlatform() return platform === 'web' } // 是否为微信小程序 static isMpWeixin() : boolean { const platform = this.getPlatform() return platform === 'mp-weixin' } // 是否为小程序(通用) static isMiniProgram() : boolean { const platform = this.getPlatform() return platform.startsWith('mp-') } } function toUTSJSONObject(input : any | null) : any | null { if (input == null) return null if (typeof input !== 'object') return null try { // 通过 JSON 归一化为普通对象,避免 UTS 中遍历/动态索引的兼容问题 return JSON.parse(JSON.stringify(input)) as UTSJSONObject } catch (e) { return null } } // 核心请求方法,APP 端直接访问,WEB 端自动代理前缀 export function request(options : RequestOptions | null) : Promise { return new Promise((resolve, reject) => { if (options == null) { reject(new Error('请求参数不能为空')) return } // 1. 处理URL let url = options.url if (typeof url !== 'string' || url.length === 0) { reject(new Error('请求地址不能为空')) return } // 2. 根据平台处理 URL if (PlatformUtil.isH5()) { const proxyPrefix = '/dev' const isProxy = typeof url === 'string' && url.length >= proxyPrefix.length && url.substring(0, proxyPrefix.length) === proxyPrefix const isHttp = typeof url === 'string' && (url.indexOf('http') === 0) if (!isProxy && !isHttp) { const isSlash = typeof url === 'string' && url.indexOf('/') === 0 if (!isSlash) { url = '/' + url } url = proxyPrefix + url } } else if (PlatformUtil.isApp()) { const isHttpUrl = (typeof url === 'string' && url.indexOf('http://') === 0) || (typeof url === 'string' && url.indexOf('https://') === 0) if (!isHttpUrl) { reject(new Error('App 端请求地址必须是完整的 http:// 或 https:// URL,当前:' + url)) return } } // 3. 处理请求方法(保持字面量,避免 toUpperCase 造成类型丢失) const method = (options.method ?? 'GET') as string // 4. 最终 URL const finalUrl = url // 5. 构建请求头 // if (PlatformUtil.isH5()) { // const header = mapToUTSJSONObject(headerMap); // } else if (PlatformUtil.isApp()) { // const header = UTSJSONObject(); // header['Content-Type'] = 'application/json' // } // 获取 token // const token = uni.getStorageSync('token') as string // if (typeof token === 'string' && token.length > 0) { // header['Authorization'] = 'Bearer ' + token // } // const headerMap = new Map() // headerMap.set('Content-Type', 'application/json') const token = uni.getStorageSync('token') as string | null // if (token.length > 0) headerMap.set('Authorization', 'Bearer ' + token) // const tenantId = uni.getStorageSync('tenantId') as string | null // headerMap.set('tenant-id', tenantId ?? 'default') // headerMap.set('terminal', uni.getSystemInfoSync().platform) // const header = mapToUTSJSONObject(headerMap) // 获取 tenantId const tenantIdOrigin = uni.getStorageSync('tenantId') let tenantValue = 'default' if (typeof tenantIdOrigin === 'string') { if (tenantIdOrigin != null && (tenantIdOrigin as string).length > 0) { tenantValue = tenantIdOrigin as string } } // header['tenant-id'] = tenantValue // // 获取平台信息 // try { // const systemInfo = uni.getSystemInfoSync() // if (typeof systemInfo.platform === 'string' && systemInfo.platform.length > 0) { // header['terminal'] = systemInfo.platform // } else { // header['terminal'] = 'unknown' // } // } catch (e) { // header['terminal'] = 'unknown' // } const header = { 'tenant-id': tenantValue ?? 'default', 'terminal': uni.getSystemInfoSync().platform, 'Content-Type': 'application/json', 'Authorization': token != null ? `Bearer ${token}` : '' // 添加 token } as UTSJSONObject // 6. 处理请求数据(UTS 下 uni.request 期望 Map?) // GET 参数通过 data 传递,由框架负责拼接到 query,避免手动遍历拼接导致的 UTS 编译问题 const requestData = method === 'GET' ? toUTSJSONObject(options.params) : toUTSJSONObject(options.data) // 7. 处理加载提示 const showLoading = typeof options.showLoading === 'boolean' ? options.showLoading : true const loadingMsg = typeof options.loadingMsg === 'string' ? options.loadingMsg : '加载中' if (showLoading) { uni.showLoading({ title: loadingMsg, mask: true }) } // 8. 发起请求 uni.request({ url: finalUrl, method: method, data: requestData, header: header, timeout: 30000, dataType: 'json', responseType: 'text', success: (res) => { uni.hideLoading() if (res.statusCode >= 200 && res.statusCode < 300) { resolve(res.data ?? {}) } else { reject(new Error(`请求失败:HTTP ${res.statusCode}`)) } }, fail: (err) => { uni.hideLoading() console.error('Request failed:', err) // 详细错误日志 console.error('Error code:', err.errCode) console.error('Error message:', err.errMsg) console.error('Error subject:', err.errSubject) // 根据错误码给出具体错误信息 [15] let errorMsg = '网络异常,请重试' if (err.errCode === 600009) { errorMsg = 'URL 格式不合法,请检查请求地址是否正确(App端必须使用完整URL)' } else if (err.errCode === 600003) { errorMsg = '网络中断,请检查网络连接' } else if (err.errCode === 600008) { errorMsg = '请求数据格式错误' } else if (err.errCode === 600001) { errorMsg = '请求超时,请检查网络状况' } else if (typeof err.errMsg === 'string' && err.errMsg.length > 0) { errorMsg = err.errMsg } reject(new Error(errorMsg)) } }) }) }