request.uts 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. // uni-app x UTS 标准请求封装 | APP 直接访问,WEB 需代理跨域
  2. // 修复:URL 格式错误、Header 类型错误、平台判断错误
  3. // 定义请求配置类型
  4. export type RequestOptions = {
  5. url : string
  6. method ?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
  7. params ?: any | null
  8. data ?: any | null
  9. showLoading ?: boolean
  10. loadingMsg ?: string
  11. }
  12. // 平台判断工具(运行期判断,兼容所有平台)
  13. class PlatformUtil {
  14. // 获取平台信息(使用 uniPlatform,uni-app x 推荐方式)
  15. static getPlatform() : string {
  16. try {
  17. const appBaseInfo = uni.getAppBaseInfo()
  18. if (appBaseInfo.uniPlatform != null && appBaseInfo.uniPlatform.length > 0) {
  19. return appBaseInfo.uniPlatform
  20. }
  21. // 降级方案:尝试 getSystemInfoSync
  22. const systemInfo = uni.getSystemInfoSync()
  23. if (systemInfo.platform != null && systemInfo.platform.length > 0) {
  24. return systemInfo.platform
  25. }
  26. return ''
  27. } catch (e) {
  28. return ''
  29. }
  30. }
  31. // 是否为 App 端(Android 或 iOS)
  32. static isApp() : boolean {
  33. const platform = this.getPlatform()
  34. return platform === 'app' || platform === 'android' || platform === 'ios'
  35. }
  36. // 是否为 Web 端(H5)
  37. static isH5() : boolean {
  38. const platform = this.getPlatform()
  39. return platform === 'web'
  40. }
  41. // 是否为微信小程序
  42. static isMpWeixin() : boolean {
  43. const platform = this.getPlatform()
  44. return platform === 'mp-weixin'
  45. }
  46. // 是否为小程序(通用)
  47. static isMiniProgram() : boolean {
  48. const platform = this.getPlatform()
  49. return platform.startsWith('mp-')
  50. }
  51. }
  52. function toUTSJSONObject(input : any | null) : any | null {
  53. if (input == null) return null
  54. if (typeof input !== 'object') return null
  55. try {
  56. // 通过 JSON 归一化为普通对象,避免 UTS 中遍历/动态索引的兼容问题
  57. return JSON.parse(JSON.stringify(input)) as UTSJSONObject
  58. } catch (e) {
  59. return null
  60. }
  61. }
  62. // 核心请求方法,APP 端直接访问,WEB 端自动代理前缀
  63. export function request(options : RequestOptions | null) : Promise<any> {
  64. return new Promise<any>((resolve, reject) => {
  65. if (options == null) {
  66. reject(new Error('请求参数不能为空'))
  67. return
  68. }
  69. // 1. 处理URL
  70. let url = options.url
  71. if (typeof url !== 'string' || url.length === 0) {
  72. reject(new Error('请求地址不能为空'))
  73. return
  74. }
  75. // 2. 根据平台处理 URL
  76. if (PlatformUtil.isH5()) {
  77. const proxyPrefix = '/dev'
  78. const isProxy = typeof url === 'string' && url.length >= proxyPrefix.length && url.substring(0, proxyPrefix.length) === proxyPrefix
  79. const isHttp = typeof url === 'string' && (url.indexOf('http') === 0)
  80. if (!isProxy && !isHttp) {
  81. const isSlash = typeof url === 'string' && url.indexOf('/') === 0
  82. if (!isSlash) {
  83. url = '/' + url
  84. }
  85. url = proxyPrefix + url
  86. }
  87. } else if (PlatformUtil.isApp()) {
  88. const isHttpUrl =
  89. (typeof url === 'string' && url.indexOf('http://') === 0) ||
  90. (typeof url === 'string' && url.indexOf('https://') === 0)
  91. if (!isHttpUrl) {
  92. reject(new Error('App 端请求地址必须是完整的 http:// 或 https:// URL,当前:' + url))
  93. return
  94. }
  95. }
  96. // 3. 处理请求方法(保持字面量,避免 toUpperCase 造成类型丢失)
  97. const method = (options.method ?? 'GET') as string
  98. // 4. 最终 URL
  99. const finalUrl = url
  100. // 5. 构建请求头
  101. // if (PlatformUtil.isH5()) {
  102. // const header = mapToUTSJSONObject(headerMap);
  103. // } else if (PlatformUtil.isApp()) {
  104. // const header = UTSJSONObject();
  105. // header['Content-Type'] = 'application/json'
  106. // }
  107. // 获取 token
  108. // const token = uni.getStorageSync('token') as string
  109. // if (typeof token === 'string' && token.length > 0) {
  110. // header['Authorization'] = 'Bearer ' + token
  111. // }
  112. // const headerMap = new Map<string, string>()
  113. // headerMap.set('Content-Type', 'application/json')
  114. const token = uni.getStorageSync('token') as string | null
  115. // if (token.length > 0) headerMap.set('Authorization', 'Bearer ' + token)
  116. // const tenantId = uni.getStorageSync('tenantId') as string | null
  117. // headerMap.set('tenant-id', tenantId ?? 'default')
  118. // headerMap.set('terminal', uni.getSystemInfoSync().platform)
  119. // const header = mapToUTSJSONObject(headerMap)
  120. // 获取 tenantId
  121. const tenantIdOrigin = uni.getStorageSync('tenantId')
  122. let tenantValue = 'default'
  123. if (typeof tenantIdOrigin === 'string') {
  124. if (tenantIdOrigin != null && (tenantIdOrigin as string).length > 0) {
  125. tenantValue = tenantIdOrigin as string
  126. }
  127. }
  128. // header['tenant-id'] = tenantValue
  129. // // 获取平台信息
  130. // try {
  131. // const systemInfo = uni.getSystemInfoSync()
  132. // if (typeof systemInfo.platform === 'string' && systemInfo.platform.length > 0) {
  133. // header['terminal'] = systemInfo.platform
  134. // } else {
  135. // header['terminal'] = 'unknown'
  136. // }
  137. // } catch (e) {
  138. // header['terminal'] = 'unknown'
  139. // }
  140. const header = {
  141. 'tenant-id': tenantValue ?? 'default',
  142. 'terminal': uni.getSystemInfoSync().platform,
  143. 'Content-Type': 'application/json',
  144. 'Authorization': token != null ? `Bearer ${token}` : '' // 添加 token
  145. } as UTSJSONObject
  146. // 6. 处理请求数据(UTS 下 uni.request 期望 Map<String, Any>?)
  147. // GET 参数通过 data 传递,由框架负责拼接到 query,避免手动遍历拼接导致的 UTS 编译问题
  148. const requestData = method === 'GET' ? toUTSJSONObject(options.params) : toUTSJSONObject(options.data)
  149. // 7. 处理加载提示
  150. const showLoading = typeof options.showLoading === 'boolean' ? options.showLoading : true
  151. const loadingMsg = typeof options.loadingMsg === 'string' ? options.loadingMsg : '加载中'
  152. if (showLoading) {
  153. uni.showLoading({ title: loadingMsg, mask: true })
  154. }
  155. // 8. 发起请求
  156. uni.request({
  157. url: finalUrl,
  158. method: method,
  159. data: requestData,
  160. header: header,
  161. timeout: 30000,
  162. dataType: 'json',
  163. responseType: 'text',
  164. success: (res) => {
  165. uni.hideLoading()
  166. if (res.statusCode >= 200 && res.statusCode < 300) {
  167. resolve(res.data ?? {})
  168. } else {
  169. reject(new Error(`请求失败:HTTP ${res.statusCode}`))
  170. }
  171. },
  172. fail: (err) => {
  173. uni.hideLoading()
  174. console.error('Request failed:', err)
  175. // 详细错误日志
  176. console.error('Error code:', err.errCode)
  177. console.error('Error message:', err.errMsg)
  178. console.error('Error subject:', err.errSubject)
  179. // 根据错误码给出具体错误信息 [15]
  180. let errorMsg = '网络异常,请重试'
  181. if (err.errCode === 600009) {
  182. errorMsg = 'URL 格式不合法,请检查请求地址是否正确(App端必须使用完整URL)'
  183. } else if (err.errCode === 600003) {
  184. errorMsg = '网络中断,请检查网络连接'
  185. } else if (err.errCode === 600008) {
  186. errorMsg = '请求数据格式错误'
  187. } else if (err.errCode === 600001) {
  188. errorMsg = '请求超时,请检查网络状况'
  189. } else if (typeof err.errMsg === 'string' && err.errMsg.length > 0) {
  190. errorMsg = err.errMsg
  191. }
  192. reject(new Error(errorMsg))
  193. }
  194. })
  195. })
  196. }