Files
YG_FT/frontend/src/api/request.ts
2026-08-04 17:18:50 +08:00

131 lines
4.1 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 axios, { type AxiosInstance, type AxiosRequestConfig } from 'axios'
import { ElMessage } from 'element-plus'
/**
* 用户操作分布:哪些模块路径算"业务操作"(用于看板统计)
* 请求命中这些路径时,会自动调用 record_visit 记录一次(同一模块 60 秒内去重)
*/
const VISIT_TRACKED_PREFIXES: Array<[string, string]> = [
['/fine-tune', 'fine-tune'],
['/model-eval', 'model-eval'],
['/model-inference', 'model-inference'],
['/model-compare', 'model-inference'],
['/data-process', 'data-process'],
['/data-convert', 'data-convert'],
['/model-manage', 'model-manage'],
['/dataset-manage', 'dataset'],
]
function trackVisit(url: string | undefined) {
if (!url) return
for (const [prefix, module] of VISIT_TRACKED_PREFIXES) {
if (url.includes(prefix)) {
const key = `visit:${module}`
const last = Number(sessionStorage.getItem(key) || 0)
if (Date.now() - last < 60000) return // 60 秒内去重
sessionStorage.setItem(key, String(Date.now()))
// fire-and-forget 调用后端记录接口
import('./modules/audit-visit').then(({ recordModuleVisit }) => {
recordModuleVisit(module, url).catch(() => { /* ignore */ })
}).catch(() => { /* ignore */ })
return
}
}
}
/**
* 后端统一响应格式
* code === 0 表示成功data 为业务数据
*/
export interface ApiResult<T = any> {
code: number
message?: string
data: T
}
const service: AxiosInstance = axios.create({
// Use a relative path; Vite proxies /modelTF to http://localhost:17861 in local development.
baseURL: '/modelTF',
timeout: 120000,
})
/**
* 从 localStorage 取当前用户 token登录时后端返回 platform-token-{user_id})。
* 后端鉴权中间件依赖此 header 解析当前用户身份。
*/
function getAuthToken(): string | null {
const USER_STORAGE_KEY = 'currentUser'
const raw = localStorage.getItem(USER_STORAGE_KEY)
if (raw) {
try {
const user = JSON.parse(raw)
// 后端 login 返回的 token 格式为 platform-token-{user.id}
if (user?.id) return `platform-token-${user.id}`
} catch {
/* ignore */
}
}
// 兼容改造前 admin 会话
if (localStorage.getItem('username') === 'admin') return 'platform-token-admin'
return null
}
// 请求拦截器:注入 Authorization header
service.interceptors.request.use(
(config) => {
const token = getAuthToken()
if (token) {
config.headers = config.headers || {}
config.headers['Authorization'] = `Bearer ${token}`
}
return config
},
(error) => Promise.reject(error),
)
// 响应拦截器:统一解包 { code, data, message }
service.interceptors.response.use(
(response) => {
const res = response.data as ApiResult
// 二进制流等非 JSON 响应直接返回
if (response.config.responseType === 'blob' || response.config.responseType === 'arraybuffer') {
return response
}
if (res.code === 0) {
return res.data
}
// 业务错误
const message = res.message || '请求失败'
ElMessage.error(message)
return Promise.reject(new Error(message))
},
(error) => {
const detail = error.response?.data?.detail
const message = detail?.message || error.response?.data?.message || detail || error.message || '网络异常'
ElMessage.error(message)
return Promise.reject(error)
},
)
/** GET 请求,返回已解包的 data */
export function get<T = any>(url: string, params?: any, config?: AxiosRequestConfig): Promise<T> {
return service.get(url, { params, ...config }) as unknown as Promise<T>
}
/** POST 请求 */
export function post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
return service.post(url, data, config) as unknown as Promise<T>
}
/** PUT 请求 */
export function put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
return service.put(url, data, config) as unknown as Promise<T>
}
/** DELETE 请求 */
export function del<T = any>(url: string, params?: any, config?: AxiosRequestConfig): Promise<T> {
return service.delete(url, { params, ...config }) as unknown as Promise<T>
}
export default service