73a17193d4
将 external_generation_job 扩展为含价格、确认时间、退款流水和事件审计的正式生成任务事实源 新增外部生成任务列表与确认 BFF,并让平台入口按后端任务补弹完成或失败提示 保护画布生成队列超时、快照回写和自动保存覆盖场景 将 worker 计费退款流水写入 externalGenerationJobId,并补充冷备份后 worker/controller 恢复守护
119 lines
4.2 KiB
TypeScript
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>
|
|
);
|
|
}
|