127 lines
3.5 KiB
TypeScript
127 lines
3.5 KiB
TypeScript
import Taro from '@tarojs/taro'
|
||
|
||
/**
|
||
* 后端接口请求封装
|
||
* 后端统一响应:{ code, message, data },code === 0 表示成功
|
||
* token 通过 Authorization: Bearer <token> 传入
|
||
*/
|
||
export const BASE_URL = 'https://wxbackend.tokenleaping.com'
|
||
|
||
const TOKEN_KEY = 'smart_access_token'
|
||
|
||
/** 安全隐藏 loading:没有活动 loading 时调用 hideLoading 会抛 hideToast:fail,这里吞掉 */
|
||
export function safeHideLoading(): void {
|
||
try { Taro.hideLoading() } catch { /* ignore */ }
|
||
}
|
||
|
||
// ---------- token 存取 ----------
|
||
|
||
export function getToken(): string {
|
||
try {
|
||
return Taro.getStorageSync(TOKEN_KEY) || ''
|
||
} catch {
|
||
return ''
|
||
}
|
||
}
|
||
|
||
export function setToken(token: string): void {
|
||
Taro.setStorageSync(TOKEN_KEY, token)
|
||
}
|
||
|
||
export function clearToken(): void {
|
||
try {
|
||
Taro.removeStorageSync(TOKEN_KEY)
|
||
} catch {
|
||
/* ignore */
|
||
}
|
||
}
|
||
|
||
// ---------- 类型 ----------
|
||
|
||
interface ApiResponse<T = unknown> {
|
||
code: number
|
||
message: string
|
||
data: T
|
||
}
|
||
|
||
export interface RequestOptions {
|
||
method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
|
||
data?: unknown
|
||
/** 额外请求头 */
|
||
headers?: Record<string, string>
|
||
/** 是否自动携带 token,默认 true */
|
||
auth?: boolean
|
||
}
|
||
|
||
// ---------- request ----------
|
||
|
||
/**
|
||
* 发请求。成功(code===0)返回 data;业务失败抛 Error(message);HTTP 401 清 token 并抛错。
|
||
* 调用方可自行 catch 或用 onUnauthorized 钩子统一处理跳登录。
|
||
*/
|
||
export async function request<T = unknown>(
|
||
path: string,
|
||
options: RequestOptions = {},
|
||
): Promise<T> {
|
||
const { method = 'GET', data, headers = {}, auth = true } = options
|
||
|
||
const token = getToken()
|
||
const finalHeaders: Record<string, string> = {
|
||
'Content-Type': 'application/json',
|
||
...(auth && token ? { Authorization: `Bearer ${token}` } : {}),
|
||
...headers,
|
||
}
|
||
|
||
let res: Taro.request.SuccessCallbackResult
|
||
try {
|
||
res = await Taro.request({
|
||
url: `${BASE_URL}${path}`,
|
||
method,
|
||
data,
|
||
header: finalHeaders,
|
||
})
|
||
} catch (e) {
|
||
// 网络层异常
|
||
throw new Error('网络异常,请稍后重试')
|
||
}
|
||
|
||
// HTTP 未授权:清 token,交回调处理
|
||
if (res.statusCode === 401) {
|
||
clearToken()
|
||
onUnauthorized?.()
|
||
throw new Error('登录已过期,请重新登录')
|
||
}
|
||
|
||
const body = res.data as ApiResponse<T>
|
||
|
||
// 后端业务码非 0:当作错误抛出
|
||
if (body && typeof body.code === 'number' && body.code !== 0) {
|
||
throw new Error(body.message || '请求失败')
|
||
}
|
||
|
||
return (body && typeof body.code === 'number' ? body.data : body) as T
|
||
}
|
||
|
||
/** 401 钩子,由业务方注册(如跳转登录页) */
|
||
export let onUnauthorized: (() => void) | null = null
|
||
export function setOnUnauthorized(handler: () => void): void {
|
||
onUnauthorized = handler
|
||
}
|
||
|
||
// ---------- 便捷方法 ----------
|
||
|
||
export const http = {
|
||
get: <T = unknown>(path: string, options?: RequestOptions) =>
|
||
request<T>(path, { ...options, method: 'GET' }),
|
||
post: <T = unknown>(path: string, data?: unknown, options?: RequestOptions) =>
|
||
request<T>(path, { ...options, method: 'POST', data }),
|
||
put: <T = unknown>(path: string, data?: unknown, options?: RequestOptions) =>
|
||
request<T>(path, { ...options, method: 'PUT', data }),
|
||
patch: <T = unknown>(path: string, data?: unknown, options?: RequestOptions) =>
|
||
request<T>(path, { ...options, method: 'PATCH', data }),
|
||
del: <T = unknown>(path: string, options?: RequestOptions) =>
|
||
request<T>(path, { ...options, method: 'DELETE' }),
|
||
}
|
||
|
||
export default http
|