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:
+244
-73
@@ -4,7 +4,7 @@
|
||||
<meta charset="utf-8">
|
||||
<title>资产库 · 流·Studio</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/restraint.css?v=202605211643">
|
||||
<link rel="stylesheet" href="assets/restraint.css?v=202605211800">
|
||||
<style>
|
||||
.asset-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 14px; }
|
||||
.asset-grid.video-grid { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); }
|
||||
@@ -92,6 +92,25 @@
|
||||
}
|
||||
.asset-card { background: var(--surface); border: 1px solid var(--border-faint); border-radius: var(--r-md); cursor: pointer; transition: background .15s; position: relative; }
|
||||
.asset-card:hover { background: var(--background-lighter); border-color: var(--black-alpha-48); }
|
||||
/* 下载按钮 · hover 卡片显示,与 card-del-btn 并列 · PRD §6.5 中间产物可下载 */
|
||||
.asset-card .card-dl-btn {
|
||||
position: absolute;
|
||||
top: 8px; right: 48px;
|
||||
width: 32px; height: 32px;
|
||||
background: rgba(255,255,255,.95);
|
||||
border: 1px solid var(--black-alpha-12);
|
||||
border-radius: var(--r-md);
|
||||
display: grid; place-items: center;
|
||||
color: var(--black-alpha-56);
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
transition: opacity var(--t-base), background var(--t-base), color var(--t-base);
|
||||
z-index: 4;
|
||||
}
|
||||
.asset-card .card-dl-btn svg { width: 14px; height: 14px; }
|
||||
.asset-card:hover .card-dl-btn { opacity: 1; }
|
||||
.asset-card .card-dl-btn:hover { background: var(--heat); color: var(--accent-white); border-color: var(--heat); }
|
||||
body.edit-mode .asset-card .card-dl-btn { opacity: 0 !important; pointer-events: none !important; }
|
||||
/* 编辑模式 checkbox */
|
||||
.asset-card .card-check {
|
||||
position: absolute; top: 10px; left: 10px;
|
||||
@@ -376,10 +395,13 @@
|
||||
<div class="asset-meta">男 · 青年 · 都市白领 · 用过 2 次</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="asset-card" data-name="妈妈 · 王姐" data-gender="女" data-age="中年" data-role="居家" data-source="手动上传" data-used="1" data-added="20260415" onclick="Shell.toast('查看资产', '王姐')">
|
||||
<div class="asset-card" data-name="妈妈 · 王姐" data-gender="女" data-age="中年" data-role="居家" data-source="手动上传" data-triview="0" data-used="1" data-added="20260415" onclick="Shell.toast('查看资产', '王姐')">
|
||||
<span class="card-check" aria-hidden="true"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="3 8 7 12 13 4"/></svg></span>
|
||||
<button class="card-del-btn" type="button" title="删除资产" onclick="event.stopPropagation();" data-action="delete-asset"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2"/><path d="M19 6l-1.5 14a2 2 0 01-2 1.8H8.5a2 2 0 01-2-1.8L5 6"/></svg></button>
|
||||
<div class="placeholder asset-thumb"><span class="ph-frame">妈妈 · 居家</span></div>
|
||||
<div class="placeholder asset-thumb">
|
||||
<span class="tri-missing-badge" title="手动上传的人物未生成三视图,前往图片生成可补齐"><span class="lbl-mono">缺三视图</span></span>
|
||||
<span class="ph-frame">妈妈 · 居家</span>
|
||||
</div>
|
||||
<div class="asset-body">
|
||||
<div class="asset-name">妈妈 · 王姐</div>
|
||||
<div class="asset-meta">女 · 中年 · 居家 · 用过 1 次</div>
|
||||
@@ -430,10 +452,13 @@
|
||||
<div class="asset-meta">男 · 少年 · 学生 · 用过 1 次</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="asset-card" data-name="李爷爷" data-gender="男" data-age="老年" data-role="居家" data-source="手动上传" data-used="1" data-added="20260418" onclick="Shell.toast('查看资产', '李爷爷')">
|
||||
<div class="asset-card" data-name="李爷爷" data-gender="男" data-age="老年" data-role="居家" data-source="手动上传" data-triview="0" data-used="1" data-added="20260418" onclick="Shell.toast('查看资产', '李爷爷')">
|
||||
<span class="card-check" aria-hidden="true"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="3 8 7 12 13 4"/></svg></span>
|
||||
<button class="card-del-btn" type="button" title="删除资产" onclick="event.stopPropagation();" data-action="delete-asset"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2"/><path d="M19 6l-1.5 14a2 2 0 01-2 1.8H8.5a2 2 0 01-2-1.8L5 6"/></svg></button>
|
||||
<div class="placeholder asset-thumb"><span class="ph-frame">李爷爷 · 居家</span></div>
|
||||
<div class="placeholder asset-thumb">
|
||||
<span class="tri-missing-badge" title="手动上传的人物未生成三视图,前往图片生成可补齐"><span class="lbl-mono">缺三视图</span></span>
|
||||
<span class="ph-frame">李爷爷 · 居家</span>
|
||||
</div>
|
||||
<div class="asset-body">
|
||||
<div class="asset-name">李爷爷</div>
|
||||
<div class="asset-meta">男 · 老年 · 居家 · 用过 1 次</div>
|
||||
@@ -848,6 +873,29 @@
|
||||
<script>
|
||||
Shell.render({ active: 'library', crumbs: [{ label: '工作台', href: 'index.html' }, { label: '资产库' }] });
|
||||
|
||||
/* ─── 给所有资产卡注入下载按钮 · PRD §6.5 所有中间产物可下载 ─── */
|
||||
(function injectDownloadBtns() {
|
||||
const dlSvg = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>';
|
||||
document.querySelectorAll('.asset-card').forEach(card => {
|
||||
if (card.querySelector('.card-dl-btn')) return;
|
||||
const btn = document.createElement('button');
|
||||
btn.className = 'card-dl-btn';
|
||||
btn.type = 'button';
|
||||
btn.title = '下载资产';
|
||||
btn.innerHTML = dlSvg;
|
||||
btn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
const name = card.dataset.name || '资产';
|
||||
// 推测卡片类型用作 mono 后缀
|
||||
const grid = card.closest('.asset-grid');
|
||||
const kind = grid ? grid.dataset.tab : '';
|
||||
const kindLabel = { people: '人物 · PNG', scenes: '场景 · PNG', products: '商品 · PNG', finals: '成片 · MP4 1080p', uploads: '原始素材' }[kind] || '资产';
|
||||
Shell.toast('下载中', name + ' · ' + kindLabel);
|
||||
});
|
||||
card.appendChild(btn);
|
||||
});
|
||||
})();
|
||||
|
||||
// ============== State ==============
|
||||
const TAB_KEYS = ['people', 'scenes', 'products', 'finals', 'uploads'];
|
||||
const PAGE_SIZES = [12, 24, 48, 96];
|
||||
@@ -1671,43 +1719,124 @@ document.querySelectorAll('.asset-card').forEach(card => {
|
||||
});
|
||||
|
||||
/* ============================================================
|
||||
资产详情 modal · 替代旧 Shell.toast 点击
|
||||
资产详情 modal · 与 pipeline.html 共用参考布局 v2
|
||||
============================================================ */
|
||||
(function () {
|
||||
// 注入 modal HTML
|
||||
// 注入 modal CSS (与 pipeline.html 保持一致的 .asset-* 命名)
|
||||
const css = `
|
||||
.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-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; }
|
||||
.asset-modal-h .x { width: 30px; height: 30px; display: grid; place-items: center; background: transparent; border: 0; cursor: pointer; color: var(--black-alpha-56); border-radius: var(--r-sm); margin-left: auto; }
|
||||
.asset-modal-h .x:hover { background: var(--black-alpha-8); color: var(--accent-black); }
|
||||
.asset-modal-body { padding: 20px 24px 24px; overflow-y: auto; flex: 1; }
|
||||
.asset-detail-grid { display: grid; grid-template-columns: 340px 1fr; gap: 24px; }
|
||||
.asset-detail-lead { display: flex; flex-direction: column; gap: 10px; }
|
||||
.asset-detail-lead .ad-lead-wrap { position: relative; }
|
||||
.asset-detail-lead .placeholder.ad-lead-img { aspect-ratio: 3/4; border-radius: var(--r-md); }
|
||||
.asset-detail-lead .ad-zoom-btn { position: absolute; right: 10px; bottom: 10px; height: 28px; padding: 0 12px; background: rgba(21,20,15,.7); color: #fff; border: 0; border-radius: var(--r-pill); display: inline-flex; align-items: center; gap: 4px; font-size: 11.5px; font-family: inherit; cursor: pointer; }
|
||||
.asset-detail-lead .ad-zoom-btn:hover { background: rgba(21,20,15,.9); }
|
||||
.asset-detail-lead .ad-zoom-btn svg { width: 12px; height: 12px; }
|
||||
.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:hover { border-color: var(--heat-40); }
|
||||
.asset-detail-lead .ad-thumbs .thumb.active { border-color: var(--heat); border-width: 2px; }
|
||||
.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; }
|
||||
.asset-detail-section-h .ic svg { width: 14px; height: 14px; }
|
||||
.asset-detail-section-h .ad-ratio-chip { margin-left: auto; font-family: var(--font-mono); font-size: 10.5px; padding: 2px 8px; border-radius: var(--r-sm); background: var(--background-lighter); border: 1px solid var(--border-faint); color: var(--black-alpha-56); }
|
||||
.asset-detail-section-h .ad-icon-btn { width: 28px; height: 28px; display: grid; place-items: center; background: var(--surface); border: 1px solid var(--border-faint); border-radius: var(--r-sm); color: var(--black-alpha-56); cursor: pointer; }
|
||||
.asset-detail-section-h .ad-icon-btn:hover { color: var(--heat); border-color: var(--heat-40); }
|
||||
.asset-detail-section-h .ad-icon-btn svg { width: 12px; height: 12px; }
|
||||
.asset-detail-tri-row .placeholder { aspect-ratio: 16/9; border-radius: var(--r-md); }
|
||||
.asset-detail-tri-row .placeholder.missing { display: grid; place-items: center; border: 1px dashed var(--border-faint); background: var(--background-lighter); color: var(--black-alpha-48); font-family: var(--font-mono); font-size: 12px; padding: 12px; text-align: center; cursor: pointer; gap: 8px; }
|
||||
.asset-detail-tri-row .placeholder.missing:hover { border-color: var(--heat); color: var(--heat); }
|
||||
.ad-intro { font-size: 13px; line-height: 1.65; color: var(--black-alpha-72); margin: 0 0 12px; }
|
||||
.ad-tags { display: flex; flex-wrap: wrap; gap: 8px; }
|
||||
.ad-tags .ad-tag-chip { height: 26px; padding: 0 12px; display: inline-flex; align-items: center; background: var(--background-lighter); border: 1px solid var(--border-faint); border-radius: var(--r-sm); font-size: 12px; color: var(--accent-black); }
|
||||
.ad-tags .ad-tag-add { width: 26px; height: 26px; display: grid; place-items: center; background: var(--background-lighter); border: 1px dashed var(--black-alpha-24); border-radius: var(--r-sm); color: var(--black-alpha-56); cursor: pointer; }
|
||||
.ad-tags .ad-tag-add:hover { border-color: var(--heat); color: var(--heat); }
|
||||
.ad-tags .ad-tag-add svg { width: 12px; height: 12px; }
|
||||
.ad-props { margin-top: 18px; display: grid; grid-template-columns: repeat(3, 1fr); column-gap: 24px; row-gap: 0; border-top: 1px solid var(--border-faint); padding-top: 16px; }
|
||||
.ad-props .ad-prop { display: flex; align-items: baseline; padding: 10px 0; border-bottom: 1px solid var(--border-faint); font-size: 12.5px; min-height: 38px; }
|
||||
.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-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; }
|
||||
.asset-modal-f { padding: 14px 20px; border-top: 1px solid var(--border-faint); display: flex; align-items: center; gap: 8px; }
|
||||
.asset-modal-f .ad-foot-stats { display: flex; gap: 6px; margin-right: auto; }
|
||||
.asset-modal-f .ad-stat-btn { height: 32px; padding: 0 12px; display: inline-flex; align-items: center; gap: 6px; background: transparent; border: 1px solid var(--border-faint); border-radius: var(--r-sm); color: var(--black-alpha-72); font-size: 12.5px; font-family: inherit; cursor: pointer; }
|
||||
.asset-modal-f .ad-stat-btn:hover { background: var(--background-lighter); color: var(--accent-black); border-color: var(--black-alpha-24); }
|
||||
.asset-modal-f .ad-stat-btn svg { width: 13px; height: 13px; }
|
||||
.asset-modal-f .ad-stat-btn b { color: var(--accent-black); font-weight: 600; }
|
||||
`;
|
||||
const style = document.createElement('style');
|
||||
style.textContent = css;
|
||||
document.head.appendChild(style);
|
||||
|
||||
const modalHTML = `
|
||||
<div class="lib-detail-bg" id="lib-detail-bg" style="position:fixed; inset:0; background:rgba(0,0,0,.4); z-index:1000; display:none; align-items:center; justify-content:center; padding:40px;">
|
||||
<div class="lib-detail" style="background:var(--surface); border:1px solid var(--border-faint); border-radius:var(--r-md); width:min(880px,100%); max-height:calc(100vh - 80px); overflow:hidden; display:flex; flex-direction:column; box-shadow:0 16px 48px rgba(0,0,0,.18);">
|
||||
<div style="display:flex; align-items:center; gap:10px; padding:14px 20px; border-bottom:1px solid var(--border-faint);">
|
||||
<h2 id="lib-detail-title" style="font-size:15px; font-weight:600;">资产详情</h2>
|
||||
<span id="lib-detail-kind" style="font-family:var(--font-mono); font-size:11px; color:var(--black-alpha-48);">// kind</span>
|
||||
<button id="lib-detail-x" type="button" aria-label="关闭" style="width:30px; height:30px; display:grid; place-items:center; background:transparent; border:0; cursor:pointer; color:var(--black-alpha-56); border-radius:var(--r-sm); margin-left:auto;"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M6 6l12 12M6 18L18 6"/></svg></button>
|
||||
<div class="asset-modal-bg" id="lib-detail-bg">
|
||||
<div class="asset-modal">
|
||||
<div class="asset-modal-h">
|
||||
<h2 id="lib-detail-title">资产详情</h2>
|
||||
<span class="ad-tag" id="lib-detail-kind">/ kind</span>
|
||||
<button class="x" id="lib-detail-x" type="button" aria-label="关闭"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M6 6l12 12M6 18L18 6"/></svg></button>
|
||||
</div>
|
||||
<div style="padding:22px 24px; overflow-y:auto; flex:1;">
|
||||
<div style="display:grid; grid-template-columns:1fr 1fr; gap:22px;">
|
||||
<div>
|
||||
<div class="placeholder" style="aspect-ratio:3/4;" id="lib-detail-lead-img"><span class="ph-frame">立绘 / 主图</span></div>
|
||||
<div style="font-family:var(--font-mono); font-size:10.5px; color:var(--black-alpha-48); letter-spacing:.06em; text-transform:uppercase; margin:14px 0 8px;">// 三视图</div>
|
||||
<div id="lib-detail-tri" style="display:grid; grid-template-columns:repeat(3,1fr); gap:8px;">
|
||||
<div class="placeholder" style="aspect-ratio:1;"><span class="ph-frame">正面</span></div>
|
||||
<div class="placeholder" style="aspect-ratio:1;"><span class="ph-frame">侧面</span></div>
|
||||
<div class="placeholder" style="aspect-ratio:1;"><span class="ph-frame">背面</span></div>
|
||||
<div class="asset-modal-body">
|
||||
<div class="asset-detail-grid">
|
||||
<div class="asset-detail-lead">
|
||||
<div class="ad-lead-wrap">
|
||||
<div class="placeholder ad-lead-img" id="lib-detail-lead-img"><span class="ph-frame">立绘 / 主图</span></div>
|
||||
<button class="ad-zoom-btn" type="button">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 8V3h5M16 3h5v5M21 16v5h-5M8 21H3v-5"/></svg>
|
||||
查看大图
|
||||
</button>
|
||||
</div>
|
||||
<div class="ad-thumbs" id="lib-detail-thumbs"></div>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-family:var(--font-mono); font-size:10.5px; color:var(--black-alpha-48); letter-spacing:.06em; text-transform:uppercase; margin-bottom:8px;">// 基础信息</div>
|
||||
<div id="lib-detail-info"></div>
|
||||
<div id="lib-detail-tip" style="display:none; 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); align-items:center; gap:8px; line-height:1.5;">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" style="color:var(--heat); flex-shrink:0;"><circle cx="12" cy="12" r="9"/><path d="M12 8v4M12 16h.01"/></svg>
|
||||
<span>暂无三视图,建议用 AI 生成以保证多角度一致性</span>
|
||||
<button id="lib-detail-aigen" type="button" style="margin-left:auto; height:26px; padding:0 10px; background:var(--heat); color:var(--accent-white); border:1px solid var(--heat); border-radius:var(--r-sm); font-size:11.5px; cursor:pointer; flex-shrink:0;">AI 生成三视图</button>
|
||||
<div class="asset-detail-right">
|
||||
<div class="ad-section" id="lib-detail-tri-section">
|
||||
<div class="asset-detail-section-h">
|
||||
<span class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg></span>
|
||||
<span class="t">三视图</span>
|
||||
<span class="ad-ratio-chip" id="lib-detail-ratio">16:9</span>
|
||||
<button class="ad-icon-btn" type="button" title="下载"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3"/></svg></button>
|
||||
</div>
|
||||
<div class="asset-detail-tri-row" id="lib-detail-tri">
|
||||
<div class="placeholder"><span class="ph-frame">正 / 侧 / 背 · 三视图</span></div>
|
||||
</div>
|
||||
<div class="asset-detail-tip" id="lib-detail-tip" style="display:none;">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M12 8v4M12 16h.01"/></svg>
|
||||
<span>暂无三视图,建议用 AI 生成以保证多角度一致性</span>
|
||||
<button class="ai-gen-btn" type="button" id="lib-detail-aigen">AI 生成三视图</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ad-section">
|
||||
<div class="asset-detail-section-h">
|
||||
<span class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4 6h16M4 12h16M4 18h10"/></svg></span>
|
||||
<span class="t">简介</span>
|
||||
</div>
|
||||
<p class="ad-intro" id="lib-detail-intro"></p>
|
||||
<div class="ad-tags" id="lib-detail-tags"></div>
|
||||
</div>
|
||||
<div class="ad-props" id="lib-detail-props"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding:14px 20px; border-top:1px solid var(--border-faint); display:flex; justify-content:flex-end; gap:8px;">
|
||||
<button class="btn btn-ghost" type="button" id="lib-detail-close">关闭</button>
|
||||
<button class="btn btn-primary" type="button" id="lib-detail-apply">应用到当前项目</button>
|
||||
<div class="asset-modal-f">
|
||||
<div class="ad-foot-stats">
|
||||
<button class="ad-stat-btn" type="button">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3"/></svg>
|
||||
下载
|
||||
</button>
|
||||
</div>
|
||||
<button class="btn btn-primary" type="button" id="lib-detail-apply">使用该资产</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
@@ -1717,69 +1846,111 @@ document.querySelectorAll('.asset-card').forEach(card => {
|
||||
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 triSection = document.getElementById('lib-detail-tri-section');
|
||||
const triEl = document.getElementById('lib-detail-tri');
|
||||
const infoEl = document.getElementById('lib-detail-info');
|
||||
const ratioChip = document.getElementById('lib-detail-ratio');
|
||||
const introEl = document.getElementById('lib-detail-intro');
|
||||
const tagsEl = document.getElementById('lib-detail-tags');
|
||||
const propsEl = document.getElementById('lib-detail-props');
|
||||
const tipEl = document.getElementById('lib-detail-tip');
|
||||
|
||||
function _hash(s) { let h = 0; for (let i = 0; i < s.length; i++) h = ((h << 5) - h + s.charCodeAt(i)) | 0; return Math.abs(h); }
|
||||
function _fmtAssetId(name, k) { return 'ASSET-20240520-' + (k === 'person' ? 'M' : k === 'scene' ? 'S' : 'P') + String(_hash(name) % 1000).padStart(3, '0'); }
|
||||
function _fmtSize(name) { return (4 + (_hash(name) % 100) / 10).toFixed(1) + 'MB'; }
|
||||
function _fmtFav(name) { return String(8 + _hash(name) % 80); }
|
||||
function _fmtDl(name) { const n = 200 + _hash(name) % 1800; return n >= 1000 ? (n / 1000).toFixed(1) + 'k' : String(n); }
|
||||
|
||||
function open(card) {
|
||||
const name = card.dataset.name || '资产';
|
||||
let kind = 'asset', info = [];
|
||||
if (card.dataset.gender) { // 人物
|
||||
kind = '人物 · 模特';
|
||||
info = [
|
||||
['类别', '人物'],
|
||||
['性别', card.dataset.gender || '—'],
|
||||
['年龄段', card.dataset.age || '—'],
|
||||
['角色', card.dataset.role || '—'],
|
||||
['来源', card.dataset.source || '—'],
|
||||
['使用次数', (card.dataset.used || '0') + ' 次'],
|
||||
const used = card.dataset.used || '0';
|
||||
const source = card.dataset.source || '平台预设';
|
||||
let tagText = 'AI 素材', intro = '', tags = [], props = [], hasTri = false, isActor = false;
|
||||
|
||||
if (card.dataset.gender) {
|
||||
tagText = '人物 · 模特';
|
||||
isActor = true; hasTri = true;
|
||||
intro = (card.dataset.role || '人物模特') + ' · 可用于本项目人物资产生成';
|
||||
tags = [card.dataset.gender, card.dataset.age, card.dataset.role].filter(Boolean);
|
||||
props = [
|
||||
['性别', card.dataset.gender || '-'], ['种族', '东亚'], ['作品ID', _fmtAssetId(name, 'person')],
|
||||
['年龄段', card.dataset.age || '-'], ['角色', card.dataset.role || '-'], ['创作人', '流·Studio'],
|
||||
['身高', '中等'], ['来源', source], ['文件大小', _fmtSize(name)],
|
||||
['使用次数', used + ' 次'], ['授权', '商用'], ['发布时间', '2024-05-20'],
|
||||
];
|
||||
} else if (card.dataset.sceneType) {
|
||||
kind = '场景';
|
||||
info = [
|
||||
['类别', '场景'],
|
||||
['场景类型', card.dataset.sceneType],
|
||||
['来源', card.dataset.source || '—'],
|
||||
['使用次数', (card.dataset.used || '0') + ' 次'],
|
||||
tagText = '场景 · ' + card.dataset.sceneType;
|
||||
isActor = false; hasTri = false;
|
||||
intro = card.dataset.sceneType + ' 场景资产';
|
||||
tags = [card.dataset.sceneType, source].filter(Boolean);
|
||||
props = [
|
||||
['场景类型', card.dataset.sceneType], ['来源', source], ['作品ID', _fmtAssetId(name, 'scene')],
|
||||
['镜头', '通用'], ['光线', '自然光'], ['创作人', '流·Studio'],
|
||||
['用途', '本项目场景资产'], ['使用次数', used + ' 次'], ['文件大小', _fmtSize(name)],
|
||||
];
|
||||
} else if (card.dataset.product) {
|
||||
kind = '商品资产';
|
||||
info = [
|
||||
['类别', '商品'],
|
||||
['关联商品', card.dataset.product],
|
||||
['来源', card.dataset.source || '—'],
|
||||
['使用次数', (card.dataset.used || '0') + ' 次'],
|
||||
tagText = '商品资产';
|
||||
isActor = false;
|
||||
intro = '关联商品: ' + card.dataset.product;
|
||||
tags = ['商品', source].filter(Boolean);
|
||||
props = [
|
||||
['关联商品', card.dataset.product], ['来源', source], ['作品ID', _fmtAssetId(name, 'product')],
|
||||
['用途', '商品资产'], ['使用次数', used + ' 次'], ['创作人', '流·Studio'],
|
||||
['授权', '商用'], ['文件大小', _fmtSize(name)], ['发布时间', '2024-05-20'],
|
||||
];
|
||||
} else {
|
||||
kind = 'AI 素材';
|
||||
info = [
|
||||
['名称', name],
|
||||
['来源', card.dataset.source || '—'],
|
||||
tagText = 'AI 素材';
|
||||
isActor = false;
|
||||
intro = name;
|
||||
tags = [source].filter(Boolean);
|
||||
props = [
|
||||
['名称', name], ['来源', source], ['作品ID', _fmtAssetId(name, 'asset')],
|
||||
['创作人', '流·Studio'], ['文件大小', _fmtSize(name)], ['发布时间', '2024-05-20'],
|
||||
];
|
||||
}
|
||||
|
||||
titleEl.textContent = name;
|
||||
kindEl.textContent = '// ' + kind;
|
||||
kindEl.textContent = '/ ' + tagText;
|
||||
leadImg.innerHTML = '<span class="ph-frame">' + name + '</span>';
|
||||
// 三视图:人物显示,其他默认提示
|
||||
if (card.dataset.gender) {
|
||||
triEl.style.display = 'grid';
|
||||
|
||||
thumbsEl.innerHTML = ['v1','v2','v3'].map((t, i) => `<div class="thumb placeholder${i === 0 ? ' active' : ''}"><span class="ph-frame">${t}</span></div>`).join('');
|
||||
thumbsEl.querySelectorAll('.thumb').forEach(t => t.addEventListener('click', () => {
|
||||
thumbsEl.querySelectorAll('.thumb').forEach(x => x.classList.remove('active'));
|
||||
t.classList.add('active');
|
||||
}));
|
||||
|
||||
if (card.dataset.sceneType) {
|
||||
triSection.style.display = 'none';
|
||||
} else if (isActor) {
|
||||
triSection.style.display = '';
|
||||
triEl.classList.remove('actor');
|
||||
triEl.innerHTML = '<div class="placeholder"><span class="ph-frame">' + name + ' · 三视图 (正/侧/背)</span></div>';
|
||||
ratioChip.textContent = '16:9';
|
||||
tipEl.style.display = 'none';
|
||||
} else if (card.dataset.product) {
|
||||
// 商品:若未上传则提示 AI 生成
|
||||
triEl.style.display = 'grid';
|
||||
tipEl.style.display = 'flex';
|
||||
} else {
|
||||
triEl.style.display = 'none';
|
||||
tipEl.style.display = 'none';
|
||||
triSection.style.display = '';
|
||||
triEl.classList.remove('actor');
|
||||
ratioChip.textContent = '16:9';
|
||||
if (hasTri) {
|
||||
triEl.innerHTML = '<div class="placeholder"><span class="ph-frame">' + name + ' · 三视图</span></div>';
|
||||
tipEl.style.display = 'none';
|
||||
} else {
|
||||
triEl.innerHTML = '<div class="placeholder missing" data-tri="0"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M12 8v4M12 16h.01"/></svg><span>暂未生成三视图(16:9 单图)</span></div>';
|
||||
tipEl.style.display = 'flex';
|
||||
}
|
||||
}
|
||||
infoEl.innerHTML = info.map(([k, v]) => '<div style="display:flex; justify-content:space-between; align-items:baseline; padding:8px 0; border-bottom:1px solid var(--border-faint); font-size:12.5px;"><span style="color:var(--black-alpha-56); font-family:var(--font-mono); font-size:11px;">' + k + '</span><span style="color:var(--accent-black);">' + v + '</span></div>').join('');
|
||||
bg.style.display = 'flex';
|
||||
|
||||
introEl.textContent = intro || '暂无简介';
|
||||
tagsEl.innerHTML = tags.map(t => '<span class="ad-tag-chip">' + t + '</span>').join('') +
|
||||
'<button class="ad-tag-add" type="button" title="添加标签"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M8 3v10M3 8h10"/></svg></button>';
|
||||
propsEl.innerHTML = props.map(([k, v]) => '<div class="ad-prop"><span class="k">' + k + '</span><span class="v">' + v + '</span></div>').join('');
|
||||
|
||||
bg.classList.add('show');
|
||||
}
|
||||
function close() { bg.style.display = 'none'; }
|
||||
function close() { bg.classList.remove('show'); }
|
||||
|
||||
bg.addEventListener('click', e => { if (e.target === bg) close(); });
|
||||
document.getElementById('lib-detail-x').addEventListener('click', close);
|
||||
document.getElementById('lib-detail-close').addEventListener('click', close);
|
||||
document.getElementById('lib-detail-apply').addEventListener('click', () => {
|
||||
Shell.toast('已应用「' + titleEl.textContent + '」', '已加入当前项目');
|
||||
close();
|
||||
|
||||
Reference in New Issue
Block a user