feat: 结果页添加帧预览、GIF动画和导出选项

- 后端: GetAssets 返回素材级别 metadata,区分 frame/preview/spritesheet 类型
- 后端: 上传时区分素材类型标签,GIF URL 独立追踪
- 前端: 结果详情页默认展示拆分后的单帧网格
- 前端: 添加动作帧 GIF 预览弹窗
- 前端: 添加导出下拉菜单(整张精灵表/自动切分帧)
- 前端: 失败任务展示错误信息和重试次数
This commit is contained in:
2026-05-25 19:41:55 +08:00
parent ab2b72e746
commit a518168762
6 changed files with 301 additions and 106 deletions
+32 -36
View File
@@ -79,26 +79,41 @@ export default function ResultPage() {
)
}
return (
<div className="container page-enter" style={{ paddingTop: 24, paddingBottom: 40 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 24 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
<Link
to={`/projects/${projectId}`}
style={{ fontSize: 13, color: 'var(--text-secondary)' }}
>
if (task.status === 'failed') {
return (
<div className="container page-enter" style={{ paddingTop: 40 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 16, marginBottom: 24 }}>
<Link to={`/projects/${projectId}`} style={{ fontSize: 13, color: 'var(--text-secondary)' }}>
&larr; 返回工程
</Link>
<h1 style={{ fontSize: 22 }}>生成结果</h1>
<h1 style={{ fontSize: 22 }}>生成失败</h1>
</div>
{assets.length > 0 && (
<button
className="btn-primary"
onClick={() => downloadAssets(assets)}
style={{ padding: '8px 20px', fontSize: 13 }}
>
下载全部素材
</button>
<div className="card" style={{ textAlign: 'center', padding: 40 }}>
<p style={{ color: 'var(--error)', marginBottom: 8 }}>
{task.error || '未知错误'}
</p>
<p style={{ fontSize: 13, color: 'var(--text-secondary)' }}>
重试次数: {task.retryCount ?? 0}
</p>
</div>
</div>
)
}
return (
<div className="container page-enter" style={{ paddingTop: 24, paddingBottom: 40 }}>
<div style={{ marginBottom: 24 }}>
<Link
to={`/projects/${projectId}`}
style={{ fontSize: 13, color: 'var(--text-secondary)' }}
>
&larr; 返回工程
</Link>
<h1 style={{ fontSize: 22, marginTop: 8 }}>生成结果</h1>
{task.prompt && (
<p style={{ fontSize: 13, color: 'var(--text-secondary)', marginTop: 4 }}>
提示词: {task.prompt}
</p>
)}
</div>
@@ -108,22 +123,3 @@ export default function ResultPage() {
</div>
)
}
async function downloadAssets(assets: Asset[]) {
for (const a of assets) {
try {
const res = await fetch(a.url)
const blob = await res.blob()
const blobUrl = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = blobUrl
link.download = `${a.id}.${a.format}`
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
URL.revokeObjectURL(blobUrl)
} catch {
window.open(a.url, '_blank')
}
}
}