fix: 平台套图工作台 YYX 测试清单一轮修复(13 条)

- 平台改单选;结果分组/标签名对应所选平台
- 工作台结果区按当前商品隔离(productBatches)
- 平台套图去掉「详情排版」,只出平台头图
- 部分失败给失败占位 +「重跑这张」单张重跑;部分成功批次恢复加入资产库
- 提示词框加大;生图模型选择移入框内作胶囊靠左置底
- 去掉生成按钮下方说明文字
- 工作台「新建商品」改弹抽屉(复用 ProductCreateDrawer),不再跳商品库
- 右上搜索恒显全部平台、改为筛选结果图(按提示词)
- 结果卡参考即梦:显示提示词/平台/张数

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
zyc
2026-06-27 15:18:21 +08:00
co-authored by Claude Opus 4.8
parent 8f32ad69d3
commit 217d24f316
4 changed files with 94 additions and 21 deletions
+36 -17
View File
@@ -762,7 +762,6 @@ export function ImageWorkbenchPage({
(mode !== "model" || (!!product?.id && pickedIds.length > 0)) &&
// 平台套图:必须选商品 + 至少一个平台(P0③ 多选)
(mode !== "cover" || (!!product?.id && pickedIds.length > 0));
const anyGenerating = batches.some((b) => b.status === "generating");
/* 多批次本地持久化(行33):用 ai-tools 私有 key,不与 App 的单批 `airshelf:imgwb:{mode}` 抢同一槽。
已出图的批次直接回显;App 不可改,故"刷新时仍在跑"的批次只能尽力恢复(见回报)。 */
@@ -1291,16 +1290,11 @@ export function ImageWorkbenchPage({
原右上「1 批·比例」与「N×」徽标系重复信息,已剔除 */}
<div className="iw-pv-h">
<Quote className="quote-icon" />
{/* YYX#13:参考即梦 —— 显示用户输入的提示词 / 选择的平台 / 生成的张数 */}
<div className="pv-line">
<span className="k"></span>
<span className="v">{batchProductTitle(batch)}</span>
<span className="k"></span>
<span className="v pv-prompt">{batch.prompt || "—"}</span>
</div>
{mode === "model" && batch.modelName && (
<div className="pv-line">
<span className="k"></span>
<span className="v">{batch.modelName}</span>
</div>
)}
{mode === "cover" && (
<div className="pv-line">
<span className="k"></span>
@@ -1313,8 +1307,14 @@ export function ImageWorkbenchPage({
</span>
</div>
)}
{mode === "model" && batch.modelName && (
<div className="pv-line">
<span className="k"></span>
<span className="v">{batch.modelName}</span>
</div>
)}
<div className="pv-line">
<span className="k"></span>
<span className="k"></span>
<span className="v">
{batch.count} · {batch.ratio} · {generating ? "生成中" : failed ? "失败" : "已完成"}
{!generating && !failed && batch.results.some((a) => a.in_library)
@@ -1902,12 +1902,31 @@ export function ImageWorkbenchPage({
)}
<div className="iw-sub">
<div className="iw-sub-h">// 提示词 · 描述你想要的画面</div>
<textarea
className="textarea"
value={prompt}
onChange={(event) => setPrompt(event.target.value)}
placeholder={`例如:${meta.promptTemplate(product?.title || "商品")}`}
/>
{/* YYX#7:提示词框加大;YYX#10:生图模型选择作胶囊嵌在框内靠左置底 */}
<div className="iw-prompt-box">
<textarea
className="textarea iw-prompt-text"
value={prompt}
onChange={(event) => setPrompt(event.target.value)}
placeholder={`例如:${meta.promptTemplate(product?.title || "商品")}`}
/>
<div className="iw-prompt-bar">
<div className={`chip-wrap iw-model-pill${genModelOpen ? " open" : ""}`} data-filter="genmodel">
<button className="chip" type="button" title="生图模型" onClick={() => setGenModelOpen((v) => !v)}>
<Sparkles size={12} />
<span className="chip-label">{GEN_MODEL_OPTIONS.find((o) => o.value === genModel)?.label || "火山 Seedream"}</span>
<svg className="caret" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M4 6l4 4 4-4" /></svg>
</button>
<div className="chip-menu">
{GEN_MODEL_OPTIONS.map((o) => (
<div className={`mi${genModel === o.value ? " selected" : ""}`} key={o.value} role="button" tabIndex={0} onClick={() => { setGenModel(o.value); setGenModelOpen(false); }}>
<svg className="mi-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2"><path d="M3 8l3.5 3.5L13 5" /></svg>{o.label}
</div>
))}
</div>
</div>
</div>
</div>
</div>
</div>
@@ -1917,7 +1936,7 @@ export function ImageWorkbenchPage({
<WandSparkles size={13} />
( ¥{(candidateCount * (mode === "model" ? 0.3 : 0.5)).toFixed(2)})
</button>
<div className="iw-cta-hint">// 生成即扣费 · 生成的图先在工作台 · 点「加入资产库」才进资产库列表{anyGenerating ? " · 有批次生成中,可继续提交" : ""}</div>
{/* YYX#9:去掉生成按钮下方那行说明文字 */}
</div>
</div>
+16 -2
View File
@@ -6,8 +6,9 @@ import { SkeletonRows } from "../components/loading";
import { money, stageMeta, statusPill } from "./stage-config";
import { Progress } from "../components/pipeline-stage";
import { IconKitSvg } from "../components/IconKitSvg";
import { ProductCreateDrawer, type ProductCreatePayload } from "../components/product-create-drawer";
export function Dashboard({ products, projects, productTotal, projectTotal, billing, userName, loading = false, navigate }: {
export function Dashboard({ products, projects, productTotal, projectTotal, billing, userName, loading = false, navigate, onCreateProduct }: {
products: Product[];
projects: Project[];
productTotal?: number; // 后端真实总数(分页 count),用于「N 个/SKU」展示
@@ -16,7 +17,11 @@ export function Dashboard({ products, projects, productTotal, projectTotal, bill
userName?: string;
loading?: boolean;
navigate: (page: Page) => void;
// YYX#11:工作台点「新建商品」直接弹抽屉(复用商品库同一组件),而非跳商品库
onCreateProduct?: (payload: ProductCreatePayload) => Promise<Product | null | undefined> | void;
}) {
// YYX#11:新建商品抽屉开关
const [createDrawer, setCreateDrawer] = useState(false);
const projCount = projectTotal ?? projects.length;
const prodCount = productTotal ?? products.length;
// 资产总数:走轻量 summary 接口(各 tab 计数之和),不再吃全局 assets 全量数组
@@ -58,7 +63,7 @@ export function Dashboard({ products, projects, productTotal, projectTotal, bill
<div className="sub"><span className="mono">// {dateMono}</span><span>·</span><span>你有 <b>{running} 个项目</b> 正在进行中</span></div>
</div>
<div className="actions">
<button className="btn" type="button" onClick={() => navigate("products")}>
<button className="btn" type="button" onClick={() => (onCreateProduct ? setCreateDrawer(true) : navigate("products"))}>
<IconKitSvg name="productPlus" size={16} />
</button>
@@ -70,6 +75,15 @@ export function Dashboard({ products, projects, productTotal, projectTotal, bill
</div>
<div className="stats with-corners"><span className="corner-tr">+</span><span className="corner-bl">+</span><KpiStat label="总项目" badge="ALL" value={projCount} delta={totalDelta} /><KpiStat label="进行中" badge="WIP" value={running} /><KpiStat label="成片" badge="DONE" value={completed} /><button className="stat" type="button" onClick={() => navigate("account")}><div className="lbl"> <span className="badge">¥</span></div><div className="v">{money(billing?.account.balance)}</div><div className="bar"><span style={{ width: `${reservedPct}%` }} /></div><div className="sub"> {money(reservedNum)} / {money(fundsTotal)}</div></button></div>
<div className="dash-grid"><div><div className="section-h"><h2></h2><button className="more" type="button" onClick={() => navigate("projects")}>[ ALL · {projCount} ] </button></div><div className="card-hard">{loading && projects.length === 0 ? <SkeletonRows count={4} /> : projects.slice(0, 6).map((project) => <button className="recent-row" key={project.id} type="button" onClick={() => navigate("pipeline")}><div className="placeholder thumb"><span className="ph-frame">9:16</span></div><div className="recent-meta"><div className="name">{project.name}</div><div className="sub">{[productTitle(project.product), "AI 全生", project.video_segment_count ? `${project.video_segment_count}` : null].filter(Boolean).join(" / ")}</div></div><Progress status={project.current_stage} /><span className={`pill ${statusPill(project.status)}`}><span className="dot" />{stageMeta[project.current_stage]?.label || project.current_stage}</span><span className="btn btn-sm"></span></button>)}</div></div><div style={{ display: "flex", flexDirection: "column", gap: 24 }}><div><div className="section-h"><h2></h2><span className="more">[ /shortcuts ]</span></div><div className="shortcuts"><Shortcut name="package" title="" desc={`${prodCount} SKU`} onClick={() => navigate("products")} /><Shortcut name="images" title="" desc={`${assetCount ?? ""} `} onClick={() => navigate("library")} /><Shortcut name="creditCard" title="" desc={money(billing?.account.balance)} onClick={() => navigate("account")} /><Shortcut name="clapperboard" title="" desc={`${projCount} `} onClick={() => navigate("projects")} /></div></div><div><div className="section-h"><h2></h2><span className="more">[ FAQ ]</span></div><div className="tip"><strong></strong> <span className="mono">[ ]</span> token </div></div></div></div>
{/* YYX#11:新建商品抽屉 · 复用商品库同一组件,创建成功后跳商品库看新商品 */}
{onCreateProduct && (
<ProductCreateDrawer
open={createDrawer}
close={() => setCreateDrawer(false)}
onCreate={onCreateProduct}
onCreated={() => { setCreateDrawer(false); navigate("products"); }}
/>
)}
</>
);
}