index.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. // utils/http.ts
  2. /*
  3. *
  4. *   ┏┓   ┏┓+ +
  5. *  ┏┛┻━━━┛┻┓ + +
  6. *  ┃       ┃
  7. *  ┃   ━   ┃ ++ + + +
  8. * ████━████ ┃+
  9. *  ┃       ┃ +
  10. *  ┃   ┻   ┃
  11. *  ┃       ┃ + +
  12. *  ┗━┓   ┏━┛
  13. *    ┃   ┃
  14. *    ┃   ┃ + + + +
  15. *    ┃   ┃
  16. *    ┃   ┃ + 神兽保佑
  17. *    ┃   ┃ 代码无bug
  18. *    ┃   ┃  +
  19. *    ┃    ┗━━━┓ + +
  20. *    ┃        ┣┓
  21. *    ┃        ┏┛
  22. *    ┗┓┓┏━┳┓┏┛ + + + +
  23. *     ┃┫┫ ┃┫┫
  24. *     ┗┻┛ ┗┻┛+ + + +
  25. */
  26. import type { RequestTask } from '@dcloudio/types'
  27. import type { UniRequestConfig, UniResponse } from '@dcloudio/uni-app'
  28. // 定义基础响应类型
  29. export interface HttpResponse<T = any> {
  30. code: number
  31. message: string
  32. data: T
  33. [key: string]: any
  34. }
  35. // 请求配置类型
  36. export interface RequestConfig extends UniRequestConfig {
  37. baseURL?: string
  38. retry?: number // 重试次数
  39. retryDelay?: number // 重试延迟时间(ms)
  40. loading?: boolean // 是否显示加载状态
  41. headers?: Record<string, string>
  42. timeout?: number
  43. skipAuth?: boolean // 是否跳过认证检查
  44. data?: any
  45. url?: string
  46. method?: UniRequestConfig['method']
  47. }
  48. export class HttpClient {
  49. private defaults: RequestConfig
  50. private pendingRequests: Map<string, RequestTask>
  51. constructor(config: RequestConfig = {}) {
  52. this.defaults = {
  53. baseURL: '',
  54. timeout: 20000,
  55. retry: 0,
  56. retryDelay: 1000,
  57. loading: false,
  58. ...config
  59. }
  60. this.pendingRequests = new Map()
  61. }
  62. // token检查方法
  63. static checkTokenValidity(token: any): string | null {
  64. let tokenValue: string | null = null
  65. try {
  66. if (token) {
  67. if (typeof token === 'string') {
  68. try {
  69. const parsed = JSON.parse(token)
  70. if (parsed && typeof parsed.value === 'string') {
  71. tokenValue = parsed.value
  72. } else {
  73. tokenValue = token
  74. }
  75. } catch (e) {
  76. tokenValue = token
  77. }
  78. }
  79. else if (typeof token === 'object' && token.value) {
  80. tokenValue = token.value
  81. }
  82. }
  83. } catch (e) {
  84. console.error('Token 有效性检查失败:', e)
  85. }
  86. return tokenValue
  87. }
  88. private createRequestKey(config: RequestConfig): string {
  89. return `${config.method}-${config.url}-${JSON.stringify(config.data)}`
  90. }
  91. async request<T = any>(config: RequestConfig): Promise<T> {
  92. let mergedConfig: RequestConfig = { ...this.defaults, ...config }
  93. const requestKey = this.createRequestKey(mergedConfig)
  94. // 取消重复请求
  95. if (this.pendingRequests.has(requestKey)) {
  96. this.pendingRequests.get(requestKey)?.abort()
  97. }
  98. return new Promise((resolve, reject) => {
  99. const sendRequest = (retryCount = 0) => {
  100. if (mergedConfig.loading) {
  101. uni.showLoading({ title: '加载中...', mask: true })
  102. }
  103. try {
  104. const task = uni.request({
  105. ...mergedConfig,
  106. url: (mergedConfig.baseURL ?? '') + mergedConfig.url,
  107. success: (response) => {
  108. const res = response.data as HttpResponse
  109. if (res.code === 200) {
  110. resolve(res as T)
  111. } else {
  112. uni.showToast({
  113. title: res.message,
  114. icon: 'none'
  115. })
  116. reject(res)
  117. }
  118. },
  119. fail: (error) => {
  120. // 重试逻辑
  121. if (retryCount < mergedConfig.retry!) {
  122. setTimeout(() => {
  123. sendRequest(retryCount + 1)
  124. }, mergedConfig.retryDelay)
  125. } else {
  126. uni.showToast({
  127. title: '网络错误,请重试',
  128. icon: 'none'
  129. })
  130. reject(error)
  131. }
  132. },
  133. complete: () => {
  134. this.pendingRequests.delete(requestKey)
  135. if (mergedConfig.loading) {
  136. uni.hideLoading()
  137. }
  138. }
  139. }) as RequestTask
  140. this.pendingRequests.set(requestKey, task)
  141. } catch (error) {
  142. reject(error)
  143. }
  144. }
  145. sendRequest()
  146. })
  147. }
  148. // 快捷方法
  149. get<T = any>(url: string, config?: RequestConfig) {
  150. return this.request<T>({ ...config, url, method: 'GET' })
  151. }
  152. post<T = any>(url: string, data?: any, config?: RequestConfig) {
  153. return this.request<T>({ ...config, url, data, method: 'POST' })
  154. }
  155. delete<T = any>(url: string, data?: any, config?: RequestConfig) {
  156. return this.request<T>({ ...config, url, data, method: 'DELETE' })
  157. }
  158. put<T = any>(url: string, data?: any, config?: RequestConfig) {
  159. return this.request<T>({ ...config, url, data, method: 'PUT' })
  160. }
  161. }
  162. // 创建实例
  163. export const http = new HttpClient({
  164. // baseURL: 'http://192.168.1.34:8080/jeecg-boot/app',
  165. baseURL: 'http://192.168.1.166:8080/jeecg-boot/app',
  166. // baseURL: 'http://192.168.0.11:8080/jeecg-boot/app',
  167. headers: {
  168. 'Content-Type': 'application/json'
  169. }
  170. })
  171. // 请求拦截器
  172. uni.addInterceptor('request', {
  173. invoke(args: UniApp.RequestOptions) {
  174. // 跳过不需要认证的请求
  175. if (args.header && (args.header as any).skipAuth) return
  176. const tokenStorage = uni.getStorageSync('TOKEN')
  177. const tokenValue = HttpClient.checkTokenValidity(tokenStorage)
  178. if (tokenValue) {
  179. args.header = args.header || {}
  180. args.header['x-access-token'] = tokenValue
  181. } else {
  182. console.log('[请求拦截器] 无有效Token')
  183. }
  184. },
  185. fail(err) {
  186. console.error('请求拦截器失败:', err)
  187. }
  188. })
  189. //响应拦截器
  190. uni.addInterceptor('request', {
  191. success: (res) => {
  192. const data = res.data as HttpResponse
  193. if (data.code === 401) {
  194. uni.showToast({
  195. title: data.message || '登录已失效,请重新登录',
  196. icon: 'none',
  197. duration: 2000
  198. })
  199. setTimeout(() => {
  200. uni.clearStorage()
  201. }, 500)
  202. return {
  203. ...res,
  204. errMsg: `Token失效: ${data.message}`,
  205. data: null
  206. }
  207. } else if (data.code !== 200) {
  208. uni.showToast({
  209. title: data.message,
  210. icon: 'none'
  211. })
  212. return {
  213. ...res,
  214. errMsg: `业务错误: ${data.message}`,
  215. data: null
  216. }
  217. }
  218. return res
  219. },
  220. fail: (err) => {
  221. console.error('响应拦截器捕获错误:', err)
  222. uni.showToast({
  223. title: '网络错误,请重试',
  224. icon: 'none'
  225. })
  226. return err
  227. }
  228. })