改前基线、改后复测都在同一台机器、同一固定 Chromium(/opt/hermes/.playwright/chromium_headless_shell-1243)上跑。
所有数字来自脚本输出,原日志在 tmp/v14/(*-before.log / *-after.log)。
| 文件 | 改动 |
|---|---|
src/components/kf-opportunity-card.js:173 |
<p class="one">${o.oneLiner \|\| '(未填写一句话说明)'}</p> → ${o.oneLiner ? html\${o.oneLiner} ` : ''}`(不产出 DOM,不是空元素) |
src/components/kf-opportunity-card.js:152 |
<kf-glass-card> 加布尔属性 ?has-one=${!!o.oneLiner} |
src/components/kf-opportunity-card.js:66 |
新增 :host(:not([has-one])) .nums { margin-top: auto; } —— 无描述卡被同排卡撑高的那一段余量(1 行 42px + 1 条 gap 12px ≈ 53px)落在标题与指标区之间的一条缝里 |
src/layout/kf-app.js:251 |
抽屉里同款兜底删掉:${opp.oneLiner ? html\${opp.oneLiner} ` : ''}` |
没有用「暂无描述 / 待补充」这类替代文案;src/core/ 未动。
.wrap::before/::after 两个零高弹性空位分走余量,实测闭合布局把余量全给了一个空位(.nums 紧贴标题,54px 全堆在它下方),
而且为了排对顺序加了 order !important,改动面大。放弃。.nums 时,量出来 .nums 被按继承的 align-self: stretch 拉满 .wrap 高度(规格里 .wrap 有 height:100%,子项默认 stretch)。
放弃。.nums { margin-top: auto }:flex 列里 auto 外边距吃掉剩余空间、标题区不动;有描述卡没有剩余空间,规则对它们不产生任何影响。
实测无描述卡的缝落在「标题 → 指标区」(下表单测坐标)。node scripts/verify-v14.mjs,新增脚本)· /pool 卡片数 9;卡高 [389,389,389,389,389,389,389,389,389];行内极差 0px;全部极差 0px
· 含「未填写」元素数 0
· 有描述 8 张(.one 文本:加班到十点想吃饱又怕胖… / 露营带的都是泡面… / … / 陈静在 S3 独立上传…);无描述 1 张 学生党实惠大份拌面
· 卡内最大连续空白:9 张全部 = 0px
判分:4/4 达标
| 要求 | 实测 | 结论 |
|---|---|---|
| 含「未填写」元素数 = 0 | 0(跨 shadow 扫 9 张卡;抽屉页另测) | ✅ |
| 9 张卡高度差 ≤ 2px | 389 × 9,差 0 | ✅ |
| 每张卡内最大连续空白 ≤ 24px | 全部 0px(叶子文字矩形两两相邻) | ✅ |
| 有描述 8 张描述仍在 | 8 张 .one 文本完整 |
✅ |
抽屉(/pool/:id 深链打开) |
抽屉已挂(文本含「决策与状态」),全文无「未填写」,.one 元素数 0 |
✅ |
无描述卡的数据(同一行 3 张卡,页面坐标):
.title 底 .nums 顶 缝 .dec/.src/.acts 底
无描述 611 623 12px 827
有描述 611 676 66px 880 ← 底下三行贴着卡底,与无描述卡同一 y 网格(行首卡)
/pool 的 P2(同排卡「来源行」底边差):拿到一份干净的采样(tmp/v14/p2dbg.mjs):同一行三张卡 .src 底边 782.7 / 836.3 / 836.3,
无描述卡在上,两张有描述卡彼此 0px —— 无描述卡的余量落在标题与指标区之间,没有把 .src 挤歪。
git stash 回到改动前代码,verify-visual-v12.mjs --page /pool 的 P2 = ✓;/pool src 53.6px、/pool acts 53.6px,正好等于「没有 .one 的那 54px」);.src 底边 782.7 / 836.3 / 836.3,与 ✗ 时脚本自己报的 top/h 完全一致;v12 的 MEASURE 单独跑 → 不变形;独立脚本复刻 hover 循环 + 滚动 + 快照 → 不变形;page.clock.install() 后再跑 → 每次 evaluate 落到同一个时钟微秒,但仍不变形 —— 排除「时钟粒度」;.title 加一条相等的 min-height(任何值变更,包括设为原值以外的 44px)→ .nums 立刻从 61.6 变 62,
但 .wrap 高度少掉 52.6px、.src 底随之上移 —— 等价于一次「有 .one 时的布局」,正好是脚本 ✗ 时看到的样子;--page /pool 单页模式恰好把两种测量放在同一个页面会话里而放大。
全量模式(/ /pool /stages /reports)中 /pool 每次都整页导航重新挂载,量到的同样是变形值——
说明这个残留并非只在单页模式出现,具体哪条样式变化触发仍未定位到根因(其余候选,如 hover 合成事件、滚动、clock、离线/通知,均已单独测过、不触发)。
本条如实标为未查明,不与本次改动混为一谈:改动前同一脚本同一页是 ✓,改动后是 ✗,但独立采样显示真实布局是对的;
判定标准以任务书「二、验收」为准(已全部达标),P2 的 ✗ 记录在此,不当作已解释的结论。未查明。 本轮把三个问题按顺序查完(1、2 排除;3 给出 /pool 补 import 前后的实测数),都没有复现「CARD_CSS 没进 adoptedStyleSheets」;
至于 v12 文档里写的那次「.top 之后整段丢失」,当前代码与当前浏览器无法复现,只能把它归为「未核实的历史现象」。
adoptedStyleSheets 实际长度探针 tmp/v14/sheets.mjs(跨 shadow 数 view-* 的 shadowRoot.adoptedStyleSheets,并逐份扫 CSSOM 找含 .cfoot 的规则):
补 import 后(当前 HEAD 状态):
| 页面 | adoptedStyleSheets | 含 .cfoot 的份数 | 每份规则数 | elementStyles |
|---|---|---|---|---|
| /pool | 4 | 2 | 20 / 102 / 32 / 96 | 3 |
| / | 3 | 0 | 20 / 67 / 96 | 2 |
| /stages | 4 | 1 | 20 / 102 / 36 / 96 | 3 |
| /reports | 5 | 1 | 20 / 102 / 35 / 36 / 96 | 4 |
| /report-type | 路由不存在(未量) | — | — | — |
临时去掉 view-pool.js 的 CARD_CSS(, CARD_CSS, 删掉)后:
| 页面 | adoptedStyleSheets | 含 .cfoot 的份数 | 规则数 |
|---|---|---|---|
| /pool | 3 | 1 | 20 / 32 / 96 |
| /stages | 4 | 1 | 20 / 102 / 36 / 96 |
| /reports | 5 | 1 | 20 / 102 / 35 / 36 / 96 |
→ /pool 补 import 前 3 份、补后 4 份,差的正好是一份含 .cfoot 的 102 条规则表 = CARD_CSS。
补回后 4 份 = VIEW_CSS(20)+ CARD_CSS(102)+ 本页 css`(32)+ BASE/Lit 共享(96),**没有一项被跳过**。
.cfoot在 /pool 有 2 份命中,是因为view-pool页脚自己也写了几条.cfoot收尾规则(view-pool.js:100那处),不是重复的CARD_CSS`。
view-pool.js 的 static styles 里有没有 undefined?当前:static styles = [VIEW_CSS, CARD_CSS, css\…`],三项都是CSSResult;LitfinalizeStyles的实现是
new Set(s.flat(Infinity).reverse())再逐个unshift,**既不跳过成员也不过滤 undefined**(lib/vendor/@lit/reactive-element/v2.1.2/reactive-element.js),
而adoptStyles又是整体赋值shadowRoot.adoptedStyleSheets = sheets.map(...)—— 所以「数组里一个undefined` 导致后面被跳过」这个机制在本仓库的 Lit 版本里不成立。
排除(不是「查到即停」的那个根因,是排除项)。
view-pool.js → report-card.js 的间接依赖是否造成循环、拿到 undefined?grep -n "^import" src/pages/report-card.js:只依赖 lit、core/index.js、components/cjk.js、components/icons.js、components/kf-viz.js,不 import 任何 view;view-stages / view-reports / view-report-type)与 view-pool 从 report-card.js 取的是同一个模块实例,实测 adoptedStyleSheets 里那份 102 条规则的表在四页都出现(见上表);CARD_CSS 不是 undefined:去掉 import 后 /pool 少的就是那张 102 条规则表,说明 import 时拿到的是真实对象。
排除模块循环导致 undefined。| 假设 | 结论 | 依据 |
|---|---|---|
static styles 里有 undefined 把后面项跳过 |
排除 | 当前数组三项均为 CSSResult;Lit 2.1.2 的 finalizeStyles / adoptStyles 不跳过、不过滤(源码见上) |
report-card.js ←→ view-pool.js 循环依赖 → undefined |
排除 | report-card.js 无 view 依赖;实测补 import 后 102 条规则表确实进了 shadow |
/pool 路由懒加载时序导致样式晚到 |
排除 | 三次独立新页 + 同一页二次导航,adoptedStyleSheets 都是 [4, 含 .cfoot=2] |
v12 时期的「.top 之后整段丢失」由某条 CSS 语法错误截断 |
未验证(不在任务书三个问题内,本轮未展开) | 当前代码无法复现;只记录,不当作结论 |
verify-visual-v13.mjs 当前仍打印:
豁免:cfoot.padding 0px 0px 0px 12px —— 页脚根节点纵向内衬归零、右侧与卡片右缘对齐
豁免:cfoot.border 1px 0px 0px 1px —— 页脚根节点左边线(行内分隔),上/右/下三边相等
/pool 的页脚卡在 28px 的排序行里(.frow.toolbar .cfoot),而 /reports 的 .cfoot 是独立一行。
CARD_CSS 的 .cfoot 自带 padding: 20px 4px 12px,纵向内衬不归零会把 28px 的行撑到 61px 并换行(v13 报告亦有实测);
左边 1px 竖线是「排序段 | 页脚」之间的行内分隔,/reports 没有相邻的排序段。view-pool.js:100 原地,本轮按任务书「不要动」未改。| 项 | 改前 | 改后 | 结论 |
|---|---|---|---|
verify-visual-v12.mjs(全量) |
11/13,exit 0(基线不判分) | 10/13 | 两项老问题(圆角 0px 口径、字号 ≤5)数字逐字相同;其余 10 项(含 P3 两栏 [267,267]、同排卡高 30 行、hover 152+292 个、筛选栏、底部留白)与改前 --tag before 输出 diff 为空;新增 1 项 P2 ✗,见「一」末尾的未查明说明 |
verify-visual-v13.mjs |
4/4(基线不判分) | 3/4 | 一 / 二 / 三 三段 JSON 与改前逐字节相同;唯一失分项是 v12 子进程里的 P2(同上) |
node --test 'test/*.test.js' |
— | 46 / 46 通过 | ✅ |
node scripts/check.mjs |
— | CHECK OK | ✅ |
src/core/ |
— | 未改 | ✅ |
| 已达标项(圆角 / 等高 / hover / 底部留白 / 筛选栏 / 工作台两栏 / 排序按钮) | — | 逐项数字不变(对照 tmp/v14/v12-before.log 与 v12-after.log 同名项) |
✅ |
改动文件:src/components/kf-opportunity-card.js、src/layout/kf-app.js、新增 scripts/verify-v14.mjs、本报告 docs/v14-report.md。查证用的临时脚本都在 tmp/v14/(未跟踪,未提交)。
复跑命令:
node scripts/verify-v14.mjs # 4/4
node scripts/verify-visual-v12.mjs # 10/13(P2 ✗,见上)
node scripts/verify-visual-v13.mjs # 3/4(失分项同上)
node --test 'test/*.test.js' # 46/46
node scripts/check.mjs # CHECK OK