feat(web): T-WEB-008/009 管理端界面(歌曲/模型/动作/审核 + VRM 预览)
This commit is contained in:
@@ -0,0 +1,230 @@
|
||||
/** 契约端点封装:页面只调用这些函数,不直接拼 URL。 */
|
||||
|
||||
import { apiDelete, apiGet, apiPatch, apiPost, apiUpload } from './client';
|
||||
import type {
|
||||
ContractVersion,
|
||||
Health,
|
||||
Model,
|
||||
ModelPage,
|
||||
ModelUpdate,
|
||||
Motion,
|
||||
MotionPage,
|
||||
MotionUpdate,
|
||||
PendingPage,
|
||||
PublishStatus,
|
||||
ReviewActionBody,
|
||||
ReviewPage,
|
||||
Song,
|
||||
SongCreate,
|
||||
SongPage,
|
||||
SongUpdate,
|
||||
TargetType,
|
||||
} from './types';
|
||||
|
||||
/* ------------------------------------------------------------------ 列表 */
|
||||
|
||||
export interface ListParams {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
export function listSongs(
|
||||
params: ListParams & { q?: string; status?: PublishStatus | ''; artist?: string },
|
||||
): Promise<SongPage> {
|
||||
return apiGet<SongPage>('/api/songs', {
|
||||
page: params.page,
|
||||
pageSize: params.pageSize,
|
||||
q: params.q,
|
||||
status: params.status,
|
||||
artist: params.artist,
|
||||
});
|
||||
}
|
||||
|
||||
export function listMotions(
|
||||
params: ListParams & {
|
||||
q?: string;
|
||||
songId?: number | '';
|
||||
format?: string;
|
||||
status?: PublishStatus | '';
|
||||
},
|
||||
): Promise<MotionPage> {
|
||||
return apiGet<MotionPage>('/api/motions', {
|
||||
page: params.page,
|
||||
pageSize: params.pageSize,
|
||||
q: params.q,
|
||||
songId: params.songId,
|
||||
format: params.format,
|
||||
status: params.status,
|
||||
});
|
||||
}
|
||||
|
||||
export function listModels(
|
||||
params: ListParams & { q?: string; format?: string; status?: PublishStatus | '' },
|
||||
): Promise<ModelPage> {
|
||||
return apiGet<ModelPage>('/api/models', {
|
||||
page: params.page,
|
||||
pageSize: params.pageSize,
|
||||
q: params.q,
|
||||
format: params.format,
|
||||
status: params.status,
|
||||
});
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ 曲目 */
|
||||
|
||||
export function createSong(payload: SongCreate): Promise<Song> {
|
||||
return apiPost<Song>('/api/songs', payload);
|
||||
}
|
||||
|
||||
export function updateSong(id: number, payload: SongUpdate): Promise<Song> {
|
||||
return apiPatch<Song>(`/api/songs/${id}`, payload);
|
||||
}
|
||||
|
||||
export function deleteSong(id: number): Promise<void> {
|
||||
return apiDelete(`/api/songs/${id}`);
|
||||
}
|
||||
|
||||
/** multipart 字段名固定为 file。 */
|
||||
export function uploadSongAudio(id: number, file: File): Promise<Song> {
|
||||
const form = new FormData();
|
||||
form.append('file', file);
|
||||
return apiUpload<Song>(`/api/songs/${id}/audio`, form);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ 动作 */
|
||||
|
||||
export interface MotionUploadInput {
|
||||
file: File;
|
||||
name: string;
|
||||
songId?: number;
|
||||
fps?: number;
|
||||
autoPreprocess: boolean;
|
||||
status: PublishStatus;
|
||||
sourceType: string;
|
||||
sourceName: string;
|
||||
sourceUrl: string;
|
||||
license: string;
|
||||
author: string;
|
||||
}
|
||||
|
||||
export function uploadMotion(input: MotionUploadInput): Promise<Motion> {
|
||||
const form = new FormData();
|
||||
form.append('file', input.file);
|
||||
form.append('name', input.name);
|
||||
if (input.songId !== undefined) form.append('songId', String(input.songId));
|
||||
if (input.fps !== undefined) form.append('fps', String(input.fps));
|
||||
form.append('autoPreprocess', input.autoPreprocess ? 'true' : 'false');
|
||||
form.append('status', input.status);
|
||||
form.append('sourceType', input.sourceType);
|
||||
form.append('sourceName', input.sourceName);
|
||||
form.append('sourceUrl', input.sourceUrl);
|
||||
form.append('license', input.license);
|
||||
form.append('author', input.author);
|
||||
return apiUpload<Motion>('/api/motions', form);
|
||||
}
|
||||
|
||||
export function updateMotion(id: number, payload: MotionUpdate): Promise<Motion> {
|
||||
return apiPatch<Motion>(`/api/motions/${id}`, payload);
|
||||
}
|
||||
|
||||
export function deleteMotion(id: number): Promise<void> {
|
||||
return apiDelete(`/api/motions/${id}`);
|
||||
}
|
||||
|
||||
export function preprocessMotion(id: number): Promise<Motion> {
|
||||
return apiPost<Motion>(`/api/motions/${id}/preprocess`, {});
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ 模型 */
|
||||
|
||||
export interface ModelUploadInput {
|
||||
file: File;
|
||||
name: string;
|
||||
isPrimary: boolean;
|
||||
status: PublishStatus;
|
||||
sourceType: string;
|
||||
sourceName: string;
|
||||
sourceUrl: string;
|
||||
license: string;
|
||||
author: string;
|
||||
}
|
||||
|
||||
export function uploadModel(input: ModelUploadInput): Promise<Model> {
|
||||
const form = new FormData();
|
||||
form.append('file', input.file);
|
||||
form.append('name', input.name);
|
||||
form.append('isPrimary', input.isPrimary ? 'true' : 'false');
|
||||
form.append('status', input.status);
|
||||
form.append('sourceType', input.sourceType);
|
||||
form.append('sourceName', input.sourceName);
|
||||
form.append('sourceUrl', input.sourceUrl);
|
||||
form.append('license', input.license);
|
||||
form.append('author', input.author);
|
||||
return apiUpload<Model>('/api/models', form);
|
||||
}
|
||||
|
||||
export function updateModel(id: number, payload: ModelUpdate): Promise<Model> {
|
||||
return apiPatch<Model>(`/api/models/${id}`, payload);
|
||||
}
|
||||
|
||||
export function deleteModel(id: number): Promise<void> {
|
||||
return apiDelete(`/api/models/${id}`);
|
||||
}
|
||||
|
||||
export function setPrimaryModel(id: number): Promise<Model> {
|
||||
return apiPost<Model>(`/api/models/${id}/primary`, {});
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ 审核 */
|
||||
|
||||
export function listPending(params: ListParams & { status: PublishStatus }): Promise<PendingPage> {
|
||||
return apiGet<PendingPage>('/api/reviews', {
|
||||
status: params.status,
|
||||
page: params.page,
|
||||
pageSize: params.pageSize,
|
||||
});
|
||||
}
|
||||
|
||||
export function listReviewRecords(
|
||||
params: ListParams & { targetType?: TargetType | '' },
|
||||
): Promise<ReviewPage> {
|
||||
return apiGet<ReviewPage>('/api/reviews', {
|
||||
targetType: params.targetType,
|
||||
page: params.page,
|
||||
pageSize: params.pageSize,
|
||||
});
|
||||
}
|
||||
|
||||
export function reviewApprove(
|
||||
targetType: TargetType,
|
||||
id: number,
|
||||
body: ReviewActionBody,
|
||||
): Promise<void> {
|
||||
return apiPost<void>(`/api/reviews/${targetType}/${id}/approve`, body);
|
||||
}
|
||||
|
||||
export function reviewReject(
|
||||
targetType: TargetType,
|
||||
id: number,
|
||||
body: ReviewActionBody,
|
||||
): Promise<void> {
|
||||
return apiPost<void>(`/api/reviews/${targetType}/${id}/reject`, body);
|
||||
}
|
||||
|
||||
export function reviewSubmit(
|
||||
targetType: TargetType,
|
||||
id: number,
|
||||
body: ReviewActionBody,
|
||||
): Promise<void> {
|
||||
return apiPost<void>(`/api/reviews/${targetType}/${id}/submit`, body);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- 目录/健康 */
|
||||
|
||||
export function getHealth(): Promise<Health> {
|
||||
return apiGet<Health>('/health');
|
||||
}
|
||||
|
||||
export function getCatalogVersion(): Promise<ContractVersion> {
|
||||
return apiGet<ContractVersion>('/api/catalog/version');
|
||||
}
|
||||
Reference in New Issue
Block a user