news-classifier/client/src/utils/request.ts

219 lines
5.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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