request.js 3.51 KB
// 统一请求层:封装 baseUrl、自动带 token、解析后端 RESTful 响应、401 处理
import { BASE_URL, REQUEST_TIMEOUT, TOKEN_KEY, MEMBER_KEY, LOGIN_PAGE } from '@/utils/config'

// token 失效对应的业务码(沿用管理端约定)
const TOKEN_INVALID_CODES = [401, 600, 601, 602]

let redirecting = false

function clearAuthStorage() {
  uni.removeStorageSync(TOKEN_KEY)
  uni.removeStorageSync(MEMBER_KEY)
}

function redirectToLogin() {
  clearAuthStorage()
  if (redirecting) return
  redirecting = true
  uni.reLaunch({
    url: LOGIN_PAGE,
    complete: () => {
      setTimeout(() => {
        redirecting = false
      }, 800)
    }
  })
}

function toast(message) {
  uni.showToast({
    title: message || '请求出错,请重试',
    icon: 'none'
  })
}

/**
 * 发起请求
 * @param {Object} options
 * @param {String} options.url        接口路径(含 api/ 前缀)
 * @param {String} options.method     GET/POST/PUT/DELETE
 * @param {Object} options.data       参数(GET 也放 data,会作为 query)
 * @param {Object} options.header     自定义请求头
 * @param {Boolean} options.auth      是否需要携带 token(默认 true)
 * @param {Boolean} options.loading   是否显示 loading(默认 false)
 * @param {Boolean} options.silent    出错时是否静默(不弹 toast,默认 false)
 * @returns {Promise<any>} resolve 后端 data 字段
 */
export default function request(options = {}) {
  const {
    url,
    method = 'GET',
    data = {},
    header = {},
    auth = true,
    loading = false,
    silent = false
  } = options

  return new Promise((resolve, reject) => {
    if (loading) {
      uni.showLoading({ title: '加载中', mask: true })
    }

    const finalHeader = { 'content-type': 'application/json', ...header }
    const token = uni.getStorageSync(TOKEN_KEY)
    if (auth && token) {
      // token 存储时已含 "Bearer ",做一次兜底
      finalHeader['Authorization'] = String(token).indexOf('Bearer') === 0 ? token : ('Bearer ' + token)
    }

    uni.request({
      url: BASE_URL + url,
      method: String(method).toUpperCase(),
      data,
      header: finalHeader,
      timeout: REQUEST_TIMEOUT,
      success: (res) => {
        const statusCode = res.statusCode
        const body = res.data || {}

        // HTTP 401 未授权
        if (statusCode === 401) {
          redirectToLogin()
          reject(new Error('未登录或登录已过期'))
          return
        }

        // 非 2xx
        if (statusCode < 200 || statusCode >= 300) {
          const msg = body && body.msg ? body.msg : `请求失败(${statusCode})`
          if (!silent) toast(msg)
          reject(new Error(msg))
          return
        }

        // 后端 RESTful 响应:{ code, msg, data }
        const code = body.code
        if (code === undefined || code === null) {
          // 无标准包裹(极少数接口),直接返回原始 body
          resolve(body)
          return
        }

        if (code === 200) {
          resolve(body.data !== undefined ? body.data : null)
          return
        }

        if (TOKEN_INVALID_CODES.indexOf(code) > -1) {
          redirectToLogin()
          reject(new Error(body.msg || '登录已过期'))
          return
        }

        if (!silent) toast(body.msg)
        reject(new Error(body.msg || '请求出错'))
      },
      fail: (err) => {
        if (!silent) toast('网络异常,请稍后重试')
        reject(err)
      },
      complete: () => {
        if (loading) uni.hideLoading()
      }
    })
  })
}