219 lines
5.0 KiB
TypeScript
219 lines
5.0 KiB
TypeScript
import { fetch as tauriFetch } from '@tauri-apps/plugin-http'
|
||
|
||
/**
|
||
* API响应数据类型
|
||
*/
|
||
export interface ApiResponse<T = any> {
|
||
code: number
|
||
message: string
|
||
data: T
|
||
timestamp?: number
|
||
}
|
||
|
||
/**
|
||
* 基础URL
|
||
*/
|
||
const BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8080/api'
|
||
|
||
/**
|
||
* 检测是否在 Tauri 环境中
|
||
* 通过检查 __TAURI__ 全局变量来判断
|
||
*/
|
||
const isTauriEnvironment = (): boolean => {
|
||
return typeof window !== 'undefined' && '__TAURI__' in window
|
||
}
|
||
|
||
/**
|
||
* 构建带查询参数的 URL
|
||
*/
|
||
function buildUrl(url: string, params?: Record<string, any>): string {
|
||
if (!params || Object.keys(params).length === 0) {
|
||
return url
|
||
}
|
||
|
||
const searchParams = new URLSearchParams()
|
||
Object.entries(params).forEach(([key, value]) => {
|
||
if (value !== undefined && value !== null) {
|
||
searchParams.append(key, String(value))
|
||
}
|
||
})
|
||
|
||
const queryString = searchParams.toString()
|
||
return queryString ? `${url}?${queryString}` : url
|
||
}
|
||
|
||
/**
|
||
* 获取 fetch 函数
|
||
* 在 Tauri 环境中使用 tauriFetch,否则使用原生 fetch
|
||
*/
|
||
function getFetch() {
|
||
return isTauriEnvironment() ? tauriFetch : window.fetch.bind(window)
|
||
}
|
||
|
||
/**
|
||
* 创建请求配置
|
||
*/
|
||
function createRequestConfig(options?: RequestInit): RequestInit {
|
||
const token = localStorage.getItem('token')
|
||
const headers: HeadersInit = {
|
||
'Content-Type': 'application/json',
|
||
...options?.headers
|
||
}
|
||
|
||
if (token) {
|
||
headers['Authorization'] = `Bearer ${token}`
|
||
}
|
||
|
||
return {
|
||
...options,
|
||
headers
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理响应
|
||
*/
|
||
async function handleResponse<T>(response: Response): Promise<T> {
|
||
const data = await response.json()
|
||
|
||
if (!response.ok) {
|
||
throw new Error(data.message || `HTTP ${response.status}`)
|
||
}
|
||
|
||
// 检查业务状态码
|
||
if (data.code !== 200) {
|
||
throw new Error(data.message || '请求失败')
|
||
}
|
||
|
||
return data.data
|
||
}
|
||
|
||
/**
|
||
* 处理错误
|
||
*/
|
||
function handleError(error: any): never {
|
||
if (error.message) {
|
||
console.error('请求错误:', error.message)
|
||
}
|
||
|
||
// 处理特定错误状态
|
||
if (error.message?.includes('401')) {
|
||
localStorage.removeItem('token')
|
||
localStorage.removeItem('userRole')
|
||
localStorage.removeItem('userInfo')
|
||
window.location.href = '/login'
|
||
}
|
||
|
||
throw error
|
||
}
|
||
|
||
/**
|
||
* HTTP 请求封装
|
||
*/
|
||
export const http = {
|
||
/**
|
||
* GET 请求
|
||
*/
|
||
async get<T = any>(url: string, options?: RequestInit & { params?: Record<string, any> }): Promise<T> {
|
||
try {
|
||
const fetchFn = getFetch()
|
||
const config = createRequestConfig(options)
|
||
const fullUrl = buildUrl(url, options?.params)
|
||
|
||
const response = await fetchFn(`${BASE_URL}${fullUrl}`, {
|
||
...config,
|
||
method: 'GET'
|
||
})
|
||
|
||
return await handleResponse<T>(response)
|
||
} catch (error) {
|
||
return handleError(error)
|
||
}
|
||
},
|
||
|
||
/**
|
||
* POST 请求
|
||
*/
|
||
async post<T = any>(url: string, data?: any, options?: RequestInit & { params?: Record<string, any> }): Promise<T> {
|
||
try {
|
||
const fetchFn = getFetch()
|
||
const config = createRequestConfig(options)
|
||
const fullUrl = buildUrl(url, options?.params)
|
||
|
||
const response = await fetchFn(`${BASE_URL}${fullUrl}`, {
|
||
...config,
|
||
method: 'POST',
|
||
body: JSON.stringify(data)
|
||
})
|
||
|
||
return await handleResponse<T>(response)
|
||
} catch (error) {
|
||
return handleError(error)
|
||
}
|
||
},
|
||
|
||
/**
|
||
* PUT 请求
|
||
*/
|
||
async put<T = any>(url: string, data?: any, options?: RequestInit & { params?: Record<string, any> }): Promise<T> {
|
||
try {
|
||
const fetchFn = getFetch()
|
||
const config = createRequestConfig(options)
|
||
const fullUrl = buildUrl(url, options?.params)
|
||
|
||
const response = await fetchFn(`${BASE_URL}${fullUrl}`, {
|
||
...config,
|
||
method: 'PUT',
|
||
body: JSON.stringify(data)
|
||
})
|
||
|
||
return await handleResponse<T>(response)
|
||
} catch (error) {
|
||
return handleError(error)
|
||
}
|
||
},
|
||
|
||
/**
|
||
* DELETE 请求
|
||
*/
|
||
async delete<T = any>(url: string, options?: RequestInit & { params?: Record<string, any> }): Promise<T> {
|
||
try {
|
||
const fetchFn = getFetch()
|
||
const config = createRequestConfig(options)
|
||
const fullUrl = buildUrl(url, options?.params)
|
||
|
||
const response = await fetchFn(`${BASE_URL}${fullUrl}`, {
|
||
...config,
|
||
method: 'DELETE'
|
||
})
|
||
|
||
return await handleResponse<T>(response)
|
||
} catch (error) {
|
||
return handleError(error)
|
||
}
|
||
},
|
||
|
||
/**
|
||
* PATCH 请求
|
||
*/
|
||
async patch<T = any>(url: string, data?: any, options?: RequestInit & { params?: Record<string, any> }): Promise<T> {
|
||
try {
|
||
const fetchFn = getFetch()
|
||
const config = createRequestConfig(options)
|
||
const fullUrl = buildUrl(url, options?.params)
|
||
|
||
const response = await fetchFn(`${BASE_URL}${fullUrl}`, {
|
||
...config,
|
||
method: 'PATCH',
|
||
body: JSON.stringify(data)
|
||
})
|
||
|
||
return await handleResponse<T>(response)
|
||
} catch (error) {
|
||
return handleError(error)
|
||
}
|
||
}
|
||
}
|
||
|
||
export default http
|