v2.1: DESTACO 风格选型界面 + 参数组合补全(缺组合也能出零件号)

网页版选型改版 (仅网页版, 桌面版待同步)
- 新增零件号实时条: 边选边拼, 已确定片段实心高亮, 未定片段虚化, 进度条随选推进;
  浅色配色 (初版深色块与页面不协调, 已改); 选满且唯一命中时整块转青色实心
- 技术方案: 40 系列中仅 KC 有编码模板 → 改用「当前候选型号的最长公共前缀」,
  已验证 40/40 系列前缀单调增长(不回退)
- 参数选择由下拉菜单改为可视化选项块: 悬停抬升+青色投影, 选中青色填充+✓,
  不可选取值置灰(虚线+划线, 不消失, 让用户看到选项存在但当前组合不适用),
  再点一次取消; 有说明的显示代号+说明, 纯数值用紧凑文字块
- 参数联动仍复用 allowedOptions 规则引擎, 语义未变, 只改渲染

参数组合补全
- 原逻辑: 必须有真实数模才能生成型号 → 库里没有的组合选不出来也出不了号
- infer-sku-template.py 从已有型号反推编码模板, 得 23 个系列 100% 可复现
- verify-templates.py 作为门禁: 用模板重渲染每个已有型号必须逐字一致 ——
  该检查抓出 ULM/ULP 行程补零位数写错(2位写3位), 否则会生成 3000 个不存在的零件号
- gen-missing-combos.py 补全 8 个系列共 3157 个型号 (全库 54136 → 57293)
- 占位数模: 3157 个型号共用 1 个 PLACEHOLDER.step(4.5KB), 构建器按路径打包只存一份,
  数据包仅 2.10GB → 2.14GB (中途用硬链接方案踩坑: NTFS 上限 1024, 超出退化为拷贝白占 500MB)
- _pending_models.csv 输出待补数模清单, 供 AI 补完后替换 step_file

新增脚本: infer-sku-template / verify-templates / quantify-combo-gap /
          gen-missing-combos / gen-placeholder-step / check-combo-unique
新增文档: docs/更改说明-v2.1.md

发现未处理 (已记录在更改说明):
- 夹紧缸 9 系列一组参数对应多型号 (310 个型号, 编码含参数外尾缀, 旧码/新码混用)
- UAGP/LAGP 参数顺序与官方编码结构不符 (传感器应在转角之前)
- 17 个系列因模板不干净未补全 (宁可少补, 不生成可能不存在的零件号)

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
wangruiguo
2026-09-15 02:14:55 +08:00
parent 3c4f236cba
commit fc5ee1dd36
19 changed files with 7355 additions and 25 deletions

View File

