feat(应用): 实现带可折叠侧边栏的仪表盘布局
添加从根路径到/app/dashboard的服务器端重定向 创建包含主内容容器的应用布局组件 实现带响应式侧边栏和顶栏的仪表盘页面 添加带平滑过渡效果的侧边栏切换功能 设置基础用户状态管理仓库 使用对等依赖标志更新package-lock.json中的依赖项 从锁定文件中移除冗余的picomatch条目 添加带路径解析导入的登录页面组件
This commit is contained in:
132
src/lib/api/httpClient.ts
Normal file
132
src/lib/api/httpClient.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
// src/lib/api/httpClient.ts
|
||||
|
||||
import { browser } from '$app/environment';
|
||||
import type { HttpMethod, JsonObject, JsonValue } from '$lib/types/http.ts';
|
||||
import { authStore } from '$lib/stores/authStore.ts';
|
||||
import type { ApiResult } from '$lib/types/api.ts';
|
||||
|
||||
|
||||
interface RequestOptions extends Omit<RequestInit, 'method' | 'body'> {
|
||||
body?: JsonObject | FormData;
|
||||
}
|
||||
const API_BASE_URL = import.meta.env.VITE_PUBLIC_API_URL || 'http://localhost:18888/api';
|
||||
|
||||
let currentToken: string | null = null;
|
||||
let currentTokenHead: string | null = null;
|
||||
|
||||
if (browser) {
|
||||
// 只有在浏览器环境下才订阅,防止 SSR 内存泄漏
|
||||
authStore.subscribe(state => {
|
||||
currentToken = state.token;
|
||||
currentTokenHead = state.tokenHead;
|
||||
});
|
||||
}
|
||||
const normalizeHeaders = (headers?: HeadersInit):Record<string, string> =>{
|
||||
const result:Record<string,string> = {};
|
||||
|
||||
if (!headers){
|
||||
return result;
|
||||
}
|
||||
|
||||
if (headers instanceof Headers){
|
||||
headers.forEach((value, key) => {
|
||||
result[key.toLowerCase()] = value;
|
||||
});
|
||||
}else if (Array.isArray(headers)){
|
||||
headers.forEach(([key, value]) => {
|
||||
result[key.toLowerCase()] = value;
|
||||
})
|
||||
}else {
|
||||
Object.keys(headers).forEach(key => {
|
||||
result[key.toLowerCase()] = headers[key.toLowerCase()] as string;
|
||||
})
|
||||
}
|
||||
return result;
|
||||
}
|
||||
export class HttpError extends Error {
|
||||
public status: number;
|
||||
public details: JsonValue | string;
|
||||
|
||||
constructor(message: string, status: number, details: JsonValue | string) {
|
||||
super(message);
|
||||
this.name = 'HttpError';
|
||||
this.status = status;
|
||||
this.details = details;
|
||||
|
||||
// 保持正确的原型链
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(this, HttpError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const httpRequest= async <T>(
|
||||
url:string,
|
||||
method: HttpMethod,
|
||||
options: RequestOptions = {}
|
||||
):Promise<ApiResult<T>> =>{
|
||||
const fullUrl = `${API_BASE_URL}${url}`;
|
||||
const { body, headers, ...rest} = options;
|
||||
|
||||
const requestHeaders: Record<string, string> = normalizeHeaders(headers);
|
||||
let requestBody:BodyInit | undefined;
|
||||
|
||||
if (body instanceof FormData){
|
||||
requestBody = body;
|
||||
}else if (body){
|
||||
requestHeaders['content-type'] = 'application/json';
|
||||
requestBody = JSON.stringify(body);
|
||||
}
|
||||
|
||||
if (currentToken && currentTokenHead) {
|
||||
requestHeaders['authorization'] = `${currentTokenHead} ${currentToken}`;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(fullUrl,{
|
||||
method,
|
||||
headers: requestHeaders,
|
||||
body: requestBody,
|
||||
...rest
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
|
||||
let errorDetail;
|
||||
|
||||
try {
|
||||
|
||||
errorDetail = await response.json()
|
||||
|
||||
}catch (e){
|
||||
console.error('Error parsing JSON:', e);
|
||||
errorDetail = await response.text()
|
||||
}
|
||||
|
||||
const message = `HTTP Error ${response.status} (${response.statusText})`;
|
||||
throw new HttpError(message, response.status, errorDetail);
|
||||
}
|
||||
|
||||
const contentType = response.headers.get('Content-Type');
|
||||
if (contentType && contentType.includes('application/json')){
|
||||
return (await response.json() ) as ApiResult<T>;
|
||||
}
|
||||
|
||||
return {code:200, msg:'OK', data:null} ;
|
||||
|
||||
}catch (error){
|
||||
console.error(`API Request Failed to ${fullUrl}:`, error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export const api = {
|
||||
get: <T>(url: string, options?: RequestOptions) => httpRequest<T>(url, 'GET', options),
|
||||
post: <T>(url: string, body: JsonObject, options?: RequestOptions) => httpRequest<T>(url, 'POST', { ...options, body }),
|
||||
put: <T>(url: string, body: JsonObject, options?: RequestOptions) => httpRequest<T>(url, 'PUT', { ...options, body }),
|
||||
delete: <T>(url: string, options?: RequestOptions) => httpRequest<T>(url, 'DELETE', options),
|
||||
patch: <T>(url: string, body: JsonObject, options?: RequestOptions) => httpRequest<T>(url, 'PATCH', { ...options, body }),
|
||||
};
|
||||
Reference in New Issue
Block a user