feat(workbench): 统一立绘详情页参考布局 · 三视图全 16:9 · 工作台批次追加

详情页 (pipeline / library / model-photo)
- 统一参考布局:大立绘+缩略 strip+查看大图,右栏 三视图+简介(标签 chip)+3 列属性表
- 底部仅留「下载」+「使用该资产」,去除收藏 / 关闭

三视图固定单张 16:9
- pipeline / library / model-photo / asset-factory / product-studio 全部同步
- 移除原 actor 3 列 3:4 拆图,改为单容器 16:9

图片工作台 (model-photo / platform-cover)
- 立即生成 + 全部重跑 + 单张重跑 均追加新批次到下方,旧批次保留
- 批量按钮下沉到每批次下方,与图片网格左对齐
- hover 重跑/采用 icon 缩小至 26px,右下角横向,无遮罩层
- 立即生成后不再自动新增「编辑中」草稿卡

新建商品 drawer
- 无 onSave 回调时默认跳转 product-detail
- 卖点新增 「+ 添加卖点」按钮(输入框下方独立行,左对齐)

product-detail
- 视频项目卡片状态 pill 改为 4 态(已完成/视频生成 4/6/已归档/故事板失败)
- 移除视频卡个体「通过/不通过/归档」状态切换
- 去掉冗余「通过」status 筛选;过滤逻辑兼容缺失按钮

sidebar (shell.js)
- 图片生成补 badge 12,团队去 badge

