Archived
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:
@@ -548,13 +548,46 @@ describe('件① 评估输入补全', () => {
|
||||
expect(steps.find((s) => s.item === 'S1_inputs')?.view).toBe('input-supplement');
|
||||
});
|
||||
|
||||
it('四块都在,行都绑本单草稿(不是整表)', () => {
|
||||
it('四块都在,行都绑本单草稿(不是整表);档案卡行源是 parts.profile 本身', () => {
|
||||
expect(html).toContain('data-rows="mine:sub_eval.part"');
|
||||
expect(html).toContain('data-rows="mine:parts.profile"');
|
||||
expect(html).toContain('data-rows="mine:parts.parameter"');
|
||||
expect(html).toContain('data-rows="mine:parts.chip_type"');
|
||||
expect(html).toContain('data-rows="mine:parts.type_param"');
|
||||
});
|
||||
|
||||
/*
|
||||
* 2026-08-31 用户实测三连的回归网(视图侧两条,配方侧第三条在 steps.test.ts):
|
||||
* ① 「品牌…点保存根本不会保存」—— 病根:块① 行源曾是 `mine:sub_eval.part`,那张案卷表
|
||||
* 没有品牌这些列,renderTemplate 只认行作用域,显示与孪生件的值永远空白。行源必须是
|
||||
* `mine:parts.profile` 本身(S1 第④笔 prune:true 保证恒有两行)。
|
||||
* ② 「明明没选类型,界面自己给上了」—— data-by 行里缺键落回外层同名字段(debt I3),
|
||||
* 查名 pill 摆出这一行没有的类型。守卫认 selectCurrent 按这一行自己的值写下的
|
||||
* option[selected]:真选上才摆名,没选摆「待选择」。
|
||||
*/
|
||||
it('档案卡:行源 = mine:parts.profile、一个宿主一道闸;嵌套的角色/在库查表全只读(嵌套行里有 data-col 会被 collect 并成一条)', () => {
|
||||
expect((html.match(/data-edit="parts\.profile"/g) ?? []).length).toBe(1);
|
||||
expect(html).toContain('data-rows="mine:parts.profile" data-edit="parts.profile"');
|
||||
expect(html).not.toContain('data-rows="mine:sub_eval.part" data-edit="parts.profile"');
|
||||
const nested = [...html.matchAll(/<span data-rows="mine:sub_eval\.part" data-by="internal_pn=internal_pn">((?:(?!<\/span>)[\s\S])*?)<\/span>/g)];
|
||||
expect(nested.length).toBeGreaterThanOrEqual(2);
|
||||
for (const m of nested) expect(m[1] ?? '').not.toContain('data-col');
|
||||
});
|
||||
|
||||
it('类型名带守卫:这一行的下拉真有选中项才摆名,没选摆「待选择」—— data-by 外层回落的谎话进不了屏(单层 :has,嵌套 :has 是非法选择器会被整条丢弃)', () => {
|
||||
expect(html).toContain('.dz-type:not(:has(option[selected])) .tsel > span{display:none}');
|
||||
expect(html).toContain('待选择');
|
||||
/* 回归网:不许再写回嵌套 :has 的形 —— 那条选择器非法,浏览器静默丢弃,守卫等于没有 */
|
||||
expect(html).not.toContain(':has(+ select.jc-cell:not(:has(');
|
||||
});
|
||||
|
||||
it('三级占位:必填红 / 建议橘 —— 空格子直说要不要填(档案卡与参数值表两处)', () => {
|
||||
expect(html).toContain('.fcell.req .jc-vcell:empty::after{content:"必填"');
|
||||
expect(html).toContain('.fcell.sug .jc-vcell:empty::after{content:"建议"');
|
||||
expect(html).toContain('td.pv .jc-vcell:empty::after{content:"必填"');
|
||||
expect(html).toContain('td.sg .jc-vcell:empty::after{content:"建议"');
|
||||
});
|
||||
|
||||
it('芯片类型是从台账现读的下拉 —— 写死等于「只能选出包那天就有的那几种」(块①的 chip_type_id 与块②b 的 type_id 都是)', () => {
|
||||
expect(html).toContain('data-options="parts.chip_type"');
|
||||
expect(html).toContain('data-option-value="type_id"');
|
||||
@@ -626,7 +659,7 @@ describe('件① 评估输入补全', () => {
|
||||
expect(html).toContain('data-col="param_name"');
|
||||
});
|
||||
|
||||
it('可改的四张表各有一道编辑闸', () => {
|
||||
it('可改的三张表 + 档案卡各有一道编辑闸', () => {
|
||||
for (const address of ['parts.profile', 'parts.chip_type', 'parts.type_param', 'parts.parameter']) {
|
||||
expect(html).toContain(`data-edit="${address}"`);
|
||||
}
|
||||
@@ -711,14 +744,14 @@ describe('件① 评估输入补全', () => {
|
||||
* 只读态与编辑态下「#」列逐行都是空白。钉死 `.jc-tbl-wrap` 现在也接了这五条规则,
|
||||
* 且这张件确实只用 `.jc-tbl-wrap`(没有表在用没打这条补丁就会数不出来的 `.tbl-wrap`)。
|
||||
*/
|
||||
it('四张表的序号列真的会数:.jc-tbl-wrap 也接了 CSS 计数器(不再只认 .tbl-wrap)', () => {
|
||||
it('三张表的序号列真的会数:.jc-tbl-wrap 也接了 CSS 计数器(不再只认 .tbl-wrap)', () => {
|
||||
expect(html).toMatch(/\.jc-tbl-wrap\s+table\s*,\s*\.tbl-wrap\s+table\s*\{\s*counter-reset:\s*rn;?\s*\}/);
|
||||
expect(html).toMatch(/\.jc-tbl-wrap\s+table\s+td\.rn\s*,\s*\.tbl-wrap\s+table\s+td\.rn\s*\{\s*counter-increment:\s*rn;?\s*\}/);
|
||||
expect(html).toMatch(/\.jc-tbl-wrap\s+table\s+td\.rn\s+\.jc-rn-num::before\s*,\s*\.tbl-wrap\s+table\s+td\.rn\s+\.jc-rn-num::before\s*\{\s*content:\s*counter\(rn\);?\s*\}/);
|
||||
// 这张件的四张表都走 .jc-tbl-wrap(没有 .tbl-wrap)—— 补丁真的接在会用到的类上(Task 7b 新增 parts.chip_type 这一张,3→4)
|
||||
// 这张件的三张表都走 .jc-tbl-wrap(没有 .tbl-wrap)—— 2026-08-31 块① 换成档案卡(不是表),4→3
|
||||
expect(html).not.toContain('class="tbl-wrap"');
|
||||
const jcTblWrapCount = html.match(/class="jc-tbl-wrap"/g)?.length ?? 0;
|
||||
expect(jcTblWrapCount).toBe(4);
|
||||
expect(jcTblWrapCount).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user