添加登录和后端校验

完成后端设计(未在本仓库体现),通过安全的手段完成了登录鉴权
This commit is contained in:
2026-08-06 16:27:36 +08:00
commit b19a56003f
286 changed files with 51650 additions and 0 deletions
+126
View File
@@ -0,0 +1,126 @@
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