清理
- 删除 v2/ 历史镜像目录(与 电商AI平台/ 重复,Dockerfile build context 不依赖)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
iye
2026-05-21 21:12:03 +08:00
co-authored by Claude Opus 4.7
parent 2a3ae88dc5
commit 8a783ca36f
34 changed files with 2147 additions and 29201 deletions
+22 -15
View File
@@ -557,9 +557,6 @@
}
.asset-card .meta .pill {
padding: 2px 8px;
background: var(--accent-emerald-bg, #e6f4ec);
color: var(--accent-emerald, #1f8a51);
border: 1px solid var(--accent-emerald-bd, #c4e3d1);
border-radius: var(--r-sm);
font-size: 10.5px;
font-weight: 500;
@@ -912,7 +909,7 @@
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M4 6l4 4 4-4"/></svg>
</button>
<button class="filter" type="button" data-key="status">
全部状态
通过
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M4 6l4 4 4-4"/></svg>
</button>
<div class="right">
@@ -953,10 +950,6 @@
<div class="tab-pane" data-pane="videos">
<div class="pd-toolbar">
<div class="total">该商品视频项目 <span class="ct">(4)</span></div>
<button class="filter" type="button" data-key="status">
全部状态
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M4 6l4 4 4-4"/></svg>
</button>
<div class="right">
<button class="filter" type="button" data-key="sort">
最新导出
@@ -966,10 +959,10 @@
</div>
<div class="asset-grid">
<div class="asset-card"><div class="thumb placeholder" style="aspect-ratio: 9/16;"><span class="type-pill">视频 · 9:16</span><span class="ph-frame">补水面膜 · v3</span></div><div class="meta"><span class="pill pass" data-status="pass" title="点击切换状态">通过</span><span class="date">2026-05-20 12:08</span></div></div>
<div class="asset-card"><div class="thumb placeholder" style="aspect-ratio: 9/16;"><span class="type-pill">视频 · 9:16</span><span class="ph-frame">补水面膜 · v2</span></div><div class="meta"><span class="pill pass" data-status="pass" title="点击切换状态">通过</span><span class="date">2026-05-19 10:24</span></div></div>
<div class="asset-card"><div class="thumb placeholder" style="aspect-ratio: 9/16;"><span class="type-pill">视频 · 9:16</span><span class="ph-frame">熬夜急救 · v1</span></div><div class="meta"><span class="pill archive" data-status="archive" title="点击切换状态">归档</span><span class="date">2026-05-18 21:42</span></div></div>
<div class="asset-card"><div class="thumb placeholder" style="aspect-ratio: 9/16;"><span class="type-pill">视频 · 9:16</span><span class="ph-frame">补水面膜 · v1</span></div><div class="meta"><span class="pill fail" data-status="fail" title="点击切换状态">不通过</span><span class="date">2026-05-17 16:00</span></div></div>
<div class="asset-card" data-proj-status="done"><div class="thumb placeholder" style="aspect-ratio: 9/16;"><span class="type-pill">视频 · 9:16</span><span class="ph-frame">补水面膜 · v3</span></div><div class="meta"><span class="pill ok"><span class="dot"></span>已完成</span><span class="date">2026-05-20 12:08</span></div></div>
<div class="asset-card" data-proj-status="wip"><div class="thumb placeholder" style="aspect-ratio: 9/16;"><span class="type-pill">视频 · 9:16</span><span class="ph-frame">补水面膜 · v2</span></div><div class="meta"><span class="pill info"><span class="dot"></span>视频生成 4/6</span><span class="date">2026-05-19 10:24</span></div></div>
<div class="asset-card" data-proj-status="archived"><div class="thumb placeholder" style="aspect-ratio: 9/16;"><span class="type-pill">视频 · 9:16</span><span class="ph-frame">熬夜急救 · v1</span></div><div class="meta"><span class="pill neutral"><span class="dot"></span>已归档</span><span class="date">2026-05-18 21:42</span></div></div>
<div class="asset-card" data-proj-status="fail"><div class="thumb placeholder" style="aspect-ratio: 9/16;"><span class="type-pill">视频 · 9:16</span><span class="ph-frame">补水面膜 · v1</span></div><div class="meta"><span class="pill err"><span class="dot"></span>故事板失败</span><span class="date">2026-05-17 16:00</span></div></div>
</div>
<div class="pd-more"><button type="button">加载更多</button></div>
@@ -1273,15 +1266,17 @@
// 每个 (pane, key) 的可选项 + 默认值(数组首项)
const OPTIONS = {
'assets:type': ['全部类型', '模特上身图', '平台套图', '三视图'],
'assets:status': ['全部状态', '通过', '不通过', '归档'],
// 状态:默认 通过 · 没有「全部状态」选项,只能切换 通过 / 不通过 / 归档
'assets:status': ['通过', '不通过', '归档'],
'assets:sort': ['最新生成', '最早生成'],
'videos:status': ['全部状态', '通过', '不通过', '归档'],
'videos:sort': ['最新导出', '最早导出'],
'tasks:type': ['全部类型', '模特上身图', '平台套图', '视频素材', '三视图'],
'tasks:status': ['全部状态', '已完成', '生成中', '排队中', '失败'],
'tasks:date': ['全部', '今天', '近 7 天', '近 30 天'],
'tasks:sort': ['提交时间倒序', '提交时间正序'],
};
// 「状态」永远视为已生效筛选(没有"全部"选项),即便选了默认值也走过滤逻辑
const ALWAYS_APPLY_KEYS = new Set(['status']);
// 任务行 status pill 的 class → 中文标签
const TASK_STATUS_MAP = { ok: '已完成', gen: '生成中', wait: '排队中', err: '失败', fail: '失败' };
// assets 卡片 data-status → 中文标签
@@ -1385,6 +1380,7 @@
btn.dataset.value = val;
[...btn.childNodes].forEach(n => { if (n.nodeType === 3) n.remove(); });
btn.insertBefore(document.createTextNode(val + ' '), btn.firstChild);
// 仅在切到非默认值时才高亮(状态 chip 即便永远过滤,视觉也保持中性,与「全部类型」一致)
btn.classList.toggle('filtered', val !== btn.dataset.default);
}
@@ -1396,7 +1392,9 @@
});
const isDefault = (key) => {
const btn = paneEl.querySelector('.pd-toolbar .filter[data-key="' + key + '"]');
return !btn || btn.dataset.value === btn.dataset.default;
if (!btn) return true; // 该 pane 没这个 filter → 等同默认,不过滤
if (ALWAYS_APPLY_KEYS.has(key)) return false; // 状态有按钮时,永远走过滤
return btn.dataset.value === btn.dataset.default;
};
if (paneId === 'assets' || paneId === 'videos') {
@@ -1429,6 +1427,15 @@
}
updateCount(paneEl, visible);
toggleEmpty(paneEl, visible === 0);
// 不足 2 行时不显示「加载更多」按钮(布局后用 offsetTop 统计行数)
const moreEl = paneEl.querySelector('.pd-more');
if (moreEl) {
requestAnimationFrame(() => {
const visCards = [...paneEl.querySelectorAll('.asset-card')].filter(c => c.style.display !== 'none');
const rows = new Set(visCards.map(c => c.offsetTop)).size;
moreEl.style.display = rows >= 2 ? '' : 'none';
});
}
} else if (paneId === 'tasks') {
const rows = [...paneEl.querySelectorAll('.task-table .task-row:not(.head)')];
let visible = 0;