Files
2026-07-15 22:38:33 +08:00

122 lines
3.2 KiB
JavaScript

/**
* 统一请求封装:baseURL、Bearer Token、业务 code 处理。
* 接口地址统一从 .env 的 VITE_API_BASE_URL 读取(见 api/config.js)。
*/
import { getToken, logout } from '@/utils/auth.js'
import { resolveBaseURL, getBaseURL } from '@/api/config.js'
const BASE_URL = resolveBaseURL()
export { getBaseURL }
/**
* @param {{ url: string, method?: string, data?: object, header?: object, silent?: boolean }} options
* @returns {Promise<any>} 业务 data 字段(若无 data 则返回整包)
*/
export function request(options = {}) {
const { url, method = 'GET', data, header = {}, silent = false, timeout = 30000 } = options
const token = getToken()
const headers = {
'Content-Type': 'application/json',
...header
}
if (token) {
headers.Authorization = `Bearer ${token}`
}
const fullUrl = BASE_URL.replace(/\/$/, '') + url
return new Promise((resolve, reject) => {
uni.request({
url: fullUrl,
method: method.toUpperCase(),
data: data || {},
header: headers,
timeout,
success(res) {
const status = res.statusCode
const body = res.data || {}
if (status === 401 || body.code === 401) {
logout()
if (!silent) {
uni.showToast({ title: body.msg || '请重新登录', icon: 'none' })
}
setTimeout(() => {
uni.reLaunch({ url: '/pages/login/login' })
}, 400)
reject(new Error(body.msg || '未授权'))
return
}
if (status < 200 || status >= 300) {
const msg = body.msg || `请求失败(${status})`
if (!silent) uni.showToast({ title: msg, icon: 'none' })
reject(new Error(msg))
return
}
// 统一后端 { code, msg, data }
if (typeof body.code !== 'undefined' && body.code !== 200) {
const msg = body.msg || '操作失败'
if (!silent) uni.showToast({ title: msg, icon: 'none' })
const err = new Error(msg)
err.code = body.code
err.response = body
reject(err)
return
}
resolve(typeof body.data !== 'undefined' ? body.data : body)
},
fail(err) {
const detail = (err && err.errMsg) || ''
console.error('[request fail]', fullUrl, detail)
if (!silent) {
uni.showToast({
title: detail.includes('timeout') ? '请求超时' : '网络异常,请检查接口地址',
icon: 'none'
})
}
reject(new Error(detail || '网络异常'))
}
})
})
}
/** 返回完整响应体(含 code/msg),用于需要自行判断 code 的场景 */
export function requestRaw(options = {}) {
const { url, method = 'GET', data, header = {} } = options
const token = getToken()
const headers = {
'Content-Type': 'application/json',
...header
}
if (token) {
headers.Authorization = `Bearer ${token}`
}
const fullUrl = BASE_URL.replace(/\/$/, '') + url
return new Promise((resolve, reject) => {
uni.request({
url: fullUrl,
method: method.toUpperCase(),
data: data || {},
header: headers,
timeout: 30000,
success(res) {
resolve(res.data || {})
},
fail(err) {
console.error('[requestRaw fail]', fullUrl, err)
reject(err)
}
})
})
}
export function generateId() {
return `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`
}