Files
dukang/apps/mini-hq/src/lib/api.ts
T
jacy 8f83624ffe chore(dev): move local API default port to 3010
Avoid conflict with another local service on 3000; align Vite proxies and docs.
2026-08-07 21:03:23 +08:00

100 lines
2.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Taro from '@tarojs/taro';
function resolveApiBase(): string {
const origin =
typeof TARO_APP_API_ORIGIN !== 'undefined' && TARO_APP_API_ORIGIN
? TARO_APP_API_ORIGIN
: process.env.TARO_ENV === 'h5'
? 'http://localhost:3010'
: '';
if (origin) {
return `${origin.replace(/\/$/, '')}/api/v1`;
}
return '/api/v1';
}
export const API_BASE = resolveApiBase();
const TOKEN_KEY = 'hq_access_token';
const CLIENT_APP = 'HQ_WEB';
export function getToken(): string {
try {
return Taro.getStorageSync(TOKEN_KEY) || '';
} catch {
return '';
}
}
export function saveToken(token: string) {
Taro.setStorageSync(TOKEN_KEY, token);
}
export function clearToken() {
Taro.removeStorageSync(TOKEN_KEY);
}
export function isLoggedIn(): boolean {
return !!getToken();
}
export function redirectToLogin() {
Taro.reLaunch({ url: '/pages/login/index' });
}
export function logout() {
clearToken();
redirectToLogin();
}
type ReqOptions = {
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
data?: Record<string, unknown> | unknown;
auth?: boolean;
};
function parseBody(data: unknown): { code?: number; message?: string } {
if (data && typeof data === 'object') {
return data as { code?: number; message?: string };
}
return {};
}
/** 统一请求:注入 X-Client-App + Bearer,解包 { code, message, data }401 自动回登录 */
export async function request<T = unknown>(path: string, options: ReqOptions = {}): Promise<T> {
const header: Record<string, string> = {
'Content-Type': 'application/json',
'X-Client-App': CLIENT_APP,
};
const token = getToken();
if (token) header.Authorization = `Bearer ${token}`;
const res = await Taro.request({
url: `${API_BASE}${path}`,
method: options.method ?? 'GET',
data: options.data as Record<string, unknown>,
header,
});
const status = res.statusCode;
const body = parseBody(res.data);
if (status === 401 || body?.code === 401) {
clearToken();
redirectToLogin();
throw new Error(body?.message || '登录已过期,请重新登录');
}
if (status === 404 && body.code === undefined) {
throw new Error('接口不可达,请确认 API 服务已启动');
}
if (status >= 400 || body.code !== 0) {
throw new Error(body?.message || `请求失败(${status})`);
}
return (res.data as { data: T }).data;
}
export type Paginated<T> = { items: T[]; total: number };
export function toast(title: string, icon: 'success' | 'error' | 'none' = 'none') {
Taro.showToast({ title, icon, duration: 1800 });
}