85 lines
3.0 KiB
TypeScript
85 lines
3.0 KiB
TypeScript
|
|
import axios from 'axios'
|
|||
|
|
import { isWebView2 } from './env'
|
|||
|
|
|
|||
|
|
// WebView2 自定义协议前缀
|
|||
|
|
const WEBVIEW2_BASE = 'app://api/'
|
|||
|
|
|
|||
|
|
// 普通浏览器 HTTP API 地址,按需修改
|
|||
|
|
const HTTP_BASE = 'http://localhost:5000/api/'
|
|||
|
|
|
|||
|
|
// ─── axios 实例 ────────────────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
const http = axios.create({
|
|||
|
|
headers: { 'Content-Type': 'application/json' },
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 请求拦截器:仅在浏览器环境下注入鉴权 Token
|
|||
|
|
// WebView2 本地运行,不需要鉴权
|
|||
|
|
http.interceptors.request.use((config) => {
|
|||
|
|
if (!isWebView2()) {
|
|||
|
|
const token = localStorage.getItem('authToken')
|
|||
|
|
if (token) {
|
|||
|
|
config.headers.Authorization = `Bearer ${token}`
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return config
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 响应拦截器:统一解包 C# 返回的 { success, data/error } 结构
|
|||
|
|
// C# BuildSuccessResponseBody 固定格式:{ "success": true, "data": ... }
|
|||
|
|
// 错误格式:{ "success": false, "error": "..." }
|
|||
|
|
// WebView2 桥接和 HTTP 两个环境返回结构相同,拦截器可统一处理
|
|||
|
|
http.interceptors.response.use(
|
|||
|
|
(response) => {
|
|||
|
|
const payload = response.data as { success: boolean; data?: unknown; error?: string }
|
|||
|
|
if (payload?.success === false) {
|
|||
|
|
return Promise.reject(new Error(payload.error ?? '请求失败'))
|
|||
|
|
}
|
|||
|
|
return (payload?.data ?? payload) as never
|
|||
|
|
},
|
|||
|
|
(error) => {
|
|||
|
|
const msg: string =
|
|||
|
|
error.response?.data?.error ??
|
|||
|
|
error.response?.data?.message ??
|
|||
|
|
error.message ??
|
|||
|
|
'网络错误'
|
|||
|
|
return Promise.reject(new Error(msg))
|
|||
|
|
},
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// ─── 统一请求方法 ──────────────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
interface RequestOptions {
|
|||
|
|
method?: string
|
|||
|
|
headers?: Record<string, string>
|
|||
|
|
body?: unknown
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export async function request<T = unknown>(endpoint: string, options: RequestOptions = {}): Promise<T> {
|
|||
|
|
const url = (isWebView2() ? WEBVIEW2_BASE : HTTP_BASE) + endpoint
|
|||
|
|
|
|||
|
|
// WebView2:直接走桥接 fetch(桥接脚本已完整覆盖 window.fetch)
|
|||
|
|
if (isWebView2()) {
|
|||
|
|
const res = await fetch(url, {
|
|||
|
|
method: options.method ?? 'GET',
|
|||
|
|
headers: { 'Content-Type': 'application/json', ...(options.headers ?? {}) },
|
|||
|
|
body: options.body !== undefined ? JSON.stringify(options.body) : undefined,
|
|||
|
|
})
|
|||
|
|
const payload = await res.json() as { success: boolean; data?: T; error?: string }
|
|||
|
|
// eslint-disable-next-line no-debugger
|
|||
|
|
debugger
|
|||
|
|
if (payload?.success === false) {
|
|||
|
|
throw new Error(payload.error ?? '请求失败')
|
|||
|
|
}
|
|||
|
|
return (payload?.data ?? payload) as T
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 普通浏览器:走 axios(拦截器处理鉴权和响应解包)
|
|||
|
|
return http.request<T>({
|
|||
|
|
url,
|
|||
|
|
method: options.method ?? 'GET',
|
|||
|
|
headers: options.headers,
|
|||
|
|
data: options.body,
|
|||
|
|
}) as Promise<T>
|
|||
|
|
}
|