Files
rond-with-you/.workbuddy/memory/2026-10-09.md
T
tamakyi ab7ccd22ce 前端改造:设计令牌 + 卡片层级 + 图表可读性 + 类型色收口 + 窄屏导航
先出方案(frontend-plan-2026-10-09.md)再动手,四批按顺序落地。方法上先存基线、再用同一份
探针在改动前后逐值 diff —— 这批里有 6 处问题只有「量」才看得见,看截图全是正常的。

A 设计令牌(app.css 为主)
- 字阶收敛到 6 档 --fs-xs/sm/md/lg/xl/2xl = 11/13/15/21/26/34px。原来 app.css 有 19 种字号、
  模板里另有 54 处内联字号,光 12~15px 之间就挤了 4 档 —— 层级挤不出来,只能靠加粗硬顶。
- 两个「字外档」不是漏的:--fs-control: 16px(窄屏表单控件必须 ≥16px,否则 iOS 聚焦放大整页,
  属功能约束);--glyph-*(✕/箭头/空态图标按控件尺寸定,硬塞进字阶只会把按钮撑坏)。
- 字重收敛到 400/500/600(删掉 650 与 700)。
- 圆角 4 档 --r-*、阴影 3 档 --sh-1/2/3(暗色整套覆盖)、间距 --sp-1..5、
  卡片内边距单列 --card-pad(与折叠卡片标题行的负 margin 绑在一起,改一个即改两处)。
- 门禁 TestNoBareFontSizePx:静态扫 app.css 与全部模板,出现 font-size: <数字>px 即红。

B 卡片分主次
- .card--hero / .card(默认)/ .card--quiet 三档。quiet 走「页面底色 + 细边 + 无阴影」——
  第一版拿 --bg-2 当底色,实测它与页面底 #f6f7f8 只差 4/255,看不出边界、像破了个洞。
- 首屏 KPI 从「4×2 等重白盒」改成 .kpi-lead(主数 + 一行上下文)+ 5 个常规指标,8 个数字一个没少。

C 图表说人话
- 月度分布:柱底加基线;峰值柱不透明拉满、柱顶标次数(.pv),副标题里不再重复写「最高 N 次」。
- 一天中的分布:把「0/6/12/18 四个数字」换成 6 小时一段的标尺(深夜/上午/下午/晚上),
  用同一套 24 列网格,段边界与柱子严格对齐。
- 首页月度图同样加峰值锚点(给 HomeData 补 MaxMonthly,与统计页同口径 maxOf)。
- 空态没动:查下来已经统一(8 页共用 emptystate、局部走 .empty)。

D1 类型色语义化(结论与计划不同,按实测改了)
- 先算对比度:白底上 yellow 1.87 / mint 2.12 / orange 2.20 / green 2.22 / cyan 2.54 / teal 2.57,
  6 个色连图形对象 3:1 都到不了;暗底只有 indigo 2.90 不够。
- 所以**没有**把类型色用到 chips / 表格类型列 / KPI 强调 —— 那几处要的是文字色,只会更难读。
  这也解释了全站为什么一直只用 8px 小圆点做身份标记。浅色那 6 个色一个没动:
  它们是手机端与地图上的同一套身份色,改暗会破坏对应关系。
- 实际做的:色值收口成 --cat-* / --cat-*-soft(26 个变量);模板 colorof → csscolor / csssoft(19 处);
  暗色只提亮 indigo(2.90 → 3.55:1);colorOf 成了死代码,删掉。
