fix(w05): 数据工单三病一改 —— 档案卡上桌、类型守卫、prune 跳过已落地

2026-08-31 用户实测三连 + 「设计只是头上加了动画,操作感没变」:

① 类型「自己给上了」:data-by 行缺键落回外层同名字段(debt I3),查名 pill 摆出
  案卷锚(原 PN)的类型 —— 这一行根本没有的值。守卫认 selectCurrent 按行作用域写的
  option[selected]:真选上才摆名,没选摆「待选择」。守卫必须单层 :has ——
  :has() 里嵌 :has() 是非法选择器,整条被浏览器静默丢弃(第一版栽过,预览截图逮回)。

② 「品牌…点保存根本不会保存」:块① 行源是 sub_eval.part,那张案卷表没有主数据列,
  renderTemplate 只认行作用域 —— 保存真落草稿,显示与孪生件永远空白(四步重写后的
  潜伏缺陷:e2e 走 API、浏览器走查在 swap 版上,两条验证线都照不到,真人一填才现形)。
  行源改 mine:parts.profile 本身(S1 ④ prune:true 恒有两行),行里天生有全部列。

③ 「改回同一类型,下面还是两个类型」:②③ 自投影为绕「已落地行撤不掉」的死锁
  (2026-08-30 e2e 案例 2)特意不带 prune,没落地的陈旧行也跟着积在屏上。
  治在平台:draftDrop 加配方专用旗 skipLanded(跳过并出声「N 行已随数据工单入库、
  这一笔不清它」,不炸;模型那一路的硬拒一个字不变),②③ 恢复 prune:true ——
  死锁与积行两头都收,debt「prune 与确认即入库的架构性冲突」销账。
  quiet 拍仍不撤行(F3),清理落在每次确认工单那一拍。

④ 设计:块① 从 11 列横滑表推翻成电路板工作台 —— 深色板底上两张可就地编辑的
  芯片档案卡(SOIC 封装 SVG + 角色/在库徽 + 类型行 + 双栏主数据格),编辑态整卡
  变表单;三级占位(必填红/建议橘)上卡与参数值表;参数值表所属料号列加角色徽。
  嵌套的角色/在库查表全只读(嵌套行里有 data-col 会被 collect 并成一条,测钉死)。

