日期框换成自控输入(原生 date 会吞位)+ 气温图桌面端铺满 + 同行卡片不再被拉伸
三件事都由用户实测反馈,前两件先复现再动手。
一、日期框:敲 20260606 变成 202606/06(用户要「永远解决」)
- **实测复现**:写了个真实按键工具(CDP Input.dispatchKeyEvent 每字符带 text;
技能里的 `--press` 只给 Enter 带 text,数字敲不进去)。在 /stats 的「起始日期」上敲 20260606:
敲之前 `INPUT|date|`、attached=false;敲完显示 `202606/06`、`el.value` 是**空**(非法)—— 截图为证。
- **根因**:筛选区的起止日期是原生 `<input type="date">`,而挂载代码只写了
`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` 改为**无条件引入**(公共页筛选区也要用;flatpickr 仍按需加载 ——
没有它时只挂输入引导、不给日历,省 60 多 KB),挂载在 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 挑的选择器即红(做过变异验证);
JS 单测补 11 条逐键模拟(45/45)。
二、逐月平均气温图在桌面端很小
- 量出来的:10 个月固定 36px 宽,只占 1042px 里的 **396px**,右边 600 多像素全空。
- 窄屏保持「固定 36px + 横滚」(挤在一起月份标签会糊),宽屏改 `flex: 1 1 36px; max-width: 96px`
并把轨道从 84px 加到 130px、字号提到 `--fs-sm`。
三、类型分布与一天中的分布同行高度不齐
- grid 默认 `align-items: stretch`,把 256px 的「一天中的分布」拉到与 1411px 的「类型分布」一样高,
卡片内部留一大片空白。改成 `.grid { align-items: start }`。
回归:./scripts/verify.sh **PASS=349 FAIL=0 SKIP=0**;移动端 390 / 桌面 1440 溢出与改前一致。
⚠️ 两个验证纪律(都写进 details 三十二):
- 验收跑着的时候别并发跑测试/变异实验(两次踩到,其中一次变异正好被验收抓到,那轮结果作废)。
- **变异验证「内嵌文件」(templates/static)之后必须 `go clean -cache`**:`//go:embed` 的内容
在「改坏→还原」后,构建缓存会继续给出变异期间编译的那份 —— 实测同一个测试
`go test ./internal/web/` 绿、`go test ./...` 红,`go clean -testcache` 不管用。
This commit is contained in:
@@ -191,6 +191,76 @@ func TestNarrowNavCollapsesToMenu(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- 日期输入必须走统一入口 attachAll ----------
|
||||
//
|
||||
// 2026-10-09 用户报的 bug:筛选区是原生 <input type=date>,而挂载代码只写了
|
||||
// querySelectorAll('input[type=datetime-local]') —— **类型变体没人管**,于是 Chrome 的年段
|
||||
// 吞下 6 位数字,敲 20260606 变成 202606/06、el.value 还是空的(筛选条件静默丢掉)。
|
||||
//
|
||||
// 守卫三件事:
|
||||
//
|
||||
// ① 模板里不许再出现按 type 挑日期框的选择器 —— 那个选择器只能待在 datetime-input.js 里;
|
||||
// ② layout.html 必须**无条件**引入 datetime-input.js(公共页的筛选框也要用,那边不加载 flatpickr);
|
||||
// ③ 统一入口 attachAll 必须存在,且四类页面都调它。
|
||||
func TestDateInputsGoThroughAttachAll(t *testing.T) {
|
||||
js, err := staticFS.ReadFile("static/js/datetime-input.js")
|
||||
if err != nil {
|
||||
t.Fatalf("读取 datetime-input.js 失败: %v", err)
|
||||
}
|
||||
if !strings.Contains(string(js), "function attachAll(") {
|
||||
t.Fatal("datetime-input.js 里没有 attachAll —— 统一入口没了,以后新加的输入类型又会被漏掉")
|
||||
}
|
||||
|
||||
layout, err := tmplFS.ReadFile("templates/layout.html")
|
||||
if err != nil {
|
||||
t.Fatalf("读取 layout.html 失败: %v", err)
|
||||
}
|
||||
lay := string(layout)
|
||||
// flatpickr 那段是「按需加载」的,datetime-input.js 不能跟着一起被关进去
|
||||
if i := strings.Index(lay, "{{if .Flatpickr}}"); i >= 0 {
|
||||
if j := strings.Index(lay[i:], "{{end}}"); j > 0 {
|
||||
if strings.Contains(lay[i:i+j], "datetime-input.js") {
|
||||
t.Error("datetime-input.js 被关进 {{if .Flatpickr}} 了 —— 公共页不加载 flatpickr," +
|
||||
"筛选区的 <input type=date> 就没人改造成文本框,原生那个吞位的老毛病会原样回来")
|
||||
}
|
||||
}
|
||||
}
|
||||
if !strings.Contains(lay, "rondDatetime.attachAll(") {
|
||||
t.Error("layout.html 没有调用 attachAll —— 日期框不会在页面加载时被改造")
|
||||
}
|
||||
|
||||
names, err := fs.Glob(tmplFS, "templates/*.html")
|
||||
if err != nil {
|
||||
t.Fatalf("列举模板失败: %v", err)
|
||||
}
|
||||
adminNames, err := fs.Glob(tmplFS, "templates/admin/*.html")
|
||||
if err != nil {
|
||||
t.Fatalf("列举后台模板失败: %v", err)
|
||||
}
|
||||
sels := []string{"input[type=datetime-local]", "input[type=date]"}
|
||||
for _, name := range append(names, adminNames...) {
|
||||
b, err := tmplFS.ReadFile(name)
|
||||
if err != nil {
|
||||
t.Fatalf("读取 %s 失败: %v", name, err)
|
||||
}
|
||||
for _, sel := range sels {
|
||||
if strings.Contains(string(b), sel) {
|
||||
t.Errorf("%s 里又出现手写的日期选择器 %q —— 一律走 rondDatetime.attachAll(),"+
|
||||
"否则新加的输入类型会被漏掉(2026-10-09 的日期框 bug 就是这么来的)", name, sel)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, name := range []string{"templates/admin/edit.html", "templates/admin/photos.html", "templates/admin/topics.html"} {
|
||||
b, err := tmplFS.ReadFile(name)
|
||||
if err != nil {
|
||||
t.Fatalf("读取 %s 失败: %v", name, err)
|
||||
}
|
||||
if !strings.Contains(string(b), "attachAll(") {
|
||||
t.Errorf("%s 没有调用 attachAll(它自己那批日期框会退回原生控件)", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- 字阶纪律:app.css 与模板内联样式都只走令牌 ----------
|
||||
//
|
||||
// 改前 app.css 里散着 10 / 10.5 / 11 / 11.5 / 12 / 12.5 / 12.8 / 13 / 13.5 / 14 / 14.5 / 15 /
|
||||
|
||||
Reference in New Issue
Block a user