@@ -245,8 +245,17 @@ main { flex: 1; display: flex; min-height: 0; }
font-size: 16px; font-weight: 700; margin-bottom: 12px;
color: var(--gray-900); letter-spacing: -.3px; line-height: 1.4;
}
.param-row { margin-bottom: 11px; }
.param-row label { display: block; font-size: 12px; color: var(--text-dim); margin-bottom: 4px; }
.param-row { margin-bottom: 14px; }
.param-row label {
display: flex; align-items: baseline; gap: 7px;
font-size: 11px; font-weight: 700; color: var(--text-faint);
text-transform: uppercase; letter-spacing: .5px; margin-bottom: 7px;
}
/* 维度已选值回显在其后 */
.param-row label .picked {
text-transform: none; letter-spacing: 0; font-weight: 600;
color: var(--primary-700); font-size: 11.5px;
}
.param-row select, .param-input {
width: 100%; height: 36px; padding: 0 10px; font-size: 13px;
border: 1px solid var(--border); border-radius: var(--radius-sm);
@@ -258,6 +267,104 @@ main { flex: 1; display: flex; min-height: 0; }
}
#actions { margin-top: 16px; display: flex; gap: 8px; flex-wrap: wrap; }
/* ============================================================ 零件号实时条 (DESTACO 风)
* 边选边拼: 已确定部分实心高亮, 未定部分虚化占位。 */
/* ============================================================ 零件号实时条
* 边选边拼: 已确定片段的实心高亮, 未定片段虚化占位。
* 配色走浅色主调 (与整体卡片风格一致), 不用深色块。 */
.sku-bar {
background: linear-gradient(135deg, var(--primary-50), #ffffff 70%);
border: 1px solid var(--primary-200);
border-radius: var(--radius); padding: 13px 15px; margin-bottom: 14px;
box-shadow: var(--shadow-card);
}
.sku-bar .sku-label {
font-size: 10px; text-transform: uppercase; letter-spacing: .7px;
color: var(--text-faint); margin-bottom: 5px;
}
.sku-bar .sku-code {
font-family: ui-monospace, Consolas, monospace;
font-size: 20px; font-weight: 700; letter-spacing: -.4px;
word-break: break-all; line-height: 1.25; min-height: 25px;
}
.sku-bar .sku-fixed { color: var(--primary-700); } /* 已确定片段 */
.sku-bar .sku-tbd { color: var(--gray-300); } /* 待定占位 */
.sku-bar .sku-prog {
margin-top: 9px; height: 3px; border-radius: 999px;
background: var(--primary-100); overflow: hidden;
}
.sku-bar .sku-prog > i {
display: block; height: 100%; width: 0;
background: linear-gradient(90deg, var(--primary-400), var(--primary-600));
border-radius: 999px; transition: width .3s cubic-bezier(.2,.8,.3,1);
}
/* 零件号完整: 整块转为实心青色, 作为"完成"的强反馈 */
.sku-bar.done {
background: linear-gradient(135deg, var(--primary-500), var(--primary-600));
border-color: transparent;
box-shadow: var(--shadow-btn);
}
.sku-bar.done .sku-label { color: rgba(255,255,255,.72); }
.sku-bar.done .sku-fixed { color: #fff; }
.sku-bar.done .sku-tbd { color: rgba(255,255,255,.5); }
.sku-bar.done .sku-prog { background: rgba(255,255,255,.28); }
.sku-bar.done .sku-prog > i { background: #fff; }
.sku-bar.done .sku-hint { color: rgba(255,255,255,.85); }
.sku-bar .sku-hint {
margin-top: 8px; font-size: 11px; color: var(--text-dim);
}
/* ============================================================ 选项块 (替代下拉)
* 有图用卡片, 无图用文字块; 一行多个, 自动换行。 */
.opt-grid {
display: grid; gap: 7px;
grid-template-columns: repeat(auto-fill, minmax(72px, 1fr));
}
.opt-grid.wide { grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); }
.opt {
position: relative; cursor: pointer; user-select: none;
border: 1.5px solid var(--border); border-radius: var(--radius-sm);
background: var(--surface); padding: 8px 9px;
display: flex; flex-direction: column; align-items: center; gap: 3px;
min-height: 46px; justify-content: center;
transition: transform .18s cubic-bezier(.2,.8,.3,1),
border-color .18s, background .18s, box-shadow .18s;
}
.opt:hover {
transform: translateY(-2px); border-color: var(--primary-400);
box-shadow: 0 6px 16px rgba(20,184,166,.18);
}
.opt .opt-k {
font-family: ui-monospace, Consolas, monospace;
font-size: 13px; font-weight: 700; color: var(--gray-900);
line-height: 1.2; text-align: center; word-break: break-word;
}
.opt .opt-d {
font-size: 10px; color: var(--text-faint); text-align: center;
line-height: 1.25; word-break: break-word;
}
/* 选中态: 青色填充 */
.opt.on {
border-color: var(--primary-500);
background: var(--primary-50);
box-shadow: 0 0 0 2px rgba(20,184,166,.22);
}
.opt.on .opt-k { color: var(--primary-700); }
.opt.on::after {
content: '✓'; position: absolute; top: 3px; right: 5px;
font-size: 10px; color: var(--primary-600); font-weight: 700;
}
/* 不可选 (被联动规则排除): 置灰 + 划线 + 不可点 */
.opt.off {
opacity: .32; cursor: not-allowed; background: var(--gray-50);
border-style: dashed;
}
.opt.off .opt-k { text-decoration: line-through; }
.opt.off:hover { transform: none; box-shadow: none; border-color: var(--border); }
/* 无图时的纯文字块: 更紧凑 */
.opt.txt { min-height: 38px; padding: 7px 8px; }
.opt.txt .opt-k { font-size: 12.5px; }
/* ------------------------------------------------------------ 按钮 */
.btn {
display: inline-flex; align-items: center; justify-content: center;