契约:loop-engine port draftDrop +skipLanded/+landed(纯新增,CONTRACT.md 已记,
api-report 已刷)。测:107 包内(三锚变异验证)+ auto-runner skipLanded(变异验证)
+ 2520 全仓;2.7.0 装库 SQL 核过(档案卡宿主/守卫/胶囊/S1 四笔 prune)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
This commit is contained in:
xcs
2026-08-31 14:12:10 +08:00
co-authored by Claude Fable 5
parent e2db7cfa01
commit 5a7bb9b58f
14 changed files with 280 additions and 103 deletions
@@ -246,9 +246,61 @@ table.on tr.jc-row-static td{background:var(--acc-wash);border-top:1px dashed va
*{animation:none !important;transition:none !important}
}
/* ================= 电路板工作台(件① 的两张档案卡)================= */
.board{position:relative;margin:0 0 6px;padding:20px 16px 18px;border-radius:16px;
background:
repeating-linear-gradient(0deg, rgba(255,255,255,.03) 0 1px, transparent 1px 26px),
repeating-linear-gradient(90deg, rgba(255,255,255,.03) 0 1px, transparent 1px 26px),
radial-gradient(700px 260px at 16% -30%, var(--hero-tint), transparent 62%),
linear-gradient(158deg, #13212f 0%, var(--board) 58%, #0f1d2b 100%)}
.board::before{content:"";position:absolute;left:4%;right:4%;top:50%;border-top:2px dashed var(--glow-line)}
.board::after{content:"";position:absolute;top:50%;left:4%;width:9px;height:9px;margin-top:-4px;border-radius:999px;background:var(--glow);box-shadow:0 0 14px 3px var(--glow-line),0 0 4px 1px var(--glow);animation:sig 3.4s linear infinite}
.dossiers{position:relative;z-index:1;display:grid;grid-template-columns:repeat(auto-fit,minmax(272px,1fr));gap:16px}
.dossier{display:flex;flex-direction:column;gap:11px;background:rgba(255,255,255,.97);border-radius:16px;padding:15px 16px 14px;box-shadow:0 14px 30px rgba(3,10,18,.5),inset 0 1px 0 rgba(255,255,255,.7);transition:box-shadow .18s,transform .18s}
.dossier:hover{transform:translateY(-2px);box-shadow:0 18px 36px rgba(3,10,18,.55),inset 0 1px 0 rgba(255,255,255,.7)}
.dz-top{display:flex;align-items:center;gap:12px}
.dz-top .csvg{width:64px;flex:none}
.dz-id{display:flex;flex-direction:column;gap:4px;min-width:0}
.dz-role{display:flex;gap:6px;flex-wrap:wrap}
.dz-pn{font-size:15px;font-weight:800;overflow-wrap:anywhere;line-height:1.3}
.dz-pn .jc-vcell{font-size:15px;font-weight:800;color:var(--ink)}
.dz-type{display:flex;align-items:center;gap:9px;flex-wrap:wrap;padding:8px 10px;border-radius:10px;background:var(--acc-wash);border:1px solid var(--acc-line)}
.dz-lb{flex:none;font-style:normal;font-size:10.5px;font-weight:800;letter-spacing:.05em;color:var(--ink-4)}
.dz-type .jc-cell{flex:1 1 140px;width:auto}
/* 类型名守卫:这一行的下拉真有选中项(selectCurrent 按行作用域写的 option[selected])才摆名 ——
行里没选,data-by 落回外层的名一个字都不摆,换「待选择」胶囊。
守卫挂在共同祖先 .dz-type 上、单层 :has —— :has() 里再嵌 :has() 是非法选择器,整条会被浏览器
静默丢弃(第一版就栽在这:守卫写了等于没写,XL 那张卡照样摆出它没有的类型)。 */
.tsel{flex:1 1 auto;min-width:0}
.tsel > span > i{display:inline-block;font-size:11.5px;font-weight:800;color:var(--acc-deep);background:var(--acc-soft);border-radius:999px;padding:1px 11px}
.dz-type:not(:has(option[selected])) .tsel > span{display:none}
.dz-type:not(:has(option[selected])) .tsel::after{content:"待选择 · 点「编辑」挑一个";display:inline-block;font-size:11px;font-weight:700;color:var(--amber);background:var(--amber-soft);border:1px dashed #eeda9e;border-radius:999px;padding:1px 10px}
.dz-grid{display:grid;grid-template-columns:1fr 1fr;gap:9px 12px}
.fcell{display:flex;flex-direction:column;gap:3px;min-width:0}
.fcell.wide{grid-column:1 / -1}
.fcell > i{font-style:normal;font-size:10.5px;font-weight:800;letter-spacing:.05em;color:var(--ink-4)}
.fcell.req > i::after{content:" *";color:var(--danger)}
.fcell .jc-vcell{font-size:12.5px}
/* 三级占位:必填红 / 建议橘(空格子直说要不要填,不摆一枚含糊的破折号) */
.fcell.req .jc-vcell:empty::after{content:"必填";font-size:10.5px;font-weight:800;color:var(--danger);background:var(--danger-soft);border-radius:999px;padding:0 9px}
/* 徽标包装的必填格(如生命周期):没值时徽标 class 恰为裸 "stx"(class-of 没投出 v-*),
wrapper 不为 :empty,上一条够不着 —— 认裸 class 补同一枚胶囊(单层 :has) */
.fcell.req .jc-vcell:has(> [class="stx"])::after{content:"必填";font-size:10.5px;font-weight:800;color:var(--danger);background:var(--danger-soft);border-radius:999px;padding:0 9px}
.fcell.sug .jc-vcell:empty::after{content:"建议";font-size:10.5px;font-weight:800;color:var(--amber);background:var(--amber-soft);border-radius:999px;padding:0 9px}
/* 档案卡的编辑态(非表格容器,.on 落在 data-edit 元素自身) */
.dossiers.on .jc-vcell{display:none}
.dossiers.on .jc-vcell.ro{display:block}
.dossiers.on .jc-cell{display:block}
.dossiers.on .dz-role{display:flex}
/* 参数值表:所属料号列的角色徽 + 值/说明列的三级占位 */
.pkrole{margin-bottom:2px}
.jc-tbl-wrap td.pv .jc-vcell:empty::after{content:"必填";font-size:10.5px;font-weight:800;color:var(--danger);background:var(--danger-soft);border-radius:999px;padding:0 9px}
.jc-tbl-wrap td.sg .jc-vcell:empty::after{content:"建议";font-size:10.5px;font-weight:800;color:var(--amber);background:var(--amber-soft);border-radius:999px;padding:0 9px}
/* ================= 窄屏 ================= */
@media(max-width:620px){
.card-head{padding:16px 14px 18px}
.dz-grid{grid-template-columns:1fr}
.card-bd{padding:16px 14px 6px}
.hero-title{font-size:19.5px}
.stage{gap:14px}