request.js 4.46 KB
import config from '@/config.js'
import _ from 'underscore'

const excludeUrls = ['getMemberCountInfo', 'getInfo']
const SUCCESS_CODES = [0, 200]
const TOKEN_EXPIRE_CODES = [60001, 60002, 401]

// Loading计数器
let loadingCount = 0

// 获取Token
function getToken() {
  try {
    return uni.getStorageSync('token') || ''
  } catch (e) {
    console.error('获取token失败:', e)
    return ''
  }
}

// 获取请求头
function getHeaders() {
  return {
    'Authorization': getToken(),
    'Content-Type': 'application/json',
    'Content-Language': 'zh_CN',
    'Ztx-Per-Id': uni.getStorageSync('perId') || '-1'
  }
}

// 显示Loading
function showLoading(title = '加载中...') {
  if (loadingCount == 0) {
    uni.showLoading({title, mask: true})
  }
  loadingCount++
}

// 隐藏Loading
function hideLoading() {
  loadingCount--
  if (loadingCount == 0) {
    uni.hideLoading()
  }
}

// Token过期处理
function handleTokenExpire() {
  // 清除本地存储
  uni.removeStorageSync('token')
  uni.removeStorageSync('perId')
  
  // 跳转登录页
  setTimeout(() => {
    uni.redirectTo({
      url: '/login/login'
    })
  }, 1500)
}

// 显示错误提示
function showError(msg) {
  console.log('showError called:', msg)
  // 先隐藏可能存在的 loading,确保 Toast 能显示
  if (loadingCount > 0) {
    loadingCount = 0
    uni.hideLoading()
  }
  uni.showToast({
    title: msg || '请求失败',
    icon: 'none',
    duration: 3000
  })
}

const request = function (req) {
  // 参数校验
  if (!req || !req.url) {
    return Promise.reject(new Error('请求参数不完整'))
  }
  
  req.method = (req.method || 'GET').toUpperCase()
  const validMethods = ['GET', 'POST', 'PUT', 'DELETE']
  
  if (!validMethods.includes(req.method)) {
    const error = `暂不支持的请求方式: ${req.method}`
    showError(error)
    return Promise.reject(new Error(error))
  }
  
  // 显示Loading
  if (req.showLoading != false) {
    showLoading(req.loadingTitle)
  }
  
  return new Promise((resolve, reject) => {
    uni.request({
      url: config.baseUrl_api + req.url,
      method: req.method,
      data: req.params,
      header: getHeaders(),
      timeout: req.timeout || 60000 * 5 // 添加超时设置
    }).then(response => {
      const {statusCode, data} = response
     
      // HTTP状态码处理
      if (statusCode != 200) {
        throw new Error(`HTTP ${statusCode}`)
      }
      
      // 业务状态码处理
      const isSuccess = SUCCESS_CODES.includes(data.code) ||
        data.pageData?.code == 200 ||
        excludeUrls.some(url => req.url.includes(url))
      
      if (isSuccess) {
        resolve(data)
        return
      }
      
      // Token过期处理
      if (TOKEN_EXPIRE_CODES.includes(data.code)) {
        handleTokenExpire()
        reject(new Error('登录已过期,请重新登录'))
        return
      }

      // 业务错误(code != 200 且非 Token 过期)- 统一显示错误提示
      const errorMsg = data.msg || '操作失败'
      showError(errorMsg)

      // code == 500 时仅显示提示,不抛出异常,避免页面 catch 覆盖错误信息
      if (data.code == 500) {
        return
      }

      reject(new Error(errorMsg))
      
    }).catch(error => {
      console.error('请求失败:', error)

      // 网络错误处理
      if (error.errMsg) {
        if (error.errMsg.includes('timeout')) {
          showError('请求超时,请稍后重试')
        } else if (error.errMsg.includes('fail')) {
          showError('网络连接失败')
        } else {
          showError('请求失败,请稍后重试')
        }
      }

      reject(error)
    }).finally(() => {
      // 延迟隐藏 loading,确保错误提示能显示
      setTimeout(() => {
        if (req.showLoading != false) {
          hideLoading()
        }
      }, 100)
    })
  })
}

// 添加便捷方法
request.get = (url, params, options = {}) => {
  return request({...options, url, method: 'GET', params})
}

request.post = (url, params, options = {}) => {
  return request({...options, url, method: 'POST', params})
}

request.put = (url, params, options = {}) => {
  return request({...options, url, method: 'PUT', params})
}

request.delete = (url, params, options = {}) => {
  return request({...options, url, method: 'DELETE', params})
}

export default request