fix(core): 资产详情大图随数据显示,缩略图已出大图不再卡转圈
- 三视图/立绘大图改为跟数据(url)走,出图完成即显示,不再被在途轮询的 busy 标志挡住——根治「小缩略图已出、大图一直转圈」 - 一并带上工作区其余改动(script_agent / services / tests / ai-tools / actor-library / App) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -424,7 +424,7 @@ export function PipelinePage(props: {
|
||||
scriptModelName: string;
|
||||
textModels?: ModelConfig[]; onAdoptScript: (scriptId: string) => void | Promise<unknown>;
|
||||
onUpdateShot: (payload: { segment_id: string; narration?: string; visual_prompt?: string; duration_seconds?: number }) => Promise<unknown>;
|
||||
onAddShot: (afterSegmentId: string) => Promise<unknown>;
|
||||
onAddShot: (afterSegmentId: string, content?: { narration?: string; visual_prompt?: string }) => Promise<unknown>;
|
||||
onDeleteShot: (segmentId: string) => Promise<unknown>;
|
||||
// 行35 · 单条分镜重跑(后端只重写该 segment);行28/34 · 把设定/标签合并进 project.metadata 持久化
|
||||
onRerunShot?: (segmentId: string, instruction?: string) => Promise<unknown>;
|
||||
@@ -650,7 +650,7 @@ export function PipelinePage(props: {
|
||||
}
|
||||
// 流程步骤4 · 演员库覆盖层:replace 带要替换的目标(已生成卡=groupId;seed 占位卡还没组=kind+label);
|
||||
// browse 带 studio 直接进「添加人物工作台」
|
||||
const [actorLib, setActorLib] = useState<{ mode: "browse" | "replace"; studio?: boolean; groupId?: string; seedKind?: "person" | "scene"; seedLabel?: string } | null>(null);
|
||||
const [actorLib, setActorLib] = useState<{ mode: "browse" | "replace"; studio?: boolean; groupId?: string; seedKind?: "person" | "scene"; seedLabel?: string; addNew?: boolean } | null>(null);
|
||||
function openActorReplace(entity: AssetEntity) {
|
||||
setActorLib({ mode: "replace", groupId: entity.group.id });
|
||||
}
|
||||
@@ -658,9 +658,11 @@ export function PipelinePage(props: {
|
||||
function openActorReplaceSeed(kind: "person" | "scene", tag: string) {
|
||||
setActorLib({ mode: "replace", seedKind: kind, seedLabel: tag });
|
||||
}
|
||||
async function pickActor(assetId: string) {
|
||||
async function pickActor(assetId: string, assetName?: string) {
|
||||
if (actorLib?.mode === "replace") {
|
||||
if (actorLib.groupId) await onAttachBaseAsset({ group_id: actorLib.groupId }, assetId);
|
||||
// 新增人物(无既有 group / 无脚本标签):用所选演员名当 label 建一个新人物组挂上
|
||||
else if (actorLib.addNew && actorLib.seedKind) await onAttachBaseAsset({ kind: actorLib.seedKind, label: (assetName || "").trim() }, assetId);
|
||||
else if (actorLib.seedKind) await onAttachBaseAsset({ kind: actorLib.seedKind, label: actorLib.seedLabel }, assetId);
|
||||
}
|
||||
setActorLib(null);
|
||||
@@ -2153,7 +2155,7 @@ export function PipelinePage(props: {
|
||||
{/* 行34 · 该卡之后挂着的本地草稿分镜(点「添加分镜」插入,可编辑,有内容失焦才落库) */}
|
||||
{draftShots.filter((d) => d.afterId === shot.id).map((draft) => (
|
||||
<DraftShotCard key={draft.id} draft={draft}
|
||||
onCommit={(d) => { setDraftShots((list) => list.filter((x) => x.id !== d.id)); if ((d.narration || d.visual).trim()) void onAddShot(d.afterId || shot.id); }}
|
||||
onCommit={(d) => { setDraftShots((list) => list.filter((x) => x.id !== d.id)); if ((d.narration || d.visual).trim()) void onAddShot(d.afterId || shot.id, { narration: d.narration, visual_prompt: d.visual }); }}
|
||||
onCancel={(id) => setDraftShots((list) => list.filter((x) => x.id !== id))} />
|
||||
))}
|
||||
<div className="shot-insert-gap">
|
||||
@@ -2465,7 +2467,7 @@ export function PipelinePage(props: {
|
||||
<h3>{KIND_LABEL[kind]} · {entities.length} 个</h3>
|
||||
<span className="spacer"></span>
|
||||
{/* 对齐设计稿:克制的小按钮(人物去演员库工作台;场景直接生成一版) */}
|
||||
<button className="btn btn-sm" type="button" data-stop disabled={isBusy(customBusy)} onClick={() => { if (kind === "person") setActorLib({ mode: "browse", studio: true }); else setSceneDrafts((d) => [...d, { id: `scene-draft-${Date.now()}`, title: "", prompt: genPrompt }]); }}>{isBusy(customBusy) ? "生成中…" : `+ 新增${KIND_LABEL[kind]}`}</button>
|
||||
<button className="btn btn-sm" type="button" data-stop disabled={isBusy(customBusy)} onClick={() => { if (kind === "person") setActorLib({ mode: "replace", seedKind: "person", addNew: true }); else setSceneDrafts((d) => [...d, { id: `scene-draft-${Date.now()}`, title: "", prompt: genPrompt }]); }}>{isBusy(customBusy) ? "生成中…" : `+ 新增${KIND_LABEL[kind]}`}</button>
|
||||
</div>
|
||||
<div className="asset-grid-2">
|
||||
{/* 新增场景:本地占位草稿卡(可改标题 + 提示词),编辑完点「AI 生成」才出图;出图成功后移除草稿 */}
|
||||
@@ -3279,12 +3281,13 @@ export function PipelinePage(props: {
|
||||
{/* 左:大立绘 + 版本缩略 */}
|
||||
<div className="asset-detail-lead">
|
||||
<div className="ad-lead-wrap">
|
||||
<div className={`placeholder ad-lead-img${portraitUrl && !busyPortrait ? " has-mock-media" : ""}`} style={portraitUrl && !busyPortrait ? mediaStyle(portraitUrl) : undefined}>
|
||||
{busyPortrait
|
||||
{/* 同三视图:大图随数据(portraitUrl)走,出图完成即显示,不被在途 busyPortrait 卡转圈 */}
|
||||
<div className={`placeholder ad-lead-img${portraitUrl ? " has-mock-media" : ""}`} style={portraitUrl ? mediaStyle(portraitUrl) : undefined}>
|
||||
{!portraitUrl && (busyPortrait
|
||||
? <div style={{ display: "flex", flexDirection: "column", gap: 8, alignItems: "center" }}><span className="spinner" aria-hidden="true"></span><span className="ph-frame">立绘生成中…</span></div>
|
||||
: !portraitUrl && <span className="ph-frame">立绘</span>}
|
||||
: <span className="ph-frame">立绘</span>)}
|
||||
</div>
|
||||
{portraitUrl && !busyPortrait && <button className="ad-zoom-btn" type="button" title="查看大图" onClick={() => setPreview({ src: portraitUrl, kind: "image", name: `${entity.name} · 立绘` })}>{zoomSvg}</button>}
|
||||
{portraitUrl && <button className="ad-zoom-btn" type="button" title="查看大图" onClick={() => setPreview({ src: portraitUrl, kind: "image", name: `${entity.name} · 立绘` })}>{zoomSvg}</button>}
|
||||
</div>
|
||||
{portraitVersions.length > 0 && (
|
||||
<div className="ad-thumbs">
|
||||
@@ -3311,12 +3314,14 @@ export function PipelinePage(props: {
|
||||
</div>
|
||||
<div className="asset-detail-tri-row">
|
||||
<div className="ad-tri-wrap">
|
||||
<div className={`placeholder${triUrl && !busyTri ? " has-mock-media" : ""}`} style={triUrl && !busyTri ? mediaStyle(triUrl) : undefined}>
|
||||
{busyTri
|
||||
{/* 大图随数据(triUrl)走,不被在途 poll 的 busyTri 挡住:出图完成(缩略图已有 url)即显示,
|
||||
避免「缩略图已出、大图还在转圈」——busyTri 只在「还没任何结果」时显示生成中占位 */}
|
||||
<div className={`placeholder${triUrl ? " has-mock-media" : ""}`} style={triUrl ? mediaStyle(triUrl) : undefined}>
|
||||
{!triUrl && (busyTri
|
||||
? <div style={{ display: "flex", flexDirection: "column", gap: 8, alignItems: "center" }}><span className="spinner" aria-hidden="true"></span><span className="ph-frame">三视图生成中…</span></div>
|
||||
: !triUrl && <span className="ph-frame">正 / 侧 / 背 · 三视图</span>}
|
||||
: <span className="ph-frame">正 / 侧 / 背 · 三视图</span>)}
|
||||
</div>
|
||||
{triUrl && !busyTri && <button className="ad-zoom-btn" type="button" title="查看大图" onClick={() => setPreview({ src: triUrl, kind: "image", name: `${entity.name} · 三视图` })}>{zoomSvg}</button>}
|
||||
{triUrl && <button className="ad-zoom-btn" type="button" title="查看大图" onClick={() => setPreview({ src: triUrl, kind: "image", name: `${entity.name} · 三视图` })}>{zoomSvg}</button>}
|
||||
</div>
|
||||
</div>
|
||||
{!triUrl && !busyTri && (
|
||||
|
||||
Reference in New Issue
Block a user