device-request.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. import env from '../config/env.js'
  2. /**
  3. * 设备端专用请求工具
  4. * 特点:
  5. * 1. 不添加用户端的 deviceInfo 参数
  6. * 2. 不处理登录过期逻辑
  7. * 3. 保留 JSESSIONID 管理
  8. * 4. 简化 loading 逻辑(无延迟)
  9. */
  10. // Loading 管理器 - 简化版
  11. const loadingManager = {
  12. loadingCount: 0,
  13. isShowing: false,
  14. show(options = {}) {
  15. const config = {
  16. title: '加载中...',
  17. mask: true,
  18. ...options
  19. }
  20. // 如果已经有 loading 在显示,只增加计数
  21. if (this.loadingCount > 0) {
  22. this.loadingCount++
  23. return
  24. }
  25. // 设备端无需延迟,直接显示
  26. if (!this.isShowing) {
  27. try {
  28. uni.showLoading({
  29. title: config.title,
  30. mask: config.mask
  31. })
  32. this.isShowing = true
  33. } catch (error) {
  34. console.warn('showLoading failed:', error)
  35. }
  36. }
  37. this.loadingCount++
  38. },
  39. hide() {
  40. this.loadingCount = Math.max(0, this.loadingCount - 1)
  41. if (this.loadingCount === 0 && this.isShowing) {
  42. try {
  43. uni.hideLoading()
  44. } catch (error) {
  45. console.warn('hideLoading failed:', error)
  46. } finally {
  47. this.isShowing = false
  48. }
  49. }
  50. },
  51. forceHide() {
  52. this.loadingCount = 0
  53. if (this.isShowing) {
  54. try {
  55. uni.hideLoading()
  56. } catch (error) {
  57. console.warn('forceHide failed:', error)
  58. } finally {
  59. this.isShowing = false
  60. }
  61. }
  62. },
  63. safeShowToast(options) {
  64. if (this.isShowing) {
  65. this.forceHide()
  66. setTimeout(() => {
  67. uni.showToast(options)
  68. }, 100)
  69. } else {
  70. uni.showToast(options)
  71. }
  72. }
  73. }
  74. const deviceRequest = {
  75. async get(url, params = {}, options = {}) {
  76. return this.request(url, 'GET', params, options)
  77. },
  78. async post(url, data = {}, options = {}) {
  79. return this.request(url, 'POST', data, options)
  80. },
  81. async request(url, method, data, options = {}) {
  82. // 解析 loading 配置
  83. let loadingConfig
  84. if (options.loading === false) {
  85. loadingConfig = false
  86. } else {
  87. loadingConfig = {
  88. show: true,
  89. title: '加载中...',
  90. mask: true,
  91. ...(typeof options.loading === 'object' ? options.loading : {})
  92. }
  93. }
  94. // 解析请求配置
  95. const requestConfig = {
  96. timeout: 15000,
  97. ...options.request
  98. }
  99. const shouldShowLoading = loadingConfig !== false && loadingConfig.show !== false
  100. // 显示 loading
  101. if (shouldShowLoading) {
  102. loadingManager.show(loadingConfig)
  103. }
  104. // 🎯 参考 request.js (161-168行),自动添加 devId 和 sbmc
  105. // 设备端从 userInfo 获取,而不是从 deviceInfo
  106. let finalUrl = url
  107. // 检查 URL 是否已包含 devId 参数(login 接口已手动添加)
  108. if (!url.includes('devId=')) {
  109. const userInfo = uni.getStorageSync('userInfo') || {}
  110. const devId = userInfo.devId || ''
  111. const sbmc = userInfo.sbmc || ''
  112. // 只有在有 devId 时才添加参数
  113. if (devId) {
  114. const separator = url.includes('?') ? '&' : '?'
  115. finalUrl = `${url}${separator}devId=${devId}&sbmc=${sbmc}`
  116. }
  117. }
  118. // 数据格式转换
  119. let requestData = data
  120. if (options.formData && data && typeof data === 'object') {
  121. requestData = Object.keys(data).map(key => {
  122. const value = data[key]
  123. if (Array.isArray(value)) {
  124. return value.map(item => `${encodeURIComponent(key)}=${encodeURIComponent(item)}`).join('&')
  125. } else {
  126. return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`
  127. }
  128. }).join('&')
  129. }
  130. return new Promise((resolve, reject) => {
  131. uni.request({
  132. url: `${env.baseUrl}${finalUrl}`,
  133. method,
  134. data: requestData,
  135. timeout: requestConfig.timeout,
  136. header: (() => {
  137. const headers = {}
  138. if (options.formData) {
  139. headers['content-type'] = 'application/x-www-form-urlencoded'
  140. } else {
  141. headers['content-type'] = 'application/json'
  142. }
  143. // 携带 JSESSIONID
  144. const jsessionId = uni.getStorageSync('JSESSIONID')
  145. if (jsessionId) {
  146. headers['Cookie'] = `JSESSIONID=${jsessionId}`
  147. }
  148. return headers
  149. })(),
  150. success: (res) => {
  151. // 获取响应头
  152. const headers = res.header
  153. // 处理 Set-Cookie,保存 JSESSIONID
  154. const setCookie = headers['set-cookie'] || headers['Set-Cookie']
  155. if (setCookie) {
  156. const match = setCookie.match(/JSESSIONID=([^;]+)/)
  157. if (match && match[1]) {
  158. console.log('🔑 设备端获取到 JSESSIONID:', match[1])
  159. uni.setStorageSync('JSESSIONID', match[1])
  160. }
  161. }
  162. if (res.statusCode === 200) {
  163. // 检查服务器错误
  164. if (res && typeof res.data === 'string' && res.data.includes('页面执行时错误')) {
  165. reject({
  166. message: '服务器处理错误' + res.data,
  167. })
  168. loadingManager.safeShowToast({
  169. title: '服务器处理错误',
  170. icon: 'none'
  171. })
  172. return
  173. }
  174. // 设备端不处理登录过期,直接返回数据
  175. const responseData = {
  176. data: res.data,
  177. }
  178. resolve(responseData)
  179. } else {
  180. reject(res)
  181. }
  182. },
  183. fail: (err) => {
  184. loadingManager.safeShowToast({
  185. title: '网络请求失败',
  186. icon: 'none'
  187. })
  188. reject(err)
  189. },
  190. complete: () => {
  191. if (shouldShowLoading) {
  192. loadingManager.hide()
  193. }
  194. }
  195. })
  196. })
  197. }
  198. }
  199. // 添加便捷方法
  200. deviceRequest.loadingManager = loadingManager
  201. // 静默请求
  202. deviceRequest.silent = {
  203. get: (url, params = {}) => deviceRequest.get(url, params, { loading: false }),
  204. post: (url, data = {}) => deviceRequest.post(url, data, { loading: false }),
  205. }
  206. // 带自定义 loading 文字的请求
  207. deviceRequest.withLoading = (title) => ({
  208. get: (url, params = {}) => deviceRequest.get(url, params, { loading: { title } }),
  209. post: (url, data = {}) => deviceRequest.post(url, data, { loading: { title } }),
  210. })
  211. export default deviceRequest