123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- // utils/http.ts
- /*
- *
- * ┏┓ ┏┓+ +
- * ┏┛┻━━━┛┻┓ + +
- * ┃ ┃
- * ┃ ━ ┃ ++ + + +
- * ████━████ ┃+
- * ┃ ┃ +
- * ┃ ┻ ┃
- * ┃ ┃ + +
- * ┗━┓ ┏━┛
- * ┃ ┃
- * ┃ ┃ + + + +
- * ┃ ┃
- * ┃ ┃ + 神兽保佑
- * ┃ ┃ 代码无bug
- * ┃ ┃ +
- * ┃ ┗━━━┓ + +
- * ┃ ┣┓
- * ┃ ┏┛
- * ┗┓┓┏━┳┓┏┛ + + + +
- * ┃┫┫ ┃┫┫
- * ┗┻┛ ┗┻┛+ + + +
- */
- import type { RequestTask } from '@dcloudio/types'
- import type { UniRequestConfig, UniResponse } from '@dcloudio/uni-app'
- // 定义基础响应类型
- export interface HttpResponse<T = any> {
- code: number
- message: string
- data: T
- [key: string]: any
- }
- // 请求配置类型
- export interface RequestConfig extends UniRequestConfig {
- baseURL?: string
- retry?: number // 重试次数
- retryDelay?: number // 重试延迟时间(ms)
- loading?: boolean // 是否显示加载状态
- headers?: Record<string, string>
- timeout?: number
- skipAuth?: boolean // 是否跳过认证检查
- data?: any
- url?: string
- method?: UniRequestConfig['method']
- }
- export class HttpClient {
- private defaults: RequestConfig
- private pendingRequests: Map<string, RequestTask>
- constructor(config: RequestConfig = {}) {
- this.defaults = {
- baseURL: '',
- timeout: 20000,
- retry: 0,
- retryDelay: 1000,
- loading: false,
- ...config
- }
- this.pendingRequests = new Map()
- }
- // token检查方法
- static checkTokenValidity(token: any): string | null {
- let tokenValue: string | null = null
- try {
- if (token) {
- if (typeof token === 'string') {
- try {
- const parsed = JSON.parse(token)
- if (parsed && typeof parsed.value === 'string') {
- tokenValue = parsed.value
- } else {
- tokenValue = token
- }
- } catch (e) {
- tokenValue = token
- }
- }
- else if (typeof token === 'object' && token.value) {
- tokenValue = token.value
- }
- }
- } catch (e) {
- console.error('Token 有效性检查失败:', e)
- }
- return tokenValue
- }
- private createRequestKey(config: RequestConfig): string {
- return `${config.method}-${config.url}-${JSON.stringify(config.data)}`
- }
- async request<T = any>(config: RequestConfig): Promise<T> {
- let mergedConfig: RequestConfig = { ...this.defaults, ...config }
- const requestKey = this.createRequestKey(mergedConfig)
-
- // 取消重复请求
- if (this.pendingRequests.has(requestKey)) {
- this.pendingRequests.get(requestKey)?.abort()
- }
- return new Promise((resolve, reject) => {
- const sendRequest = (retryCount = 0) => {
- if (mergedConfig.loading) {
- uni.showLoading({ title: '加载中...', mask: true })
- }
- try {
- const task = uni.request({
- ...mergedConfig,
- url: (mergedConfig.baseURL ?? '') + mergedConfig.url,
- success: (response) => {
- const res = response.data as HttpResponse
- if (res.code === 200) {
- resolve(res as T)
- } else {
- uni.showToast({
- title: res.message,
- icon: 'none'
- })
- reject(res)
- }
- },
- fail: (error) => {
- // 重试逻辑
- if (retryCount < mergedConfig.retry!) {
- setTimeout(() => {
- sendRequest(retryCount + 1)
- }, mergedConfig.retryDelay)
- } else {
- uni.showToast({
- title: '网络错误,请重试',
- icon: 'none'
- })
- reject(error)
- }
- },
- complete: () => {
- this.pendingRequests.delete(requestKey)
- if (mergedConfig.loading) {
- uni.hideLoading()
- }
- }
- }) as RequestTask
- this.pendingRequests.set(requestKey, task)
- } catch (error) {
- reject(error)
- }
- }
- sendRequest()
- })
- }
- // 快捷方法
- get<T = any>(url: string, config?: RequestConfig) {
- return this.request<T>({ ...config, url, method: 'GET' })
- }
- post<T = any>(url: string, data?: any, config?: RequestConfig) {
- return this.request<T>({ ...config, url, data, method: 'POST' })
- }
- delete<T = any>(url: string, data?: any, config?: RequestConfig) {
- return this.request<T>({ ...config, url, data, method: 'DELETE' })
- }
- put<T = any>(url: string, data?: any, config?: RequestConfig) {
- return this.request<T>({ ...config, url, data, method: 'PUT' })
- }
- }
- // 创建实例
- export const http = new HttpClient({
- // baseURL: 'http://192.168.1.34:8080/jeecg-boot/app',
- baseURL: 'http://192.168.1.166:8080/jeecg-boot/app',
- // baseURL: 'http://192.168.0.11:8080/jeecg-boot/app',
- headers: {
- 'Content-Type': 'application/json'
- }
- })
- // 请求拦截器
- uni.addInterceptor('request', {
- invoke(args: UniApp.RequestOptions) {
- // 跳过不需要认证的请求
- if (args.header && (args.header as any).skipAuth) return
- const tokenStorage = uni.getStorageSync('TOKEN')
- const tokenValue = HttpClient.checkTokenValidity(tokenStorage)
-
- if (tokenValue) {
- args.header = args.header || {}
- args.header['x-access-token'] = tokenValue
- } else {
- console.log('[请求拦截器] 无有效Token')
- }
- },
- fail(err) {
- console.error('请求拦截器失败:', err)
- }
- })
- //响应拦截器
- uni.addInterceptor('request', {
- success: (res) => {
- const data = res.data as HttpResponse
- if (data.code === 401) {
- uni.showToast({
- title: data.message || '登录已失效,请重新登录',
- icon: 'none',
- duration: 2000
- })
- setTimeout(() => {
- uni.clearStorage()
- }, 500)
- return {
- ...res,
- errMsg: `Token失效: ${data.message}`,
- data: null
- }
- } else if (data.code !== 200) {
- uni.showToast({
- title: data.message,
- icon: 'none'
- })
- return {
- ...res,
- errMsg: `业务错误: ${data.message}`,
- data: null
- }
- }
- return res
- },
- fail: (err) => {
- console.error('响应拦截器捕获错误:', err)
- uni.showToast({
- title: '网络错误,请重试',
- icon: 'none'
- })
- return err
- }
- })
|