fix(w05): 走查逮到的三处 —— 漏掉的 profile 闸 · 圆点投影搬到行外包裹层 · 类型查名加诚实守卫

① profile 的编辑闸与缺项红带在第一版 body 里整个漏掉:宿主在而闸不在,轨道打不开编辑,
XL 的必填永远补不上(Task 7 那颗「按不下去的确认钮」同款)。「四道闸」旧断言只查
data-edit 宿主没查 [data-edit-for] 闸,逮不到 —— 断言补强为闸 + 开钮都在。

② 行内的 data-class-of 只认行作用域(renderRows 末段 renderTemplate(…, one) 拿的是
行对象),作用域级读数在行内投不出 —— 圆点上的 data-class-of 是死属性,永远不亮。
投影搬到行外的三层 .pj 包裹层,样式表按后代选择器点亮;代价是两条轨道的圆点一起亮
(基座数本来就是表级的,分到每颗料头上的数基座没有,不编)。

③ data-by 在行缺键时落回外层同名字段(终审 I3 已记档的平台缺陷):XL 没选类型、
它的 sub_eval.part 行与 parts.profile 草稿行都没有 chip_type_id 键,查名 pill 借外层
摊平的 chip_type_id 摆出「DC-DC 降压稳压器」—— 档案卡与字段行都在替人表态。
守卫认 selectCurrent 按这一行自己的值写下的 option[selected]:行里没真选上,
名字不摆、摆「(未选择)」;判断全在样式表。

