AI创作批量生成任务 main V1 init
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
import React from 'react';
|
||||
import { Empty, Spin, Tag, Typography } from 'antd';
|
||||
import { PlayCircleFilled } from '@ant-design/icons';
|
||||
import type { GenerationAITaskOut } from '../../types';
|
||||
|
||||
interface Props {
|
||||
task: GenerationAITaskOut;
|
||||
resolveUrl: (url?: string | null) => string;
|
||||
onPreview: (url: string, type: 'image' | 'video', title: string) => void;
|
||||
}
|
||||
|
||||
const spanByCount = (count: number, index: number): number => {
|
||||
if (count <= 1) return 6;
|
||||
if (count === 2 || count === 4) return 3;
|
||||
if (count === 3) return index < 2 ? 3 : 6;
|
||||
return index < 3 ? 2 : 3;
|
||||
};
|
||||
|
||||
const LABELS: Record<string, string> = {
|
||||
pending: '待处理', queued: '已入队', preparing: '准备中', generating: '生成中',
|
||||
creating_provider_task: '创建任务中', waiting_remote: '等待生成', polling: '轮询中',
|
||||
result_ready: '结果就绪', download_queued: '等待下载', downloading: '下载中',
|
||||
retry_waiting: '等待重试', completed: '已完成', failed: '生成失败',
|
||||
download_failed: '下载失败', deleted: '已删除',
|
||||
};
|
||||
|
||||
const GenerationTaskResourceGrid: React.FC<Props> = ({ task, resolveUrl, onPreview }) => {
|
||||
const count = Math.max(1, Math.min(5, Number(task.generationCount || task.childItems?.length || 1)));
|
||||
const sortedChildren = [...(task.childItems || [])].sort((a, b) => Number(a.generationIndex || 0) - Number(b.generationIndex || 0));
|
||||
const items: GenerationAITaskOut[] = sortedChildren.length
|
||||
? sortedChildren
|
||||
: (count > 1
|
||||
? Array.from({ length: count }, (_, index) => ({ ...task, id: `${task.id}-${index + 1}`, generationIndex: index + 1, childItems: [] }))
|
||||
: [task]);
|
||||
|
||||
return (
|
||||
<div style={{ width: '100%', height: 430, display: 'grid', gridTemplateColumns: 'repeat(6, minmax(0,1fr))', gridAutoRows: 'minmax(0,1fr)', gap: items.length > 1 ? 8 : 0 }}>
|
||||
{items.map((item, index) => {
|
||||
const status = item.displayStatus || item.pipelineStage || item.status || 'pending';
|
||||
const isVideo = item.genType === 'video';
|
||||
const resultUrl = resolveUrl(isVideo ? item.videoUrl : item.imageUrl);
|
||||
const coverUrl = resolveUrl(item.videoCoverUrl);
|
||||
const active = ['pending', 'queued', 'preparing', 'generating', 'creating_provider_task', 'waiting_remote', 'polling', 'result_ready', 'download_queued', 'downloading', 'retry_waiting'].includes(status);
|
||||
return (
|
||||
<div key={item.id} style={{ gridColumn: `span ${spanByCount(items.length, index)}`, minWidth: 0, minHeight: 0, border: '1px solid #edf0f5', borderRadius: 10, overflow: 'hidden', position: 'relative', background: '#f8f9fc' }}>
|
||||
{resultUrl && status !== 'deleted' ? (
|
||||
<button type="button" onClick={() => onPreview(resultUrl, isVideo ? 'video' : 'image', `生成结果 ${item.generationIndex || index + 1}`)} style={{ width: '100%', height: '100%', padding: 0, border: 0, background: 'transparent', cursor: 'pointer', position: 'relative' }}>
|
||||
{isVideo ? (coverUrl ? <img src={coverUrl} alt="视频封面" style={{ width: '100%', height: '100%', objectFit: 'contain' }} /> : <video src={resultUrl} muted preload="metadata" style={{ width: '100%', height: '100%', objectFit: 'contain' }} />) : <img src={resultUrl} alt="生成图片" style={{ width: '100%', height: '100%', objectFit: 'contain' }} />}
|
||||
{isVideo ? <PlayCircleFilled style={{ position: 'absolute', left: '50%', top: '50%', transform: 'translate(-50%,-50%)', color: '#fff', fontSize: 38, filter: 'drop-shadow(0 3px 8px rgba(0,0,0,.35))' }} /> : null}
|
||||
</button>
|
||||
) : (
|
||||
<div style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 9, padding: 12, textAlign: 'center' }}>
|
||||
{active ? <Spin size="small" /> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={null} />}
|
||||
<Tag color={status === 'deleted' ? 'default' : (status === 'download_failed' || status === 'failed' ? 'error' : 'processing')}>{LABELS[status] || status}</Tag>
|
||||
{item.errorMessage && !active ? <Typography.Text type="danger" style={{ fontSize: 11 }}>{item.errorMessage}</Typography.Text> : null}
|
||||
</div>
|
||||
)}
|
||||
{items.length > 1 ? <span style={{ position: 'absolute', top: 6, left: 6, padding: '1px 7px', borderRadius: 10, color: '#fff', background: 'rgba(17,24,39,.58)', fontSize: 11 }}>#{item.generationIndex || index + 1}</span> : null}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default GenerationTaskResourceGrid;
|
||||
Reference in New Issue
Block a user