chore: add frontend/src/services/apiClient.ts

This commit is contained in:
2026-04-29 10:53:35 +08:00
parent bba2d29571
commit 046671bfdb
+100
View File
@@ -0,0 +1,100 @@
/**
* API 客户端 - ApiClient
* 功能:封装 fetch API,提供统一的 HTTP 请求处理、错误捕获和响应解析
* 设计模式:类封装 + 泛型支持,确保类型安全
* 使用方式:通过导出单例 apiClient 调用,避免重复实例化
*/
import type { ApiResponse } from '../types';
// 直接使用后端 URL,避免 Vite 代理问题
// 生产环境应改为环境变量 VITE_API_BASE_URL
const API_BASE_URL = 'http://localhost:3001/api';
class ApiClient {
private baseUrl: string;
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
}
/**
* 核心请求方法 - 处理 URL 拼接、请求配置、响应解析和错误捕获
* @param endpoint - API 路径(如 '/accounts'
* @param options - fetch 配置对象,支持 method、headers、body 等
* @returns 标准化的 ApiResponse 对象
*/
private async request<T>(
endpoint: string,
options: RequestInit = {}
): Promise<ApiResponse<T>> {
const url = `${this.baseUrl}${endpoint}`;
// 默认设置 Content-Type 为 application/json,允许调用方覆盖
const config: RequestInit = {
headers: {
'Content-Type': 'application/json',
...options.headers,
},
...options,
};
try {
const response = await fetch(url, config);
const data = await response.json();
// 根据 HTTP 状态码判断请求是否成功
if (!response.ok) {
throw new Error(data.message || 'Request failed');
}
return data;
} catch (error) {
// 统一错误日志输出,方便调试
console.error('API Error:', error);
throw error;
}
}
/**
* GET 请求 - 支持 URL 查询参数拼接
* @param endpoint - API 路径
* @param params - 查询参数对象,自动忽略 undefined/null 值
*/
async get<T>(endpoint: string, params?: Record<string, string | number>): Promise<ApiResponse<T>> {
let url = endpoint;
if (params) {
const searchParams = new URLSearchParams();
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined && value !== null) {
searchParams.append(key, String(value));
}
});
url = `${endpoint}?${searchParams.toString()}`;
}
return this.request<T>(url, { method: 'GET' });
}
// API: POST 请求 - 用于创建资源
async post<T>(endpoint: string, data?: unknown): Promise<ApiResponse<T>> {
return this.request<T>(endpoint, {
method: 'POST',
body: data ? JSON.stringify(data) : undefined,
});
}
// API: PUT 请求 - 用于全量更新资源
async put<T>(endpoint: string, data?: unknown): Promise<ApiResponse<T>> {
return this.request<T>(endpoint, {
method: 'PUT',
body: data ? JSON.stringify(data) : undefined,
});
}
// API: DELETE 请求 - 用于删除资源
async delete<T>(endpoint: string): Promise<ApiResponse<T>> {
return this.request<T>(endpoint, { method: 'DELETE' });
}
}
// 导出单例 - 全局共享一个 ApiClient 实例
export const apiClient = new ApiClient(API_BASE_URL);