73 lines
1.6 KiB
TypeScript
73 lines
1.6 KiB
TypeScript
/**
|
|
* HTTP 请求封装
|
|
* - 自动注入 Authorization token
|
|
* - 统一异常处理
|
|
* - 类型友好的响应
|
|
*/
|
|
|
|
const API_BASE = import.meta.env.VITE_API_BASE || '/pms/api';
|
|
|
|
export interface ApiResponse<T = unknown> {
|
|
code: number;
|
|
data: T;
|
|
message: string;
|
|
}
|
|
|
|
export class ApiError extends Error {
|
|
public code: number;
|
|
|
|
constructor(
|
|
code: number,
|
|
message: string,
|
|
) {
|
|
super(message);
|
|
this.name = 'ApiError';
|
|
this.code = code;
|
|
}
|
|
}
|
|
|
|
function getToken(): string | null {
|
|
return localStorage.getItem('token');
|
|
}
|
|
|
|
async function request<T>(
|
|
url: string,
|
|
options: RequestInit = {},
|
|
): Promise<ApiResponse<T>> {
|
|
const token = getToken();
|
|
const headers: Record<string, string> = {
|
|
'Content-Type': 'application/json',
|
|
...(options.headers as Record<string, string>),
|
|
};
|
|
|
|
if (token) {
|
|
headers['Authorization'] = `Bearer ${token}`;
|
|
}
|
|
|
|
try {
|
|
const res = await fetch(`${API_BASE}${url}`, {
|
|
...options,
|
|
headers,
|
|
});
|
|
|
|
if (!res.ok) {
|
|
const body = await res.json().catch(() => ({}));
|
|
throw new ApiError(res.status, body.message || res.statusText);
|
|
}
|
|
|
|
return res.json();
|
|
} catch (err) {
|
|
if (err instanceof ApiError) throw err;
|
|
throw new ApiError(0, (err as Error).message || '网络错误');
|
|
}
|
|
}
|
|
|
|
export const api = {
|
|
get: <T>(url: string) => request<T>(url),
|
|
post: <T>(url: string, data?: unknown) =>
|
|
request<T>(url, { method: 'POST', body: JSON.stringify(data) }),
|
|
put: <T>(url: string, data?: unknown) =>
|
|
request<T>(url, { method: 'PUT', body: JSON.stringify(data) }),
|
|
delete: <T>(url: string) => request<T>(url, { method: 'DELETE' }),
|
|
};
|