Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f343b26444 |
262
web/app.js
262
web/app.js
@@ -246,6 +246,7 @@
|
||||
setStatus(t('目录加载完成,点击缩略图或左侧系列开始选型',
|
||||
'Catalog loaded - click a thumbnail or pick a series'));
|
||||
if (location.search.indexOf('selftest=1') >= 0) runSelfTest();
|
||||
applyDeepLink(); // URL 参数深度链接 (?series= / ?model= / ?embed=1)
|
||||
})
|
||||
.catch(function (e) {
|
||||
setStatus(t('加载 catalog.json 失败: ', 'Failed to load catalog.json: ') + e.message +
|
||||
@@ -253,6 +254,67 @@
|
||||
});
|
||||
}
|
||||
|
||||
// ---------- 深度链接 (供 Odoo 产品页 iframe 调用) ----------
|
||||
// ?series=UCBM 直接打开某系列(型号编码前缀或系列 code)
|
||||
// ?model=UCBM32AK010 直接定位到某个具体型号(复用 jumpToModel)
|
||||
// ?embed=1 嵌入模式:隐藏自身顶栏/底栏,让 iframe 里更干净
|
||||
// ?nav=0 隐藏左侧系列树(产品页已定系列,不需要再让用户挑)
|
||||
// 说明: 本函数只在 catalog.json 加载完成后调用一次(见 load())
|
||||
function applyDeepLink() {
|
||||
var q = new URLSearchParams(location.search || '');
|
||||
|
||||
// 嵌入模式:隐藏工具自身的 header/footer,避免在 Odoo 页面里出现"页中页"
|
||||
if (q.get('embed') === '1') {
|
||||
document.body.classList.add('embed-mode');
|
||||
var st = $('status');
|
||||
if (st) st.classList.add('hidden');
|
||||
var hd = document.querySelector('header');
|
||||
if (hd) hd.classList.add('hidden');
|
||||
}
|
||||
|
||||
// 隐藏左侧系列树(产品页场景)
|
||||
if (q.get('nav') === '0') {
|
||||
document.body.classList.add('nav-off');
|
||||
}
|
||||
|
||||
// 语言: ?lang=en
|
||||
var lg = q.get('lang');
|
||||
if (lg === 'en' || lg === 'zh') {
|
||||
state.lang = lg;
|
||||
renderTree();
|
||||
renderHome();
|
||||
renderCrossRef();
|
||||
}
|
||||
|
||||
// 定位型号(优先级最高)
|
||||
var model = q.get('model');
|
||||
if (model) { jumpToModel(model); return; }
|
||||
|
||||
// 打开系列:支持传系列 code(KC)或型号前缀(UCBM32)
|
||||
var want = q.get('series');
|
||||
if (!want) return;
|
||||
var up = String(want).toUpperCase();
|
||||
var hit = null;
|
||||
(state.catalog.series || []).forEach(function (s) {
|
||||
if (String(s.code).toUpperCase() === up) hit = s;
|
||||
});
|
||||
if (!hit) {
|
||||
// 退一步:按型号编码前缀匹配(如 UCBM32 → 系列 UCBM)
|
||||
(state.catalog.series || []).forEach(function (s) {
|
||||
if (hit) return;
|
||||
(s.variants || []).some(function (v) {
|
||||
if (String(v.modelCode).toUpperCase().indexOf(up) === 0) { hit = s; return true; }
|
||||
return false;
|
||||
});
|
||||
});
|
||||
}
|
||||
if (hit) {
|
||||
selectSeries(hit);
|
||||
} else {
|
||||
setStatus(t('未找到系列: ', 'Series not found: ') + want);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- 目录树 ----------
|
||||
// 大类图标 (内联 SVG, 24x24 线性, stroke=currentColor → 随主题色/选中态自动变色)
|
||||
var CAT_ICONS = {
|
||||
@@ -588,11 +650,148 @@
|
||||
im.src = a.path;
|
||||
im.alt = a.path;
|
||||
im.draggable = false; // 阻止原生图片拖拽 (见下方全局 dragstart 拦截)
|
||||
im.title = t('点击放大', 'Click to enlarge');
|
||||
// 点击放大: 同一手册页的多张图可左右切换 (← → 键)
|
||||
im.onclick = function () { openImageViewer(pages, idx); };
|
||||
mw.appendChild(cap);
|
||||
mw.appendChild(im);
|
||||
});
|
||||
}
|
||||
|
||||
// ---------- 手册页图片放大 (单击弹窗, 支持多页左右切换) ----------
|
||||
var zview = { el: null, pages: [], idx: 0 };
|
||||
|
||||
function openImageViewer(pages, idx) {
|
||||
closeImageViewer(); // 先清掉可能存在的旧弹窗
|
||||
zview.pages = pages || [];
|
||||
zview.idx = idx || 0;
|
||||
|
||||
var ov = document.createElement('div');
|
||||
ov.className = 'img-zoom-overlay';
|
||||
|
||||
var im = document.createElement('img');
|
||||
im.src = zview.pages[zview.idx].path;
|
||||
im.alt = zview.pages[zview.idx].path;
|
||||
im.draggable = false;
|
||||
|
||||
var btn = document.createElement('button');
|
||||
btn.className = 'img-zoom-close';
|
||||
btn.type = 'button';
|
||||
btn.innerHTML = '×';
|
||||
btn.setAttribute('aria-label', t('关闭', 'Close'));
|
||||
btn.onclick = function (ev) { ev.stopPropagation(); closeImageViewer(); };
|
||||
|
||||
var cap = document.createElement('div');
|
||||
cap.className = 'img-zoom-cap';
|
||||
cap.textContent = t('手册页', 'Manual page') + ' ' + (zview.idx + 1) + '/' + zview.pages.length
|
||||
+ (zview.pages.length > 1 ? t(' (← → 切换,Esc 关闭)', ' (← → to switch, Esc to close)') : '');
|
||||
|
||||
ov.appendChild(im);
|
||||
ov.appendChild(btn);
|
||||
ov.appendChild(cap);
|
||||
// 点遮罩空白处关闭(点图片本身不关)
|
||||
ov.onclick = function (ev) { if (ev.target === ov) closeImageViewer(); };
|
||||
|
||||
document.body.appendChild(ov);
|
||||
zview.el = ov;
|
||||
document.addEventListener('keydown', onZoomKey);
|
||||
}
|
||||
|
||||
function onZoomKey(ev) {
|
||||
if (!zview.el) return;
|
||||
if (ev.key === 'Escape') { closeImageViewer(); }
|
||||
else if (ev.key === 'ArrowRight' || ev.key === 'ArrowLeft') {
|
||||
if (zview.pages.length < 2) return;
|
||||
var d = ev.key === 'ArrowRight' ? 1 : -1;
|
||||
zview.idx = (zview.idx + d + zview.pages.length) % zview.pages.length;
|
||||
var im = zview.el.querySelector('img');
|
||||
im.src = zview.pages[zview.idx].path;
|
||||
var cap = zview.el.querySelector('.img-zoom-cap');
|
||||
if (cap) cap.textContent = t('手册页', 'Manual page') + ' ' + (zview.idx + 1) + '/' + zview.pages.length
|
||||
+ t(' (← → 切换,Esc 关闭)', ' (← → to switch, Esc to close)');
|
||||
}
|
||||
}
|
||||
|
||||
function closeImageViewer() {
|
||||
if (zview.el && zview.el.parentNode) zview.el.parentNode.removeChild(zview.el);
|
||||
zview.el = null;
|
||||
document.removeEventListener('keydown', onZoomKey);
|
||||
}
|
||||
|
||||
// ---------- 数据表 PDF 弹窗预览 ----------
|
||||
// 用浏览器内置 PDF 阅读器(iframe),弹窗内保留下载入口。
|
||||
// 移动端部分浏览器(尤其 iOS Safari)不内联显示 PDF,所以在弹窗里同时给下载按钮兜底。
|
||||
var pview = { el: null };
|
||||
|
||||
function openPdfViewer(url, fname) {
|
||||
closePdfViewer();
|
||||
|
||||
var ov = document.createElement('div');
|
||||
ov.className = 'pdf-zoom-overlay';
|
||||
|
||||
var box = document.createElement('div');
|
||||
box.className = 'pdf-zoom-box';
|
||||
|
||||
var head = document.createElement('div');
|
||||
head.className = 'pdf-zoom-head';
|
||||
|
||||
var title = document.createElement('span');
|
||||
title.className = 'pdf-zoom-title';
|
||||
title.textContent = fname;
|
||||
|
||||
var dl = document.createElement('a');
|
||||
dl.className = 'pdf-zoom-dl';
|
||||
dl.href = url;
|
||||
dl.setAttribute('download', fname);
|
||||
// 内联 SVG 下载图标(工具无图标字体)
|
||||
dl.innerHTML =
|
||||
'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" '
|
||||
+ 'stroke-linecap="round" stroke-linejoin="round" style="width:13px;height:13px;vertical-align:-2px">'
|
||||
+ '<path d="M12 3v12"/><path d="m7 11 5 5 5-5"/><path d="M4 20h16"/></svg> '
|
||||
+ t('下载', 'Download');
|
||||
|
||||
var btn = document.createElement('button');
|
||||
btn.className = 'img-zoom-close pdf-zoom-close';
|
||||
btn.type = 'button';
|
||||
btn.innerHTML = '×';
|
||||
btn.setAttribute('aria-label', t('关闭', 'Close'));
|
||||
btn.onclick = function (ev) { ev.stopPropagation(); closePdfViewer(); };
|
||||
|
||||
head.appendChild(title);
|
||||
head.appendChild(dl);
|
||||
head.appendChild(btn);
|
||||
|
||||
var fr = document.createElement('iframe');
|
||||
fr.className = 'pdf-zoom-frame';
|
||||
fr.src = url + '#toolbar=1&view=FitH';
|
||||
fr.setAttribute('title', fname);
|
||||
|
||||
box.appendChild(head);
|
||||
box.appendChild(fr);
|
||||
ov.appendChild(box);
|
||||
// 点遮罩空白处关闭(点弹窗内部不关)
|
||||
ov.onclick = function (ev) { if (ev.target === ov) closePdfViewer(); };
|
||||
|
||||
document.body.appendChild(ov);
|
||||
pview.el = ov;
|
||||
document.addEventListener('keydown', onPdfKey);
|
||||
}
|
||||
|
||||
function onPdfKey(ev) {
|
||||
if (pview.el && ev.key === 'Escape') closePdfViewer();
|
||||
}
|
||||
|
||||
function closePdfViewer() {
|
||||
if (pview.el && pview.el.parentNode) {
|
||||
// 先清空 iframe.src 再移除,避免某些浏览器里 PDF 后台继续加载
|
||||
var fr = pview.el.querySelector('iframe');
|
||||
if (fr) fr.src = 'about:blank';
|
||||
pview.el.parentNode.removeChild(pview.el);
|
||||
}
|
||||
pview.el = null;
|
||||
document.removeEventListener('keydown', onPdfKey);
|
||||
}
|
||||
|
||||
// 空字符串是合法枚举值 (如 磁石=""=无磁石), 用哨兵值避免与"未选择"混淆
|
||||
function optValue(v) { return str(v) === '' ? '__EMPTY__' : str(v); }
|
||||
|
||||
@@ -772,8 +971,15 @@
|
||||
if (a.kind === 'dimDrawing' && (a.model === v.modelCode || att === null)) att = a;
|
||||
});
|
||||
if (att) {
|
||||
$('dimImage').src = att.path;
|
||||
$('dimImage').classList.remove('hidden');
|
||||
var dimEl = $('dimImage');
|
||||
dimEl.src = att.path;
|
||||
dimEl.classList.remove('hidden');
|
||||
dimEl.title = t('点击放大', 'Click to enlarge');
|
||||
dimEl.style.cursor = 'zoom-in';
|
||||
// 技术文档(尺寸图)同样支持点击弹窗放大
|
||||
dimEl.onclick = function () {
|
||||
openImageViewer([{ path: att.path }], 0);
|
||||
};
|
||||
$('dimHint').classList.add('hidden');
|
||||
}
|
||||
// 数据表
|
||||
@@ -781,9 +987,30 @@
|
||||
return a.kind === 'datasheet' && a.lang === state.lang;
|
||||
})[0] || (s.attachments || []).filter(function (a) { return a.kind === 'datasheet'; })[0];
|
||||
if (dsAtt) {
|
||||
// 下载行为 (2026-08-24): 点按钮直接下载 PDF, 与 STEP 下载一致
|
||||
ds.href = dsAtt.path;
|
||||
ds.setAttribute('download', dsAtt.path.split('/').pop() || 'datasheet.pdf');
|
||||
// 数据表 PDF: 点击弹窗预览(用浏览器内置 PDF 阅读器),弹窗内保留下载入口。
|
||||
// 原来是直接下载(2026-08-24),用户反馈希望先预览(2026-09-16)。
|
||||
// ⚠️ 不能沿用 <a download>:那会直接下载。改成 button + 弹窗。
|
||||
ds.removeAttribute('href');
|
||||
ds.removeAttribute('download');
|
||||
ds.style.cursor = 'pointer';
|
||||
ds.title = t('点击预览', 'Click to preview');
|
||||
// 带预览图标,区别于纯下载按钮(用户要求能一眼看出可预览)。
|
||||
// ⚠️ 工具没有图标字体(只带 JSZip),所以用**内联 SVG**(与分类图标同做法)。
|
||||
ds.innerHTML =
|
||||
'<svg class="ds-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" '
|
||||
+ 'stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">'
|
||||
+ '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>'
|
||||
+ '<path d="M14 2v6h6"/><path d="M8 13h2.5a1.5 1.5 0 0 1 0 3H8v-6"/>'
|
||||
+ '</svg>'
|
||||
+ '<span class="ds-txt">' + t('数据表 PDF', 'Datasheet PDF') + '</span>'
|
||||
+ '<svg class="ds-ico ds-ico-eye" viewBox="0 0 24 24" fill="none" stroke="currentColor" '
|
||||
+ 'stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">'
|
||||
+ '<circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5M11 8v6M8 11h6"/>'
|
||||
+ '</svg>';
|
||||
ds.onclick = function (ev) {
|
||||
ev.preventDefault();
|
||||
openPdfViewer(dsAtt.path, dsAtt.path.split('/').pop() || 'datasheet.pdf');
|
||||
};
|
||||
ds.classList.remove('hidden');
|
||||
}
|
||||
|
||||
@@ -1508,7 +1735,14 @@
|
||||
$('cartDownload').textContent = t('下载全部 (zip + BOM)', 'Download All (zip + BOM)');
|
||||
renderCartBadge();
|
||||
$('btnDownload').textContent = t('下载 STEP', 'Download STEP');
|
||||
$('btnDatasheet').textContent = t('数据表 PDF', 'Datasheet PDF');
|
||||
// ⚠️ 数据表按钮带图标(pdf + 放大镜),不能用 textContent 整段替换,
|
||||
// 否则切语言时图标会被抹掉。只改文字节点。
|
||||
var dsBtn = $('btnDatasheet');
|
||||
if (dsBtn) {
|
||||
var dsTxt = dsBtn.querySelector('.ds-txt');
|
||||
if (dsTxt) dsTxt.textContent = t('数据表 PDF', 'Datasheet PDF');
|
||||
else dsBtn.textContent = t('数据表 PDF', 'Datasheet PDF');
|
||||
}
|
||||
$('dimHint').textContent = t('选型完成后显示尺寸图', 'Dimension drawing appears after selection');
|
||||
var tabs = document.querySelectorAll('.tab');
|
||||
tabs[0].textContent = t('3D / 尺寸图', '3D / Dimension');
|
||||
@@ -1576,10 +1810,14 @@
|
||||
}
|
||||
|
||||
// ---- 3D 查看器联动 (新迪 STEPViewer: occt-import-js + three.js, 支持真实 STEP) ----
|
||||
// 本地联调: /viewer/ 前缀由 serve-web.ps1 -ViewerRoot 映射到新迪 dev 目录
|
||||
// 生产同域部署 (3d.ruiguo.wang): 改为 '/STEPViewer/index.html'
|
||||
// (模型文件已同步到 STEPViewer/step/ 目录, file 参数 = 目录内相对路径)
|
||||
var VIEWER_BASE = '/viewer/STEPViewer/index.html';
|
||||
// 部署自适应: 原来硬编码 '/viewer/STEPViewer/index.html' + 站点根绝对路径 '/step/…',
|
||||
// 一旦部署到子路径(如 Odoo 的 /catalog/)就会 404。
|
||||
// 改为从当前页面 URL 推导根路径,本地联调 / 生产子路径 / 根路径都能用。
|
||||
// 例: 页面 /catalog/index.html → BASE_DIR = '/catalog'
|
||||
var BASE_DIR = location.pathname.replace(/\/[^/]*$/, ''); // 去掉文件名, 得 '/catalog' 或 ''
|
||||
// 查看器位置: 优先同目录下的 viewer/STEPViewer(生产整站一起部署时就是这个)
|
||||
// 回退到 /viewer/STEPViewer(本地 serve-web.ps1 -ViewerRoot 联调用)
|
||||
var VIEWER_BASE = BASE_DIR + '/viewer/STEPViewer/index.html';
|
||||
|
||||
function load3D(variant) {
|
||||
var frame = $('viewerFrame');
|
||||
@@ -1588,8 +1826,8 @@
|
||||
hint.classList.add('hidden');
|
||||
frame.classList.remove('hidden');
|
||||
canvas.classList.add('hidden');
|
||||
// 站点根绝对路径 (/step/...): 模型始终从站点 step\ 目录取, 查看器无需自带模型副本 (踩坑 #20)
|
||||
frame.src = VIEWER_BASE + '?file=' + encodeURIComponent('/' + variant.step) + '&embed=1&bg=grey&menu=min';
|
||||
// 模型路径同样要带 BASE_DIR 前缀(原为站点根 '/' + step)
|
||||
frame.src = VIEWER_BASE + '?file=' + encodeURIComponent(BASE_DIR + '/' + variant.step) + '&embed=1&bg=grey&menu=min';
|
||||
if (v3d.fallbackTimer) clearTimeout(v3d.fallbackTimer);
|
||||
// 查看器不可用 (404/离线) → 回退内置 Canvas 渲染
|
||||
v3d.fallbackTimer = setTimeout(function () {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>OneBot数模下载</title>
|
||||
<link rel="stylesheet" href="style.css?v=11">
|
||||
<link rel="stylesheet" href="style.css?v=12">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
@@ -43,6 +43,16 @@
|
||||
<div class="sku-hint" id="skuHint"></div>
|
||||
</div>
|
||||
<div id="paramPanel"></div>
|
||||
</section>
|
||||
<div id="split2" class="split hidden" title="拖拽调整宽度"></div>
|
||||
|
||||
<section id="content" class="content hidden">
|
||||
<div class="tabs" id="tabsBar">
|
||||
<button class="tab active" data-tab="3d">3D / 尺寸图</button>
|
||||
<button class="tab" data-tab="table">参数表</button>
|
||||
<button class="tab" data-tab="search">搜索结果</button>
|
||||
<button class="tab" data-tab="cross">平替对照</button>
|
||||
<span class="tabs-spacer"></span>
|
||||
<div id="actions">
|
||||
<a id="btnDownload" class="btn primary hidden" download>下载 STEP</a>
|
||||
<select id="btnGenFmt" class="hidden" title="下载格式 (仅非标档位在线生成)">
|
||||
@@ -53,15 +63,6 @@
|
||||
<a id="btnDatasheet" class="btn hidden">下载 PDF</a>
|
||||
<button id="btnAddCart" class="btn hidden">加入清单</button>
|
||||
</div>
|
||||
</section>
|
||||
<div id="split2" class="split hidden" title="拖拽调整宽度"></div>
|
||||
|
||||
<section id="content" class="content hidden">
|
||||
<div class="tabs">
|
||||
<button class="tab active" data-tab="3d">3D / 尺寸图</button>
|
||||
<button class="tab" data-tab="table">参数表</button>
|
||||
<button class="tab" data-tab="search">搜索结果</button>
|
||||
<button class="tab" data-tab="cross">平替对照</button>
|
||||
</div>
|
||||
<div class="content-back">
|
||||
<button id="btnBackHome" class="btn">← 返回系列总览</button>
|
||||
@@ -122,6 +123,6 @@
|
||||
|
||||
<script src="libs/jszip.min.js?v=1"></script>
|
||||
<script src="config.js?v=1"></script>
|
||||
<script src="app.js?v=18"></script>
|
||||
<script src="app.js?v=19"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
117
web/style.css
117
web/style.css
@@ -265,7 +265,16 @@ main { flex: 1; display: flex; min-height: 0; }
|
||||
.param-row select:focus, .param-input:focus {
|
||||
border-color: var(--primary-500); box-shadow: 0 0 0 3px rgba(20,184,166,.15);
|
||||
}
|
||||
#actions { margin-top: 16px; display: flex; gap: 8px; flex-wrap: wrap; }
|
||||
/* #actions 原在左侧参数面板底部,现移到 tab 行右侧(与 3D/尺寸图 等同一行)。
|
||||
元素 id 未变,JS 按 id 取用,位置移动不影响逻辑。 */
|
||||
#actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
|
||||
/* tab 行的弹性占位:把 #actions 推到最右 */
|
||||
.tabs-spacer { flex: 1; min-width: 8px; }
|
||||
/* 窄屏时按钮换行到下一行,避免挤压 tab */
|
||||
@media (max-width: 1100px) {
|
||||
.tabs { flex-wrap: wrap; }
|
||||
.tabs-spacer { flex-basis: 100%; height: 0; }
|
||||
}
|
||||
|
||||
/* ============================================================ 零件号实时条 (DESTACO 风)
|
||||
* 边选边拼: 已确定部分实心高亮, 未定部分虚化占位。 */
|
||||
@@ -387,10 +396,114 @@ main { flex: 1; display: flex; min-height: 0; }
|
||||
|
||||
.hidden { display: none !important; }
|
||||
|
||||
/* ------------------------------------------------------------ 嵌入模式
|
||||
供 Odoo 产品页 iframe 使用 (?embed=1):隐藏工具自身的顶栏与底栏,
|
||||
避免在网站页面里出现"页中页",同时把高度让给内容区。
|
||||
由 app.js 的 applyDeepLink() 给 body 加 .embed-mode 触发。 */
|
||||
body.embed-mode header,
|
||||
body.embed-mode footer { display: none !important; }
|
||||
body.embed-mode { height: 100vh; }
|
||||
|
||||
/* 隐藏模式 (?nav=0):嵌入 Odoo 产品页时用。
|
||||
产品页已经决定了是哪个系列,左侧系列树、分隔条、「返回系列总览」纯属多余,
|
||||
隐藏后让参数面板 + 内容区铺满整宽,观感更干净。 */
|
||||
body.nav-off #tree,
|
||||
body.nav-off #split1,
|
||||
body.nav-off .content-back { display: none !important; }
|
||||
/* 隐藏侧栏后,参数面板适当加宽(原来 330px 是因为旁边还有树+主区抢空间) */
|
||||
body.nav-off .params { width: 300px; }
|
||||
|
||||
/* ------------------------------------------------------------ 内容区 / 标签页 */
|
||||
.content { flex: 1; display: flex; flex-direction: column; min-width: 0; padding: 14px; }
|
||||
.tabs { display: flex; gap: 6px; flex: none; }
|
||||
.tabs { display: flex; gap: 6px; flex: none; align-items: center; }
|
||||
.content-back { flex: none; margin: 10px 0 0; }
|
||||
|
||||
/* ------------------------------------------------------------ 手册页图片放大
|
||||
点击手册页图片 → 全屏遮罩显示大图。点遮罩任意处 / 按 Esc 关闭。
|
||||
用 position:fixed 覆盖整个视口(含 iframe 内嵌场景)。 */
|
||||
.manual-img { cursor: zoom-in; }
|
||||
|
||||
/* 数据表按钮图标(内联 SVG,工具无图标字体) */
|
||||
.ds-ico { width: 15px; height: 15px; flex: none; vertical-align: -3px; }
|
||||
.ds-ico-eye { opacity: .65; }
|
||||
#btnDatasheet { display: inline-flex; align-items: center; gap: 6px; }
|
||||
#btnDatasheet:hover .ds-ico-eye { opacity: 1; }
|
||||
#btnDatasheet .ds-txt { white-space: nowrap; }
|
||||
|
||||
.img-zoom-overlay {
|
||||
position: fixed; inset: 0; z-index: 900;
|
||||
background: rgba(17, 24, 39, .88);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
padding: 24px; cursor: zoom-out;
|
||||
animation: imgZoomIn .16s ease-out;
|
||||
}
|
||||
@keyframes imgZoomIn { from { opacity: 0; } to { opacity: 1; } }
|
||||
.img-zoom-overlay img {
|
||||
max-width: 100%; max-height: 100%;
|
||||
object-fit: contain; border-radius: 6px;
|
||||
box-shadow: 0 12px 48px rgba(0, 0, 0, .5);
|
||||
}
|
||||
.img-zoom-close {
|
||||
position: absolute; top: 14px; right: 18px;
|
||||
width: 40px; height: 40px; border: 0; border-radius: 50%;
|
||||
background: rgba(255, 255, 255, .16); color: #fff;
|
||||
font-size: 22px; line-height: 1; cursor: pointer;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
transition: background .18s;
|
||||
}
|
||||
.img-zoom-close:hover { background: rgba(255, 255, 255, .3); }
|
||||
.img-zoom-cap {
|
||||
position: absolute; bottom: 14px; left: 0; right: 0;
|
||||
text-align: center; color: rgba(255, 255, 255, .75); font-size: 12.5px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ 数据表 PDF 弹窗预览
|
||||
点击「数据表」→ 弹窗内用浏览器内置 PDF 阅读器打开,标题栏含下载入口。 */
|
||||
.pdf-zoom-overlay {
|
||||
position: fixed; inset: 0; z-index: 910;
|
||||
background: rgba(17, 24, 39, .72);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
padding: 24px;
|
||||
animation: imgZoomIn .16s ease-out;
|
||||
}
|
||||
.pdf-zoom-box {
|
||||
width: min(1100px, 100%); height: min(92vh, 100%);
|
||||
display: flex; flex-direction: column;
|
||||
background: var(--surface, #fff);
|
||||
border-radius: 10px; overflow: hidden;
|
||||
box-shadow: 0 16px 56px rgba(0, 0, 0, .45);
|
||||
}
|
||||
.pdf-zoom-head {
|
||||
flex: none; display: flex; align-items: center; gap: 12px;
|
||||
padding: 10px 12px 10px 16px;
|
||||
border-bottom: 1px solid var(--border, #e5e7eb);
|
||||
background: var(--surface, #fff);
|
||||
}
|
||||
.pdf-zoom-title {
|
||||
flex: 1; font-size: 13px; font-weight: 600; color: var(--gray-900, #111827);
|
||||
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||
}
|
||||
.pdf-zoom-dl {
|
||||
flex: none; font-size: 12.5px; text-decoration: none;
|
||||
padding: 6px 14px; border-radius: 6px;
|
||||
background: var(--primary, #14b8a6); color: #fff;
|
||||
transition: filter .18s;
|
||||
}
|
||||
.pdf-zoom-dl:hover { filter: brightness(.94); }
|
||||
.pdf-zoom-close {
|
||||
position: static; flex: none;
|
||||
width: 32px; height: 32px; font-size: 18px;
|
||||
background: var(--gray-100, #f3f4f6); color: var(--gray-700, #374151);
|
||||
}
|
||||
.pdf-zoom-close:hover { background: var(--gray-200, #e5e7eb); }
|
||||
.pdf-zoom-frame {
|
||||
flex: 1; width: 100%; border: 0; background: #525659;
|
||||
}
|
||||
@media (max-width: 767.98px) {
|
||||
.pdf-zoom-overlay { padding: 0; }
|
||||
.pdf-zoom-box { width: 100%; height: 100%; border-radius: 0; }
|
||||
}
|
||||
.tab {
|
||||
height: 36px; padding: 0 16px; border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm); background: var(--surface);
|
||||
|
||||
Reference in New Issue
Block a user