import { useUserStore } from '@/store'; const BASE_URL = '/api/v1'; // 后端统一响应格式 export interface ApiResponse { code: number; message: string; data: T; } // 列表响应格式 export interface ListResponse { list: T[]; total: number; } // 通用请求函数 async function request( method: string, url: string, body?: any, params?: Record ): Promise { // 构建 URL(含 query params) let fullUrl = `${BASE_URL}${url}`; if (params) { const searchParams = new URLSearchParams(); Object.entries(params).forEach(([key, value]) => { if (value !== undefined && value !== null && value !== '') { searchParams.append(key, String(value)); } }); const qs = searchParams.toString(); if (qs) fullUrl += `?${qs}`; } // 获取 token const token = useUserStore.getState().token || localStorage.getItem('token'); const headers: Record = { 'Content-Type': 'application/json', }; if (token) { headers['Authorization'] = `Bearer ${token}`; } const response = await fetch(fullUrl, { method, headers, body: body ? JSON.stringify(body) : undefined, }); if (response.status === 401) { useUserStore.getState().logout(); window.location.href = '/login'; throw new Error('登录已过期,请重新登录'); } const json: ApiResponse = await response.json(); if (json.code && json.code !== 200) { throw new Error(json.message || '请求失败'); } // Return json.data for standard responses, or full json for non-standard ones return (json.data !== undefined ? json.data : json) as T; } // 导出便捷方法 export const http = { get: (url: string, params?: Record) => request('GET', url, undefined, params), post: (url: string, body?: any) => request('POST', url, body), put: (url: string, body?: any) => request('PUT', url, body), delete: (url: string, body?: any) => request('DELETE', url, body), getRaw: async (url: string) => { const tk = useUserStore.getState().token; const baseUrl = import.meta.env.VITE_API_BASE_URL || ''; const resp = await fetch(`${baseUrl}/api/v1${url}`, { headers: { Authorization: `Bearer ${tk}` } }); if (!resp.ok) throw new Error(`HTTP ${resp.status}`); return resp.text(); }, putRaw: async (url: string, body: string, contentType: string) => { const tk = useUserStore.getState().token; const baseUrl = import.meta.env.VITE_API_BASE_URL || ''; const resp = await fetch(`${baseUrl}/api/v1${url}`, { method: 'PUT', headers: { Authorization: `Bearer ${tk}`, 'Content-Type': contentType }, body, }); if (!resp.ok) throw new Error(`HTTP ${resp.status}`); return resp.json(); }, // 二进制下载(blob)— 用于 tar.gz / 文件下载 download: async (url: string, filename?: string) => { const tk = useUserStore.getState().token; const baseUrl = import.meta.env.VITE_API_BASE_URL || ''; const resp = await fetch(`${baseUrl}/api/v1${url}`, { headers: { Authorization: `Bearer ${tk}` }, }); if (!resp.ok) throw new Error(`HTTP ${resp.status}`); const blob = await resp.blob(); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = filename || resp.headers.get('Content-Disposition')?.match(/filename="?(.+?)"?$/)?.[1] || 'download'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(a.href); }, }; export default http;