diff --git a/backend/internal/handler/generate.go b/backend/internal/handler/generate.go index 0aa9bce..a47eacd 100755 --- a/backend/internal/handler/generate.go +++ b/backend/internal/handler/generate.go @@ -308,14 +308,18 @@ func getTaskDBID(ctx context.Context, taskID string) uint { // RecentAssetItem 首页素材展示项。 type RecentAssetItem struct { - Key string `json:"key"` - URL string `json:"url"` - 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"` + Key string `json:"key"` + URL string `json:"url"` + Format string `json:"format"` + Prompt string `json:"prompt"` + AssetType string `json:"assetType"` + MetaType string `json:"metaType"` + TaskID string `json:"taskId"` + ProjectID string `json:"projectId"` + ProjectName string `json:"projectName"` + Width int `json:"width"` + Height int `json:"height"` + CreatedAt string `json:"createdAt"` } // GetRecentAssets 获取最近生成的素材列表(已完成任务的全部素材)。 @@ -323,23 +327,31 @@ func GetRecentAssets(c *gin.Context) { limit := 60 var dbRows []struct { - Key string - URL string - Format string - Prompt string - AssetType string - MetaType string - TaskID string - ProjectID uint - CreatedAt string + Key string + URL string + Format string + Prompt string + AssetType string + MetaType string + TaskID string + ProjectID uint + ProjectName string + Width int + Height int + CreatedAt string } 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 + t.external_id as task_id, t.project_id, + p.name as project_name, + CAST(COALESCE(json_extract(t.metadata, '$.frameWidth'), '0') AS INTEGER) as width, + CAST(COALESCE(json_extract(t.metadata, '$.frameHeight'), '0') AS INTEGER) as height, + t.created_at as created_at FROM assets a INNER JOIN tasks t ON a.task_id = t.id + INNER JOIN projects p ON t.project_id = p.id WHERE t.status = 'completed' ORDER BY a.created_at DESC LIMIT ?`, limit). @@ -348,14 +360,18 @@ func GetRecentAssets(c *gin.Context) { result := make([]RecentAssetItem, len(dbRows)) for i, r := range dbRows { result[i] = RecentAssetItem{ - Key: r.Key, - URL: storageSvc.GetSignedURL(r.Key), - Format: r.Format, - Prompt: r.Prompt, - AssetType: r.AssetType, - MetaType: r.MetaType, - TaskID: r.TaskID, - CreatedAt: r.CreatedAt, + Key: r.Key, + URL: storageSvc.GetSignedURL(r.Key), + Format: r.Format, + Prompt: r.Prompt, + AssetType: r.AssetType, + MetaType: r.MetaType, + TaskID: r.TaskID, + ProjectID: fmt.Sprintf("%d", r.ProjectID), + ProjectName: r.ProjectName, + Width: r.Width, + Height: r.Height, + CreatedAt: r.CreatedAt, } } diff --git a/frontend/src/api/types.ts b/frontend/src/api/types.ts index d4ee998..3bf0c14 100755 --- a/frontend/src/api/types.ts +++ b/frontend/src/api/types.ts @@ -107,6 +107,10 @@ export interface RecentAssetItem { assetType: string metaType: string taskId: string + projectId: string + projectName: string + width: number + height: number createdAt: string } diff --git a/frontend/src/components/AssetGallery.tsx b/frontend/src/components/AssetGallery.tsx index 621a489..33bddbc 100644 --- a/frontend/src/components/AssetGallery.tsx +++ b/frontend/src/components/AssetGallery.tsx @@ -37,10 +37,117 @@ function matchCategory(item: RecentAssetItem, cat: string): boolean { } } +function DetailModal({ item, onClose }: { item: RecentAssetItem; onClose: () => void }) { + const downloadUrl = `/api/v1/assets/download?key=${encodeURIComponent(item.key)}` + + return ( +
+
+
e.stopPropagation()} + > + + +
+ {item.prompt} +
+ + + + + + + 0 ? `${item.width} x ${item.height}` : '—'} /> + + +
+ +
+ + 下载 + + +
+
+
+ ) +} + +function Row({ label, value }: { label: string; value: string }) { + return ( + + + {label} + + {value} + + ) +} + export default function AssetGallery() { const [assets, setAssets] = useState([]) const [loading, setLoading] = useState(true) const [activeCat, setActiveCat] = useState('all') + const [detailItem, setDetailItem] = useState(null) useEffect(() => { getRecentAssets() @@ -99,7 +206,8 @@ export default function AssetGallery() {
setDetailItem(item)} >
)} + + {/* 详情弹窗 */} + {detailItem && ( + setDetailItem(null)} /> + )} ) }