Files
YG_FT/frontend/src/api/request.ts

145 lines
4.6 KiB
TypeScript
Raw Normal View History

import axios, { type AxiosInstance, type AxiosRequestConfig } from 'axios'
import { ElMessage } from 'element-plus'
2026-08-03 17:24:45 +08:00
/**
* "业务操作"
* record_visit 60
*/
const VISIT_TRACKED_PREFIXES: Array<[string, string]> = [
['/fine-tune', 'fine-tune'],
['/model-eval', 'model-eval'],
['/model-inference', 'model-inference'],
2026-08-04 17:02:57 +08:00
['/model-compare', 'model-inference'],
2026-08-03 17:24:45 +08:00
['/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',
2026-08-03 16:20:21 +08:00
timeout: 120000,
})
2026-08-03 09:34:08 +08:00
/**
* 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
}
function getActiveTenantId(): string | null {
return localStorage.getItem('activeTenantId')
}
/** Headers shared by Axios and raw fetch requests such as SSE streaming. */
export function getAuthHeaders(): Record<string, string> {
const headers: Record<string, string> = {}
const token = getAuthToken()
if (token) headers.Authorization = `Bearer ${token}`
const tenantId = getActiveTenantId()
if (tenantId) headers['X-Tenant-ID'] = tenantId
return headers
}
2026-08-03 09:34:08 +08:00
// 请求拦截器:注入 Authorization header
service.interceptors.request.use(
2026-08-03 09:34:08 +08:00
(config) => {
const authHeaders = getAuthHeaders()
if (Object.keys(authHeaders).length) {
2026-08-03 09:34:08 +08:00
config.headers = config.headers || {}
Object.assign(config.headers, authHeaders)
2026-08-03 09:34:08 +08:00
}
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