|
|
|
@ -12,6 +12,21 @@ import type { |
|
|
|
Activity, |
|
|
|
UpdateProfileRequest, |
|
|
|
ChangePasswordRequest, |
|
|
|
FileInfo, |
|
|
|
FileListRequest, |
|
|
|
FileListResponse, |
|
|
|
UpdateFileRequest, |
|
|
|
MenuItem, |
|
|
|
CreateMenuRequest, |
|
|
|
UpdateMenuRequest, |
|
|
|
RoleInfo, |
|
|
|
CreateRoleRequest, |
|
|
|
UpdateRoleRequest, |
|
|
|
OrgInfo, |
|
|
|
CreateOrgRequest, |
|
|
|
UpdateOrgRequest, |
|
|
|
OrgMember, |
|
|
|
UserOrgInfo, |
|
|
|
} from '@/types' |
|
|
|
|
|
|
|
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8888/api/v1' |
|
|
|
@ -84,6 +99,11 @@ class ApiClient { |
|
|
|
this.token = null |
|
|
|
} |
|
|
|
|
|
|
|
// SSO
|
|
|
|
async getSSOLoginUrl(): Promise<{ login_url: string }> { |
|
|
|
return this.request<{ login_url: string }>('/auth/sso/login-url') |
|
|
|
} |
|
|
|
|
|
|
|
async refreshToken(refreshToken: string): Promise<LoginResponse> { |
|
|
|
return this.request<LoginResponse>('/refresh', { |
|
|
|
method: 'POST', |
|
|
|
@ -103,7 +123,7 @@ class ApiClient { |
|
|
|
|
|
|
|
// 如果后端已经返回标准格式,直接返回
|
|
|
|
if ('success' in rawData) { |
|
|
|
return rawData as UserListResponse |
|
|
|
return rawData as unknown as UserListResponse |
|
|
|
} |
|
|
|
|
|
|
|
// 包装成标准格式
|
|
|
|
@ -169,7 +189,7 @@ class ApiClient { |
|
|
|
|
|
|
|
// 如果后端已经返回标准格式,直接返回
|
|
|
|
if ('success' in rawData) { |
|
|
|
return rawData as ApiResponse<DashboardStats> |
|
|
|
return rawData as unknown as ApiResponse<DashboardStats> |
|
|
|
} |
|
|
|
|
|
|
|
// 包装成标准格式
|
|
|
|
@ -186,7 +206,7 @@ class ApiClient { |
|
|
|
|
|
|
|
// 如果后端已经返回标准格式,直接返回
|
|
|
|
if ('success' in rawData) { |
|
|
|
return rawData as ApiResponse<Activity[]> |
|
|
|
return rawData as unknown as ApiResponse<Activity[]> |
|
|
|
} |
|
|
|
|
|
|
|
// 包装成标准格式
|
|
|
|
@ -198,6 +218,184 @@ class ApiClient { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// File Management
|
|
|
|
async uploadFile(file: File, category?: string, isPublic?: boolean): Promise<ApiResponse<FileInfo>> { |
|
|
|
const url = `${API_BASE_URL}/file/upload` |
|
|
|
const formData = new FormData() |
|
|
|
formData.append('file', file) |
|
|
|
if (category) formData.append('category', category) |
|
|
|
if (isPublic !== undefined) formData.append('isPublic', isPublic ? 'true' : 'false') |
|
|
|
|
|
|
|
const headers: Record<string, string> = {} |
|
|
|
if (this.token) { |
|
|
|
headers['Authorization'] = `Bearer ${this.token}` |
|
|
|
} |
|
|
|
|
|
|
|
const response = await fetch(url, { method: 'POST', headers, body: formData }) |
|
|
|
const data = await response.json() |
|
|
|
if (!response.ok) throw new Error(data.message || 'Upload failed') |
|
|
|
if ('id' in data) return { code: 200, message: 'success', success: true, data } |
|
|
|
return data |
|
|
|
} |
|
|
|
|
|
|
|
async getFiles(params: FileListRequest = {}): Promise<FileListResponse> { |
|
|
|
const queryParams = new URLSearchParams() |
|
|
|
if (params.page) queryParams.append('page', params.page.toString()) |
|
|
|
if (params.pageSize) queryParams.append('pageSize', params.pageSize.toString()) |
|
|
|
if (params.keyword) queryParams.append('keyword', params.keyword) |
|
|
|
if (params.category) queryParams.append('category', params.category) |
|
|
|
if (params.mimeType) queryParams.append('mimeType', params.mimeType) |
|
|
|
|
|
|
|
const rawData = await this.request<{ total: number; list: FileInfo[] }>(`/files?${queryParams}`) |
|
|
|
if ('success' in rawData) return rawData as unknown as FileListResponse |
|
|
|
return { |
|
|
|
code: 200, message: 'success', success: true, |
|
|
|
data: { list: rawData.list || [], total: rawData.total || 0 }, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
async getFile(id: number): Promise<ApiResponse<FileInfo>> { |
|
|
|
const rawData = await this.request<FileInfo>(`/file/${id}`) |
|
|
|
if ('success' in rawData) return rawData as unknown as ApiResponse<FileInfo> |
|
|
|
return { code: 200, message: 'success', success: true, data: rawData } |
|
|
|
} |
|
|
|
|
|
|
|
async getFileUrl(id: number): Promise<string> { |
|
|
|
const url = `${API_BASE_URL}/file/${id}/url` |
|
|
|
const headers: Record<string, string> = {} |
|
|
|
if (this.token) headers['Authorization'] = `Bearer ${this.token}` |
|
|
|
// This endpoint either serves the file directly (local) or returns JSON with URL
|
|
|
|
// For simplicity, return the endpoint URL — browser can use it directly
|
|
|
|
return url |
|
|
|
} |
|
|
|
|
|
|
|
async updateFile(id: number, data: UpdateFileRequest): Promise<ApiResponse<FileInfo>> { |
|
|
|
const rawData = await this.request<FileInfo>(`/file/${id}`, { |
|
|
|
method: 'PUT', |
|
|
|
body: JSON.stringify(data), |
|
|
|
}) |
|
|
|
if ('success' in rawData) return rawData as unknown as ApiResponse<FileInfo> |
|
|
|
return { code: 200, message: 'success', success: true, data: rawData } |
|
|
|
} |
|
|
|
|
|
|
|
async deleteFile(id: number): Promise<ApiResponse<void>> { |
|
|
|
return this.request<ApiResponse<void>>(`/file/${id}`, { method: 'DELETE' }) |
|
|
|
} |
|
|
|
|
|
|
|
// Menu Management
|
|
|
|
async getCurrentMenus(): Promise<{ list: MenuItem[] }> { |
|
|
|
const rawData = await this.request<{ list: MenuItem[] }>('/menus/current') |
|
|
|
if ('success' in rawData) return (rawData as any).data || rawData |
|
|
|
return rawData |
|
|
|
} |
|
|
|
|
|
|
|
async getMenuList(): Promise<{ list: MenuItem[] }> { |
|
|
|
const rawData = await this.request<{ list: MenuItem[] }>('/menus') |
|
|
|
if ('success' in rawData) return (rawData as any).data || rawData |
|
|
|
return rawData |
|
|
|
} |
|
|
|
|
|
|
|
async createMenu(data: CreateMenuRequest): Promise<MenuItem> { |
|
|
|
return this.request<MenuItem>('/menu', { method: 'POST', body: JSON.stringify(data) }) |
|
|
|
} |
|
|
|
|
|
|
|
async updateMenu(id: number, data: UpdateMenuRequest): Promise<MenuItem> { |
|
|
|
return this.request<MenuItem>(`/menu/${id}`, { method: 'PUT', body: JSON.stringify(data) }) |
|
|
|
} |
|
|
|
|
|
|
|
async deleteMenu(id: number): Promise<ApiResponse<void>> { |
|
|
|
return this.request<ApiResponse<void>>(`/menu/${id}`, { method: 'DELETE' }) |
|
|
|
} |
|
|
|
|
|
|
|
// Role Management
|
|
|
|
async getRoles(): Promise<{ list: RoleInfo[] }> { |
|
|
|
const rawData = await this.request<{ list: RoleInfo[] }>('/roles') |
|
|
|
if ('success' in rawData) return (rawData as any).data || rawData |
|
|
|
return rawData |
|
|
|
} |
|
|
|
|
|
|
|
async createRole(data: CreateRoleRequest): Promise<RoleInfo> { |
|
|
|
return this.request<RoleInfo>('/role', { method: 'POST', body: JSON.stringify(data) }) |
|
|
|
} |
|
|
|
|
|
|
|
async updateRole(id: number, data: UpdateRoleRequest): Promise<RoleInfo> { |
|
|
|
return this.request<RoleInfo>(`/role/${id}`, { method: 'PUT', body: JSON.stringify(data) }) |
|
|
|
} |
|
|
|
|
|
|
|
async deleteRole(id: number): Promise<ApiResponse<void>> { |
|
|
|
return this.request<ApiResponse<void>>(`/role/${id}`, { method: 'DELETE' }) |
|
|
|
} |
|
|
|
|
|
|
|
async getRoleMenus(roleId: number): Promise<{ menuIds: number[] }> { |
|
|
|
const rawData = await this.request<{ menuIds: number[] }>(`/role/${roleId}/menus`) |
|
|
|
if ('success' in rawData) return (rawData as any).data || rawData |
|
|
|
return rawData |
|
|
|
} |
|
|
|
|
|
|
|
async setRoleMenus(roleId: number, menuIds: number[]): Promise<ApiResponse<void>> { |
|
|
|
return this.request<ApiResponse<void>>(`/role/${roleId}/menus`, { |
|
|
|
method: 'PUT', body: JSON.stringify({ menuIds }), |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
// Organization Management
|
|
|
|
async getOrganizations(): Promise<{ list: OrgInfo[] }> { |
|
|
|
const rawData = await this.request<{ list: OrgInfo[] }>('/organizations') |
|
|
|
if ('success' in rawData) return (rawData as any).data || rawData |
|
|
|
return rawData |
|
|
|
} |
|
|
|
|
|
|
|
async createOrganization(data: CreateOrgRequest): Promise<OrgInfo> { |
|
|
|
return this.request<OrgInfo>('/organization', { method: 'POST', body: JSON.stringify(data) }) |
|
|
|
} |
|
|
|
|
|
|
|
async updateOrganization(id: number, data: UpdateOrgRequest): Promise<OrgInfo> { |
|
|
|
return this.request<OrgInfo>(`/organization/${id}`, { method: 'PUT', body: JSON.stringify(data) }) |
|
|
|
} |
|
|
|
|
|
|
|
async deleteOrganization(id: number): Promise<ApiResponse<void>> { |
|
|
|
return this.request<ApiResponse<void>>(`/organization/${id}`, { method: 'DELETE' }) |
|
|
|
} |
|
|
|
|
|
|
|
async getOrgMembers(orgId: number): Promise<{ list: OrgMember[] }> { |
|
|
|
const rawData = await this.request<{ list: OrgMember[] }>(`/organization/${orgId}/members`) |
|
|
|
if ('success' in rawData) return (rawData as any).data || rawData |
|
|
|
return rawData |
|
|
|
} |
|
|
|
|
|
|
|
async addOrgMember(orgId: number, userId: number, roleId: number): Promise<ApiResponse<void>> { |
|
|
|
return this.request<ApiResponse<void>>(`/organization/${orgId}/member`, { |
|
|
|
method: 'POST', body: JSON.stringify({ userId, roleId }), |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
async updateOrgMember(orgId: number, userId: number, roleId: number): Promise<ApiResponse<void>> { |
|
|
|
return this.request<ApiResponse<void>>(`/organization/${orgId}/member/${userId}`, { |
|
|
|
method: 'PUT', body: JSON.stringify({ roleId }), |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
async removeOrgMember(orgId: number, userId: number): Promise<ApiResponse<void>> { |
|
|
|
return this.request<ApiResponse<void>>(`/organization/${orgId}/member/${userId}`, { |
|
|
|
method: 'DELETE', |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
// User Org Context
|
|
|
|
async getUserOrgs(): Promise<{ list: UserOrgInfo[] }> { |
|
|
|
const rawData = await this.request<{ list: UserOrgInfo[] }>('/profile/orgs') |
|
|
|
if ('success' in rawData) return (rawData as any).data || rawData |
|
|
|
return rawData |
|
|
|
} |
|
|
|
|
|
|
|
async switchOrg(orgId: number): Promise<{ token: string }> { |
|
|
|
const rawData = await this.request<{ token: string }>('/profile/current-org', { |
|
|
|
method: 'PUT', body: JSON.stringify({ orgId }), |
|
|
|
}) |
|
|
|
if ('success' in rawData) return (rawData as any).data || rawData |
|
|
|
return rawData |
|
|
|
} |
|
|
|
|
|
|
|
// Health check
|
|
|
|
async healthCheck(): Promise<{ status: string }> { |
|
|
|
try { |
|
|
|
|