|
@@ -1,5 +1,3 @@
|
|
|
-// src/utils/request.ts
|
|
|
|
|
-import Request from 'luch-request'
|
|
|
|
|
import {
|
|
import {
|
|
|
BASE_API,
|
|
BASE_API,
|
|
|
MODE,
|
|
MODE,
|
|
@@ -10,93 +8,209 @@ import {
|
|
|
} from './config'
|
|
} from './config'
|
|
|
import { useUserStore } from '@/stores/modules/user'
|
|
import { useUserStore } from '@/stores/modules/user'
|
|
|
|
|
|
|
|
-const http = new Request({
|
|
|
|
|
- timeout: TIMEOUT,
|
|
|
|
|
-})
|
|
|
|
|
-
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
-/* 全局配置 */
|
|
|
|
|
|
|
+/* 类型定义 */
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
-// #ifdef MP-WEIXIN
|
|
|
|
|
-http.setConfig((config) => {
|
|
|
|
|
- config.baseURL = BASE_API
|
|
|
|
|
- return config
|
|
|
|
|
-})
|
|
|
|
|
-// #endif
|
|
|
|
|
-
|
|
|
|
|
-// #ifdef H5
|
|
|
|
|
-http.setConfig((config) => {
|
|
|
|
|
- config.baseURL = MODE === 'development' ? '' : BASE_API
|
|
|
|
|
- return config
|
|
|
|
|
-})
|
|
|
|
|
-// #endif
|
|
|
|
|
|
|
+/** 后端统一返回结构 */
|
|
|
|
|
+export interface ApiResponse<T = any> {
|
|
|
|
|
+ code: number
|
|
|
|
|
+ msg: string
|
|
|
|
|
+ data: T
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+export interface BaseOptions {
|
|
|
|
|
+ header?: UniApp.RequestOptions['header']
|
|
|
|
|
+ timeout?: number
|
|
|
|
|
+ silent?: boolean
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+export interface RequestConfig extends BaseOptions {
|
|
|
|
|
+ url: string
|
|
|
|
|
+ method?: UniApp.RequestOptions['method']
|
|
|
|
|
+ data?: any
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
-/* 白名单接口 */
|
|
|
|
|
|
|
+/* 核心 request */
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
-const RESPONSE_WHITE_LIST = ['/auth/oauth/token', '/auth/mobile/token/sms']
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * 默认请求:永远返回 ApiResponse<T>
|
|
|
|
|
+ * - 白名单:不校验 code,但仍返回 ApiResponse<T>
|
|
|
|
|
+ * - 非白名单:校验 code,不通过 reject(ApiResponse<T>)
|
|
|
|
|
+ */
|
|
|
|
|
+export function request<T = any>(config: RequestConfig): Promise<ApiResponse<T>> {
|
|
|
|
|
+ const userStore = useUserStore()
|
|
|
|
|
+
|
|
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
|
|
+ uni.request({
|
|
|
|
|
+ url: resolveUrl(config.url),
|
|
|
|
|
+ method: config.method || 'GET',
|
|
|
|
|
+ data: config.data,
|
|
|
|
|
+ timeout: config.timeout ?? TIMEOUT,
|
|
|
|
|
+ header: buildHeaders(config.url, userStore, config.header),
|
|
|
|
|
+
|
|
|
|
|
+ success(res) {
|
|
|
|
|
+ const result = res.data as ApiResponse<T>
|
|
|
|
|
+
|
|
|
|
|
+ // 白名单接口:不校验 code,直接放行(仍返回 ApiResponse<T>)
|
|
|
|
|
+ if (isWhiteList(config.url)) {
|
|
|
|
|
+ return resolve(result)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 业务失败
|
|
|
|
|
+ if (!SUCCESS_CODE_LIST.includes(result.code)) {
|
|
|
|
|
+ if (!config.silent) {
|
|
|
|
|
+ uni.showToast({
|
|
|
|
|
+ title: result.msg || '业务错误',
|
|
|
|
|
+ icon: 'none',
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ return reject(result)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ resolve(result)
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ fail(err) {
|
|
|
|
|
+ uni.showToast({
|
|
|
|
|
+ title: '网络异常',
|
|
|
|
|
+ icon: 'none',
|
|
|
|
|
+ })
|
|
|
|
|
+ reject(err)
|
|
|
|
|
+ },
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 特殊接口:后端不包 ApiResponse,直接返回 T
|
|
|
|
|
+ * - 不做 code 校验
|
|
|
|
|
+ * - 适用于少量 legacy 接口
|
|
|
|
|
+ */
|
|
|
|
|
+export function requestRaw<T = any>(config: RequestConfig): Promise<T> {
|
|
|
|
|
+ const userStore = useUserStore()
|
|
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
|
|
+ uni.request({
|
|
|
|
|
+ url: resolveUrl(config.url),
|
|
|
|
|
+ method: config.method || 'GET',
|
|
|
|
|
+ data: config.data,
|
|
|
|
|
+ timeout: config.timeout ?? TIMEOUT,
|
|
|
|
|
+ header: buildHeaders(config.url, userStore, config.header),
|
|
|
|
|
+ success(res) {
|
|
|
|
|
+ resolve(res.data as T)
|
|
|
|
|
+ },
|
|
|
|
|
+ fail(err) {
|
|
|
|
|
+ uni.showToast({
|
|
|
|
|
+ title: '网络异常',
|
|
|
|
|
+ icon: 'none',
|
|
|
|
|
+ })
|
|
|
|
|
+ reject(err)
|
|
|
|
|
+ },
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
-/* 请求拦截器 */
|
|
|
|
|
|
|
+/* 默认 http(get / post 语法糖) */
|
|
|
/* -------------------------------------------------------------------------- */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
-http.interceptors.request.use(
|
|
|
|
|
- (config) => {
|
|
|
|
|
- config.header = config.header || {}
|
|
|
|
|
- const userStore = useUserStore()
|
|
|
|
|
- const token = userStore.token
|
|
|
|
|
- const url = config.url || ''
|
|
|
|
|
-
|
|
|
|
|
- if (url.includes('auth/mobile/token/sms')) {
|
|
|
|
|
- config.header.Authorization = NOT_OAUTH_BASIC_TOKEN
|
|
|
|
|
- } else if (url.includes('auth/oauth/token')) {
|
|
|
|
|
- config.header.Authorization = OAUTH_BASIC_TOKEN
|
|
|
|
|
- config.header['TENANT-ID'] = '1'
|
|
|
|
|
- } else if (token) {
|
|
|
|
|
- config.header.Authorization = `Bearer ${token}`
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return config
|
|
|
|
|
|
|
+const http = {
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 默认 get:返回 ApiResponse<T>
|
|
|
|
|
+ */
|
|
|
|
|
+ get<T = any>(url: string, params?: any, options?: BaseOptions) {
|
|
|
|
|
+ return request<T>({
|
|
|
|
|
+ url,
|
|
|
|
|
+ method: 'GET',
|
|
|
|
|
+ data: params,
|
|
|
|
|
+ ...options,
|
|
|
|
|
+ })
|
|
|
},
|
|
},
|
|
|
- (config) => Promise.reject(config)
|
|
|
|
|
-)
|
|
|
|
|
|
|
|
|
|
-/* -------------------------------------------------------------------------- */
|
|
|
|
|
-/* 响应拦截器 */
|
|
|
|
|
-/* -------------------------------------------------------------------------- */
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 默认 post:返回 ApiResponse<T>
|
|
|
|
|
+ */
|
|
|
|
|
+ post<T = any>(url: string, data?: any, options?: BaseOptions) {
|
|
|
|
|
+ return request<T>({
|
|
|
|
|
+ url,
|
|
|
|
|
+ method: 'POST',
|
|
|
|
|
+ data,
|
|
|
|
|
+ ...options,
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 特殊接口 get:返回 T
|
|
|
|
|
+ */
|
|
|
|
|
+ getRaw<T = any>(url: string, params?: any, options?: BaseOptions) {
|
|
|
|
|
+ return requestRaw<T>({
|
|
|
|
|
+ url,
|
|
|
|
|
+ method: 'GET',
|
|
|
|
|
+ data: params,
|
|
|
|
|
+ ...options,
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
-http.interceptors.response.use(
|
|
|
|
|
- (response) => {
|
|
|
|
|
- const { config, data } = response
|
|
|
|
|
- const url = config?.url || ''
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 特殊接口 post:返回 T
|
|
|
|
|
+ */
|
|
|
|
|
+ postRaw<T = any>(url: string, data?: any, options?: BaseOptions) {
|
|
|
|
|
+ return requestRaw<T>({
|
|
|
|
|
+ url,
|
|
|
|
|
+ method: 'POST',
|
|
|
|
|
+ data,
|
|
|
|
|
+ ...options,
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- // 1️⃣ 白名单接口:直接放行
|
|
|
|
|
- if (RESPONSE_WHITE_LIST.some((item) => url.includes(item))) {
|
|
|
|
|
- return response
|
|
|
|
|
- }
|
|
|
|
|
|
|
+export default http
|
|
|
|
|
|
|
|
- const { code, msg } = (data || {}) as ApiResponse<any>
|
|
|
|
|
|
|
+/* -------------------------------------------------------------------------- */
|
|
|
|
|
+/* 工具函数 */
|
|
|
|
|
+/* -------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
- if (!SUCCESS_CODE_LIST.includes(code)) {
|
|
|
|
|
- uni.showToast({
|
|
|
|
|
- title: msg || '业务错误',
|
|
|
|
|
- icon: 'none',
|
|
|
|
|
- })
|
|
|
|
|
- return Promise.reject(response)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+function resolveUrl(url: string) {
|
|
|
|
|
+ // 小程序环境:必须使用绝对路径
|
|
|
|
|
+ // #ifdef MP
|
|
|
|
|
+ return BASE_API + url
|
|
|
|
|
+ // #endif
|
|
|
|
|
+
|
|
|
|
|
+ // H5 环境:开发时允许走代理(相对路径)
|
|
|
|
|
+ // #ifdef H5
|
|
|
|
|
+ if (MODE === 'development') return url
|
|
|
|
|
+ return BASE_API + url
|
|
|
|
|
+ // #endif
|
|
|
|
|
+
|
|
|
|
|
+ // 其他平台兜底
|
|
|
|
|
+ return BASE_API + url
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function isWhiteList(url: string) {
|
|
|
|
|
+ return ['/auth/oauth/token', '/auth/mobile/token/sms'].some((item) => url.includes(item))
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function buildHeaders(
|
|
|
|
|
+ url: string,
|
|
|
|
|
+ userStore: ReturnType<typeof useUserStore>,
|
|
|
|
|
+ extraHeader?: UniApp.RequestOptions['header']
|
|
|
|
|
+) {
|
|
|
|
|
+ const headers: Record<string, any> = {
|
|
|
|
|
+ ...(extraHeader || {}),
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // ⚠️ 注意:不解包,仍然返回 { code, msg, data }
|
|
|
|
|
- return response
|
|
|
|
|
- },
|
|
|
|
|
- (error) => {
|
|
|
|
|
- uni.showToast({
|
|
|
|
|
- title: '网络异常',
|
|
|
|
|
- icon: 'none',
|
|
|
|
|
- })
|
|
|
|
|
- return Promise.reject(error)
|
|
|
|
|
|
|
+ const token = userStore.token
|
|
|
|
|
+
|
|
|
|
|
+ if (url.includes('auth/mobile/token/sms')) {
|
|
|
|
|
+ headers.Authorization = NOT_OAUTH_BASIC_TOKEN
|
|
|
|
|
+ } else if (url.includes('auth/oauth/token')) {
|
|
|
|
|
+ headers.Authorization = OAUTH_BASIC_TOKEN
|
|
|
|
|
+ headers['TENANT-ID'] = '1'
|
|
|
|
|
+ } else if (token) {
|
|
|
|
|
+ headers.Authorization = `Bearer ${token}`
|
|
|
}
|
|
}
|
|
|
-)
|
|
|
|
|
|
|
|
|
|
-export default http
|
|
|
|
|
|
|
+ return headers
|
|
|
|
|
+}
|