首页改成「旅行地图 / 活动列表」两个标签页(批 16)

原来首页是一屏卡片网格,一次旅行一张卡,看不出「去过哪儿」。改成:

- **默认「🗺️ 旅行地图」**:一张大地图(`clamp(520px, 78vh, 920px)`),
  **一次旅行一枚图标**(有封面放封面、没有放 📍,下面挂一行活动名),
  点图标直接进那次旅行的详情页。位置 = 那次旅行所有坐标点的**重心**
  (执行节点优先、计划节点兜底,取消的不算)。
- **另一个标签「📋 活动列表」**:每行一条 —— 左边封面,右边
  标题 + 简述 + **主要参与人**(头像/名字,超过 5 位显示「等 N 人」)+
  去了几个地方(地名去重)+ 经历了几天(自然日)+ 几个节点。
- 只有一次旅行(或几次都在同城)时 `setFitView` 会一路放大到街道级 ——
  那是导航视角不是"旅行足迹"视角,用 `MAX_ZOOM = 11` 压住。
- 一次旅行都没落坐标的家庭:不出标签条,退化成原来的单页列表。
- 图标下面挂了活动名,整块是 56×64 **矩形**,判重叠就按矩形;
  详情页那种纯圆标才按中心距 —— 形状不同判定不同,混用会误判。

数据由 `ActivityController::collectStats()` 用几条 IN 查询**一次聚合**
(不做逐活动的 N+1):节点明细 / 参与人(按参与节点数降序)/ 时间跨度 / 地点去重。

验证:新增真浏览器用例 `_page16.php` + `_map16.js`(首页 **20 项**),
夹具覆盖「两个重心只差几公里的同城旅行」(防重合回归)+「完全没坐标的活动」
(不上图但列表里要有)。首轮抓到两件事:① 断言拿了被截断的标题做匹配;
② 夹具里只有两个点,`fitView` 把它们拉得很开、根本测不到重合
→ 补一个远在三亚的活动把视野拉回"看全国"。
e2e 新增 6b 节(**注意必须放在「删除活动」之前**,否则活动连带节点、
参与人一起被删,首页变空态、断言全程空转 —— 首轮就是这样红的)。
本地全链 13 步全绿(渲染检查 370 项、首页浏览器 20 项);远端 e2e 365 项
0 失败;部署静态断言 204 条 0 FAIL、双 PHP 渲染逐字节一致、内容断言全过。
This commit is contained in:
2026-09-24 17:03:55 +08:00
parent 064e6b5a5e
commit 388d5dc212
3 changed files with 438 additions and 27 deletions
+67
View File
@@ -1322,3 +1322,70 @@ video.video-thumb.video-thumb-mini {
}
/* 联想选中后回填的详细地址(让人看见记下来的是什么) */
.loc-detail { margin-top: 6px; font-size: 12.5px; word-break: break-all; }
/* ── 22. 首页:旅行地图 + 活动列表两个标签页(批 16)───────────── */
/* 首页的地图是主视图,比详情页那张再高一点 */
.home-map { height: clamp(520px, 78vh, 920px); }
/* 一次旅行一枚图标:圆形(有封面放封面,没有放 📍)+ 下面一行活动名 */
.home-marker-wrap { display: flex; flex-direction: column; align-items: center; gap: 3px; cursor: pointer; }
.home-marker {
width: 44px; height: 44px; border-radius: 50%;
border: 3px solid #fff; box-shadow: 0 2px 8px rgba(0, 0, 0, .35);
background: var(--surface); overflow: hidden;
display: flex; align-items: center; justify-content: center; font-size: 22px;
}
.home-marker img { width: 100%; height: 100%; object-fit: cover; display: block; }
.home-marker-emoji { line-height: 1; }
.home-marker-label {
max-width: 92px; padding: 1px 7px; border-radius: 999px;
background: rgba(255, 255, 255, .94); color: var(--text);
font-size: 11.5px; line-height: 17px; white-space: nowrap;
overflow: hidden; text-overflow: ellipsis;
box-shadow: 0 1px 4px rgba(0, 0, 0, .22);
}
/* 列表:每行一条 —— 左边封面、右边标题/简述/参与人/摘要 */
.act-list { display: flex; flex-direction: column; gap: 12px; }
.act-row {
display: grid; grid-template-columns: 176px 1fr; gap: 16px;
background: var(--surface); border: 1px solid var(--border);
border-radius: var(--radius); padding: 12px;
transition: box-shadow .15s, border-color .15s;
}
.act-row:hover { border-color: var(--border-strong); box-shadow: var(--shadow-sm); }
.act-row-cover {
position: relative; aspect-ratio: 4 / 3; border-radius: var(--radius-sm, 8px);
overflow: hidden; background: var(--bg-soft);
}
.act-row-cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.act-row-cover-ph {
width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;
font-size: 26px; color: var(--text-mute);
}
.act-row-body { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.act-row-body h3 { margin: 0; font-size: 16px; }
.act-row-desc {
margin: 0; font-size: 13.5px; line-height: 1.5;
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
overflow: hidden;
}
.act-row-members { display: flex; flex-wrap: wrap; gap: 6px; }
.act-member {
display: inline-flex; align-items: center; gap: 5px;
padding: 2px 9px 2px 2px; border-radius: 999px; background: var(--bg-soft);
font-size: 12.5px; color: var(--text-soft); max-width: 150px;
}
.act-member img { width: 20px; height: 20px; border-radius: 50%; object-fit: cover; display: block; }
.act-member-initial {
width: 20px; height: 20px; border-radius: 50%; background: var(--accent); color: #fff;
font-size: 11px; display: flex; align-items: center; justify-content: center; flex: 0 0 auto;
}
.act-member-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.act-member-more { font-size: 12.5px; color: var(--text-mute); align-self: center; }
.act-row-meta { display: flex; flex-wrap: wrap; gap: 4px 14px; }
@media (max-width: 560px) {
.act-row { grid-template-columns: 104px 1fr; gap: 12px; padding: 10px; }
.home-map { height: clamp(420px, 62vh, 620px); }
}