• 取消请求功能
  • 失败自动重试机制
  • 并发请求控制

核心代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

// 响应内容格式

export interface InResult<T> {

  code: number | string

  message: string

  success: boolean

# data: T

}

// 请求参数格式

interface RequestOptions {

  baseURL?: string

  url: string

  method?: 'GET' 'POST' 'PUT' 'DELETE'

  data?: Record<string, any>

  params?: Record<string, any>

  header?: Record<stringstring>

  loading?: boolean // 是否显示加载中提示

  loadingText?: string // 加载中提示文本

  removeToken?: boolean // 是否移除token

  cache?: boolean // 是否缓存响应结果

  returnResponse?: boolean // 直接返回响应

}

// 设置请求头

function setRequestHeaders(url: string) {

  const LOGIN_URL = '/auth/oauth2/token'

  if (url.includes(LOGIN_URL)) {

    return { Authorization: `Basic Z2VveHNwYWNlOmdlb3hzcGFjZQ==` }

  }

  const token = uni.getStorageSync('token')

  if (token) {

    return { Authorization: `Bearer ${token}` }

  }

  return {} as Record<stringstring>

}

// 请求和响应拦截器

function requestAndResponseInterceptor() {

  uni.addInterceptor('request', {

    // 调用前置拦截器

    invoke(options: RequestOptions) {

      if (options.loading) {

        uni.showLoading({

          title: options.loadingText || '加载中...',

          mask: true,

        })

      }

      options.header = {

        ...options.header,

        ...setRequestHeaders(options.url),

      }

      // 移除token

      if (options.removeToken) {

        delete options.header.Authorization

      }

      // 处理params 参数

      if (options.params) {

        const urlPrams: string[] = []

        Object.keys(options.params).forEach((key) => {

          urlPrams.push(`${key}=${options.params![key]}`)

        })

        if (options.url.includes('?')) {

          options.url += urlPrams.join('&')

        }

        else {

          options.url += `?${urlPrams.join('&')}`

        }

      }

      return options

    },

    // 调用后置拦截器

    success(res) {

      return res

    },

    fail(err) {

      uni.showToast({

        title: '网络请求失败',

        icon: 'none',

      })

      return Promise.reject(err)

    },

    complete(option) {

      console.log('option.errMsg', option.errMsg)

      setTimeout(() => {

        uni.hideLoading()

      }, 15000)

    },

  })

}

// 调用请求拦截器和响应拦截器

requestAndResponseInterceptor()

const cacheMap = new Map<string, any>()

// 封装网络请求

export async function request(options: RequestOptions): Promise<any> {

  const { baseURL, url, header = {}, cache, returnResponse } = options

  // 合并配置

  const config = {

    ...options,

    url: url.startsWith('http') ? options.url : baseURL + options.url,

    header: {

      'Content-Type''application/json',

      ...header, // 允许自定义header

    },

    timeout: 10000, // 超时时间(ms)

  }

  if (cache) {

    if (cacheMap.has(url)) {

      return cacheMap.get(url)

    }

  }

  try {

    const response = await uni.request(config)

    if (options.loading) {

      uni.hideLoading()

    }

    // 响应拦截器

    if (response.statusCode === 200) {

      const data = returnResponse ? response : response.data

      if (cache) {

        cacheMap.set(url, data)

      }

      // @ts-expect-error 判断异常

      if (response.data.code !== '20000') {

        // @ts-expect-error 判断异常

        toast(response.data.msg, {

          icon: 'fail',

        })

      }

      return data

    }

    if (response.statusCode === 401) {

      toast('登录已过期,请重新登录!', {}, () => {

        uni.redirectTo({

          url: '/user/login/index',

        })

      })

    }

    else {

      toast('系统服务异常!')

    }

  }

  catch (error) {

    return Promise.reject(error)

  }

}

toast 封装代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

export function toast(title: string, options?: { duration?: number, icon?: 'success' 'loading' 'error' 'none' 'fail' 'exception', mask?: boolean }, callback?: () => void) {

  const { mask = true, duration = 1000, icon = 'none' } = options || {}

  if (title && title.length > 14) {  // 当作字符长度>14时使用showModal展示

    uni.showModal({

      content: title,

      showCancel: false,

      success() {

        if (callback && typeof callback === 'function') {

          const timer = setTimeout(() => {

            callback()

            clearTimeout(timer)

          }, duration)

        }

      },

    })

  }

  else {

    uni.showToast({

      title,

      message: title,

      icon,

      mask,

      duration,

      success() {

        if (callback && typeof callback === 'function') {

          const timer = setTimeout(() => {

            callback()

            clearTimeout(timer)

          }, duration)

        }

      },

    })

  }

}

使用示例

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

import { type InResult, request } from '@/utils/request'

export interface IUser {

    id: string,

    name: string,

    age?: number

}

export function getUserList(data: any): Promise<InResult<{ records: Array<IUser>, total: number }>> {

  return request({

    baseURL,

    url: '/user/page',

    method: 'POST',

    params: data,

    loading: true,

  })

更多推荐