| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 |
- import { joinApiUrl } from '@/config'
- import { getToken } from '@/utils/auth'
- import errorCode from '@/utils/errorCode'
- import { useUserStore } from '@/store/user'
- /** 是否正在处理 401 跳转,避免重复提示 */
- export const isRelogin = { show: false }
- const SESSION_EXPIRED_MSG = '登录过期,即将登录'
- const LOGIN_PAGE_URL = '/pages/login/index'
- const LOGIN_REDIRECT_DELAY_MS = 400
- /** 与 ruoyi-ui tansParams 一致:null / undefined / 空字符串不参与序列化 */
- function isEmptyParam(value) {
- return value === null || value === undefined || value === ''
- }
- /**
- * 剔除空值形参(GET 查询用)
- */
- export function sanitizeParams(params) {
- if (params == null || typeof params !== 'object') {
- return params
- }
- if (Array.isArray(params)) {
- return params
- }
- const result = {}
- Object.keys(params).forEach((key) => {
- const value = params[key]
- if (isEmptyParam(value)) {
- return
- }
- if (typeof value === 'object' && !Array.isArray(value)) {
- const nested = sanitizeParams(value)
- if (nested && Object.keys(nested).length > 0) {
- result[key] = nested
- }
- return
- }
- result[key] = value
- })
- return result
- }
- function appendQuery(url, params) {
- const parts = []
- const build = (obj, prefix) => {
- Object.keys(obj).forEach((key) => {
- const value = obj[key]
- const name = prefix ? `${prefix}[${key}]` : key
- if (value !== null && value !== '' && typeof value !== 'undefined') {
- if (typeof value === 'object' && !Array.isArray(value)) {
- build(value, name)
- } else {
- parts.push(`${encodeURIComponent(name)}=${encodeURIComponent(value)}`)
- }
- }
- })
- }
- build(params, '')
- if (!parts.length) {
- return url
- }
- const qs = parts.join('&')
- return url + (url.indexOf('?') >= 0 ? '&' : '?') + qs
- }
- /**
- * 封装 uni.request,响应格式与 ruoyi-ui axios 拦截器一致
- */
- export function request(options = {}) {
- const header = { ...(options.header || {}) }
- const skipToken = header.isToken === false
- delete header.isToken
- delete header.repeatSubmit
- // 登录等接口不得携带过期 Token,否则部分环境下会误返回 401
- if (skipToken) {
- delete header.Authorization
- } else if (getToken()) {
- header.Authorization = 'Bearer ' + getToken()
- }
- const method = (options.method || 'GET').toUpperCase()
- let url = (options.url || '').startsWith('http')
- ? options.url
- : joinApiUrl(options.url)
- let requestData = options.data !== undefined ? options.data : options.params
- if (method === 'GET' && requestData && typeof requestData === 'object' && !Array.isArray(requestData)) {
- const cleaned = sanitizeParams(requestData)
- const keys = Object.keys(cleaned || {})
- if (keys.length) {
- url = appendQuery(url, cleaned)
- }
- requestData = undefined
- }
- return new Promise((resolve, reject) => {
- uni.request({
- url,
- method,
- data: requestData,
- header: {
- 'Content-Type': 'application/json;charset=utf-8',
- ...header
- },
- timeout: options.timeout || 10000,
- success: (res) => {
- const httpStatus = res.statusCode || 200
- const data = parseResponseData(res.data)
- const bizCode = data.code !== undefined && data.code !== null ? Number(data.code) : null
- const code = bizCode !== null && !Number.isNaN(bizCode) ? bizCode : httpStatus >= 200 && httpStatus < 300 ? 200 : httpStatus
- const msg = resolveErrorMessage(data, code)
- if (code === 401) {
- // 登录、验证码等未带 Token 的接口:仍展示后端 msg
- if (skipToken) {
- showErrorToast(msg)
- reject(new Error(msg))
- } else {
- handle401SessionExpired()
- reject(new Error(SESSION_EXPIRED_MSG))
- }
- return
- }
- if (code !== 200) {
- if (!options.silent) {
- showErrorToast(msg)
- }
- reject(new Error(msg || 'error'))
- return
- }
- resolve(data)
- },
- fail: (err) => {
- let message = err.errMsg || '网络异常'
- if (message.includes('timeout')) {
- message = '系统接口请求超时'
- } else if (message.includes('fail')) {
- message = '后端接口连接异常'
- }
- showErrorToast(message)
- reject(err)
- }
- })
- })
- }
- /** 解析响应体(uni 有时返回字符串) */
- function parseResponseData(raw) {
- if (raw == null || raw === '') {
- return {}
- }
- if (typeof raw === 'object') {
- return raw
- }
- if (typeof raw === 'string') {
- try {
- return JSON.parse(raw)
- } catch (e) {
- return { msg: raw }
- }
- }
- return {}
- }
- /**
- * 错误文案:优先后端 msg,其次 errorCode 映射,最后默认
- */
- function resolveErrorMessage(data, code) {
- const serverMsg = data && data.msg != null ? String(data.msg).trim() : ''
- if (serverMsg) {
- return serverMsg
- }
- const key = String(code)
- if (errorCode[key]) {
- return errorCode[key]
- }
- return errorCode.default || '请求失败'
- }
- function showErrorToast(message) {
- const title = (message && String(message).trim()) || errorCode.default || '请求失败'
- uni.showToast({
- title: title.length > 40 ? title.slice(0, 40) + '…' : title,
- icon: 'none',
- duration: 3000
- })
- }
- function handle401SessionExpired() {
- if (isRelogin.show) {
- return
- }
- isRelogin.show = true
- uni.showToast({
- title: SESSION_EXPIRED_MSG,
- icon: 'none',
- duration: 2000
- })
- setTimeout(() => {
- const userStore = useUserStore()
- userStore.fedLogOut().finally(() => {
- isRelogin.show = false
- uni.reLaunch({ url: LOGIN_PAGE_URL })
- })
- }, LOGIN_REDIRECT_DELAY_MS)
- }
- export default request
|