const BASE_URL = '/api' interface ApiResponse { code: number msg: string data: T } async function request(url: string, options?: RequestInit): Promise { const res = await fetch(`${BASE_URL}${url}`, { ...options, credentials: 'include', // 携带 Cookie headers: { 'Content-Type': 'application/json', ...options?.headers } }) const json: ApiResponse = await res.json() if (json.code === 401) { // 未登录或登录过期,跳转到登录页 window.location.href = '/login' throw new Error(json.msg || '请先登录') } if (json.code !== 200) { throw new Error(json.msg || '请求失败') } return json.data } // 检查登录状态(不触发自动跳转) export async function checkAuth(): Promise { try { const res = await fetch(`${BASE_URL}/auth/me`, { credentials: 'include', headers: { 'Content-Type': 'application/json' } }) const json: ApiResponse<{ username: string }> = await res.json() return json.code === 200 } catch { return false } } export const api = { auth: { login: (data: { username: string; password: string }) => request<{ user: string }>('/auth/login', { method: 'POST', body: JSON.stringify(data) }), logout: () => request('/auth/logout', { method: 'POST' }), me: () => request<{ username: string }>('/auth/me'), register: (data: { username: string; password: string; email: string }) => request('/auth/register', { method: 'POST', body: JSON.stringify(data) }) }, tasks: { list: (params?: { page?: number; page_size?: number; name?: string }) => { const query = new URLSearchParams() if (params?.page) query.set('page', String(params.page)) if (params?.page_size) query.set('page_size', String(params.page_size)) if (params?.name) query.set('name', params.name) return request(`/tasks?${query}`) }, create: (data: Partial) => request('/tasks', { method: 'POST', body: JSON.stringify(data) }), update: (id: number, data: Partial) => request(`/tasks/${id}`, { method: 'PUT', body: JSON.stringify(data) }), delete: (id: number) => request(`/tasks/${id}`, { method: 'DELETE' }), execute: (id: number) => request(`/execute/task/${id}`, { method: 'POST' }) }, scripts: { list: () => request('/scripts'), create: (data: Partial