diff --git a/backend/internal/handler/generate.go b/backend/internal/handler/generate.go index a2671fe..0aa9bce 100755 --- a/backend/internal/handler/generate.go +++ b/backend/internal/handler/generate.go @@ -313,13 +313,14 @@ type RecentAssetItem struct { Format string `json:"format"` Prompt string `json:"prompt"` AssetType string `json:"assetType"` + MetaType string `json:"metaType"` // frame / spritesheet / preview TaskID string `json:"taskId"` CreatedAt string `json:"createdAt"` } // GetRecentAssets 获取最近生成的素材列表(已完成任务的全部素材)。 func GetRecentAssets(c *gin.Context) { - limit := 20 + limit := 60 var dbRows []struct { Key string @@ -327,6 +328,7 @@ func GetRecentAssets(c *gin.Context) { Format string Prompt string AssetType string + MetaType string TaskID string ProjectID uint CreatedAt string @@ -334,6 +336,7 @@ func GetRecentAssets(c *gin.Context) { db.GetDB().WithContext(c.Request.Context()). Raw(`SELECT a.key, a.url, a.format, t.prompt, t.asset_type as asset_type, + COALESCE(json_extract(a.metadata, '$.type'), 'frame') as meta_type, t.external_id as task_id, t.project_id, t.created_at as created_at FROM assets a INNER JOIN tasks t ON a.task_id = t.id @@ -350,6 +353,7 @@ func GetRecentAssets(c *gin.Context) { Format: r.Format, Prompt: r.Prompt, AssetType: r.AssetType, + MetaType: r.MetaType, TaskID: r.TaskID, CreatedAt: r.CreatedAt, } diff --git a/frontend/src/api/types.ts b/frontend/src/api/types.ts index a4d22a5..d4ee998 100755 --- a/frontend/src/api/types.ts +++ b/frontend/src/api/types.ts @@ -105,6 +105,7 @@ export interface RecentAssetItem { format: string prompt: string assetType: string + metaType: string taskId: string createdAt: string } diff --git a/frontend/src/components/AssetGallery.tsx b/frontend/src/components/AssetGallery.tsx index 87b45e9..621a489 100644 --- a/frontend/src/components/AssetGallery.tsx +++ b/frontend/src/components/AssetGallery.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react' +import { useEffect, useState, useMemo } from 'react' import { getRecentAssets } from '../api/generate' import type { RecentAssetItem } from '../api/types' @@ -9,9 +9,38 @@ const ASSET_TYPE_LABELS: Record = { animation: '动画', } +const CATEGORIES = [ + { key: 'all', label: '全部' }, + { key: 'spritesheet', label: '精灵表' }, + { key: 'frame', label: '帧' }, + { key: 'tileset', label: '场景瓦片' }, + { key: 'background', label: '背景' }, + { key: 'ui', label: 'UI' }, +] + +function matchCategory(item: RecentAssetItem, cat: string): boolean { + switch (cat) { + case 'all': + return true + case 'spritesheet': + return item.metaType === 'spritesheet' + case 'frame': + return item.metaType === 'frame' || item.metaType === 'preview' || item.format === 'gif' + case 'tileset': + return item.assetType === 'sprite' && item.metaType === 'spritesheet' + case 'background': + return item.assetType === 'background' + case 'ui': + return item.assetType === 'ui' + default: + return true + } +} + export default function AssetGallery() { const [assets, setAssets] = useState([]) const [loading, setLoading] = useState(true) + const [activeCat, setActiveCat] = useState('all') useEffect(() => { getRecentAssets() @@ -20,81 +49,110 @@ export default function AssetGallery() { .finally(() => setLoading(false)) }, []) - if (loading) return null + const filtered = useMemo( + () => assets.filter(item => matchCategory(item, activeCat)), + [assets, activeCat] + ) + if (loading) return null if (assets.length === 0) return null return (
-

最近生成

-
- {assets.map((item, i) => ( -
{ - // navigate to result page — need projectId for URL - // For now just open the image +

最近生成

+ + {/* 分类标签 */} +
+ {CATEGORIES.map(cat => ( +
+ {cat.label} + ))}
+ + {/* 素材网格 */} + {filtered.length === 0 ? ( +

暂无此类素材

+ ) : ( +
+ {filtered.map((item, i) => ( +
+
+ {item.prompt} +
+
+ + {ASSET_TYPE_LABELS[item.assetType] || item.assetType} + +
+

+ {item.prompt} +

+
+ ))} +
+ )}
) }