# 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)但表单在「统计脚本」卡片处截断, 没保存按钮、没 ``。这就是用户看到的「报错」。 **判据**:`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,含 ``/保存按钮)。 **教训**: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=`。实测:`` → 返回 `""`(整段被静默清空), 而页面仍提示「设置已保存」⇒ 静默失效。建议只在标签属性里查 src,或把「被清空」回传前端显式提示。 3. **P2**:开闭标签只拒 `shut > open`;末尾未闭合 `` 会被判为粘贴错误」漏了「少一个也会拒」(判据已改成严格配平); ②「`conf/app.ini` 的 `track_script` 会作为这里的默认值」有歧义:它**不会回填文本框**, 而是「这个框留空时,注入时回落到 ini 的值」。已改成准确说法。 实测热加载生效(debug=true,无需重编),页面 19996B、含 ``,旧文案 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. **`/` 浏览器默认 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`(原生 `
`,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 的文字**;暗色下菜单/类型色/阴影均按暗色变量生效。 --- ## 提交 + 完整回归(含「原有代码是否受影响」专项) **提交**:`ab7ccd2`「前端改造:设计令牌 + 卡片层级 + 图表可读性 + 类型色收口 + 窄屏导航」 (32 文件,+997/−283)。A/B 与 C/D 本想拆两笔,但 `app.css`/`layout.html`/`view.go` 里两批改动交织, 按文件拆不开、按 hunk 拆容易做出「中间那笔编译不过」的历史,**合并成一笔、消息里分段**,并在提交消息里写明原因。 **完整验证链**(一次跑齐,全部串行): - `./scripts/verify.sh`:**PASS=348 FAIL=0 SKIP=0** - `./scripts/verify.sh --pkg ./internal/fog/ --race`:**PASS=22 FAIL=0 SKIP=0**(mingw 在 PATH 里,-race 可用) - JS 单测 34/34 - 移动端 320 / 390 + 桌面 1440 审计:**320/390 溢出全 0**;1440 只有 −15(滚动条宽,既有口径) **「原有代码受影响」专项检查**(静态扫描 + 逐文件回读 diff): - 自定义属性:定义 90 / 使用 61,**「用了但没定义」为 0**(先前一轮报的 12 个全是我正则的假阳性: CSS 一行写多个属性、`--cat-*` 是 Go 侧动态拼的、模板 `:root` 里也有定义)。 - 定义了没人用的:只剩 28 个 `--cat-*`(Go 动态拼接,运行时实测 14 种颜色都渲染出来了)—— 另外揪出 `--sp-5` 真没人用,已用到「停一下」页的留白上(正好是大间距场景)。 - 模板函数:**没有真孤儿**(先前列表是正则把 `appleColors` 的键当成了函数名)。 - 已删项残留:`colorof`/`--radius`/`--shadow`/旧测试名 全部 0 残留(`app.css` 里那句注释也顺手改对了)。 - 逐文件回读:19 个模板的 diff 只有字号替换与 `colorof`→`csscolor`,没有误伤; Go 侧 diff 只有 `MaxMonthly`、`cssColor/cssColorSoft`、注册表与 `regexp` 导入。 - 顺带发现(**既有、与本批无关**):`.g3`/`.g4` 是 HEAD 里就有的死类(模板/JS 都没引用); `.notice-card` 等不是死类 —— 它在 `notice.go` 里由 Go 拼 HTML,我的模板侧扫描覆盖不到。 **验证中抓到的一例失败(已实证是环境抖动)**: `TestOpenCreatesMissingDatabase` 报「建库后首次连接超时」10.02s。根因是 `db.Open` 的 ping **写死 10s**, 当时库正被验收 + autovacuum + CDP 审计一起压着;停掉审计单独跑 **4.1s 通过**。 ⚠️ 这条测试失败还会把临时库 `rond_test_autocreate_*` **留在库里**(清库的 defer 在 `Open` 成功之后才注册)——已清掉残留。 两条结论都写进了 details 三十二的验收小节。 --- ## 第三批:日期框(用户报的硬 bug)+ 两处观感 **用户报**:① 逐月平均气温图在桌面端很小;② 日期框敲 `20260606` 变成 `202606/06`(要「永远解决」); ③ 类型分布与一天中的分布同行高度不齐、下面一大片空白。 ### ② 日期框:**实测复现**后定位(写了真实按键工具 `storage/_type.py`) 技能里的 `--press` 只给 Enter 带 `text`,数字敲不进去(Chrome 不执行默认动作),所以单独写了一个 CDP 打字脚本(每字符 keyDown(text) + keyUp)。用它复现: - 敲之前 `INPUT|date|`、`attached=false`;敲完 `202606/06`、`value=""` —— 截图为证。 - **根因**:筛选区的「起始/结束日期」是原生 ``,而挂载代码只写了 `querySelectorAll('input[type=datetime-local]')` ⇒ **type=date 从没人管**。 Chrome 的年段允许 >4 位数字(年份上限 275760),8 位被切成 年=202606 + 月=06。 原生分段状态读不到、`setSelectionRange` 对 date 类输入直接抛 ⇒ 修不了,只能换控件。 **做法**:`datetime-input.js` 加只日期模式(`SPEC.date`:年/月/日,输出 `Y-m-d`)+ **统一入口 `attachAll(root)`** 按 `el.type` 分派;`attach` 里先把控件换成 `text`(引导会写 `2026-06-` 这种半成品, 原生控件对非法值一律置空)。`datetime-input.js` 改为**无条件引入**(公共页筛选区也要用), 挂载在 body 末尾**立刻**跑一次(别只等 DOMContentLoaded,否则先露原生框再变文本框)。 三个后台页改用 `attachAll`(顺带覆盖 `mfrom/mto`,且 date 类走 `enableTime:false`)。 **验证**:真实按键敲 20260606 → `value="2026-06-06"` ✓;端到端 `from=2025-08-01&to=2025-08-31` 筛出 **186 次**,与月度图峰值月(186)完全吻合 ✓(排除「解析成空导致恒 0」)。 守卫 `TestDateInputsGoThroughAttachAll`(模板出现按 type 挑的选择器即红;变异验证过)。 ### ① 气温图:10 个月只用了 1042px 里的 396px `.temp-col` 固定 36×84。改成宽屏 `flex: 1 1 36px; max-width: 96px` + 轨道 130px + 字号提到 `--fs-sm`; 窄屏保持固定宽 + 横滚(否则月份标签糊成一团)。 ### ③ 同行卡片高度不齐:grid 默认 stretch 「类型分布」1411px 把「一天中的分布」也拉成 1411px,后者内部一大片空白。 `.grid { align-items: start }` 后两项各按自然高(256px)。 **回归**:verify.sh 348 例 0 skip、JS 单测 **45/45**(新增 11 条逐键模拟)、移动端 390 / 桌面 1440 审计溢出与改前一致。 ⚠️ **纪律(又踩一次)**:验收跑着的时候别并发跑别的(我并发跑了变异测试,verify 当场把注入的旧写法抓成 FAIL —— 守卫是对的,但那轮结果得作废重跑)。这条上次已写进 details 三十二,这次是实锤。