地点库 / 时间线 / 统计:窄屏筛选卡默认收成一条(与地图页筛选面板同形)

手机上这三页共用的筛选卡一上来就占掉大半屏(时间预设 + 已选条件 + 类型 chips + 六个输入框,
实测 945px),内容反而看不见。按地图页那套 .panel-fold 收成一条:

- 折叠条显示「筛选 + 当前时间范围〔+ N 个条件〕」,展开/收起箭头都能点;
- 状态记在 localStorage('rond-filters-fold'),翻页/换页不会又自己弹开;
- 收起只是 CSS 隐藏,表单控件照旧随提交带上(act/hw/sort 那几个 hidden 也在);
- 整组显示规则都在窄屏媒体查询里 ⇒ 桌面端 .collapsed 不生效、折叠条也 display:none,零影响;
- 折叠脚本紧跟表单(放 body 末尾会先按展开画一帧再收起,手机上就是闪一下整张大卡)。

实测:390 三页站长/访客都默认收起、条高 44、内容隐藏、滚动时粘在导航下方(top=50);
筛选卡 945px → 46px;1440 无变化;320/350 摘要不溢出;全站审计 320/390/1440 溢出全 0、
0 JS 报错、小触控只减不增(A/B 31/181/82 vs 35/185/86);关键词回车提交未受影响
(折叠条是 type="button",默认按钮仍是「查询」)。

