This commit is contained in:
Azmat@qq.com
2026-09-16 16:52:01 +08:00
parent 7f5307d97c
commit b487b65575
5 changed files with 158 additions and 26 deletions
+68 -11
View File
@@ -1488,6 +1488,8 @@ function ConfirmCard({
const cardIsVideo = message.payload.kind !== "image" && isVideo;
const durationChanged =
cardIsVideo && Boolean(draft.duration) && Boolean(snapshot.duration) && draft.duration !== snapshot.duration;
const durationSeconds = Number(String(draft.duration || "").replace(/\D/g, ""));
const willGenerateInSegments = cardIsVideo && durationSeconds > 30 && durationSeconds <= 60;
const setField = (key: string, value: string) => setDraft((prev) => ({ ...prev, [key]: value }));
const summary = paramLine(draft, cardIsVideo) || "当前参数";
// 确认卡积分:改模型/分辨率/时长/张数时按后台挂牌实时重算(与后端 quote_* 同口径;标准团队系数=1)
@@ -1537,6 +1539,8 @@ function ConfirmCard({
)}
{durationChanged ? (
<p className="omni-confirm-hint">改时长会重新生成脚本。确认后先重写方案,不会直接出片。</p>
) : willGenerateInSegments ? (
<p className="omni-confirm-hint">{durationSeconds} 秒将拆成 2 段生成,每段最长 30 秒;片段完成后先给你预览,再由你决定是否合并成片。</p>
) : null}
<div className="omni-confirm-foot">
<span>{submitted ? "已确认,正在出片" : "点确认后才会提交生成"}</span>
@@ -1917,25 +1921,72 @@ function withoutLegacyGateArtifacts(list: CreationMessage[]): CreationMessage[]
return hidden.size ? list.filter((message) => !hidden.has(message.id)) : list;
}
function ProcessCard({ payload }: { payload: Record<string, unknown> }) {
function ProcessCard({
payload,
onPreview,
}: {
payload: Record<string, unknown>;
onPreview?: (src: string, kind: "image" | "video", name: string) => void;
}) {
const isSegmentedVideo = payload.kind === "video_segments";
const isMerge = payload.kind === "video_merge";
const isVideo = payload.kind === "video" || isSegmentedVideo || isMerge;
const segmentCount = Array.isArray(payload.segments) ? payload.segments.length : 0;
const assets = (payload.assets as Array<Record<string, string>> | undefined) || [];
const completedSegments = Number(payload.completed_segment_count || 0);
const waitingSegments = isSegmentedVideo ? Math.max(1, segmentCount - completedSegments) : 1;
return (
<section className="omni-result-card omni-process-card">
<div className="omni-result-media">
<figure className="omni-result-tile">
<div className="omni-process-frame" aria-hidden="true">
<span className="omni-process-ring" />
<strong>{isVideo ? "视频生成中" : "图片生成中"}</strong>
</div>
</figure>
<div className={`omni-result-media${isSegmentedVideo ? " is-grid" : ""}`}>
{assets.map((asset, index) => {
const cover = asset.cover || asset.url || "";
const url = asset.url || cover;
const video = asset.type === "video";
const label = String(asset.label || `第 ${index + 1} 段`);
return (
<figure className="omni-result-tile" key={asset.id || `${url}-${index}`}>
<button
type="button"
className="omni-result-preview"
onClick={() => url && onPreview?.(url, video ? "video" : "image", label)}
>
<img src={cover} alt={label} />
{video ? (
<span className="omni-result-play" aria-hidden="true">
<Play />
</span>
) : null}
</button>
</figure>
);
})}
{Array.from({ length: waitingSegments }).map((_, index) => (
<figure className="omni-result-tile" key={`generating-${index}`}>
<div className="omni-process-frame" aria-hidden="true">
<span className="omni-process-ring" />
<strong>{isVideo ? "视频生成中" : "图片生成中"}</strong>
</div>
</figure>
))}
</div>
<div className="omni-result-info">
<div>
<strong>{isMerge ? "正在合并成片" : isSegmentedVideo ? `正在生成 ${segmentCount || 2} 段视频` : isVideo ? "正在生成视频" : "正在出图"}</strong>
<small>{isMerge ? "正在拼接已确认的片段" : isSegmentedVideo ? "片段完成后会先展示给你确认" : isVideo ? "方案编译中,请稍候" : "画面生成中,请稍候"}</small>
<strong>{
isMerge
? "正在合并成片"
: isSegmentedVideo && completedSegments
? `第 ${completedSegments} 段已生成,正在生成第 ${completedSegments + 1} 段`
: isSegmentedVideo
? `正在生成 ${segmentCount || 2} 段视频`
: isVideo ? "正在生成视频" : "正在出图"
}</strong>
<small>{
isMerge
? "正在拼接已确认的片段"
: isSegmentedVideo && completedSegments
? "已生成的片段可以先点击预览"
: isSegmentedVideo ? "片段完成后会先展示给你确认" : isVideo ? "方案编译中,请稍候" : "画面生成中,请稍候"
}</small>
</div>
</div>
</section>
@@ -2772,7 +2823,13 @@ export function OmniSessionPage({
);
}
case "generating":
return <ProcessCard key={message.clientKey || message.id} payload={message.payload} />;
return (
<ProcessCard
key={message.clientKey || message.id}
payload={message.payload}
onPreview={(src, kind, name) => setAssetPreview({ src, kind, name })}
/>
);
case "result":
return (
<ResultCard