- ⚠️ 返回值必须是 template.CSS:html/template 的 CSS 过滤器不认识 var(),普通 string 会被静默
  换成 ZgotmplZ —— 页面上所有圆点当场变透明,而且不报错、模板测试照过。挂了它等于关掉那道过滤,
  所以数据侧色值先过严格校验(只有 # + 6 位 hex 放行),否则 #"><img… 能逃出 style 属性。
  守卫 TestCSSColorSurvivesTemplateSanitizer 把两个方向都钉住(做过变异验证)。

D2 窄屏导航
- 改前 ≤620px 靠横向滚动兜底:390 溢 63px、320 溢 133px,「统计」被切成半个字紧贴后面的按钮,
  渐隐提示实际看不出来。现在链接本体抽成 {{define "navitems"}} 渲染两遍:桌面 .nav-links 平铺、
  窄屏 .nav-menu(原生 <details>,不靠 JS 也能开合);JS 只补点外面 / Esc / 点链接后收起。
- ⚠️ 顶栏 --z-nav 抬到 1200:菜单是 .nav 的子元素、自己抬不动层叠上下文,不抬就会被地图页的
  --z-map-overlay(1100) 盖住。已在 /map 上用 elementFromPoint 实测。
- 守卫 TestNarrowNavCollapsesToMenu 钉「CSS 两半 + 模板两个类 + 渲染两遍」的成对关系。

途中发现并一并修掉的(都只有量 DOM 才看得见)
- Leaflet 的 .leaflet-container 字号是 0.75rem、跟着根字号漂:根 14 → 13px 后地图署名/比例尺
  掉到 9.8px(低于本站 11px 下限)、弹窗正文也跟着缩。已按自己的字阶钉死。
- h1..h4 原来只设了字重与 margin、没设字号,靠浏览器默认的 2em/1.5em/1.17em —— 同样跟着根字号漂:
  /stats 那两个 h2 从 21px 变成 19.5px,比同页卡片标题大一圈。已显式给出字号。
- <b>/<strong> 浏览器默认 700,比全站最高层级(标题 600)还重,已钉成 600。

回归
- ./scripts/verify.sh:PASS=348 FAIL=0 SKIP=0
- internal/fog 加 -race:PASS=22 FAIL=0 SKIP=0
- JS 单测 34/34
- 移动端 320 / 390 溢出全 0(改前 /admin 在 320 还溢 34px);桌面 1440 与改前一致(−15 是滚动条宽)
- 全站 7 个页面都没有低于 11px 的文字

说明:A/B 与 C/D 本想拆两笔提交,但 app.css / layout.html / view.go 里两批改动交织,按文件拆不开、
按 hunk 拆又容易做出「中间那笔编译不过」的历史,所以合成一笔、在消息里分段。
2026-10-09 17:01:08 +08:00

187 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 2026-10-09
## 排查:/admin/settings 报错(旧二进制 + 新模板)
**现象**:「统计脚本做成后台开关」那次提交(4015ddf)之后,访问 /admin/settings 报错。
**根因(已实测复现)**:跑的是**旧二进制**(根目录 `rond.exe`,10-08 18:06 编译,早于 18:27 的提交),
磁盘上的模板却是新的。`debug=true` 下 `pageSet()` 每次渲染都从磁盘重解析模板,
于是新 `settings.html:148` 引用了旧 `setting.Settings` 结构体里没有的 `TrackEnabled` → 执行期报错:
```
渲染 admin/settings 失败: template: settings.html:148:85: executing "content"
at <.Settings.TrackEnabled>: can't evaluate field TrackEnabled in type setting.Settings
```
`render` 是流式直写 ⇒ header 已发(HTTP 200)但表单在「统计脚本」卡片处截断,
没保存按钮、没 `</form></html>`。这就是用户看到的「报错」。
**判据**:`strings <二进制> | grep -c cleanTrackScript` —— 旧根二进制 0,新编译的 2。
(注意别用 `strings | grep TrackScript` 判定,config 里本来就有这个名字,旧二进制也命中。)
**修复**:`go build -o rond.exe .`(+ `bin/rond.exe`),重启。旧二进制备份到
`bin/rond.exe.old-20261008-stale`。修后 /admin/settings 完整(19642B,含 `</html>`/保存按钮)。
**教训**:Go 结构与模板字段是成对改的,`debug=true` 的热加载只覆盖模板 —— 改 Go 必须重编译重启,
否则就是「新模板 + 旧结构 = 执行期报错」。想根治可在启动时把所有页面各渲染一遍做冒烟(现在没有)。
## 其他发现(未改代码)
1. **P1 待确认**:`settings` 行 `pages=null`(写入时间 2026-10-08 18:14:05)⇒ 访客侧
`/map /places /timeline /stats /report` 全部 404、导航只剩 1 个链接。代码语义是
「全不选 = 访客只能看主页」,属合法状态,像是测 404 分档时留下的。**没动数据,等确认**。
2. **P2**:`cleanTrackScript` 的 `src=` 检查是**全文 `strings.Index`**,会误伤脚本体内的
`src=`。实测:`<script>var i=new Image();i.src=u;</script>` → 返回 `""`(整段被静默清空),
而页面仍提示「设置已保存」⇒ 静默失效。建议只在标签属性里查 src,或把「被清空」回传前端显式提示。
3. **P2**:开闭标签只拒 `shut > open`;末尾未闭合 `<script>`(`open > shut`)会吞掉后面内容。
实测 `<script>x</script><script>y` 与 `<script>y` 都被放行。建议改成要求 `open == shut`。
4. **运维**:根 `rond.exe` 与 `bin/rond.exe` 两份二进制容易走偏(这次根目录那份就是旧的)。
考虑只保留 `bin/`,或在启动日志打印构建时间便于比对。
**回归**:`go vet` 干净、`gofmt -l` 空、JS 单测 34/34、`go test ./...` 13 包全 ok 0 FAIL。
(注:`internal/web` 里 110 例是 DB 门控的,不开 `ROND_TEST_CONF`/`ROND_SRC` 会 skip,不算真跑过。)
---
## 用户确认后的收尾(四件)
用户反馈:「管理员访问也只剩主页,这好像不对」+ P2 按建议修 + 二进制只留根目录 + 防复发一并做。
### 1. P1 更正:不是配置问题,是导航缺 IsAdmin 分支
上面第 1 条判断得不完整。实测:站长访问 /map /places /timeline /stats /report **全是 200**
(`guardPage` 只拦访客),但导航里只有「主页」⇒ `layout.html` 的页面显隐写的是
`{{if .Settings.HasPage "x"}}`,**没有 IsAdmin 分支**,与 `guardPage` 口径不一致。
改成 `{{if or .IsAdmin (.Settings.HasPage "x")}}` 五处。门禁 `nav_visibility_test.go`
(三向不变量 + 变异验证)。**访客侧的 pages=null 没动**(仍是「只对访客关」的配置状态)。
### 2. P2 修复:`cleanTrackScript` 重写
- `src=` 只在 `<script …>` **开标签内部**找(原来对整段 `strings.Index`,`i.src=u` 被误伤清空)。
- 开闭标签从「只拒 `shut > open`」改成**严格配平**(末尾未闭合的开标签原来会被放行)。
- 签名改 `(script, warn string)`,说明码经 `?warn=` 回显成人话;判危险时**整段不采纳、
库里原脚本与开关不动**。
- 实测(测试实例 8081 / `rond_fog_test`,用完已还原):`i.src=u` 现在原样落库;
不配平 → `?warn=unbalanced` 且库值不变;`javascript:` → `?warn=badsrc` 且库值不变;
5000 字符 → `?warn=truncated` 且落库 4096。注入范围精确复核:只在公开页(/、/map),
后台与登录页 0。
### 3. `bin/` 删除
15 个 exe、412MB 全删;只保留根 `rond.exe`。技能 `rond-web-verify` 与 details 十八的
`bin/rond.exe` 写法已同步改掉。
### 4. 防复发:启动自检 `smokeRender`
`template_smoke.go`:`pageProbe`(24 个页面各一份最小可用数据,键与 `pageFiles` 双向校验)
+ `New()` 里全量渲染一遍,失败即 `log.Fatalf` 拒启。实测:往 settings.html 插一个不存在的
字段 → 启动报 `模板自检未通过…admin/settings: can't evaluate field …`,退出码 1。
门禁 `template_smoke_test.go`(含变异验证:把探针换成字段更少的类型必须报错)。
### 5. 用户裁决 + 文案订正
- **访客侧 `pages=null` 不动**:用户明确说「访客那一侧是根据我在后台配置的显示范围公开的,
倒不如说原来默认就是这样才对」⇒ 那是**预期配置**,不是故障。已把这个结论写进
details 四十一,免得后续会话又去「修」数据。
- **settings.html 的统计脚本文案订正**(P3):原文两处不准 ——
①「多一个 `</script>` 会被判为粘贴错误」漏了「少一个也会拒」(判据已改成严格配平);
②「`conf/app.ini` 的 `track_script` 会作为这里的默认值」有歧义:它**不会回填文本框**,
而是「这个框留空时,注入时回落到 ini 的值」。已改成准确说法。
实测热加载生效(debug=true,无需重编),页面 19996B、含 `</html>`,旧文案 0 命中。
- 验收:官方 `./scripts/verify.sh` → **PASS=346 FAIL=0 SKIP=0**(改动前那次全量;
之后只动了这一个模板文案,重跑了全部模板门禁与 `TestSmokeRender*`,全绿)。
## 提交 + 前端现代化方案
**提交**:`d4e4257`「启动模板自检 + 站长导航放行 + 统计脚本关口收紧」(12 文件,+564/−75)。
⚠️ **`shots/` 里的截图是旧状态,做视觉判断前必须现场重截**:`mobile-home.png` 还显示顶栏
折成 3 行、右按钮竖排,实测现在 390 下品牌名只留 🐾、按钮「后台」、`navH=51` 且不溢出。
差点拿旧图当现状写进方案。
**现状实测(CDP,真实视口)**:
- 卡片:17 个盒子(`.card`/`.kpi`/`.filters-card`)**只有 2 种样式指纹** ⇒ 层级扁平。
- 字阶 8 档(10/12/13/14/15/18/21/23),10–15px 挤了 5 档;字重 400/500/600/**650**。
- 正文色只有 4 种;类型色 10 种只用在圆点与柱子,没进界面语义层。
- 站长导航 `scrollWidth=277`:430→254(溢 23)、390→214(溢 63)、320→144(溢 133);
390 下「统计」被切成半个字紧贴「后台」,`.overflow-right` 的渐隐看不见。
- 已有优点别改坏:KPI 已是 `tabular-nums`、`--text-3` 按 AA 调过、暗色模式已有 3 段 media。
**产出**:`frontend-plan-2026-10-09.md`(仓库根,与 `audit-2026-09-21.md` 并列)。
四批:A 设计令牌收敛(字阶砍到 6 档、字重 3 档、间距/圆角/阴影令牌)→ B 卡片分 hero/常规/次级
+ 首屏 1 个大数 → C 手写图表加基线/峰值/轴标(不上图表库)→ D 类型色语义化 + 窄屏导航。
**明确不做**:前端框架/构建链、图表库、信息架构重构。建议先做 A+B(约 1.5 天,纯 CSS + 少量 class)。
方案**未改任何代码**,等选批次。
---
## 落地:A + B 批次(用户选了「先按 A-B 去做」)
**做法**:先跑基线存档(`ab.js` 探针 + `audit/run_all.py --mobile 390` / `--overflow 1440`),
再改,再用同一份探针跑一遍逐值 diff。CSS 改动量 187+/150−,另有 19 个模板共 54 处内联字号并档。
**关键结果**(同一探针,改动前 → 后):
- `/` 的 (字号,字重) 组合 390:**13 → 10**、1440:**15 → 10**;`/places` 10 → **5**;
`/timeline` 10 → **6**;`/stats` 15 → **9**;`/admin/settings` 10 → **7**。
- 最小字号到处都 ≥11px(原来有 10 / 10.5px)。
- 卡片样式档:`/` 2 → **3**,`/admin/settings` 1 → **2**。
- 溢出:移动端审计**逐值一致**;桌面 1440 唯一差异是 `/map` 露外元素 9 → 8。
- 验收 `./scripts/verify.sh`:**PASS=347 FAIL=0 SKIP=0**。
**踩到的四处**(都只有「量 DOM 的 (字号,字重) 组合」才看得见,截图上看都正常):
1. **Leaflet 字号跟着根字号漂**:vendor `.leaflet-container { font-size: .75rem }`,
根 14 → 13px 后地图署名/比例尺掉到 **9.8px**、弹窗正文也跟着缩。
已在 app.css 钉死(容器 `--fs-sm`、署名/比例尺 `--fs-xs`)。
2. **项目自带的静态守卫抓到我**:`TestNavLinksNarrowRuleHasNoDeadPadding` 写死了 `font-size: 13px`,
并档后它红了。守卫是对的,改判据为「钉规则还在、且设了字号」;另补 `TestNoBareFontSizePx`
(静态扫 app.css + 模板禁裸 px 字号,做过变异验证)。
3. **`h1..h4` 从没设过字号**(原来只有字重与 margin),靠浏览器默认 `2em/1.5em/1.17em` ⇒ 也跟着根字号漂:
根 14 → 13px 后,`/stats` 里没被 `.card > h2` 命中的两个 h2 从 21px 变成 19.5px,比同页卡片标题(15px)大一圈。
已显式给出 h1/h2/h3 字号,实测 12 个 h2 统一 `15px/600`。
4. **`<b>/<strong>` 浏览器默认 700**,比全站最高层级(标题 600)还重 ⇒ 已钉成 600。
**教训(已写进 details 四十二)**:quiet 卡片第一版用 `--bg-2`(#fafbfb),
与页面底 #f6f7f8 只差 4/255 ⇒ 实测看不出边界、像「破了个洞」。**配色差异必须量,不能凭「应该能看出来」。**
**运维提醒**:测试库跑到 180MB / 21 万死元组时 `verify.sh` 会从 8 分钟拖到 12 分钟以上
(脚本 200MB 才自动 VACUUM);手动 `VACUUM (ANALYZE)` 后回到 ~10 分钟、137MB / 0 死元组。
**发现未修**:7 个模板工作区里是 CRLF,而 `.gitattributes` 要求 `eol=lf`
(`admin/index.html`、`admin/login.html`、`admin/twofa-login.html`、`admin/twofa.html`、
`map.html`、`places.html`、`timeline.html`)。本批按字节替换保住了各自行尾、diff 无行尾噪声,
要不要统一另开一次清理。
---
## 落地:C + D 批次(用户选了「C和D继续做」)
**C 图表**:月度分布加基线 + 峰值柱标次数(`.pv`,副标题不再重复写「最高 N 次」);
一天中的分布把「0/6/12/18 四个数字」换成 **6 小时一段的标尺**(深夜/上午/下午/晚上),
用**同一套 24 列 grid** 保证段边界与柱子严格对齐;首页月度图同样加峰值(给 `HomeData` 补了 `MaxMonthly`)。
**空态那条划掉了** —— 查下来已经统一(8 页共用 `emptystate`、局部走 `.empty`)。
**D1 类型色**:先算对比度,结论推翻了原计划 ——
- 白底上 6 个色低于图形 3:1(yellow 1.87 / mint 2.12 / orange 2.20 / green 2.22 / cyan 2.54 / teal 2.57)
- 暗底只有 indigo 2.90 不够
所以**没有**把类型色做成 chips / 表格类型列 / KPI 强调(那几处要的是文字色,会更难读)——
这恰好解释了全站为什么一直只用 8px 小圆点做标记。浅色 6 个色一个没动(是手机端/地图的同一套身份色)。
实际做了:色值收口成 `--cat-*` + `--cat-*-soft`(26 个变量)、模板 `colorof` → `csscolor`/`csssoft`(19 处)、
暗色只提亮 indigo。`colorOf` 已成死代码,删掉。
⚠️ **踩到 `ZgotmplZ`**:`html/template` 的 CSS 过滤器不认识 `var()`,函数返回普通 string 会被替换成
`ZgotmplZ`,页面上所有圆点当场变透明、**且不报错、模板测试照过**。
必须返回 `template.CSS`;而挂了它等于关掉过滤,所以数据侧色值先过严格校验(只有 `#`+6 位 hex 放行)。
已加守卫 `TestCSSColorSurvivesTemplateSanitizer`(两个方向都钉,做过变异验证)。
**D2 窄屏导航**:≤620px 整行收进 `.nav-menu`(原生 `<details>`,JS 只补点外面/Esc/点链接后收起)。
⚠️ 顶栏 z-index 抬到 `--z-nav: 1200`(必须高于地图浮层 1100,否则展开的菜单被筛选面板盖住),
已在 `/map` 用 `elementFromPoint` 实测。改前 390 溢 63px、320 溢 133px(「统计」被切半个字)。
**回归**:`verify.sh` **PASS=348 FAIL=0 SKIP=0**;320/390 溢出**全 0**(改前 `/admin` 在 320 还溢 34px);
桌面 1440 与改前一致;**7 个页面都没有低于 11px 的文字**;暗色下菜单/类型色/阴影均按暗色变量生效。