refactor(viewer): 单文件查看器拆成 webapp/ 多文件,走本地 http;Worker 换成真文件
前端不再是"一个 16MB HTML + 一个 2955 行模板"。现在: webapp/index.html 343 行 DOM 骨架,零内联数据 webapp/styles/*.css 811 行 CSS 按段切成 8 个文件 webapp/js/app.js 原内联主脚本逐字搬出(仍是普通脚本,S5 才改 ESM) webapp/js/ik-solver.js / ik-glue.js / vendor/three.min.js(原 web/ 三个文件) webapp/workers/ik.worker.js 真 Worker 文件 webapp/generated/00_data.js 生成物(网格/工程图/行程/碰撞/螺钉) dist/机器人手臂3D查看器.html 分发用单文件,由 build_viewer 内联上面这些文件产出 入口:python -m arm.viewer.serve 后开 http://127.0.0.1:8123/ (serve 带 no-store,改完刷新即新构建,不再需要 Ctrl+F5;显式 MIME, .js 被 Windows 注册表判成 text/plain 会让模块静默不执行) 删掉 src/viewer_tpl.html 与 web/ 目录(旧产物移入 backups/pre_split_20261015/)。 工程图不再维护第二份 sheets.jsdata —— 直接读 draw_sheet 写出的 4 个真 SVG。 实测中修掉的问题: - serve 的 / 原先"内部映射"到 webapp/index.html,导致文档基址是 /、相对路径的 8 个 CSS 与 4 个 JS 全部 404(整页无样式无脚本)。改成 302 跳转。 - file:// 下 new Worker('workers/…') 构造阶段不抛错、错误以异步 onerror 到达, 所以"构造失败才试 Blob 兜底"永远不会触发;改为按协议选路。两条路径都已实测 backend=worker(http 走文件、file:// 走 Blob)。 - Worker 握手 3s 在 headless 并发下会误判"没起来"(截图里红徽标即此),放宽到 10s, 并把后端状态暴露成 window.__IK_BACKEND / __IK_BACKEND_LOG 供测试直读。 - 截图竞态:4 并发 × 每页 14MB 数据时,虚拟时间预算会在页面加载完成前到期, 拍到半初始化画面(且能"稳定地坏",旧基线里 m9_motion 就是这么混进来的)。 并发降到 2、预算 40s、serve 加 HTTP/1.1+大缓冲+字节缓存,并用"同一构建两轮" 重新界定稳定性:99 张逐字节一致、6 张属噪声改走像素容差(mean<=0.02、单像素<=32)。 - 关卡自身两处假通过已修:node 输出被 GBK 解码炸掉时不再算 PASS; 线束干涉测试读不到通道源码时会 exit 2,而不是"零通道=零穿透=通过"。 关卡结果:python -m arm.build_model 退出码 0;10 项数据哈希与基线一致; IK exact 段逐字节一致;99 张截图逐字节一致 + 6 张在噪声阈内;线束干涉测试 1800 点 0 穿透。
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
/* 设计令牌、reset、整体栅格与左栏/树/页脚 */
|
||||
:root{--bg:#0f1115;--pan:#171a21;--pan2:#1f242e;--tx:#e6e8ee;--dim:#8b93a7;--ac:#4c9aff;--ok:#3ddc97;--warn:#ffb454;--line:#2b323e}
|
||||
*{box-sizing:border-box}
|
||||
html,body{margin:0;height:100%;background:var(--bg);color:var(--tx);
|
||||
font:13px/1.55 "Microsoft YaHei","Segoe UI",system-ui,sans-serif;overflow:hidden;
|
||||
-webkit-user-select:none;user-select:none}
|
||||
#app{display:grid;grid-template-columns:322px minmax(430px,1fr);grid-template-rows:46px auto 1fr 26px;
|
||||
height:100%;min-width:0;overflow-x:auto}
|
||||
header{grid-column:1/-1;display:flex;align-items:center;gap:8px;padding:0 12px;flex-wrap:nowrap;
|
||||
overflow-x:auto;overflow-y:hidden;background:var(--pan);border-bottom:1px solid var(--line)}
|
||||
header .q{white-space:nowrap;flex:none;color:var(--dim);font-size:12px}
|
||||
header b{font-size:14px;white-space:nowrap;flex:none}
|
||||
header .sp{flex:1 1 auto;min-width:6px}
|
||||
button,select{background:var(--pan2);color:var(--tx);border:1px solid var(--line);
|
||||
border-radius:5px;padding:4px 9px;font:inherit;cursor:pointer;white-space:nowrap;flex:none}
|
||||
button:hover{border-color:var(--ac);color:#fff}
|
||||
button.on{background:#22406b;border-color:var(--ac)}
|
||||
#bHarn.on{background:#5c3b1e;border-color:#f59e0b;color:#fbbf24}
|
||||
aside{background:var(--pan);overflow:auto;padding:8px;min-width:0}
|
||||
#left{border-right:1px solid var(--line)}
|
||||
#app.l{grid-template-columns:322px minmax(430px,1fr)}
|
||||
#stage{position:relative;overflow:hidden;min-width:0;
|
||||
background:radial-gradient(120% 120% at 50% 0%,#1b2028 0%,#0d0f13 70%)}
|
||||
canvas{display:block;width:100%;height:100%}
|
||||
footer{grid-column:1/-1;background:var(--pan);border-top:1px solid var(--line);
|
||||
display:flex;align-items:center;gap:14px;padding:0 12px;color:var(--dim);font-size:12px;
|
||||
white-space:nowrap;overflow:hidden}
|
||||
@media (max-width:1150px){
|
||||
#app{grid-template-columns:300px minmax(400px,1fr)}
|
||||
}
|
||||
@media (max-width:760px){
|
||||
#app{grid-template-columns:minmax(0,1fr)}
|
||||
#left{display:none}
|
||||
#app.l #left{display:block}
|
||||
}
|
||||
h3{font-size:12px;margin:10px 0 6px;color:var(--ac);letter-spacing:.4px;-webkit-user-select:none;user-select:none}
|
||||
.row{display:flex;align-items:center;gap:6px;margin:3px 0}
|
||||
.row label{flex:1;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;-webkit-user-select:none;user-select:none}
|
||||
.row .q{color:var(--dim);font-size:11px;-webkit-user-select:none;user-select:none}
|
||||
.chip{display:inline-block;width:9px;height:9px;border-radius:2px;margin-right:5px;flex:none}
|
||||
.pr{color:var(--ok)}.ex{color:var(--warn)}.mo{color:#9aa4b8}
|
||||
table{width:100%;border-collapse:collapse;font-size:11.5px;-webkit-user-select:text;user-select:text}
|
||||
th,td{border-bottom:1px solid var(--line);padding:3px 4px;text-align:left;vertical-align:top}
|
||||
th{color:var(--dim);font-weight:400;position:sticky;top:0;background:var(--pan);-webkit-user-select:none;user-select:none}
|
||||
.j-box{margin-bottom:7px;padding:3px 4px 1px;border-radius:4px;transition:background .15s}
|
||||
.j-box:hover{background:rgba(255,255,255,.03)}
|
||||
.j-box label{cursor:default;font-weight:500}
|
||||
.j-val{font-size:11px;color:var(--ac);cursor:pointer;padding:1px 5px;border-radius:3px;
|
||||
background:rgba(76,154,255,.1);font-variant-numeric:tabular-nums;transition:all .15s;
|
||||
-webkit-user-select:none;user-select:none}
|
||||
.j-val:hover{background:var(--ac);color:#fff}
|
||||
input[type=range]{width:100%;height:18px;accent-color:var(--ac);cursor:pointer;display:block;margin:1px 0}
|
||||
label em{font-style:normal;color:var(--mu);font-size:10.5px;margin-left:4px}
|
||||
label em.ok{color:#5ddc90}
|
||||
.gr{font-size:11px;color:var(--dim);margin:2px 0 4px;-webkit-user-select:none;user-select:none}
|
||||
#hud{position:absolute;left:10px;top:8px;pointer-events:none;font-size:11.5px;color:var(--dim);
|
||||
background:rgba(15,17,21,.72);border:1px solid var(--line);border-radius:6px;padding:6px 9px;max-width:60%}
|
||||
#hud b{color:var(--tx)}
|
||||
#hud:empty{display:none}
|
||||
#note{position:absolute;right:10px;bottom:10px;max-width:330px;background:rgba(15,17,21,.88);
|
||||
border:1px solid var(--ac);border-radius:7px;padding:8px 10px;font-size:12px;display:none;
|
||||
-webkit-user-select:text;user-select:text}
|
||||
.kv{display:grid;grid-template-columns:auto 1fr;gap:2px 8px;font-size:11.5px;-webkit-user-select:text;user-select:text}
|
||||
.kv i{color:var(--dim);font-style:normal;-webkit-user-select:none;user-select:none}
|
||||
.tab{padding:4px 9px;-webkit-user-select:none;user-select:none}
|
||||
.bad{color:#ff6b6b}.good{color:var(--ok)}
|
||||
svg{display:block;width:100%;height:auto;background:#fff;border-radius:6px}
|
||||
#sheet{display:none;padding:10px;overflow:auto;height:100%}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/* 右边缘可收起 BOM 抽屉 */
|
||||
/* ---- edge BOM dock (collapsible drawer at the right edge) ---- */
|
||||
#bomTab{position:fixed;right:0;top:64px;z-index:30;writing-mode:vertical-rl;text-orientation:mixed;
|
||||
background:var(--pan2);color:var(--tx);border:1px solid var(--line);border-right:none;
|
||||
border-radius:6px 0 0 6px;padding:12px 6px;font:inherit;cursor:pointer;letter-spacing:3px}
|
||||
#bomTab:hover{border-color:var(--ac);color:#fff}
|
||||
#bomTab b{color:var(--ac)}
|
||||
#bomDock{position:fixed;right:0;top:46px;bottom:26px;width:min(430px,94vw);z-index:29;
|
||||
transform:translateX(103%);transition:transform .22s ease;display:flex;flex-direction:column;
|
||||
background:var(--pan);border-left:1px solid var(--ac);box-shadow:-10px 0 28px rgba(0,0,0,.5)}
|
||||
#bomDock.open{transform:none}
|
||||
#bomDock .dk-hd{display:flex;align-items:center;gap:8px;padding:9px 10px;flex:none;
|
||||
border-bottom:1px solid var(--line)}
|
||||
#bomDock .dk-hd b{font-size:13px}
|
||||
#bomDock .dk-hd .q{color:var(--dim);font-size:11px;min-width:0;flex:0 1 auto;
|
||||
overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
#bomDock .dk-x{margin-left:auto}
|
||||
#bomDock .dk-bd{overflow:auto;padding:8px 11px 16px;flex:1;min-height:0}
|
||||
.dk-sec{margin:4px 0 5px;color:var(--ac);font-size:12px;letter-spacing:.4px}
|
||||
.dk-note{color:var(--dim);font-size:11px;margin:1px 0 6px}
|
||||
#screws td{white-space:normal}
|
||||
#screws td:first-child{white-space:nowrap}
|
||||
#screws tbody tr{cursor:pointer;transition:background .12s}
|
||||
#screws tbody tr:hover{background:rgba(76,154,255,.14)}
|
||||
#screws tbody tr.scrHl{background:#1d3f74;box-shadow:inset 3px 0 0 var(--warn)}
|
||||
.hudlink{pointer-events:auto;cursor:pointer;color:var(--ac);text-decoration:underline;margin-left:8px}
|
||||
@media (max-width:760px){#bomTab{top:auto;bottom:36px}#bomDock{top:46px;bottom:26px}}
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
/* 分步装配向导面板 */
|
||||
/* 分步装配向导面板样式 */
|
||||
#sheetGuideView {
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: var(--bg);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
z-index: 15;
|
||||
overflow: hidden;
|
||||
}
|
||||
.guide-header {
|
||||
padding: 8px 14px;
|
||||
background: var(--pan);
|
||||
border-bottom: 1px solid var(--line);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
flex: none;
|
||||
}
|
||||
.guide-steps-bar {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
overflow-x: auto;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
.guide-step-chip {
|
||||
background: var(--pan2);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 20px;
|
||||
padding: 4px 12px;
|
||||
font-size: 11.5px;
|
||||
color: var(--dim);
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
white-space: nowrap;
|
||||
transition: all .15s;
|
||||
}
|
||||
.guide-step-chip:hover {
|
||||
border-color: var(--warn);
|
||||
color: var(--tx);
|
||||
}
|
||||
.guide-step-chip.active {
|
||||
background: #78350f;
|
||||
border-color: #f59e0b;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
box-shadow: 0 0 10px rgba(245,158,11,.3);
|
||||
}
|
||||
.guide-body {
|
||||
display: grid;
|
||||
grid-template-columns: 360px 1fr;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
@media (max-width: 900px) {
|
||||
.guide-body {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: auto 1fr;
|
||||
}
|
||||
}
|
||||
.guide-panel-left {
|
||||
background: var(--pan);
|
||||
border-right: 1px solid var(--line);
|
||||
overflow-y: auto;
|
||||
padding: 14px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
.guide-panel-right {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: #0f1217;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.guide-card {
|
||||
background: var(--pan2);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
}
|
||||
.guide-card-title {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: var(--warn);
|
||||
margin-bottom: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
.guide-part-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
.guide-part-badge {
|
||||
background: rgba(255,255,255,.05);
|
||||
border: 1px solid rgba(255,255,255,.1);
|
||||
border-radius: 4px;
|
||||
padding: 3px 7px;
|
||||
font-size: 11px;
|
||||
color: var(--tx);
|
||||
cursor: pointer;
|
||||
transition: all .15s;
|
||||
}
|
||||
.guide-part-badge:hover {
|
||||
border-color: var(--ac);
|
||||
background: rgba(76,154,255,.15);
|
||||
color: #fff;
|
||||
}
|
||||
.guide-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-top: auto;
|
||||
padding-top: 10px;
|
||||
}
|
||||
.guide-btn {
|
||||
flex: 1;
|
||||
padding: 7px 0;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 5px;
|
||||
transition: all .15s;
|
||||
}
|
||||
.guide-btn-nav {
|
||||
background: var(--pan2);
|
||||
border: 1px solid var(--line);
|
||||
color: var(--tx);
|
||||
}
|
||||
.guide-btn-nav:hover {
|
||||
border-color: var(--ac);
|
||||
color: #fff;
|
||||
}
|
||||
.guide-btn-3d {
|
||||
background: linear-gradient(135deg, #b45309, #d97706);
|
||||
border: 1px solid #f59e0b;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
.guide-btn-3d:hover {
|
||||
background: linear-gradient(135deg, #d97706, #f59e0b);
|
||||
}
|
||||
small{color:var(--dim)}
|
||||
@@ -0,0 +1,67 @@
|
||||
/* IK 控制条与左栏结果面板 */
|
||||
/* ===================== IK 控制条(两行:目标+姿态 / 模式+状态) ===================== */
|
||||
#ikbar{grid-column:1/-1;display:flex;flex-direction:column;gap:4px;padding:5px 10px;
|
||||
background:linear-gradient(180deg,#1c212b,#161922);border-bottom:1px solid var(--line)}
|
||||
#ikbar .ikrow{display:flex;align-items:center;gap:6px;min-height:26px;
|
||||
overflow-x:auto;overflow-y:hidden;white-space:nowrap}
|
||||
#ikbar .ik-t{color:var(--ac);font-weight:700;letter-spacing:.8px;flex:none;font-size:11.5px;
|
||||
border:1px solid var(--ac);border-radius:4px;padding:1px 6px}
|
||||
#ikbar .lb{color:var(--dim);flex:none;font-size:12px}
|
||||
#ikbar input[type=number]{width:68px;background:#0d1015;color:var(--tx);border:1px solid var(--line);
|
||||
border-radius:4px;padding:3px 5px;font:inherit;font-variant-numeric:tabular-nums;flex:none}
|
||||
#ikbar input[type=number]:focus{outline:none;border-color:var(--ac)}
|
||||
#ikbar .vsep{width:1px;height:20px;background:var(--line);flex:none;margin:0 3px}
|
||||
#ikbar .st{color:var(--dim);flex:none;font-size:11.5px;font-variant-numeric:tabular-nums}
|
||||
#ikbar .sep{color:#3b4453;margin:0 5px}
|
||||
#ikbar .hint{color:var(--dim);font-size:11px;flex:none}
|
||||
#ikbar .good{color:var(--ok)} #ikbar .bad{color:#ff6b6b} #ikbar .hi{color:var(--warn)}
|
||||
#ikSolve{background:#1d3f74;border-color:var(--ac);color:#fff;font-weight:600}
|
||||
#ikSolve:hover{background:#24508f}
|
||||
#ikPose.on{background:#3a2b63;border-color:#a06bff;color:#fff;font-weight:600}
|
||||
#ikPoseBox{display:none;align-items:center;gap:6px}
|
||||
#ikPoseBox input[type=number]{width:54px}
|
||||
#ikMixBox{display:none;align-items:center;gap:6px;flex:none}
|
||||
#ikMixBox input[type=range]{width:118px;flex:none;accent-color:var(--ac);cursor:pointer}
|
||||
#ikMixBox .st{min-width:56px}
|
||||
|
||||
/* ===================== IK 结果面板(左栏顶部) ===================== */
|
||||
#ikpanel{margin:0 0 10px;border:1px solid var(--line);border-radius:6px;background:#141821;padding:7px 8px}
|
||||
/* 徽标必须可换行:CJK 的最小内容宽度=一个字,若徽标 nowrap+flex:none,
|
||||
长状态文案会把标题压成竖排一字一行(实测 h3 缩到 12px) */
|
||||
#ikpanel .ikh{display:flex;flex-wrap:wrap;align-items:center;gap:4px 6px;margin-bottom:5px}
|
||||
#ikpanel .ikh h3{margin:0;font-size:12px;color:var(--ac);letter-spacing:.3px;
|
||||
min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
.badge{margin-left:auto;font-size:10.5px;padding:1px 7px;border-radius:9px;border:1px solid;
|
||||
flex:0 1 auto;max-width:100%;text-align:center;line-height:1.35;text-wrap:balance}
|
||||
.badge.wait{color:var(--dim);border-color:var(--line)}
|
||||
.badge.goodb{color:#0c1a12;background:var(--ok);border-color:var(--ok)}
|
||||
.badge.warnb{color:#241a06;background:var(--warn);border-color:var(--warn)}
|
||||
.badge.badb{color:#2a0a0a;background:#ff6b6b;border-color:#ff6b6b}
|
||||
#ikTab{font-size:11px}
|
||||
#ikTab td.num{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
|
||||
#ikTab td.dim{color:var(--dim);white-space:nowrap}
|
||||
#ikpanel .kv{margin-top:6px}
|
||||
#ikpanel .kv i{white-space:nowrap}
|
||||
#ikpanel .kv span,#ikpanel .kv b{overflow-wrap:anywhere;word-break:break-word;min-width:0}
|
||||
#ikpanel .good{color:var(--ok)} #ikpanel .bad{color:#ff6b6b}
|
||||
#ikpanel .kv .hi{color:var(--warn);font-variant-numeric:tabular-nums}
|
||||
#ikAltN{font-size:10.5px;color:var(--dim);margin:7px 0 3px;line-height:1.45;white-space:normal}
|
||||
.altrow{display:flex;align-items:center;gap:5px;padding:2px 4px;border-radius:4px;cursor:pointer;
|
||||
font-size:11px;font-variant-numeric:tabular-nums;color:var(--dim)}
|
||||
.altrow:hover{background:rgba(76,154,255,.16)}
|
||||
.altrow .ai{color:#5b6478;width:10px;flex:none;text-align:right}
|
||||
.altrow b{color:var(--ac);width:54px;flex:none;text-align:right;font-weight:500}
|
||||
.altrow .aq{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
#ikpanel details{margin-top:8px;border-top:1px solid var(--line);padding-top:6px}
|
||||
#ikpanel details>summary{cursor:pointer;color:var(--ac);font-size:11.5px;outline:none}
|
||||
#ikpanel details .doc{margin-top:5px;font-size:11px;color:var(--dim);line-height:1.7;white-space:normal;
|
||||
-webkit-user-select:text;user-select:text}
|
||||
#ikpanel details .doc b{color:var(--tx)}
|
||||
#ikpanel details .doc code{color:var(--warn);font-family:Consolas,"Courier New",monospace;font-size:10.5px}
|
||||
#ikpanel details .doc ol{margin:3px 0 3px 16px;padding:0}
|
||||
#ikpanel details .doc li{margin:3px 0}
|
||||
#ikpanel details .doc hr{border:none;border-top:1px solid var(--line);margin:6px 0}
|
||||
#ikpanel .src{font-size:10.5px;color:var(--dim);margin-top:5px;white-space:normal;line-height:1.5}
|
||||
#ikChkBox{margin-top:7px}
|
||||
#ikChkBox summary{margin-bottom:2px}
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
/* 装配图工具栏、页签、主视口与画布 */
|
||||
/* ==================== 交互式装配图查看器 (Interactive Assembly Drawing) ==================== */
|
||||
#sheet {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 0; left: 0; width: 100%; height: 100%;
|
||||
flex-direction: column;
|
||||
background: #11141a;
|
||||
overflow: hidden;
|
||||
z-index: 10;
|
||||
padding: 0;
|
||||
}
|
||||
body.sheet-mode #sheet {
|
||||
display: flex !important;
|
||||
}
|
||||
/* 隐藏 3D 专属工具条按钮(当处于装配图时) */
|
||||
body.sheet-mode #bAsm,
|
||||
body.sheet-mode #bExp,
|
||||
body.sheet-mode #exp,
|
||||
body.sheet-mode #cam,
|
||||
body.sheet-mode #bWire,
|
||||
body.sheet-mode #bXRay,
|
||||
body.sheet-mode #bScr,
|
||||
body.sheet-mode #bHarn,
|
||||
body.sheet-mode #bEnv,
|
||||
body.sheet-mode #bFit {
|
||||
display: none !important;
|
||||
}
|
||||
body.sheet-mode footer #footModel { display: none !important; }
|
||||
body.sheet-mode footer #footSheet { display: inline !important; }
|
||||
|
||||
/* 顶部装配图工具栏 */
|
||||
#sheetToolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 12px;
|
||||
background: var(--pan);
|
||||
border-bottom: 1px solid var(--line);
|
||||
flex: none;
|
||||
min-height: 40px;
|
||||
overflow-x: auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.sheet-tabs {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
align-items: center;
|
||||
}
|
||||
.sheet-tab {
|
||||
background: var(--pan2);
|
||||
color: var(--tx);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 5px;
|
||||
padding: 4px 10px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
transition: all .15s ease;
|
||||
}
|
||||
.sheet-tab:hover {
|
||||
border-color: var(--ac);
|
||||
color: #fff;
|
||||
}
|
||||
.sheet-tab.on {
|
||||
background: #1e3a66;
|
||||
border-color: var(--ac);
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 0 10px rgba(76,154,255,.25);
|
||||
}
|
||||
.sheet-tab.guide-tab {
|
||||
background: rgba(245, 158, 11, 0.12);
|
||||
border-color: rgba(245, 158, 11, 0.4);
|
||||
color: #f59e0b;
|
||||
}
|
||||
.sheet-tab.guide-tab.on {
|
||||
background: #78350f;
|
||||
border-color: #f59e0b;
|
||||
color: #fef3c7;
|
||||
box-shadow: 0 0 10px rgba(245, 158, 11, 0.35);
|
||||
}
|
||||
|
||||
.sheet-vsep {
|
||||
width: 1px;
|
||||
height: 20px;
|
||||
background: var(--line);
|
||||
margin: 0 4px;
|
||||
flex: none;
|
||||
}
|
||||
.sheet-btn-group {
|
||||
display: flex;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
flex: none;
|
||||
}
|
||||
.sheet-btn-group .sheet-btn {
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
padding: 3px 8px;
|
||||
font-size: 11.5px;
|
||||
background: var(--pan2);
|
||||
border-right: 1px solid var(--line);
|
||||
cursor: pointer;
|
||||
}
|
||||
.sheet-btn-group .sheet-btn:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
.sheet-btn-group .sheet-btn.on {
|
||||
background: var(--ac);
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sheet-layers {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
font-size: 11.5px;
|
||||
color: var(--dim);
|
||||
}
|
||||
.sheet-layers label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.sheet-layers input[type=checkbox] {
|
||||
accent-color: var(--ac);
|
||||
cursor: pointer;
|
||||
}
|
||||
.sheet-space {
|
||||
flex: 1;
|
||||
min-width: 8px;
|
||||
}
|
||||
.sheet-btn-accent {
|
||||
background: linear-gradient(135deg, #1d4ed8, #2563eb);
|
||||
color: #fff;
|
||||
border: 1px solid #60a5fa;
|
||||
font-weight: 500;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
padding: 4px 10px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
transition: all .15s;
|
||||
}
|
||||
.sheet-btn-accent:hover {
|
||||
background: linear-gradient(135deg, #2563eb, #3b82f6);
|
||||
box-shadow: 0 0 12px rgba(96,165,250,.4);
|
||||
}
|
||||
|
||||
/* 主视口区域 */
|
||||
#sheetViewport {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
cursor: grab;
|
||||
background: #0f1217;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
#sheetViewport.is-dragging {
|
||||
cursor: grabbing !important;
|
||||
}
|
||||
|
||||
/* 画布与缩放平移 */
|
||||
#sheetCanvas {
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
transform-origin: 0 0;
|
||||
will-change: transform;
|
||||
}
|
||||
#sheetSvgWrapper {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,.5);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
transition: box-shadow 0.2s;
|
||||
}
|
||||
#sheetSvgWrapper svg {
|
||||
display: block;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/* 悬停 Tooltip 与 SVG 零件高亮 */
|
||||
/* 悬停 Tooltip HUD */
|
||||
.sheet-tooltip {
|
||||
position: absolute;
|
||||
z-index: 99;
|
||||
background: rgba(15, 17, 21, 0.94);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid var(--ac);
|
||||
border-radius: 7px;
|
||||
padding: 8px 12px;
|
||||
font-size: 12px;
|
||||
color: var(--tx);
|
||||
box-shadow: 0 10px 28px rgba(0,0,0,.6);
|
||||
pointer-events: none;
|
||||
display: none;
|
||||
max-width: 310px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.sheet-tooltip.pinned {
|
||||
pointer-events: auto;
|
||||
border-color: #38bdf8;
|
||||
box-shadow: 0 12px 36px rgba(0, 168, 255, 0.35);
|
||||
}
|
||||
.sheet-tooltip .st-title {
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
margin-bottom: 4px;
|
||||
padding-bottom: 4px;
|
||||
border-bottom: 1px solid rgba(255,255,255,.1);
|
||||
}
|
||||
.sheet-tooltip .st-tag {
|
||||
font-size: 10px;
|
||||
padding: 1px 5px;
|
||||
border-radius: 3px;
|
||||
font-weight: normal;
|
||||
}
|
||||
.st-tag.pr { background: rgba(61,220,151,.2); color: var(--ok); }
|
||||
.st-tag.ex { background: rgba(255,180,84,.2); color: var(--warn); }
|
||||
.sheet-tooltip .st-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
font-size: 11px;
|
||||
color: var(--dim);
|
||||
margin: 2px 0;
|
||||
}
|
||||
.sheet-tooltip .st-row span:last-child {
|
||||
color: var(--tx);
|
||||
text-align: right;
|
||||
}
|
||||
.sheet-tooltip .st-btn {
|
||||
margin-top: 6px;
|
||||
width: 100%;
|
||||
padding: 4px 0;
|
||||
font-size: 11px;
|
||||
background: #1d4ed8;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
transition: background .15s;
|
||||
}
|
||||
.sheet-tooltip .st-btn:hover {
|
||||
background: #2563eb;
|
||||
}
|
||||
|
||||
/* SVG 交互高亮样式 */
|
||||
.sheet-part-path {
|
||||
cursor: pointer;
|
||||
transition: stroke .12s, stroke-width .12s, filter .12s;
|
||||
}
|
||||
.sheet-part-path.is-hovered {
|
||||
stroke: #00d2ff !important;
|
||||
stroke-width: 2.8px !important;
|
||||
fill: rgba(0, 210, 255, 0.16) !important;
|
||||
filter: drop-shadow(0 0 5px #00a8ff) !important;
|
||||
}
|
||||
.sheet-balloon {
|
||||
cursor: pointer;
|
||||
transition: transform .15s ease;
|
||||
}
|
||||
.sheet-balloon:hover .balloon-circle,
|
||||
.sheet-balloon.is-hovered .balloon-circle {
|
||||
fill: #0284c7 !important;
|
||||
stroke: #ffffff !important;
|
||||
stroke-width: 2px !important;
|
||||
filter: drop-shadow(0 0 6px rgba(2,132,199,.8)) !important;
|
||||
}
|
||||
.sheet-balloon:hover .balloon-text,
|
||||
.sheet-balloon.is-hovered .balloon-text {
|
||||
fill: #ffffff !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
.sheet-leader.is-hovered {
|
||||
stroke: #00d2ff !important;
|
||||
stroke-width: 1.8px !important;
|
||||
}
|
||||
.sheet-legend-row {
|
||||
cursor: pointer;
|
||||
}
|
||||
.sheet-legend-row:hover .legend-row-bg,
|
||||
.sheet-legend-row.is-hovered .legend-row-bg {
|
||||
fill: rgba(2, 132, 199, 0.3) !important;
|
||||
}
|
||||
.sheet-legend-row:hover text,
|
||||
.sheet-legend-row.is-hovered text {
|
||||
fill: #00d2ff !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
.joint-geom-part {
|
||||
cursor: pointer;
|
||||
transition: all .15s;
|
||||
}
|
||||
.joint-geom-part:hover, .joint-geom-part.is-hovered {
|
||||
stroke: #00d2ff !important;
|
||||
stroke-width: 2.2px !important;
|
||||
fill: rgba(0, 210, 255, 0.15) !important;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
/* 图纸主题(白图/蓝图/深色)、浮动工具条、缩放徽标、信息条 */
|
||||
/* 视觉风格主题 */
|
||||
#sheet.theme-paper #sheetViewport {
|
||||
background: #cbd5e1;
|
||||
}
|
||||
#sheet.theme-paper #sheetSvgWrapper {
|
||||
box-shadow: 0 10px 40px rgba(0,0,0,.35);
|
||||
}
|
||||
|
||||
#sheet.theme-blueprint #sheetViewport {
|
||||
background: #06182c;
|
||||
background-image:
|
||||
linear-gradient(rgba(14, 116, 144, 0.15) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(14, 116, 144, 0.15) 1px, transparent 1px);
|
||||
background-size: 24px 24px;
|
||||
}
|
||||
#sheet.theme-blueprint #sheetSvgWrapper {
|
||||
box-shadow: 0 0 40px rgba(14, 165, 233, 0.25);
|
||||
border: 1px solid rgba(56, 189, 248, 0.3);
|
||||
}
|
||||
#sheet.theme-blueprint .sheet-bg {
|
||||
fill: #0a2240 !important;
|
||||
}
|
||||
#sheet.theme-blueprint .sheet-border-outer,
|
||||
#sheet.theme-blueprint .sheet-border-inner,
|
||||
#sheet.theme-blueprint .sheet-title-line,
|
||||
#sheet.theme-blueprint .sheet-meta-line {
|
||||
stroke: #38bdf8 !important;
|
||||
}
|
||||
#sheet.theme-blueprint .sheet-title-text {
|
||||
fill: #f0f9ff !important;
|
||||
}
|
||||
#sheet.theme-blueprint .sheet-note-text,
|
||||
#sheet.theme-blueprint .sheet-scale-text,
|
||||
#sheet.theme-blueprint .sheet-ver-text {
|
||||
fill: #bae6fd !important;
|
||||
}
|
||||
#sheet.theme-blueprint .sheet-axis,
|
||||
#sheet.theme-blueprint .sheet-axis-label,
|
||||
#sheet.theme-blueprint .sheet-dim-line,
|
||||
#sheet.theme-blueprint .sheet-dim-ext,
|
||||
#sheet.theme-blueprint .sheet-dim-text {
|
||||
stroke: #7dd3fc !important;
|
||||
fill: #7dd3fc !important;
|
||||
}
|
||||
|
||||
#sheet.theme-dark #sheetViewport {
|
||||
background: #090b0e;
|
||||
}
|
||||
#sheet.theme-dark .sheet-bg {
|
||||
fill: #14171d !important;
|
||||
}
|
||||
#sheet.theme-dark .sheet-border-outer,
|
||||
#sheet.theme-dark .sheet-border-inner,
|
||||
#sheet.theme-dark .sheet-title-line,
|
||||
#sheet.theme-dark .sheet-meta-line {
|
||||
stroke: #374151 !important;
|
||||
}
|
||||
#sheet.theme-dark .sheet-title-text {
|
||||
fill: #f3f4f6 !important;
|
||||
}
|
||||
#sheet.theme-dark .sheet-note-text,
|
||||
#sheet.theme-dark .sheet-scale-text,
|
||||
#sheet.theme-dark .sheet-ver-text {
|
||||
fill: #9ca3af !important;
|
||||
}
|
||||
|
||||
/* 浮动工具条 */
|
||||
#sheetFloatBar {
|
||||
position: absolute;
|
||||
right: 14px;
|
||||
top: 14px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
z-index: 25;
|
||||
}
|
||||
.sfb-btn {
|
||||
background: rgba(23, 26, 33, 0.88);
|
||||
backdrop-filter: blur(8px);
|
||||
color: var(--tx);
|
||||
border: 1px solid rgba(255,255,255,.12);
|
||||
border-radius: 6px;
|
||||
padding: 6px 10px;
|
||||
font-size: 11.5px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,.35);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 36px;
|
||||
transition: all .15s ease;
|
||||
}
|
||||
.sfb-btn:hover {
|
||||
background: #1e3a66;
|
||||
border-color: var(--ac);
|
||||
color: #fff;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* 缩放指示徽标 */
|
||||
#sheetZoomBadge {
|
||||
position: absolute;
|
||||
right: 14px;
|
||||
bottom: 12px;
|
||||
background: rgba(15, 17, 21, 0.82);
|
||||
backdrop-filter: blur(6px);
|
||||
border: 1px solid var(--line);
|
||||
color: var(--dim);
|
||||
font-size: 11px;
|
||||
padding: 3px 8px;
|
||||
border-radius: 4px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
cursor: pointer;
|
||||
z-index: 20;
|
||||
}
|
||||
#sheetZoomBadge:hover {
|
||||
color: var(--ac);
|
||||
border-color: var(--ac);
|
||||
}
|
||||
|
||||
/* 信息条 */
|
||||
#sheetInfoBar {
|
||||
position: absolute;
|
||||
left: 14px;
|
||||
bottom: 12px;
|
||||
max-width: 70%;
|
||||
background: rgba(15, 17, 21, 0.85);
|
||||
backdrop-filter: blur(6px);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 5px;
|
||||
padding: 4px 10px;
|
||||
font-size: 11.5px;
|
||||
color: var(--dim);
|
||||
z-index: 20;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
#sheetInfoBar b {
|
||||
color: var(--tx);
|
||||
}
|
||||
.sheet-info-sep {
|
||||
color: var(--line);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/* 视图模式切换时的布局与显隐规则 */
|
||||
/* ============ 视图模式:三维模型(静态) / 装配图 / 运动模拟 ============ */
|
||||
/* 三维模型 与 装配图 下,全部运动学 UI 一律隐藏 */
|
||||
body:not(.motion) #ikbar,
|
||||
body:not(.motion) #ikpanel,
|
||||
body:not(.motion) #joints,
|
||||
body:not(.motion) #jointsHead,
|
||||
body:not(.motion) #footIk{display:none!important}
|
||||
/* 运动模拟只看运动:左栏去掉零件树与打印清单(其它模式保留) */
|
||||
body.motion #treeBlock{display:none!important}
|
||||
|
||||
/* 运动模拟左栏紧凑化:目标一屏放下、不下拉(实测原需下拉 127px) */
|
||||
#ikpanel{margin:0 0 6px;padding:5px 8px}
|
||||
#ikpanel .ikh{margin-bottom:3px}
|
||||
#ikpanel #ikTab th,#ikpanel #ikTab td{padding:1px 4px}
|
||||
#ikAltN{margin:5px 0 2px}
|
||||
.altrow{padding:1px 4px}
|
||||
#joints .j-box{margin-bottom:4px}
|
||||
#joints input[type=range]{height:15px}
|
||||
/* 显式行定位:IK 控制条在「三维模型/装配图」下被移除后,
|
||||
若不固定行号,网格自动排布会把页脚挤到 1fr 行并压扁(已实测) */
|
||||
#app>header{grid-row:1}
|
||||
#ikbar{grid-row:2}
|
||||
#left{grid-row:3}
|
||||
#stage{grid-row:3}
|
||||
#app>footer{grid-row:4}
|
||||
/* BOM 抽屉:运动模拟多一条两行 IK 控制条(实测 75px),需整体下移 */
|
||||
#bomDock{top:46px} #bomTab{top:64px}
|
||||
body.motion #bomDock{top:124px} body.motion #bomTab{top:140px}
|
||||
@media (max-width:760px){
|
||||
#bomTab,body.motion #bomTab{top:auto;bottom:36px}
|
||||
#bomDock,body.motion #bomDock{top:86px;bottom:26px}
|
||||
}
|
||||
Reference in New Issue
Block a user