diff --git a/apps/web/src/api/health/banners.ts b/apps/web/src/api/health/banners.ts new file mode 100644 index 0000000..9794e6d --- /dev/null +++ b/apps/web/src/api/health/banners.ts @@ -0,0 +1,107 @@ +import client from '../client'; + +// --------------------------------------------------------------------------- +// 轮播图类型 +// --------------------------------------------------------------------------- + +export interface BannerItem { + id: string; + tenant_id: string; + media_item_id: string; + title?: string; + subtitle?: string; + link_type?: string; + link_target?: string; + sort_order: number; + status: string; + start_time?: string; + end_time?: string; + image_url?: string; + thumbnail_url?: string; + media_deleted: boolean; + created_at: string; + updated_at: string; + created_by?: string; + updated_by?: string; + version: number; +} + +export interface CreateBannerReq { + media_item_id: string; + title?: string; + subtitle?: string; + link_type?: string; + link_target?: string; + sort_order?: number; + status?: string; + start_time?: string; + end_time?: string; +} + +export interface UpdateBannerReq { + media_item_id?: string; + title?: string; + subtitle?: string; + link_type?: string; + link_target?: string; + sort_order?: number; + status?: string; + start_time?: string; + end_time?: string; + version: number; +} + +export interface SortBannerReq { + items: Array<{ id: string; sort_order: number }>; +} + +// --------------------------------------------------------------------------- +// 轮播图 API +// --------------------------------------------------------------------------- + +export const bannerApi = { + /** 获取轮播图列表(可按状态筛选) */ + list: async (status?: string) => { + const { data } = await client.get<{ + success: boolean; + data: BannerItem[]; + }>('/health/banners', { params: status ? { status } : undefined }); + return data.data; + }, + + /** 创建轮播图 */ + create: async (req: CreateBannerReq) => { + const { data } = await client.post<{ + success: boolean; + data: BannerItem; + }>('/health/banners', req); + return data.data; + }, + + /** 更新轮播图 */ + update: async (id: string, req: UpdateBannerReq) => { + const { data } = await client.put<{ + success: boolean; + data: BannerItem; + }>(`/health/banners/${id}`, req); + return data.data; + }, + + /** 删除轮播图 */ + delete: async (id: string, version: number) => { + const { data } = await client.delete<{ + success: boolean; + data: null; + }>(`/health/banners/${id}`, { data: { version } }); + return data.data; + }, + + /** 轮播图排序 */ + sort: async (req: SortBannerReq) => { + const { data } = await client.put<{ + success: boolean; + data: null; + }>('/health/banners/sort', req); + return data.data; + }, +}; diff --git a/apps/web/src/api/health/media.ts b/apps/web/src/api/health/media.ts new file mode 100644 index 0000000..79ebe38 --- /dev/null +++ b/apps/web/src/api/health/media.ts @@ -0,0 +1,208 @@ +import client from '../client'; +import type { PaginatedResponse } from '../types'; + +// --------------------------------------------------------------------------- +// 媒体文件类型 +// --------------------------------------------------------------------------- + +export interface MediaItem { + id: string; + tenant_id: string; + folder_id?: string; + filename: string; + storage_path: string; + thumbnail_path?: string; + content_type: string; + file_size: number; + width?: number; + height?: number; + alt_text?: string; + is_public: boolean; + created_at: string; + updated_at: string; + created_by?: string; + updated_by?: string; + version: number; +} + +export interface MediaListParams { + page?: number; + page_size?: number; + folder_id?: string; + content_type?: string; + keyword?: string; + is_public?: boolean; +} + +export interface UpdateMediaReq { + filename?: string; + alt_text?: string; + is_public?: boolean; + folder_id?: string; + version: number; +} + +export interface MoveMediaReq { + folder_id?: string; + version: number; +} + +export interface CropReq { + x: number; + y: number; + width: number; + height: number; + version: number; +} + +// --------------------------------------------------------------------------- +// 文件夹类型 +// --------------------------------------------------------------------------- + +export interface FolderItem { + id: string; + tenant_id: string; + name: string; + parent_id?: string; + sort_order: number; + children: FolderItem[]; + item_count: number; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreateFolderReq { + name: string; + parent_id?: string; + sort_order?: number; +} + +export interface UpdateFolderReq { + name?: string; + parent_id?: string; + sort_order?: number; + version: number; +} + +// --------------------------------------------------------------------------- +// 媒体文件 API +// --------------------------------------------------------------------------- + +export const mediaApi = { + /** 分页查询媒体文件列表 */ + list: async (params: MediaListParams) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>('/health/media', { params }); + return data.data; + }, + + /** 上传媒体文件(multipart/form-data) */ + upload: async (formData: FormData) => { + const { data } = await client.post<{ + success: boolean; + data: MediaItem; + }>('/health/media/upload', formData, { + headers: { 'Content-Type': 'multipart/form-data' }, + }); + return data.data; + }, + + /** 获取单个媒体文件详情 */ + get: async (id: string) => { + const { data } = await client.get<{ + success: boolean; + data: MediaItem; + }>(`/health/media/${id}`); + return data.data; + }, + + /** 更新媒体文件信息 */ + update: async (id: string, req: UpdateMediaReq) => { + const { data } = await client.put<{ + success: boolean; + data: MediaItem; + }>(`/health/media/${id}`, req); + return data.data; + }, + + /** 删除媒体文件 */ + delete: async (id: string, version: number) => { + const { data } = await client.delete<{ + success: boolean; + data: null; + }>(`/health/media/${id}`, { data: { version } }); + return data.data; + }, + + /** 移动媒体文件到指定文件夹 */ + move: async (id: string, req: MoveMediaReq) => { + const { data } = await client.post<{ + success: boolean; + data: MediaItem; + }>(`/health/media/${id}/move`, req); + return data.data; + }, + + /** 批量删除媒体文件 */ + batchDelete: async (ids: string[]) => { + const { data } = await client.post<{ + success: boolean; + data: null; + }>('/health/media/batch-delete', { ids }); + return data.data; + }, + + /** 裁剪媒体文件 */ + crop: async (id: string, req: CropReq) => { + const { data } = await client.post<{ + success: boolean; + data: MediaItem; + }>(`/health/media/${id}/crop`, req); + return data.data; + }, +}; + +// --------------------------------------------------------------------------- +// 文件夹 API +// --------------------------------------------------------------------------- + +export const mediaFolderApi = { + /** 获取文件夹树形结构 */ + tree: async () => { + const { data } = await client.get<{ + success: boolean; + data: FolderItem[]; + }>('/health/media-folders'); + return data.data; + }, + + /** 创建文件夹 */ + create: async (req: CreateFolderReq) => { + const { data } = await client.post<{ + success: boolean; + data: FolderItem; + }>('/health/media-folders', req); + return data.data; + }, + + /** 更新文件夹 */ + update: async (id: string, req: UpdateFolderReq) => { + const { data } = await client.put<{ + success: boolean; + data: FolderItem; + }>(`/health/media-folders/${id}`, req); + return data.data; + }, + + /** 删除文件夹 */ + delete: async (id: string, version: number) => { + const { data } = await client.delete<{ + success: boolean; + data: null; + }>(`/health/media-folders/${id}`, { data: { version } }); + return data.data; + }, +};