Files
Genarrative/src/components/platform-entry/PlatformProfileGenerationQueueCard.tsx
T
kdletters 73a17193d4 升级外部生成为正式任务列表
将 external_generation_job 扩展为含价格、确认时间、退款流水和事件审计的正式生成任务事实源

新增外部生成任务列表与确认 BFF,并让平台入口按后端任务补弹完成或失败提示

保护画布生成队列超时、快照回写和自动保存覆盖场景

将 worker 计费退款流水写入 externalGenerationJobId,并补充冷备份后 worker/controller 恢复守护
2026-06-24 15:17:12 +08:00

119 lines
4.2 KiB
TypeScript

import { Loader2 } from 'lucide-react';
import { PlatformPillBadge } from '../common/PlatformPillBadge';
import { PlatformProgressBar } from '../common/PlatformProgressBar';
import {
buildExternalGenerationQueuePresentation,
type ExternalGenerationQueueStatus,
} from './platformExternalGenerationQueueStatusModel';
type PlatformProfileGenerationQueueCardProps = {
queueStatus: ExternalGenerationQueueStatus | null;
};
/**
* “我的”页里的后台生成状态卡。
* 只展示当前账号可见的队列概览,业务完成仍以各玩法草稿 / 作品回读为准。
*/
export function PlatformProfileGenerationQueueCard({
queueStatus,
}: PlatformProfileGenerationQueueCardProps) {
const presentation = buildExternalGenerationQueuePresentation(queueStatus);
if (!presentation.shouldShow) {
return null;
}
const progressValue = presentation.progress ?? 0;
return (
<section
className="platform-profile-generation-queue-card"
aria-label="生成队列"
>
<div className="flex min-w-0 items-start justify-between gap-3">
<div className="min-w-0">
<div className="flex items-center gap-2">
<span className="platform-profile-generation-queue-card__icon">
<Loader2 className="h-4 w-4" />
</span>
<span className="text-[15px] font-black text-[var(--platform-text-strong)]">
生成队列
</span>
</div>
<div className="mt-1 text-[12px] font-medium text-[var(--platform-text-base)]">
{presentation.statusLabel ?? '等待生成任务'}
</div>
</div>
{presentation.statusLabel ? (
<PlatformPillBadge
tone={
queueStatus?.currentStatus === 'failed' ? 'danger' : 'profile'
}
size="xs"
className="shrink-0 px-2.5 py-1"
>
{presentation.progressLabel
? `${presentation.statusLabel} ${presentation.progressLabel}`
: presentation.statusLabel}
</PlatformPillBadge>
) : null}
</div>
<PlatformProgressBar
value={progressValue}
indeterminate={presentation.progress == null}
ariaLabel="生成队列进度"
size="sm"
className="mt-3 bg-[rgba(240,226,214,0.88)]"
fillClassName="bg-[linear-gradient(90deg,#e47631,#ce5f2a)]"
/>
{presentation.tasks.length > 0 ? (
<div className="mt-3 space-y-2">
{presentation.tasks.map((task) => (
<div
key={task.id}
className="rounded-[8px] border border-[var(--platform-subpanel-border)] bg-white/72 px-3 py-2"
>
<div className="flex min-w-0 items-center justify-between gap-2">
<span className="min-w-0 truncate text-[12px] font-black text-[var(--platform-text-strong)]">
{task.title}
</span>
<PlatformPillBadge
tone={task.tone}
size="xxs"
className="shrink-0"
>
{task.statusLabel}
</PlatformPillBadge>
</div>
<div className="mt-1 flex min-w-0 items-center justify-between gap-2 text-[11px] font-medium text-[var(--platform-text-base)]">
<span className="min-w-0 truncate">{task.detail}</span>
<span className="shrink-0 font-black text-[var(--platform-text-strong)]">
{task.priceLabel}
</span>
</div>
</div>
))}
</div>
) : null}
<div className="mt-3 grid grid-cols-3 gap-2">
<div className="platform-profile-generation-queue-card__metric">
<span>排队</span>
<strong>{presentation.pendingLabel}</strong>
</div>
<div className="platform-profile-generation-queue-card__metric">
<span>生成</span>
<strong>{presentation.runningLabel}</strong>
</div>
<div className="platform-profile-generation-queue-card__metric">
<span>待提示</span>
<strong>{presentation.unacknowledgedTerminalCount}</strong>
</div>
</div>
</section>
);
}