审计修复:前端(模板 / CSS / JS 与无障碍)

- 地图脚本:localStorage 读写走 lsGet/lsSet 兜底(隐私模式下裸访问会打断整段脚本);
  preview() 在 /api/map-config 还没回来时不丢点(previewQueue 在 if (live) 之前排队);
  异步回包一律发序号作废,取消后回来的包不再画
- photo-exif.js:EXIF 时间全 0 不再变成 1899-11-30 写进到访时间;读 count 前先按版本卡宽度
- 数据编辑页:三个筛选表单都把其余条件用隐藏域原样带上,提交不再把别的列表的筛选清掉
- 补全页:sessionStorage 的读写一律包 try(脏数据/隐私模式都会抛)
- 窄屏:删掉被后面同特异性规则盖掉的死内边距;宽表堆叠类名成对、判据扩到所有 tb 表
- 无障碍:图片删除按钮补 aria-label,访客画廊图片补 alt
- report.html 补上漏掉的 </tr>,fog-mask 的宽表带 tb-stack

本批不碰数据库与导出路径:往返包 823/323/8/30/791/557/2246 全等、
坐标偏差 0.000000000、时间偏差 0s、迷雾 8701 块一致。
This commit is contained in:
tamakyi
2026-09-24 20:37:57 +08:00
parent b4c2a8452f
commit d5f6dfa381
18 changed files with 675 additions and 77 deletions
+48
View File
@@ -1,9 +1,12 @@
package web
import (
"bytes"
"html/template"
"path/filepath"
"strings"
"testing"
"time"
)
// TestPageTemplatesParse 把每个页面模板都解析一遍。
@@ -17,3 +20,48 @@ func TestPageTemplatesParse(t *testing.T) {
}
}
}
// TestLayoutFooterToleratesMissingLastVisit 钉住页脚在「数据集一条到访都没有」时也渲染得完。
//
// last_visit_at 可以为 NULL(refreshDatasetAgg 用 LEFT JOIN 求 max,零到访就写 NULL),
// 而 d 的形参是 time.Time、实参是 *time.Time:非 nil 时模板会自动解一层指针,**nil 时报
// "dereference of nil pointer" 并中断渲染**。render() 只把错误记进日志,客户端拿到的是
// HTTP 200 加一个「页脚之后整段不存在」的页面——滚动恢复、地图脚本引入全丢,
// 服务端一点动静都没有。所以这里断言「有收尾的 </html>」,而不只是断言没报错。
func TestLayoutFooterToleratesMissingLastVisit(t *testing.T) {
base, err := template.New("layout.html").Funcs(templateFuncs()).ParseFS(tmplFS, "templates/layout.html")
if err != nil {
t.Fatalf("解析 layout.html: %v", err)
}
tpl, err := base.Parse(`{{define "content"}}正文{{end}}`)
if err != nil {
t.Fatalf("补 content 块: %v", err)
}
last := time.Date(2026, 9, 21, 12, 0, 0, 0, time.Local)
cases := []struct {
name string
ds *Dataset
want string
}{
{"没有数据集", nil, ""},
{"数据集里一条到访都没有", &Dataset{Name: "空数据集"}, ""},
{"正常", &Dataset{Name: "有数据", Last: &last}, "数据截至 2026-09-21"},
}
for _, c := range cases {
var buf bytes.Buffer
if err := tpl.ExecuteTemplate(&buf, "layout", Page{Nav: "home", Title: "测", Dataset: c.ds}); err != nil {
t.Fatalf("%s: 渲染失败: %v", c.name, err)
}
got := buf.String()
if !strings.Contains(got, "</html>") {
t.Errorf("%s: 页面被截断(没有收尾的 </html>)", c.name)
}
if c.want == "" {
if strings.Contains(got, "数据截至") {
t.Errorf("%s: 没有可信的截止时间时不该渲染「数据截至」", c.name)
}
} else if !strings.Contains(got, c.want) {
t.Errorf("%s: 页脚应含 %q", c.name, c.want)
}
}
}