diff --git a/apps/web/src/api/assets.ts b/apps/web/src/api/assets.ts old mode 100644 new mode 100755 index b544ce6a2..8d1f04450 --- a/apps/web/src/api/assets.ts +++ b/apps/web/src/api/assets.ts @@ -157,13 +157,23 @@ export const deleteAssetLibrary = async (libraryId: string): Promise => { // ─── 素材 ────────────────────────────────────────────────── /** 获取素材库下的所有素材 */ -export const getAssets = async (libraryId: string): Promise => { - const response = await apiClient.get("/assets", { - params: { library_id: libraryId }, - }) - const items: AssetItem[] = response.data.items || [] - // 过滤掉已删除的素材(后端列表可能返回 deleted 状态,前端兜底) - return items.filter((item) => item.status !== "deleted") +export const getAssets = async ( + libraryId: string, + options?: { status?: string; page?: number; page_size?: number }, +): Promise<{ items: AssetItem[]; total: number }> => { + const params: Record = { library_id: libraryId } + // 默认拉取所有非删除状态的素材(ready/ingesting/processing/uploading/error/failed) + // 让用户能看到"处理中"的素材,不会以为上传失败了 + if (options?.status) { + params.status = options.status + } + if (options?.page) params.page = options.page + if (options?.page_size) params.page_size = options.page_size + const response = await apiClient.get("/assets", { params }) + const data = response.data || {} + const items: AssetItem[] = data.items || [] + const total: number = typeof data.total === "number" ? data.total : items.length + return { items, total } } /** 按类型获取素材(如 voice/video/image),支持可选筛选 */ diff --git a/apps/web/src/pages/assets/AssetLibrary.tsx b/apps/web/src/pages/assets/AssetLibrary.tsx index 6ca4914c2..5738d5489 100755 --- a/apps/web/src/pages/assets/AssetLibrary.tsx +++ b/apps/web/src/pages/assets/AssetLibrary.tsx @@ -74,6 +74,8 @@ interface AssetItem { fileUrl?: string status: StatusType statusLabel: string + /** 是否处于处理中状态(上传中/入库中/诊断中) */ + loading?: boolean duration?: string size: number createdAt: string @@ -94,11 +96,24 @@ const inferStatus = ( score?: number, classificationStatus?: string, assetStatus?: string, -): { status: StatusType; label: string } => { +): { status: StatusType; label: string; loading?: boolean } => { // 已删除素材(正常情况列表已过滤,这里是防御性处理) if (assetStatus === "deleted") { return { status: "bad", label: "已删除" } } + // 处理中状态:上传中 / 入库中 / 处理中 + if ( + assetStatus === "uploading" || + assetStatus === "ingesting" || + assetStatus === "processing" || + assetStatus === "pending" + ) { + return { status: "info", label: "处理中", loading: true } + } + // 失败状态 + if (assetStatus === "error" || assetStatus === "failed") { + return { status: "bad", label: "处理失败" } + } // 素材已就绪(status=ready)时,不应因 classification 未执行而显示"处理中" if (assetStatus === "ready") { if (score == null) return { status: "info", label: "待诊断" } @@ -108,7 +123,7 @@ const inferStatus = ( } // 素材未就绪:classification 正在处理中 if (classificationStatus === "processing" || classificationStatus === "pending") { - return { status: "info", label: "处理中" } + return { status: "info", label: "处理中", loading: true } } if (score == null) return { status: "info", label: "待诊断" } if (score >= 70) return { status: "ok", label: "合格" } @@ -133,7 +148,7 @@ const mapLibrary = (item: AssetLibraryItem): LibraryItem => ({ /** 将后端 ApiAssetItem 映射为前端 AssetItem */ const mapAsset = (item: ApiAssetItem): AssetItem => { - const { status, label } = inferStatus( + const { status, label, loading } = inferStatus( item.quality_score ?? undefined, item.classification_status ?? undefined, item.status ?? undefined, @@ -145,13 +160,16 @@ const mapAsset = (item: ApiAssetItem): AssetItem => { name: item.name, kind, // 视频类型不能用 file_url 做缩略图(是视频文件, 无法渲染) - thumbUrl: - (item.thumbnail_url as string | undefined) || - (metadata.thumbnail_url as string | undefined) || - (kind !== "video" ? (item.file_url as string | undefined) : undefined), + // 处理中的素材没有缩略图,显示占位符 + thumbUrl: loading + ? undefined + : (item.thumbnail_url as string | undefined) || + (metadata.thumbnail_url as string | undefined) || + (kind !== "video" ? (item.file_url as string | undefined) : undefined), fileUrl: (item.file_url as string | undefined) || (metadata.file_url as string | undefined), status, statusLabel: label, + loading, duration: metadata.duration != null ? formatDuration(metadata.duration as number) : undefined, size: item.file_size ? +(item.file_size / (1024 * 1024)).toFixed(1) : 0, createdAt: item.created_at ? new Date(item.created_at).toISOString().slice(0, 10) : "—", @@ -242,11 +260,29 @@ const AssetCard: React.FC<{ {asset.thumbUrl ? ( {asset.name} ) : ( - {kindIcon(asset.kind)} + + {asset.loading ? : kindIcon(asset.kind)} + )} - {/* 视频/配音类显示播放按钮 */} - {asset.kind === "video" && ( + {/* 处理中遮罩 */} + {asset.loading && ( +
+ + 处理中 +
+ )} + + {/* 失败状态标识 */} + {asset.status === "bad" && asset.statusLabel === "处理失败" && ( +
+ + 处理失败 +
+ )} + + {/* 视频/配音类显示播放按钮(处理中/失败不显示) */} + {asset.kind === "video" && !asset.loading && asset.status !== "bad" && ( { @@ -295,7 +331,7 @@ const AssetCard: React.FC<{