Archived
fix(w05): data-rows 宿主换 <article> —— div 宿主被行模版里第一个 </div> 截断,档案卡当场串行
浏览器走查逮到的真事故:renderRows 的 ROWS 正则按宿主自己的闭合标签非贪婪截取行模版, <div class="lanes"> 的行模版在 lab 那格的 </div> 处被截半,第二行的展开被浏览器塞进 第一行没闭合的标签里 —— 屏上「候选内部料号 / LM2596S-5.0 / 不在库」三样各来自不同的行。 A2 立的「行容器用行内不会再出现的标签」说的正是宿主(它自己用的就是 <article> 包 <section>), 我在计划里引了这条却把它按在了行标签上。.lanes / .pvgrid 两个宿主同病同修。 防再犯钉:照 ROWS 正则同款截取行模版,断言截出来的内容标签配平(截断必然留下没闭合的 标签)—— 第一版写成「不含宿主闭合标签」是空话:非贪婪截取本来就停在闭合标签之前, 那条断言永远不会红;换配平判法后,两个宿主各自退回 div 的变异都真红、复原绿。 pnpm gates 退出码 0(2496 测);包版本 2.2.0 -> 2.2.1(2.2.0 已装库,安装恒新增)。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
This commit is contained in:
@@ -727,14 +727,14 @@ describe('件① 评估输入补全', () => {
|
||||
});
|
||||
|
||||
it('**两条轨道:行源与写向同一元素,行模版是宿主直接子元素**(rowOf 认宿主直接子元素 —— 一条 lane = 一行;行模版里不许再有 <section>,行展开按第一个同名闭合标签截行内容)', () => {
|
||||
expect(html).toMatch(/<div class="lanes" data-rows="mine:sub_eval\.part" data-edit="parts\.profile">\s*<section class="lane">/);
|
||||
expect(html).toMatch(/<article class="lanes" data-rows="mine:sub_eval\.part" data-edit="parts\.profile">\s*<section class="lane">/);
|
||||
const laneBody = /<section class="lane">([\s\S]*?)<\/section>/.exec(html)?.[1] ?? '';
|
||||
expect(laneBody).not.toBe('');
|
||||
expect(laneBody).not.toContain('<section');
|
||||
});
|
||||
|
||||
it('**参数瓷贴单块平铺,瓷贴是宿主直接子元素** —— 按料嵌一层分栏会把一颗料的所有参数格并成一条记录(ui-kit view-container-collect-div-rows.test.ts 用真实 renderView+collect 钉死的「必然归并」)', () => {
|
||||
expect(html).toMatch(/<div class="pvgrid" data-rows="mine:parts\.parameter" data-edit="parts\.parameter"><section class="pv">/);
|
||||
expect(html).toMatch(/<article class="pvgrid" data-rows="mine:parts\.parameter" data-edit="parts\.parameter"><section class="pv">/);
|
||||
const pvBody = /<section class="pv">([\s\S]*?)<\/section>/.exec(html)?.[1] ?? '';
|
||||
expect(pvBody).not.toBe('');
|
||||
expect(pvBody).not.toContain('<section');
|
||||
@@ -745,6 +745,24 @@ describe('件① 评估输入补全', () => {
|
||||
expect(html).toContain('.pv .rowkey{display:none}');
|
||||
});
|
||||
|
||||
it('**data-rows 宿主的闭合标签不许出现在自己的行模版里** —— renderRows 的正则按宿主闭合标签非贪婪截取,撞上就把行模版截半、后一行嵌进前一行没闭合的标签里(2026-08-30 浏览器走查逮到:<div class="lanes"> 被 lab 那格的 </div> 截断,档案卡串行)。判法:照 ROWS 正则同款截取,截出来的行模版必须**标签配平**(截断必然留下没闭合的标签 —— 直接查「不含闭合标签」是空话,非贪婪截取本来就停在闭合标签之前)', () => {
|
||||
const hosts = [...html.matchAll(/<([a-zA-Z][\w-]*)\b[^>]*\bdata-rows="[^"]+"[^>]*>([\s\S]*?)<\/\1>/g)];
|
||||
expect(hosts.length).toBeGreaterThanOrEqual(2);
|
||||
const VOID = new Set(['input', 'br', 'img', 'hr', 'meta', 'link']);
|
||||
for (const m of hosts) {
|
||||
const inner = m[2] ?? '';
|
||||
const opens = new Map<string, number>();
|
||||
for (const tm of inner.matchAll(/<(\/?)([a-zA-Z][\w-]*)\b/g)) {
|
||||
const tag = (tm[2] ?? '').toLowerCase();
|
||||
if (VOID.has(tag)) continue;
|
||||
opens.set(tag, (opens.get(tag) ?? 0) + (tm[1] === '/' ? -1 : 1));
|
||||
}
|
||||
for (const [tag, n] of opens) {
|
||||
expect(n, `data-rows 宿主 <${m[1]}> 截出的行模版里 <${tag}> 开 ${n} 个没闭合 —— 宿主的闭合标签把模版截半了`).toBe(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('**类型不一致护栏**:distinct 是基座数,红带只在 v-2 档现身,模版不算不判', () => {
|
||||
expect(html).toMatch(/class="jc-typewarn" data-class-of="sub_eval_part_distinct_chip_type_id"/);
|
||||
expect(html).toContain('.jc-typewarn{display:none}');
|
||||
|
||||
@@ -54,7 +54,7 @@ export const manifest: ActivityManifest = {
|
||||
activityId: ACTIVITY_ID,
|
||||
name: 'ACT-WORK-05 · 差异评估',
|
||||
/* 重写不是改 —— 同版重装是 no-op(静默丢失,踩过) */
|
||||
packageVersion: '2.2.0',
|
||||
packageVersion: '2.2.1',
|
||||
category: '需求分析与方案匹配',
|
||||
sdkVersion: '0.1.0-m5',
|
||||
checksum: 'dev-checksum-part-substitution-assessment',
|
||||
|
||||
@@ -377,14 +377,17 @@ const gateBar = (): string => `<div class="jc-gate-bar">
|
||||
* 收值的授权面只认 `data-edit`,行从哪来只管这一屏摆几行摆谁的 —— 与旧 PART_TABLE 同一条道理)。
|
||||
* 选 `sub_eval.part` 做行源是因为**角色(原/候选)与在库状态**只长在它身上;`internal_pn`
|
||||
* 挂 `data-col` 之后,保存时按它认作 `parts.profile` 的主键。**人不加行**(要建哪颗料是
|
||||
* 启动参数定死的事)。行模版是 `<section>`,里面不许再出现 `<section>`(行展开按第一个
|
||||
* 同名闭合标签截行内容);档案卡 `.idcard` 是纯投影(`data-slot`,编辑态照常摆着),
|
||||
* 启动参数定死的事)。**宿主是 `<article>` 不是 `<div>`** —— `renderRows` 的正则按
|
||||
* **宿主自己的闭合标签**非贪婪截取行模版(`<div>` 宿主会在行模版里第一个 `</div>`(lab 那格)
|
||||
* 处截断,后一行的展开被浏览器塞进前一行没闭合的标签里,档案卡当场串行 —— 浏览器走查
|
||||
* 逮到的真事故,A2 立的「行容器用行内不会再出现的标签」说的正是宿主);行模版里
|
||||
* 同理不许再出现 `<article>`/`<section>`;档案卡 `.idcard` 是纯投影(`data-slot`,编辑态照常摆着),
|
||||
* 字段行 `.fld` 里才是「只读孪生 + 输入件」二选一。
|
||||
* 芯片类型选择器长在每条轨道里(`chip_type_id` 逐颗料各自一份,`distinct` 判据要求两颗
|
||||
* 恰好同值);只读态用嵌套 `data-by` 从 `parts.chip_type` 整表查人读的 `type_name`,
|
||||
* 编辑态换 `optionSelect`(选项现读)。
|
||||
*/
|
||||
const LANES = `<div class="lanes" data-rows="mine:${D}.part" data-edit="parts.profile">
|
||||
const LANES = `<article class="lanes" data-rows="mine:${D}.part" data-edit="parts.profile">
|
||||
<section class="lane">
|
||||
<div class="idcard">
|
||||
<div class="cell">
|
||||
@@ -428,7 +431,7 @@ const LANES = `<div class="lanes" data-rows="mine:${D}.part" data-edit="parts.pr
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>`;
|
||||
</article>`;
|
||||
|
||||
/** 类型不一致的护栏红带 —— 与放行判据同源(`sub_eval_part_distinct_chip_type_id = 1`),
|
||||
* 人在两条轨道选岔了,保存那一刻这条带就现身点名,不必等提交才被判据拦。显隐全在样式表
|
||||
@@ -612,7 +615,8 @@ const NEWTYPE_PANEL = `<details class="newtype">
|
||||
* 步骤③ 核心参数值 —— `parts.parameter`,一字段一格(行由 S1 配方按
|
||||
* `sub_eval.part × parts.type_param` 铺,`prune:true` 清孤行,人不加行不删行)。
|
||||
* **单块平铺**(不按轨道分两栏的机制原因见上面的大注释):瓷贴 `<section class="pv">`
|
||||
* 直接是宿主 `.pvgrid` 的子元素 = collect 的一行;所属料号以小字摆在每格头上,
|
||||
* 直接是宿主 `.pvgrid` 的子元素 = collect 的一行;宿主是 `<article>`(同 LANES:
|
||||
* `<div>` 宿主会被瓷贴里的第一个 `</div>` 截断行模版);所属料号以小字摆在每格头上,
|
||||
* 配方按料连排,同一颗料的格子天然聚在一起。
|
||||
* 主键钥匙(`internal_pn`/`param_name`)收进 `.rowkey`(display:none,BIND_ACC1_EDIT
|
||||
* 先立此法):不给人看,但必须真挂 `data-col` —— 漏了钥匙,保存的行凑不出主键,
|
||||
@@ -626,13 +630,13 @@ const PARAM_TILES = `<div class="steps"><div class="stepbox">
|
||||
<div class="sb">
|
||||
${editGate('parts.parameter', '逐格参数值核对 / 补齐,点「编辑」开格')}
|
||||
${gapbar('parts_parameter', '必填')}
|
||||
<div class="pvgrid" data-rows="mine:parts.parameter" data-edit="parts.parameter"><section class="pv">
|
||||
<article class="pvgrid" data-rows="mine:parts.parameter" data-edit="parts.parameter"><section class="pv">
|
||||
<div class="pvsrc mono" data-slot="internal_pn" data-blank></div>
|
||||
<div class="pn2"><span data-slot="param_name" data-blank></span><span class="pvu" data-slot="param_unit" data-blank></span></div>
|
||||
<span class="rowkey">${cell('internal_pn', 'mono')}${cell('param_name')}</span>
|
||||
<div class="pvrow">${cell('param_value', 'mono')}</div>
|
||||
<div class="pvrow"><span class="pvlab">说明</span>${cell('param_note')}</div>
|
||||
</section></div>
|
||||
</section></article>
|
||||
<div class="pv-empty pvnull" data-class-of="parts_parameter_rows">参数格还没铺出来 —— 先在上面把两颗料的芯片类型定下来并保存,系统会按类型自动铺出这里的参数格。</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user