- 暂时移除未使用的变量
- 配置项目依赖
- 组织项目基础结构
- 完成auth页面书写
Change-Id: I132c32f131111121619eb69240ece0a39e295c36
diff --git a/src/api/authApi.ts b/src/api/authApi.ts
new file mode 100644
index 0000000..b28b04f
--- /dev/null
+++ b/src/api/authApi.ts
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/api/client.ts b/src/api/client.ts
new file mode 100644
index 0000000..623ee78
--- /dev/null
+++ b/src/api/client.ts
@@ -0,0 +1,148 @@
+type RequestOptions = {
+ method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
+ headers?: Record<string, string>;
+ body?: unknown;
+ queryParams?: Record<string, string | number>;
+};
+
+type Interceptor = {
+ request?: (config: RequestInit) => RequestInit;
+ requestError?: (error: unknown) => Promise<never>; // 错误拦截器应始终抛出
+ response?: (response: Response) => Response | Promise<Response>;
+ // 错误拦截器必须抛出或返回与请求相同类型的结果
+ responseError?: <T>(error: unknown) => Promise<T>;
+};
+
+export class HttpClient {
+ private static instance: HttpClient;
+ private baseUrl: string;
+ private interceptors: Interceptor[] = [];
+
+ private constructor(baseUrl: string) {
+ this.baseUrl = baseUrl;
+ }
+
+ public static getInstance(baseUrl: string): HttpClient {
+ if (!HttpClient.instance) {
+ HttpClient.instance = new HttpClient(baseUrl);
+ }
+ return HttpClient.instance;
+ }
+
+ // 添加拦截器
+ addInterceptor(interceptor: Interceptor) {
+ this.interceptors.push(interceptor);
+ return this; // 支持链式调用
+ }
+
+ async request<T>(endpoint: string, options: RequestOptions = {}): Promise<T> {
+ const {
+ method = 'GET',
+ headers = {},
+ body,
+ queryParams = {}
+ } = options;
+
+ const url = new URL(`${this.baseUrl}${endpoint}`);
+ Object.entries(queryParams).forEach(([key, value]) => {
+ url.searchParams.append(key, String(value));
+ });
+
+ let config: RequestInit = {
+ method,
+ headers: {
+ 'Content-Type': 'application/json',
+ ...headers
+ }
+ };
+
+ if (body) {
+ config.body = JSON.stringify(body);
+ }
+
+ // 请求拦截器处理
+ for (const interceptor of this.interceptors) {
+ if (interceptor.request) {
+ config = interceptor.request(config);
+ }
+ }
+
+ try {
+ let response = await fetch(url.toString(), config);
+
+ // 响应拦截器处理
+ for (const interceptor of this.interceptors) {
+ if (interceptor.response) {
+ response = await interceptor.response(response);
+ }
+ }
+
+ if (!response.ok) {
+ const errorData = await response.json();
+ throw new Error(`HTTP error! Status: ${response.status}, Message: ${errorData.message || 'Unknown error'}`);
+ }
+
+ return await response.json();
+ } catch (error) {
+ // 改进的错误拦截器处理
+ let lastError = error;
+
+ for (const interceptor of this.interceptors) {
+ if (interceptor.responseError) {
+ try {
+ // 错误拦截器必须返回Promise<T>或抛出
+ return await interceptor.responseError<T>(lastError);
+ } catch (e) {
+ // 保存最新错误并继续下一个拦截器
+ lastError = e;
+ console.error('Interceptor error:', e);
+ }
+ }
+ }
+
+ // 如果所有拦截器都未能处理错误,则抛出最后一个错误
+ throw lastError;
+ }
+ }
+ get<T>(endpoint: string, queryParams?: Record<string, string | number>) {
+ return this.request<T>(endpoint, { queryParams });
+ }
+
+ post<T>(endpoint: string, body?: unknown, headers?: Record<string, string>) {
+ return this.request<T>(endpoint, { method: 'POST', body, headers });
+ }
+
+ put<T>(endpoint: string, body?: unknown, headers?: Record<string, string>) {
+ return this.request<T>(endpoint, { method: 'PUT', body, headers });
+ }
+
+ delete<T>(endpoint: string, headers?: Record<string, string>) {
+ return this.request<T>(endpoint, { method: 'DELETE', headers });
+ }
+}
+
+const client = HttpClient.getInstance('http://localhost:8080/');
+// 添加Token注入拦截器
+client.addInterceptor({
+ request: (config) => {
+ const token = localStorage.getItem('token'); // 从存储中获取token
+ if (token) {
+ config.headers = {
+ ...config.headers,
+ 'Authorization': `Bearer ${token}`
+ };
+ }
+ return config;
+ },
+ responseError: async (error) => {
+ if (error instanceof Error && error.message.includes('401')) {
+ console.log('Token expired, redirecting to login...');
+ // 示例:重定向到登录页
+ window.location.href = '/login';
+ // 必须返回Promise<never>或抛出
+ return new Promise(() => {}); // 永远不解决的Promise
+ }
+ throw error;
+ }
+});
+export default client as HttpClient;
\ No newline at end of file
diff --git a/src/api/type.ts b/src/api/type.ts
new file mode 100644
index 0000000..a5dd1ca
--- /dev/null
+++ b/src/api/type.ts
@@ -0,0 +1,10 @@
+export interface LoginRequest {
+ email: string;
+ password: string;
+}
+
+export interface LoginResponse {
+ user: string;
+ token: string;
+ refreshToken: string;
+}
\ No newline at end of file