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; } type Json = unknown; // eslint-disable-next-line @typescript-eslint/no-explicit-any async function request( method: string, url: string, body?: Json, params?: Record ): Promise { 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; } 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 !== undefined ? json.data : json) as T; } export const http = { // eslint-disable-next-line @typescript-eslint/no-explicit-any get: (url: string, params?: Record) => request('GET', url, undefined, params), // eslint-disable-next-line @typescript-eslint/no-explicit-any post: (url: string, body?: Json) => request('POST', url, body), // eslint-disable-next-line @typescript-eslint/no-explicit-any put: (url: string, body?: Json) => request('PUT', url, body), // eslint-disable-next-line @typescript-eslint/no-explicit-any delete: (url: string, body?: Json) => request('DELETE', url, body), getRaw: async (url: string): Promise => { 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): Promise => { 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(); }, download: async (url: string, filename?: string): Promise => { 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); const disp = resp.headers.get('Content-Disposition') || ''; const m = disp.match(/filename="?(.+?)"?$/); a.download = filename || (m ? m[1] : 'download'); document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(a.href); }, // eslint-disable-next-line @typescript-eslint/no-explicit-any upload: async (url: string, formData: FormData): Promise => { const tk = useUserStore.getState().token; const baseUrl = import.meta.env.VITE_API_BASE_URL || ''; const resp = await fetch(baseUrl + '/api/v1' + url, { method: 'POST', headers: { Authorization: 'Bearer ' + tk }, body: formData, }); if (!resp.ok) throw new Error('HTTP ' + resp.status); return resp.json(); }, }; export default http;