diff --git a/电商AI平台/library.html b/电商AI平台/library.html
index df56a7f..ded2218 100644
--- a/电商AI平台/library.html
+++ b/电商AI平台/library.html
@@ -1566,7 +1566,7 @@ submitBtn.addEventListener('click', () => {
card.dataset.source = '手动上传';
card.dataset.used = '0';
card.dataset.added = stamp;
- card.setAttribute('onclick', `Shell.toast('查看资产', ${JSON.stringify(name)})`);
+ card.setAttribute('onclick', `if(!document.body.classList.contains('edit-mode')&&window.LibraryAssetDetailOpen){window.LibraryAssetDetailOpen(this)}else if(!document.body.classList.contains('edit-mode')){Shell.toast('查看资产', ${JSON.stringify(name)})}`);
let metaText = '';
if (kind === 'people') {
@@ -1979,6 +1979,7 @@ document.querySelectorAll('.asset-card').forEach(card => {
.asset-modal-bg { position: fixed; inset: 0; background: rgba(0,0,0,.4); z-index: 1000; display: none; align-items: center; justify-content: center; padding: 40px; }
.asset-modal-bg.show { display: flex; }
.asset-modal { background: var(--surface); border: 1px solid var(--border-faint); border-radius: var(--r-md); width: min(1040px, 100%); max-height: calc(100vh - 80px); overflow: hidden; display: flex; flex-direction: column; box-shadow: 0 16px 48px rgba(0,0,0,.18); }
+ .asset-modal.product-mode { width: min(1180px, 100%); }
.asset-modal-h { display: flex; align-items: center; gap: 10px; padding: 14px 20px; border-bottom: 1px solid var(--border-faint); }
.asset-modal-h h2 { font-size: 15px; font-weight: 600; }
.asset-modal-h .ad-tag { font-family: var(--font-mono); font-size: 11px; color: var(--black-alpha-48); letter-spacing: .02em; }
@@ -1997,9 +1998,11 @@ document.querySelectorAll('.asset-card').forEach(card => {
.asset-detail-tri-row .placeholder:hover .ad-zoom-btn { opacity: 1; }
.asset-detail-tri-row .placeholder { position: relative; }
.asset-detail-lead .ad-thumbs { display: flex; gap: 8px; }
- .asset-detail-lead .ad-thumbs .thumb { flex: 0 0 64px; aspect-ratio: 3/4; border-radius: var(--r-sm); border: 1px solid var(--border-faint); cursor: pointer; overflow: hidden; }
+ .asset-detail-lead .ad-thumbs .thumb { flex: 0 0 64px; aspect-ratio: 3/4; border-radius: var(--r-sm); border: 1px solid var(--border-faint); cursor: pointer; overflow: hidden; background-size: cover; background-position: center; display: grid; place-items: center; }
.asset-detail-lead .ad-thumbs .thumb:hover { border-color: var(--heat-40); }
.asset-detail-lead .ad-thumbs .thumb.active { border-color: var(--heat); border-width: 2px; }
+ .asset-detail-lead .ad-thumbs .thumb.thumb-wide { aspect-ratio: 16/9; }
+ .asset-detail-lead .ad-thumbs .thumb .ph-frame { font-size: 10px; }
.asset-detail-right .ad-section + .ad-section { margin-top: 18px; }
.asset-detail-section-h { display: flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 600; color: var(--accent-black); margin-bottom: 10px; }
.asset-detail-section-h .ic { width: 14px; height: 14px; color: var(--heat); display: grid; place-items: center; }
@@ -2022,6 +2025,91 @@ document.querySelectorAll('.asset-card').forEach(card => {
.ad-props .ad-prop:nth-last-child(-n+3) { border-bottom: 0; }
.ad-props .ad-prop .k { flex: 0 0 64px; color: var(--black-alpha-56); font-family: var(--font-mono); font-size: 11px; }
.ad-props .ad-prop .v { color: var(--accent-black); font-weight: 500; word-break: break-all; }
+ .asset-detail-product[hidden], .asset-detail-right[hidden] { display: none !important; }
+ .asset-detail-product { min-width: 0; }
+ .asset-modal.product-mode .asset-modal-body { padding: 0; }
+ .asset-modal.product-mode .asset-detail-grid { grid-template-columns: 280px minmax(0, 1fr); gap: 0; min-height: 640px; }
+ .asset-modal.product-mode .asset-detail-lead { padding: 20px; border-right: 1px solid var(--border-faint); background: var(--background-lighter); gap: 16px; }
+ .asset-modal.product-mode .asset-detail-lead .placeholder.ad-lead-img { aspect-ratio: 1; background-color: var(--surface); }
+ .asset-modal.product-mode .asset-detail-lead .placeholder.ad-lead-img.tri-previewing { aspect-ratio: 16/9; background-color: var(--background-lighter); border-style: dashed; color: var(--black-alpha-64); }
+ .asset-modal.product-mode .asset-detail-lead .placeholder.ad-lead-img.tri-previewing .ph-frame { max-width: 92%; white-space: normal; line-height: 1.5; }
+ .asset-modal.product-mode .ad-thumbs { display: block; }
+ .product-side-info { display: grid; gap: 6px; }
+ .product-side-info .psi-name { font-size: 14px; font-weight: 600; color: var(--accent-black); line-height: 1.45; }
+ .product-side-info .psi-meta { font-family: var(--font-mono); font-size: 10.5px; color: var(--black-alpha-48); letter-spacing: .02em; line-height: 1.6; }
+ .product-side-nav { display: grid; gap: 6px; padding-top: 10px; border-top: 1px solid var(--border-faint); }
+ .product-side-nav .psn-item { width: 100%; height: 34px; display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 0 10px; border: 0; border-radius: var(--r-sm); background: transparent; color: var(--black-alpha-64); font: inherit; font-size: 12.5px; cursor: pointer; text-align: left; }
+ .product-side-nav .psn-item:hover { background: var(--black-alpha-4); color: var(--accent-black); }
+ .product-side-nav .psn-item.active { background: var(--heat-12); color: var(--heat); }
+ .product-side-nav .psn-item .ct { font-family: var(--font-mono); font-size: 10.5px; color: currentColor; opacity: .72; }
+ .product-gallery { padding: 22px 24px 28px; }
+ .product-gallery-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 18px; margin-bottom: 20px; }
+ .product-gallery-head .pgh-title { font-size: 14px; font-weight: 600; color: var(--accent-black); line-height: 1.45; }
+ .product-gallery-head .pgh-sub { margin-top: 4px; font-family: var(--font-mono); font-size: 10.5px; color: var(--black-alpha-48); letter-spacing: .02em; }
+ .product-gallery-head .pgh-actions { display: flex; gap: 8px; flex-shrink: 0; }
+ .product-gallery-head .pgh-actions .btn { height: 32px; }
+ .product-source-filter { display: flex; flex-wrap: wrap; gap: 8px; margin: -6px 0 20px; }
+ .product-source-filter .chip { height: 30px; padding: 0 12px; font-size: 12px; }
+ .product-block + .product-block { margin-top: 24px; padding-top: 22px; border-top: 1px solid var(--border-faint); }
+ .product-block-h { display: flex; align-items: baseline; gap: 8px; margin-bottom: 12px; }
+ .product-block-h .t { font-size: 13px; font-weight: 600; color: var(--accent-black); }
+ .product-block-h .m { font-family: var(--font-mono); font-size: 10.5px; color: var(--black-alpha-48); letter-spacing: .04em; }
+ .product-block-h .spacer { flex: 1; }
+ .product-block-h .mini-action { height: 26px; padding: 0 10px; border: 1px solid var(--border-faint); border-radius: var(--r-sm); background: var(--surface); color: var(--black-alpha-72); font-size: 11.5px; font-family: inherit; cursor: pointer; }
+ .product-block-h .mini-action:hover { background: var(--black-alpha-4); color: var(--accent-black); border-color: var(--black-alpha-24); }
+ .tri-header-actions { display: inline-flex; align-items: center; gap: 8px; }
+ .tri-header-actions[hidden] { display: none; }
+ .tri-header-actions .btn { height: 26px; padding: 0 10px; font-size: 11.5px; }
+ .product-media-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; }
+ .product-media-card { border: 1px solid var(--border-faint); border-radius: var(--r-md); overflow: hidden; background: var(--surface); cursor: pointer; transition: background var(--t-base), border-color var(--t-base); }
+ .product-media-card[hidden], .product-triview-card[hidden], .product-block[hidden] { display: none; }
+ .product-media-card:hover { background: var(--black-alpha-4); border-color: var(--black-alpha-24); }
+ .product-media-card.active { border-color: var(--heat); box-shadow: 0 0 0 1px var(--heat) inset; }
+ .product-media-card .pm-img { aspect-ratio: 1; background: var(--background-lighter); background-size: cover; background-position: center; position: relative; display: grid; place-items: center; }
+ .product-media-card .pm-img.wide { aspect-ratio: 16/9; }
+ .product-media-card .pm-img .asset-badge { top: 6px; left: 6px; }
+ .product-media-card .pm-b { padding: 9px 10px 10px; min-width: 0; }
+ .product-media-card .pm-t { font-size: 12.5px; font-weight: 600; color: var(--accent-black); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
+ .product-media-card .pm-m { margin-top: 3px; font-family: var(--font-mono); font-size: 10.5px; color: var(--black-alpha-48); letter-spacing: .02em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
+ .product-triview-card { display: grid; grid-template-columns: minmax(0, 1fr) 220px; gap: 12px; align-items: stretch; border: 1px solid var(--border-faint); border-radius: var(--r-md); padding: 12px; background: var(--surface); cursor: pointer; }
+ .product-triview-card:hover { border-color: var(--black-alpha-24); background: var(--black-alpha-4); }
+ .product-triview-card.active { border-color: var(--heat); box-shadow: 0 0 0 1px var(--heat) inset; }
+ .product-triview-card.tri-pending { border-color: var(--heat-40); background: var(--heat-12); }
+ .product-triview-card .tri-preview { min-height: 132px; aspect-ratio: 16/9; border-radius: var(--r-md); background: var(--background-lighter); border: 1px solid var(--border-faint); display: grid; place-items: center; position: relative; overflow: hidden; }
+ .product-triview-card .tri-preview .ph-frame { font-size: 11px; }
+ .tri-meta-list { display: flex; flex-direction: column; gap: 8px; }
+ .tri-meta-list .tm-row { display: flex; justify-content: space-between; gap: 12px; font-size: 12.5px; padding-bottom: 8px; border-bottom: 1px solid var(--border-faint); }
+ .tri-meta-list .tm-row:last-child { border-bottom: 0; padding-bottom: 0; }
+ .tri-meta-list .k { color: var(--black-alpha-56); font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .04em; }
+ .tri-meta-list .v { color: var(--accent-black); font-weight: 500; text-align: right; }
+ .tri-version-panel { margin-top: 10px; padding: 9px 10px 10px; border: 1px solid var(--border-faint); border-radius: var(--r-md); background: var(--background-lighter); overflow: hidden; }
+ .tri-version-panel + .product-triview-card { margin-top: 10px; }
+ .tri-version-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 8px; }
+ .tri-version-head .t { font-size: 12.5px; font-weight: 600; color: var(--accent-black); }
+ .tri-version-head .m { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .04em; color: var(--black-alpha-48); }
+ .tri-version-list { display: flex; gap: 6px; overflow-x: auto; overflow-y: hidden; padding: 0 0 4px; scrollbar-width: thin; scrollbar-color: var(--black-alpha-24) transparent; }
+ .tri-version-list::-webkit-scrollbar { height: 6px; }
+ .tri-version-list::-webkit-scrollbar-track { background: transparent; }
+ .tri-version-list::-webkit-scrollbar-thumb { background: var(--black-alpha-12); border-radius: var(--r-pill); }
+ .tri-version-list:hover::-webkit-scrollbar-thumb { background: var(--black-alpha-24); }
+ .tri-version-item { flex: 0 0 132px; min-height: 44px; padding: 7px 8px; border: 1px solid var(--border-faint); border-radius: var(--r-md); background: var(--surface); color: var(--black-alpha-64); font: inherit; cursor: pointer; text-align: left; display: grid; gap: 3px; transition: background var(--t-base), border-color var(--t-base); }
+ .tri-version-item:hover { background: var(--black-alpha-4); border-color: var(--black-alpha-24); color: var(--accent-black); }
+ .tri-version-item.active { border-color: var(--heat); box-shadow: 0 0 0 1px var(--heat) inset; color: var(--accent-black); }
+ .tri-version-item.current:not(.active) { border-color: var(--forest-bd); background: var(--forest-bg); }
+ .tri-version-item.pending { background: var(--heat-12); border-color: var(--heat-20); }
+ .tri-version-item .tv-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
+ .tri-version-item .tv-v { font-size: 12px; font-weight: 600; color: var(--accent-black); }
+ .tri-version-item .tv-tag { height: 17px; display: inline-flex; align-items: center; padding: 0 6px; border: 1px solid var(--border-faint); border-radius: var(--r-pill); font-size: 10px; white-space: nowrap; }
+ .tri-version-item .tv-tag.info { color: var(--heat); background: var(--heat-12); border-color: var(--heat-20); }
+ .tri-version-item .tv-tag.ok { color: var(--accent-forest); background: var(--forest-bg); border-color: var(--forest-bd); }
+ .tri-version-item .tv-tag.neutral { color: var(--black-alpha-56); background: var(--black-alpha-4); border-color: var(--border-faint); }
+ .tri-version-item .tv-meta { font-family: var(--font-mono); font-size: 10px; color: var(--black-alpha-48); letter-spacing: .02em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
+ @media (max-width: 1100px) {
+ .asset-detail-grid { grid-template-columns: 280px 1fr; }
+ .product-media-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
+ .product-triview-card { grid-template-columns: 1fr; }
+ .tri-version-item { flex-basis: 124px; }
+ }
.asset-detail-tip { margin-top: 10px; padding: 10px 12px; background: var(--heat-12); border: 1px solid var(--heat-20); border-radius: var(--r-sm); font-size: 12px; color: var(--accent-black); display: flex; align-items: center; gap: 8px; line-height: 1.5; }
.asset-detail-tip svg { width: 14px; height: 14px; color: var(--heat); flex-shrink: 0; }
.asset-detail-tip .ai-gen-btn { margin-left: auto; height: 26px; padding: 0 10px; background: var(--heat); color: #fff; border: 1px solid var(--heat); border-radius: var(--r-sm); font-size: 11.5px; cursor: pointer; font-family: inherit; flex-shrink: 0; display: inline-flex; align-items: center; }
@@ -2080,7 +2168,7 @@ document.querySelectorAll('.asset-card').forEach(card => {
@@ -2149,10 +2238,13 @@ document.querySelectorAll('.asset-card').forEach(card => {
document.body.insertAdjacentHTML('beforeend', modalHTML);
const bg = document.getElementById('lib-detail-bg');
+ const modalEl = bg.querySelector('.asset-modal');
const titleEl = document.getElementById('lib-detail-title');
const kindEl = document.getElementById('lib-detail-kind');
const leadImg = document.getElementById('lib-detail-lead-img');
const thumbsEl = document.getElementById('lib-detail-thumbs');
+ const genericPane = document.getElementById('lib-detail-generic-pane');
+ const productPane = document.getElementById('lib-detail-product-pane');
const triSection = document.getElementById('lib-detail-tri-section');
const triEl = document.getElementById('lib-detail-tri');
const ratioChip = document.getElementById('lib-detail-ratio');
@@ -2178,6 +2270,406 @@ document.querySelectorAll('.asset-card').forEach(card => {
let _generating = false;
let _allowGen = false; // 是否启用生成入口(missing tri-view 才启用)
+ function _esc(s) {
+ return String(s == null ? '' : s).replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
+ }
+
+ function _imgStyle(src) {
+ return src ? ' style="background-image:url(' + _esc(src) + ')"' : '';
+ }
+
+ function _productInfo(productName, assetName) {
+ const key = productName + ' ' + assetName;
+ const map = [
+ { test: /面膜|补水/i, product: '透真玻尿酸补水面膜', cat: '美妆个护', sku: 'SKU-MASK-0515', hero: 'assets/mock/product-mask.png', tone: '敏感肌 · 熬夜修护' },
+ { test: /防晒/i, product: '透真清透物理防晒霜', cat: '美妆个护', sku: 'SKU-SUN-0508', hero: 'assets/mock/product-sunscreen.png', tone: '通勤防晒 · 物理防护' },
+ { test: /耳机|南卡/i, product: '南卡 Lite Pro 蓝牙耳机', cat: '数码 3C', sku: 'SKU-EAR-0512', hero: 'assets/mock/product-earbuds.png', tone: '通勤降噪 · 长续航' },
+ { test: /速食|牛肉面|面/i, product: '滋啦速食牛肉面 · 6 桶装', cat: '食品饮料', sku: 'SKU-NOODLE-0510', hero: 'assets/mock/product-noodle.png', tone: '加班夜宵 · 热汤治愈' },
+ { test: /咖啡|三顿半/i, product: '三顿半同款冻干咖啡粉', cat: '食品饮料', sku: 'SKU-COFFEE-0505', hero: 'assets/mock/product-coffee.png', tone: '早八提神 · 冷萃口感' },
+ { test: /炸锅|小熊/i, product: '小熊 4L 可视空气炸锅', cat: '家居家电', sku: 'SKU-FRYER-0503', hero: 'assets/mock/product-air-fryer.png', tone: '小户型 · 低脂快手' },
+ { test: /瑜伽|露露/i, product: '露露同款裸感瑜伽裤', cat: '运动户外', sku: 'SKU-YOGA-0430', hero: 'assets/mock/product-yoga-pants.png', tone: '通勤运动 · 裸感高弹' },
+ ];
+ return map.find(x => x.test.test(key)) || { product: productName || assetName || '未绑定商品', cat: '商品图', sku: 'SKU-ASSET-' + (_hash(assetName || productName) % 1000), hero: '', tone: '商品素材 · 待补充标签' };
+ }
+
+ function _assetKind(name, source) {
+ if (/三视图|正.?侧.?背/.test(name)) return '商品三视图';
+ if (/AI|生成|优化/.test(source) || /AI|优化|套图|场景/.test(name)) return 'AI 生成图';
+ if (/上传|官方/.test(source) || /官方|实拍|原图/.test(name)) return '上传图';
+ return '商品详情图';
+ }
+
+ function _productMediaCard(item, idx) {
+ return `
`;
+ }
+
+ function _renderProductDetail(card, name, source, used) {
+ const productName = card.dataset.product || name.split('·')[0].trim() || name;
+ const info = _productInfo(productName, name);
+ const currentKind = _assetKind(name, source);
+ const hero = info.hero;
+ const detailImages = [
+ { sourceKey: 'detail', badge: '主图', title: '官方主图', meta: '商品详情页 · 1:1', img: hero, frame: info.product },
+ { sourceKey: 'detail', badge: '细节', title: '包装细节', meta: '商品详情页 · 4:3', img: hero, frame: '包装细节' },
+ { sourceKey: 'detail', badge: '场景', title: '使用场景', meta: '商品详情页 · 3:4', img: hero, frame: '使用场景' },
+ { sourceKey: 'detail', badge: '当前', title: name, meta: source + ' · 当前查看', img: hero, frame: name },
+ ];
+ const triImage = { sourceKey: 'tri', badge: '三视图', title: '正 / 侧 / 背合图', meta: '16:9 · 当前采用 v2', img: '', wide: true, frame: info.product + ' · 商品三视图' };
+ const aiImages = [
+ { sourceKey: 'tryon', badge: '模特上身', title: 'Ava · 试用图', meta: '图片生成 · 已入库', img: hero, frame: '模特上身图' },
+ { sourceKey: 'platform', badge: '平台头图', title: '小红书首图', meta: '图片生成 · 3:4', img: hero, frame: '平台头图' },
+ { sourceKey: 'creation', badge: '图片创作', title: '卖点海报', meta: '图片生成 · 1:1', img: hero, frame: '卖点海报' },
+ ];
+ const allMedia = [...detailImages, triImage, ...aiImages];
+ const triIndex = detailImages.length;
+ let triVersion = 2;
+ let triVersionSeq = triVersion;
+ let triGeneratingVersion = null;
+ let triViewingVersion = triVersion;
+ let triHistory = [
+ { version: 2, state: 'current', date: '2026-05-27', source: 'image-2 · 商品库生成' },
+ { version: 1, state: 'history', date: '2026-05-19', source: '商品详情页补录' },
+ ];
+
+ leadImg.style.backgroundImage = hero ? `url("${hero}")` : '';
+ leadImg.style.backgroundSize = 'cover';
+ leadImg.style.backgroundPosition = 'center';
+ leadImg.innerHTML = hero ? '' : '
' + _esc(name) + '';
+ thumbsEl.innerHTML = `
+
+
${_esc(info.product)}
+
// ${_esc(info.cat)} · ${_esc(info.tone)}
+
// 共 ${allMedia.length} 张 · 商品详情图 ${detailImages.length} · 三视图 1 · AI 生成图 ${aiImages.length}
+
+
+
+
+
+
`;
+
+ titleEl.textContent = info.product + ' · 商品图片';
+ kindEl.textContent = '/ 商品图';
+ productPane.innerHTML = `
+
+
+
+
按来源整理商品详情页中的全部图片
+
// 当前查看: ${_esc(currentKind)} · 用过 ${_esc(used)} 次
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 商品详情图
+ // 商品详情页已绑定图片,不是单独的平台头图
+
+
${detailImages.map((item, i) => _productMediaCard(item, i)).join('')}
+
+
+
+
+ 商品三视图
+ // 单张 16:9 · 正 / 侧 / 背 合一
+
+
+
+
+
+
+ 版本记录
+ // 横向滚动查看,当前采用已标记
+
+
+
+
+
+ ${_esc(info.product)} · 三视图(正 / 侧 / 背) · v2
+
+
+
+
+
+
+
+ AI 生成图
+ // 模特上身 / 平台头图 / 图片创作
+
+
+
+
${aiImages.map((item, i) => _productMediaCard(item, detailImages.length + 1 + i)).join('')}
+
+
+ `;
+
+ function triVersionInfo(version) {
+ const row = triHistory.find(v => v.version === version) || triHistory.find(v => v.state === 'current') || triHistory[0];
+ const isGenerating = row.state === 'generating';
+ const isCurrent = row.state === 'current' || row.version === triVersion;
+ const stateText = isGenerating ? '生成中' : (isCurrent ? '当前采用' : '可切换采用');
+ const tag = isGenerating ? 'info' : (isCurrent ? 'ok' : '');
+ const suffix = isGenerating ? ' 生成中' : (isCurrent ? ' 当前采用' : '');
+ return {
+ ...row,
+ isGenerating,
+ isCurrent,
+ isAdoptable: !isGenerating && !isCurrent,
+ stateText,
+ tag,
+ versionText: `v${row.version} · ${row.date}`,
+ frame: `${info.product} · 三视图(正 / 侧 / 背) · v${row.version}${suffix}`,
+ };
+ }
+
+ function renderTriVersionHistory() {
+ const historyEl = productPane.querySelector('[data-tri-history]');
+ if (!historyEl) return;
+ const rows = [...triHistory].sort((a, b) => b.version - a.version).map(row => triVersionInfo(row.version));
+ historyEl.innerHTML = rows.map(row => `
+
+ `).join('');
+ historyEl.querySelectorAll('[data-tri-version]').forEach(btn => {
+ btn.addEventListener('click', e => {
+ e.stopPropagation();
+ viewTriVersion(Number(btn.dataset.triVersion));
+ });
+ });
+ historyEl.querySelector('.tri-version-item.active')?.scrollIntoView({ block: 'nearest', inline: 'nearest' });
+ }
+
+ function renderTriPreview() {
+ const triCard = productPane.querySelector('.product-triview-card');
+ const headerActions = productPane.querySelector('.tri-header-actions');
+ if (!triCard) return;
+ const preview = triCard.querySelector('.tri-preview');
+ const meta = triCard.querySelector('.tri-meta-list');
+ const view = triVersionInfo(triViewingVersion);
+ triCard.classList.toggle('tri-pending', view.isGenerating);
+ if (headerActions) headerActions.hidden = !view.isAdoptable;
+ preview.innerHTML = view.isGenerating
+ ? '
'
+ : `
${_esc(view.frame)}`;
+ meta.innerHTML = `
+
状态${_esc(view.stateText)}
+
版本${_esc(view.versionText)}
+
用途视频镜头一致性 / 商品角度参考
+
来源${_esc(view.source)}
`;
+ }
+
+ function viewTriVersion(version) {
+ triViewingVersion = version;
+ renderTriPreview();
+ renderTriVersionHistory();
+ selectProductMedia(triIndex);
+ }
+
+ function renderTriState(mode) {
+ const triCard = productPane.querySelector('.product-triview-card');
+ const regenBtn = productPane.querySelector('[data-product-action="regen-tri"]');
+ if (!triCard) return;
+ if (regenBtn) {
+ const hasExtraVersion = triHistory.some(row => row.state === 'candidate' || row.state === 'generating');
+ regenBtn.disabled = Boolean(triGeneratingVersion);
+ regenBtn.textContent = triGeneratingVersion ? '生成中...' : (hasExtraVersion ? '再次生成' : '重新生成');
+ }
+ renderTriPreview();
+ renderTriVersionHistory();
+ }
+
+ function selectProductMedia(idx) {
+ const item = allMedia[idx] || allMedia[0];
+ const isTri = item.sourceKey === 'tri';
+ const triView = triVersionInfo(triViewingVersion);
+ const triLabel = triView.isGenerating
+ ? info.product + ' · 三视图生成中'
+ : info.product + ' · 三视图 · v' + triView.version + (triView.isCurrent ? ' 当前采用' : '');
+ leadImg.classList.toggle('tri-previewing', isTri && !item.img);
+ if (!item.img) {
+ leadImg.classList.remove('has-mock-media', 'mock-label');
+ leadImg.style.removeProperty('--mock-media-url');
+ leadImg.dataset.mockMediaApplied = '1';
+ }
+ leadImg.style.backgroundImage = item.img ? `url("${item.img}")` : '';
+ leadImg.innerHTML = item.img ? '' : '
' + _esc(isTri ? triLabel : (item.frame || item.title)) + '';
+ productPane.querySelectorAll('[data-product-media]').forEach(x => x.classList.toggle('active', Number(x.dataset.productMedia) === idx));
+ }
+
+ function openProductUpload() {
+ resetUploadModal();
+ uploadState.kind = 'products';
+ kindSel.value = 'products';
+ syncKindFields();
+ const productSelect = $('upload-product');
+ const matchedOption = [...productSelect.options].find(opt => {
+ const text = opt.textContent.trim();
+ return text && (info.product.includes(text) || text.includes(info.product.replace(/^透真玻尿酸/, '透真').replace(/ ·.*/, '')));
+ });
+ if (matchedOption) productSelect.value = matchedOption.value;
+ nameInput.value = info.product + ' · 补充图';
+ syncSubmit();
+ modalBg.style.zIndex = '1300';
+ Shell.openModal('upload-modal-bg');
+ setTimeout(() => fileInput.click(), 120);
+ }
+
+ function startTriRegenerate() {
+ if (triGeneratingVersion) return;
+ const nextVersion = ++triVersionSeq;
+ triGeneratingVersion = nextVersion;
+ triViewingVersion = nextVersion;
+ triHistory = [
+ { version: nextVersion, state: 'generating', date: '生成中', source: 'image-2 · 资产库重跑' },
+ ...triHistory.filter(row => row.version !== nextVersion),
+ ];
+ renderTriState('loading');
+ selectProductMedia(triIndex);
+ setTimeout(() => {
+ const generated = triHistory.find(row => row.version === nextVersion);
+ if (!generated) return;
+ generated.state = 'candidate';
+ generated.date = '刚刚生成';
+ triGeneratingVersion = null;
+ triViewingVersion = nextVersion;
+ triImage.title = '正 / 侧 / 背合图 v' + nextVersion;
+ triImage.meta = '16:9 · v' + nextVersion;
+ triImage.frame = info.product + ' · 商品三视图 · v' + nextVersion;
+ renderTriState('pending');
+ selectProductMedia(triIndex);
+ Shell.toast('三视图已生成', 'v' + nextVersion + ' 已加入版本记录,可切换采用');
+ }, 1200);
+ }
+
+ function adoptTriVersion() {
+ const selected = triHistory.find(row => row.version === triViewingVersion);
+ if (!selected || selected.state === 'generating' || selected.version === triVersion) return;
+ triHistory = triHistory.map(row => {
+ if (row.version === selected.version) return { ...row, state: 'current' };
+ if (row.state === 'generating') return row;
+ return { ...row, state: 'candidate' };
+ });
+ triVersion = selected.version;
+ triVersionSeq = Math.max(triVersionSeq, triVersion);
+ triViewingVersion = triVersion;
+ triImage.title = '正 / 侧 / 背合图 v' + triVersion;
+ triImage.meta = '16:9 · 当前采用 v' + triVersion;
+ triImage.frame = info.product + ' · 商品三视图 · v' + triVersion;
+ renderTriState('current');
+ selectProductMedia(triIndex);
+ Shell.toast('已采用此版本三视图', info.product + ' · v' + triVersion);
+ }
+
+ function applyProductSourceFilter(sourceKey) {
+ const source = sourceKey || 'all';
+ productPane.querySelectorAll('[data-product-media]').forEach(el => {
+ el.hidden = source !== 'all' && el.dataset.productSource !== source;
+ });
+ productPane.querySelectorAll('.product-block').forEach(block => {
+ const media = [...block.querySelectorAll('[data-product-media]')];
+ block.hidden = media.length > 0 && media.every(el => el.hidden);
+ });
+ const firstVisible = productPane.querySelector('[data-product-media]:not([hidden])');
+ if (firstVisible) selectProductMedia(Number(firstVisible.dataset.productMedia));
+ }
+
+ thumbsEl.querySelectorAll('[data-product-jump]').forEach(el => {
+ el.addEventListener('click', () => {
+ const target = productPane.querySelector('#' + el.dataset.productJump);
+ thumbsEl.querySelectorAll('.psn-item').forEach(x => x.classList.remove('active'));
+ el.classList.add('active');
+ target?.scrollIntoView({ block: 'start', behavior: 'smooth' });
+ });
+ });
+
+ productPane.querySelectorAll('[data-product-media]').forEach(el => {
+ el.addEventListener('click', () => {
+ const idx = Number(el.dataset.productMedia);
+ selectProductMedia(idx);
+ const item = allMedia[idx];
+ Shell.toast('已选中图片', item ? item.title : name);
+ });
+ });
+ renderTriState('current');
+ selectProductMedia(0);
+
+ productPane.querySelectorAll('[data-product-filter]').forEach(btn => {
+ btn.addEventListener('click', () => {
+ productPane.querySelectorAll('[data-product-filter]').forEach(x => x.classList.remove('active'));
+ btn.classList.add('active');
+ applyProductSourceFilter(btn.dataset.productFilter);
+ });
+ });
+
+ function bindProductActions() {
+ productPane.querySelectorAll('[data-product-action]').forEach(btn => {
+ if (btn.dataset.bound === '1') return;
+ btn.dataset.bound = '1';
+ btn.addEventListener('click', e => {
+ e.stopPropagation();
+ const act = btn.dataset.productAction;
+ if (act === 'open-product') {
+ location.href = 'product-detail.html?product=' + encodeURIComponent(info.product);
+ return;
+ }
+ if (act === 'upload-more') {
+ openProductUpload();
+ return;
+ }
+ if (act === 'new-video') {
+ location.href = 'projects-new.html?product=' + encodeURIComponent(info.product);
+ return;
+ }
+ if (act === 'go-generate') {
+ location.href = 'image-optimize.html?product=' + encodeURIComponent(info.product);
+ return;
+ }
+ if (act === 'regen-tri') {
+ startTriRegenerate();
+ return;
+ }
+ if (act === 'adopt-tri') {
+ adoptTriVersion();
+ return;
+ }
+ Shell.toast('已记录', info.product);
+ });
+ });
+ }
+ bindProductActions();
+ }
+
function _renderHistory() {
if (!_versions.length) { historyEl.style.display = 'none'; return; }
historyEl.style.display = 'block';
@@ -2258,13 +2750,32 @@ document.querySelectorAll('.asset-card').forEach(card => {
historyEl.style.display = 'none';
tipEl.classList.remove('is-loading');
_setAigenLabel('AI 生成三视图', false);
+ modalEl.classList.remove('product-mode');
+ genericPane.hidden = false;
+ productPane.hidden = true;
+ leadImg.style.backgroundImage = '';
+ leadImg.style.backgroundSize = '';
+ leadImg.style.backgroundPosition = '';
+ applyBtn.textContent = '保存';
const name = card.dataset.name || '资产';
_curName = name;
const used = card.dataset.used || '0';
const source = card.dataset.source || '平台预设';
+ const isProductAsset = !!card.dataset.product || card.dataset.kind === '商品';
let tagText = 'AI 素材', intro = '', tags = [], props = [], hasTri = false, isActor = false;
+ if (isProductAsset) {
+ titleEl.textContent = name;
+ modalEl.classList.add('product-mode');
+ genericPane.hidden = true;
+ productPane.hidden = false;
+ applyBtn.textContent = '完成';
+ _renderProductDetail(card, name, source, used);
+ bg.classList.add('show');
+ return;
+ }
+
if (card.dataset.gender) {
tagText = '人物 · 模特';
isActor = true; hasTri = true;
@@ -2309,6 +2820,7 @@ document.querySelectorAll('.asset-card').forEach(card => {
titleEl.textContent = name;
kindEl.textContent = '/ ' + tagText;
+ leadImg.style.backgroundImage = '';
leadImg.innerHTML = '
' + name + '';
// 立绘 zoom 按钮(单次绑定 · 通过 name 闭包始终读最新 _curName)
const _leadZoomBtn = document.getElementById('lib-detail-lead-zoom');
@@ -2385,6 +2897,7 @@ document.querySelectorAll('.asset-card').forEach(card => {
bg.classList.add('show');
}
+ window.LibraryAssetDetailOpen = open;
// ── 二次确认弹窗 ──
const confirmBg = document.getElementById('lib-confirm-bg');
const confirmCountEl = document.getElementById('lib-confirm-count');
diff --git a/电商AI平台/projects-new.html b/电商AI平台/projects-new.html
index a0c9e64..a0c3126 100644
--- a/电商AI平台/projects-new.html
+++ b/电商AI平台/projects-new.html
@@ -442,8 +442,16 @@
/* ── shared field styles ── */
.field { display: block; margin-bottom: 16px; }
.config-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; align-items: end; margin-bottom: 16px; }
+ .config-row.single { grid-template-columns: minmax(0, 1fr); }
.config-row .field { margin-bottom: 0; }
- .duration-select { cursor: pointer; }
+ .duration-card-row { grid-template-columns: repeat(4, minmax(0, 1fr)); }
+ .duration-card { width: 100%; min-height: 108px; text-align: left; font-family: inherit; }
+ .duration-card .duration-title { font-size: 14px; font-weight: 600; color: var(--accent-black); }
+ .duration-card .duration-meta { margin-top: 5px; font-family: var(--font-mono); font-size: 10.5px; color: var(--black-alpha-48); letter-spacing: .02em; }
+ .duration-card .duration-note { margin-top: auto; padding-top: 10px; font-size: 11.5px; color: var(--black-alpha-56); line-height: 1.45; }
+ .duration-card.selected .duration-title,
+ .duration-card.selected .duration-meta { color: var(--heat); }
+ @media (max-width: 1100px) { .duration-card-row { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 900px) { .config-row { grid-template-columns: 1fr; } }
.field-label { display: block; font-size: 12.5px; color: var(--black-alpha-56); font-weight: 500; margin-bottom: 6px; }
.field-label .req { color: var(--heat); margin-left: 2px; }
@@ -654,10 +662,10 @@
];
const DURATIONS = [
- { id: '0-10', label: '0-10 秒', shots: [3, 4], tag: '黄金完播', completion: 52, conversion: 1.6 },
- { id: '0-15', label: '0-15 秒', shots: [4, 5], tag: '完播率最佳', completion: 42, conversion: 1.8 },
- { id: '0-30', label: '0-30 秒', shots: [6, 8], tag: '卖点详解', completion: 32, conversion: 2.1 },
- { id: '0-60', label: '0-60 秒', shots: [10, 12], tag: '故事化', completion: 26, conversion: 2.4 },
+ { id: '0-10', label: '0-10 秒', shots: [3, 4], tag: '快速种草' },
+ { id: '0-15', label: '0-15 秒', shots: [4, 5], tag: '标准短片' },
+ { id: '0-30', label: '0-30 秒', shots: [6, 8], tag: '卖点展开' },
+ { id: '0-60', label: '0-60 秒', shots: [10, 12], tag: '故事化' },
];
const STYLES = [
@@ -775,7 +783,7 @@
if (s.id === 'manual') return state.manualScript.trim().length >= 20;
return true;
}
- function canPass3() { return state.projectName.trim().length >= 2; }
+ function canPass3() { return state.projectName.trim().length >= 2 && !!state.duration; }
function canFinish() { return canPass1() && canPass2() && canPass3() && state.agreed && balanceAfter() >= 5; }
/* ---------- icons ---------- */
@@ -839,8 +847,8 @@
}
function startGenerate() {
- const p = getProduct(), d = getDuration(), st = getStyle();
- if (!p || !d || !st || state.projectName.trim().length < 2) return;
+ const p = getProduct(), d = getDuration();
+ if (!p || !d || state.projectName.trim().length < 2) return;
// 持久化项目, 让 projects.html 下次加载时自动 prepend 到列表
try {
const seconds = (d.id.split('-')[1] || '15');
@@ -1001,11 +1009,11 @@
============================================================ */
function railConfig() {
- const p = getProduct(), du = getDuration(), st = getStyle();
- const cfgReady = !!(du && st);
+ const p = getProduct(), du = getDuration();
+ const cfgReady = !!du && canPass3();
return [
{ n: 1, label: '选择商品', desc: p ? p.name : '未选择', done: canPass1() },
- { n: 2, label: '项目配置', desc: cfgReady ? (du.label + ' · ' + st.name) : '时长 · 风格 · 人物', done: cfgReady && canPass3() },
+ { n: 2, label: '项目配置', desc: cfgReady ? du.label : '项目名 · 视频时长', done: cfgReady },
];
}
@@ -1221,67 +1229,31 @@
}
function renderStep3() {
- const personaObj = getPersona(), durObj = getDuration(), styleObj = getStyle();
- let showReco = false, recoDur = null, recoStyle = null;
- if (personaObj && state.duration && state.scriptStyle) {
- const recoMismatch = personaObj.defaults.duration !== state.duration || personaObj.defaults.style !== state.scriptStyle;
- showReco = recoMismatch && !state.recoDismissed;
- recoDur = DURATIONS.find(d => d.id === personaObj.defaults.duration);
- recoStyle = STYLES.find(s => s.id === personaObj.defaults.style);
- }
-
return `
第 2 步 · 项目配置
-
这些设置会影响 LLM 生成脚本的方向,确认后会进入流水线第 1 步(脚本生成)。
+
基础配置只保留项目名和成片时长。脚本来源、风格和人物设定已移到流水线第 1 步由脚本助手引导。
-
+
-
-
-
-
-
-
- ${STYLES.map(s => `
-
${esc(s.name)}
-
${esc(s.note)}
- ${s.tag ? `
[ ${esc(s.tag)} ]` : ''}
-
`).join('')}
+
+
+ ${DURATIONS.map(d => ``).join('')}
-
-
-
- ${PERSONAS.map(p => `
-
${esc(p.name)}
-
${esc(p.sub)}
-
${esc(p.metric)}
-
`).join('')}
-
-
- ${showReco ? `
-
${ICONS.bulb}
-
- 抖音同人设 TOP 视频更常用 ${esc(recoDur.label)} + ${esc(recoStyle.name)}
- 当前 ${esc(durObj.label)} · ${esc(styleObj.name)} → 推荐换为同人设最优组合
-
-
-
-
` : ''}
-
-
${Object.keys(state.points).length > 0 ? `
@@ -1292,7 +1264,7 @@
}
function renderStep4() {
- const p = getProduct(), s = getSource(), personaObj = getPersona(), durObj = getDuration(), styleObj = getStyle();
+ const p = getProduct(), s = getSource(), durObj = getDuration();
const c = getCost();
const ba = balanceAfter();
const low = ba < 5;
@@ -1331,7 +1303,7 @@
// 项目配置
${esc(state.projectName)}
-
${esc(styleObj.name)} · ${esc(personaObj.name)} · ${esc(personaObj.sub)}
+
${esc(durObj.label)} · ${durObj.shots[0]}-${durObj.shots[1]} 镜 · 9:16
卖点:${esc(pointsList)}
@@ -1340,8 +1312,7 @@
// 输出参数
${esc(durObj.label)} · ${durObj.shots[0]}-${durObj.shots[1]} 镜 · 9:16
-
预估完播 ${durObj.completion}% · 预估转化 ${durObj.conversion}%
-
// 数据来源:抖音同品类 TOP 均值
+
// 进入 Stage 1 后由脚本助手确认风格与人物设定
@@ -1516,8 +1487,8 @@
${esc(title)}
-
预估完播
${durObj.completion}%
-
预估转化
${durObj.conversion}%
+
+
预估成本
¥${c.total.toFixed(2)}
${summaryBlock}
@@ -1573,8 +1544,8 @@
renderRail();
const body = $('#wiz-body');
// 单页式: 商品 (step1) + 项目配置 (原 step3, 现 step2),底部「开始」CTA
- const p = getProduct(), du = getDuration(), st = getStyle();
- const canStart = !!(p && du && st && state.projectName.trim().length >= 2);
+ const p = getProduct(), du = getDuration();
+ const canStart = !!(p && du && state.projectName.trim().length >= 2);
let html = '';
html += '
';
html += '
';