新增回归 internal/web/filters_fold_test.go(三页的挂载点 + 筛选控件仍在,做了变异验证)。
This commit is contained in:
tamakyi
2026-09-18 21:52:29 +08:00
parent 86e420bd4e
commit e8cb94230a
3 changed files with 103 additions and 1 deletions
+50
View File
@@ -0,0 +1,50 @@
package web
import (
"context"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
// TestFiltersFoldHook 盯住「地点库 / 时间线 / 统计三页共用筛选卡在窄屏默认收起」的挂载点。
//
// 折叠是纯前端的事:CSS 认 `.filters-card.collapsed`,脚本认 `#filters-fold`,两边都靠
// 模板里的类名/ID 挂钩子。谁哪天把表单的 class 换了、或者把这个按钮删了,页面照样能渲染、
// 桌面端也看不出问题 —— 只有手机会退化成「永远展开」,所以在这里钉住。
//
// ROND_TEST_CONF=conf/app-test.ini ROND_SRC=data/xxx.rondbackup go test ./internal/web -run FiltersFold -v
func TestFiltersFoldHook(t *testing.T) {
f := newMovementFixture(t)
for _, path := range []string{"/places", "/timeline", "/stats"} {
var h http.HandlerFunc
switch path {
case "/places":
h = f.srv.placesPage
case "/timeline":
h = f.srv.timelinePage
case "/stats":
h = f.srv.statsPage
}
// 访客身份:这三页公开,折叠对访客同样生效
w := httptest.NewRecorder()
h(w, httptest.NewRequest("GET", path, nil).WithContext(context.Background()))
body := w.Body.String()
if w.Code != 200 {
t.Fatalf("%s 渲染失败: code=%d", path, w.Code)
}
for _, want := range []string{
`class="card filters-card"`, // CSS 的挂载点
`id="filters-fold"`, // 折叠条
`id="filters-fold-caret"`, // 箭头(脚本要改它的字)
`id="filters-form"`, // 脚本靠它找表单;收起时表单控件仍在里面
`rond-filters-fold`, // 记收起状态的本地键:翻了页不该又弹开
`name="q"`, // 收起只是 CSS 隐藏,筛选控件得照旧提交得出去
} {
if !strings.Contains(body, want) {
t.Fatalf("%s 里找不到 %q:窄屏筛选折叠的挂载点断了", path, want)
}
}
}
}
+13
View File
@@ -993,4 +993,17 @@ label.f > span.span-actions { display: flex; align-items: center; justify-conten
/* 导航左上角那个 logo 是回首页的入口,原来只有 25×29:给它撑到 44 高,
横向也补到 44(.brand .t 在窄屏被隐藏,加内边距就够了)。 */
.brand { min-width: 44px; min-height: 44px; justify-content: center; }
/* ---------- 地点库 / 时间线 / 统计三页共用的筛选卡:窄屏默认收成一条 ----------
与地图页那块 .panel-fold 同形——手机上它一上来就占掉大半屏(预设 + 已选 + 类型 + 六个
输入框),内容反而看不见。展开时折叠条用负外边距贴到卡片上沿(圆角照着卡片补,
卡片没有 overflow:hidden,否则折叠条就粘不住了);收起时卡片内边距归零、只留那一条。
top 取 --nav-h:导航是 sticky 的,写 0 会被压在它下面。
这些规则整条在媒体查询里 ⇒ 桌面端 .collapsed 不生效,等于没影响。 */
.filters-card > .panel-fold {
margin: -18px -18px 14px; border-radius: var(--radius) var(--radius) 0 0; top: var(--nav-h);
}
.filters-card.collapsed { padding: 0; }
.filters-card.collapsed > .panel-fold { margin: 0; border-radius: var(--radius); }
.filters-card.collapsed > :not(.panel-fold) { display: none; }
}
+40 -1
View File
@@ -25,7 +25,15 @@
{{end}}
{{define "filters"}}
<form class="card" method="get" action="{{.FilterBase}}" id="filters-form">
<form class="card filters-card" method="get" action="{{.FilterBase}}" id="filters-form">
{{/* 窄屏折叠开关,与地图页筛选面板同一套(.panel-fold):桌面端它 display:none、表单照旧常开;
手机上默认收成这一条,只留「筛选 + 当前范围 + 箭头」。收起只是 CSS 隐藏,
表单里的控件都还在,筛选条件照旧随提交带上(act/hw/sort 那几个 hidden 也在)。 */}}
<button class="panel-fold" id="filters-fold" type="button" aria-controls="filters-form" aria-expanded="true">
<span>筛选</span>
<span class="pf-n">{{.RangeLabel}}{{if .ActiveChips}} · {{len .ActiveChips}} 个条件{{end}}</span>
<span class="pf-c" id="filters-fold-caret" aria-hidden="true">▾</span>
</button>
<div class="chips" style="margin-bottom:12px">
{{range .Presets}}<a class="chip {{if .Active}}on{{end}}" href="{{$.RangeLink .From .To}}">{{.Label}}</a>{{end}}
<span class="chip" style="background:transparent;color:var(--text-3)">{{.RangeLabel}}</span>
@@ -104,6 +112,37 @@
{{if .ShowHomeWork}}<input type="hidden" name="hw" value="1">{{end}}
{{if .Sort}}<input type="hidden" name="sort" value="{{.Sort}}">{{end}}
</form>
{{/* 窄屏折叠:脚本必须紧跟这个表单 —— 放到 body 末尾时,手机上会先按「全部展开」画一帧
再收起,看着就是闪一下整张大卡(地图页那块面板也是贴在面板后面)。收起态记在本地,
翻页/换页之后不该又自己弹开。 */}}
<script>
(function () {
var form = document.getElementById('filters-form');
var btn = document.getElementById('filters-fold');
if (!form || !btn) return;
var caret = document.getElementById('filters-fold-caret');
var KEY = 'rond-filters-fold';
var desk = window.matchMedia('(min-width: 621px)');
var saved = null;
try { saved = localStorage.getItem(KEY); } catch (e) { /* 隐私模式禁用了,忽略 */ }
function apply(collapsed) {
form.classList.toggle('collapsed', collapsed);
btn.setAttribute('aria-expanded', String(!collapsed));
btn.title = collapsed ? '展开筛选' : '收起筛选';
if (caret) caret.textContent = collapsed ? '▸' : '▾';
}
// 没记过状态时窄屏默认收起。桌面端这条 class 不生效(显示规则整条在窄屏媒体查询里),
// 所以同一个浏览器在手机上收起过、回桌面照样看得见筛选。
apply(saved === null ? !desk.matches : saved === '1');
btn.addEventListener('click', function () {
var collapsed = !form.classList.contains('collapsed');
apply(collapsed);
try { localStorage.setItem(KEY, collapsed ? '1' : '0'); } catch (e) { /* 同上 */ }
});
})();
</script>
{{end}}
{{define "emptystate"}}