三条新钉各做变异(恰红一条、复原绿);pnpm gates 退出码 0(2497 测)。2.2.1 -> 2.2.2。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
This commit is contained in:
xcs
2026-08-30 20:51:04 +08:00
co-authored by Claude Opus 5
parent 64fe4b43c9
commit 18de7653bd
3 changed files with 43 additions and 13 deletions
@@ -620,9 +620,11 @@ describe('件① 评估输入补全', () => {
expect(html).toContain('data-col="param_name"');
});
it('可改的四张表各有一道编辑闸', () => {
it('可改的四张表各有一道编辑闸 —— 查的是闸([data-edit-for] + 开钮),不是宿主:2026-08-30 换版面第一版恰好漏掉 profile 的闸,宿主在而闸不在,轨道打不开编辑,XL 的必填永远补不上(Task 7 那颗「按不下去的确认钮」同款),旧断言只查宿主没逮到', () => {
for (const address of ['parts.profile', 'parts.chip_type', 'parts.type_param', 'parts.parameter']) {
expect(html).toContain(`data-edit="${address}"`);
expect(html).toContain(`data-edit-for="${address}"`);
expect(html).toContain(`data-edit-open="${address}"`);
}
});
@@ -769,11 +771,18 @@ describe('件① 评估输入补全', () => {
expect(html).toContain('.jc-typewarn.v-2{display:flex');
});
it('**步骤圆点只按基座数点亮**:①③ 挂 _missing(v-0 档)、② 挂 distinct(v-1 档),不许模版自己判「办完了」', () => {
expect(html).toContain('class="sdot sd-m" data-class-of="parts_profile_missing"');
expect(html).toContain('class="sdot sd-d" data-class-of="sub_eval_part_distinct_chip_type_id"');
expect(html).toContain('class="sdot sd-m" data-class-of="parts_parameter_missing"');
expect(html).toContain('.sdot.sd-m.v-0,.sdot.sd-d.v-1{background:var(--data)');
it('**步骤圆点只按基座数点亮,投影挂在行外的包裹层** —— 行内 data-class-of 只认行作用域,作用域级读数在行内投不出(实测圆点永远不亮);圆点自己不许再挂 data-class-of(挂了也是死属性)', () => {
expect(html).toContain('class="pj pj-profile" data-class-of="parts_profile_missing"');
expect(html).toContain('class="pj pj-type" data-class-of="sub_eval_part_distinct_chip_type_id"');
expect(html).toContain('class="pj pj-param" data-class-of="parts_parameter_missing"');
expect(html).toContain('.pj-profile.v-0 .sdot.sd-1,.pj-type.v-1 .sdot.sd-2,.pj-param.v-0 .sdot.sd-3{background:var(--data)');
expect(html).not.toMatch(/class="sdot[^"]*" data-class-of=/);
});
it('**类型查名有诚实守卫**(data-by 行缺键落回外层同名字段,终审 I3):行里没真选上类型(selectCurrent 没写下 option[selected]),查名 pill 与字段行的名字都不摆', () => {
expect(html).toContain('.lane:not(:has(select[data-col="chip_type_id"] option[selected])) .idcard .tpill{display:none}');
expect(html).toContain('.lane:not(:has(select[data-col="chip_type_id"] option[selected])) .tywrap span{display:none}');
expect(html).toContain('class="jc-vcell tywrap"');
});
it('**新增类型面板是 <details>,且绑定层把抽取样式的 display:none 让开** —— 少了这条让位规则,整个面板(含两道闸与两张表)永远不可见', () => {
@@ -54,7 +54,7 @@ export const manifest: ActivityManifest = {
activityId: ACTIVITY_ID,
name: 'ACT-WORK-05 · 差异评估',
/* 重写不是改 —— 同版重装是 no-op(静默丢失,踩过) */
packageVersion: '2.2.1',
packageVersion: '2.2.2',
category: '需求分析与方案匹配',
sdkVersion: '0.1.0-m5',
checksum: 'dev-checksum-part-substitution-assessment',
@@ -219,8 +219,18 @@ th{white-space:nowrap}
input/select 同高写的)排在 P08_CSS 之后会盖过它 —— 这里(alias 排最后)按目标件的高度要回来 */
.fld input.jc-cell,.fld select.jc-cell,.pv input.jc-cell{height:36px}
@media(max-width:900px){.lanes{grid-template-columns:1fr}}
/* 步骤圆点点亮 —— 「办完了没有」全是基座数:①③ 缺格数为 0(v-0),② 不同类型恰 1 种(v-1) */
.sdot.sd-m.v-0,.sdot.sd-d.v-1{background:var(--data);border-color:var(--data);color:#fff}
.pj{display:block}
/* 步骤圆点点亮 —— 「办完了没有」全是基座数:①③ 缺格数为 0(v-0)、② 不同类型恰 1 种(v-1)。
投影在行外的 .pj 包裹层上(行内 data-class-of 只认行作用域,读数投不出 —— 见 body 那段注释) */
.pj-profile.v-0 .sdot.sd-1,.pj-type.v-1 .sdot.sd-2,.pj-param.v-0 .sdot.sd-3{background:var(--data);border-color:var(--data);color:#fff}
/* 类型查名的诚实守卫 —— data-by 在行缺键时落回外层同名字段(终审 I3 已记档的平台缺陷):
XL 还没选类型时,sub_eval.case 摊平出的 chip_type_id 会顶进来,查名 pill 摆出一个它没有的类型。
守卫认 selectCurrent 按**这一行自己的值**写下的 option[selected]:行里没真选上,名字不摆、摆「(未选择)」。
人在编辑态刚选了还没保存时守卫仍按旧值(与「基座算的数等回声」同一条纪律);保存重渲后跟上。 */
.lane:not(:has(select[data-col="chip_type_id"] option[selected])) .idcard .tpill{display:none}
.lane:not(:has(select[data-col="chip_type_id"] option[selected])) .idcard .val:has(.tpill)::after{content:"(未选择)";font-family:Inter,ui-sans-serif,system-ui,sans-serif;font-size:14px;font-weight:400;color:var(--ink-4);letter-spacing:0}
.lane:not(:has(select[data-col="chip_type_id"] option[selected])) .tywrap span{display:none}
.lane:not(:has(select[data-col="chip_type_id"] option[selected])) .tywrap::after{content:"(未选择)";color:var(--ink-4)}
/* 类型不一致护栏 —— 只在 v-2 档现身(sub_eval.part 恰两行,distinct 封顶 2;0=都没选,1=一致) */
.jc-typewarn{display:none}
.jc-typewarn.v-2{display:flex;gap:8px;margin:0 0 var(--s2);padding:10px 14px;border-radius:9px;background:var(--danger-soft);border:1px solid #f3b9b5;color:var(--danger);font-size:12.5px;line-height:1.65}
@@ -401,7 +411,7 @@ const LANES = `<article class="lanes" data-rows="mine:${D}.part" data-edit="part
</div>
<div class="steps">
<div class="stepbox">
<span class="sdot sd-m" data-class-of="parts_profile_missing">1</span>
<span class="sdot sd-1">1</span>
<div class="scard">
<div class="sh">主数据<span class="req-note">*为必填 · 在库的料现值已回填,只查漏补缺</span></div>
<div class="sb">
@@ -417,12 +427,12 @@ const LANES = `<article class="lanes" data-rows="mine:${D}.part" data-edit="part
</div>
</div>
<div class="stepbox">
<span class="sdot sd-d" data-class-of="sub_eval_part_distinct_chip_type_id">2</span>
<span class="sdot sd-2">2</span>
<div class="scard">
<div class="sh">芯片类型<span class="req-note">必填 · 决定第③步比哪些参数;两颗料必须同一个类型</span></div>
<div class="sb">
<div class="fld"><span class="fk req">芯片类型</span><div class="fv">
<span class="jc-vcell"><span data-rows="parts.chip_type" data-by="type_id=chip_type_id"><i data-slot="type_name" data-blank></i></span></span>
<span class="jc-vcell tywrap"><span data-rows="parts.chip_type" data-by="type_id=chip_type_id"><i data-slot="type_name" data-blank></i></span></span>
${optionSelect('chip_type_id', 'parts.chip_type', 'type_id', 'type_name', true)}
<div class="hint">类型库里没有?展开下面「新增类型」面板现场定义,保存后立刻出现在这个下拉里。</div>
</div></div>
@@ -624,7 +634,7 @@ const NEWTYPE_PANEL = `<details class="newtype">
* 复制,手改会被覆盖,给输入件就是让人误以为改了算数)。
*/
const PARAM_TILES = `<div class="steps"><div class="stepbox">
<span class="sdot sd-m" data-class-of="parts_parameter_missing">3</span>
<span class="sdot sd-3">3</span>
<div class="scard">
<div class="sh">核心参数值<span class="req-note">一参数一格 · 值必填;查不到就留空并在说明里写明,别编一个数顶替</span></div>
<div class="sb">
@@ -658,10 +668,21 @@ export const INPUT_SUPPLEMENT = doc({
'<span class="need">AI 预填情况</span>:在库的料现值已回填,只需查漏补缺;料号本身已按启动参数铺好,不必重填。<b>纪律</b>:查不到的参数值留空(红带会点名),不要编一个值顶替;数据工单确认后即入正式库,确认人留痕。',
]) +
gateBar() +
editGate('parts.profile', '两条轨道的主数据核对 / 补齐(缺类型的在②的下拉里选),点「编辑」开卡') +
gapbar('parts_profile', '必填') +
/*
* 三层投影包裹(pj-*):步骤圆点「办完没有」的三个基座数(profile 缺格 / 类型 distinct /
* 参数缺格)都是**作用域级读数**,而行内的 data-class-of 只认行自己的键
* (renderRows 末段 renderTemplate(…, one) 拿的是行对象,不是合并作用域 —— 实测投不出)。
* 投影挂在行外的包裹层上,样式表用后代选择器点亮圆点;代价是两条轨道的圆点一起亮
* (基座数本来就是表级的 —— 分到每颗料头上的数基座没有,不编)。
*/
`<div class="pj pj-profile" data-class-of="parts_profile_missing"><div class="pj pj-type" data-class-of="sub_eval_part_distinct_chip_type_id"><div class="pj pj-param" data-class-of="parts_parameter_missing">` +
LANES +
TYPE_WARN +
NEWTYPE_PANEL +
PARAM_TILES +
`</div></div></div>` +
flow('<b>提交 / 确认:</b>本工单只承载内容;确认请在对话区的【执行卡】中进行。确认后活动自动从「等待数据」唤醒。'),
});