diff --git a/assets/style.css b/assets/style.css
index ae892fd..cbbd7e6 100644
--- a/assets/style.css
+++ b/assets/style.css
@@ -1324,23 +1324,44 @@ video.video-thumb.video-thumb-mini {
/* 联想选中后回填的详细地址(让人看见记下来的是什么) */
.loc-detail { margin-top: 6px; font-size: 12.5px; word-break: break-all; }
-/* ── 22. 首页:旅行地图 + 活动列表两个标签页(批 16)───────────── */
-/* 一次旅行一枚图标:圆形(有封面放封面,没有放 📍)+ 下面一行活动名 */
-.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;
+/* ── 22. 首页:旅行地图 + 活动列表两个标签页(批 16,批 22 改卡片)── */
+/* 一次旅行一枚「方形小卡片」:左边封面图(没有放 📍),右边活动名 +
+ 地点/参与人摘要。同城/邻近的旅行被扇开到外围时,用一条引线连回真实位置
+ (线是 marker content 里的 SVG;外层画布 pointer-events:none,
+ 不挡底图拖拽,只有卡片本身可点)。 */
+.home-pin { position: relative; width: 600px; height: 600px; pointer-events: none; }
+/* AMap 的标记包装层 .amap-marker 尺寸跟着 content(600×600)走、默认可命中,
+ 会把别家卡片的点击整层挡住(amap-marker intercepts pointer events)。
+ 整层禁掉命中、只留卡片本身可点 —— 点击事件沿 DOM 冒泡,AMap 的
+ marker click 照常触发。 */
+#home-map-canvas .amap-marker { pointer-events: none !important; }
+.home-pin svg { position: absolute; left: 0; top: 0; overflow: visible; }
+.home-pin-line { stroke: #8aa0b8; stroke-width: 1.5; }
+.home-pin-dot { fill: #3b82f6; }
+.home-card {
+ position: absolute; transform: translate(-50%, -50%);
+ display: flex; align-items: center; gap: 7px;
+ padding: 5px 10px 5px 5px; background: rgba(255, 255, 255, .96);
+ border: 1px solid var(--border); border-radius: 10px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, .25);
+ cursor: pointer; pointer-events: auto;
+ width: max-content; max-width: 190px;
}
-.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);
+.home-card:hover { border-color: var(--accent); }
+.home-card-img {
+ width: 42px; height: 42px; border-radius: 7px; overflow: hidden; flex: none;
+ background: var(--bg-soft); display: flex; align-items: center; justify-content: center;
+ font-size: 20px;
+}
+.home-card-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
+.home-card-body { min-width: 0; }
+.home-card-title {
+ font-size: 12.5px; font-weight: 600; color: var(--text); line-height: 1.35;
+ max-width: 122px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
+}
+.home-card-sub {
+ font-size: 11px; color: var(--text-soft); line-height: 1.4; margin-top: 1px;
+ max-width: 122px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 列表:每行一条 —— 左边封面、右边标题/简述/参与人/摘要 */
diff --git a/templates/activities/index.php b/templates/activities/index.php
index fd80f03..6298bde 100644
--- a/templates/activities/index.php
+++ b/templates/activities/index.php
@@ -14,6 +14,9 @@ foreach ($activities as $a) {
'lat' => round((float)$st['lat'], 6),
'lng' => round((float)$st['lng'], 6),
'img' => !empty($a['cover_image']) ? media_cover((int)$a['id']) : '',
+ // 批 22:地图卡片右侧的简要参与人(前两位 + 总人数),与列表同一份聚合口径
+ 'members' => array_slice($st['members'] ?? [], 0, 2),
+ 'memberCount' => (int)($st['member_count'] ?? 0),
'url' => url('activities/show', ['id' => $a['id']]),
];
}
@@ -148,12 +151,17 @@ foreach ($activities as $a) {
}
if (!spots.length) return;
- /* 图标下面还挂了一行活动名,整块是 56×64 的**矩形**,所以这里按矩形判重叠。
- (详情页那两处是纯圆标,才按中心距判 —— 形状不同判定就不同,别混用。) */
- var MARK_W = 56, MARK_H = 64, STEP = MARK_W + 12;
+ /* ── 批 22:图标改成「方形小卡片」(左图右文:活动名 + 地点/参与人摘要)。
+ 同城/邻近的旅行在屏幕上挤在一起时,按**环形扇开**拉到外围规则排开,
+ 并用一条引线连回真实位置 —— 线画在 marker content 的 SVG 里,
+ position 仍是真实坐标(点卡片进对应活动,线只是视觉引导)。
+ 没被挤到的旅行不偏移也不画线,卡片就落在真实位置上。 */
+ var CARD_W = 186, CARD_H = 58; // 卡片名义尺寸(按中心距判碰用)
+ var CLUSTER_DIST = 110; // 中心距小于它 → 归为同簇(要扇开)
+ var CANVAS = 600, C = CANVAS / 2; // 每枚标记的 content 画布(真实位置在画布中心)
function shortTitle(t) {
- return (t && t.length > 8) ? t.slice(0, 8) + '…' : (t || '');
+ return (t && t.length > 10) ? t.slice(0, 10) + '…' : (t || '');
}
function esc(s) {
return String(s === null || s === undefined ? '' : s)
@@ -167,26 +175,52 @@ foreach ($activities as $a) {
map = new AMap.Map(el, { viewMode: '2D', zoom: 5 });
window.__homeMap = map;
- var markers = [];
- spots.forEach(function (s) {
+ function shortMembers(s) {
+ var names = (s.members || []).map(function (m) { return m.name; });
+ var t = names.join('、');
+ if (s.memberCount > names.length) t += ' 等 ' + s.memberCount + ' 人';
+ return t;
+ }
+ function cardHtml(s, dx, dy) {
+ var moved = Math.abs(dx) > 0.5 || Math.abs(dy) > 0.5;
var inner = s.img
? ''
- : '📍';
+ : '📍';
+ var sub = [];
+ if (s.place) sub.push(esc(s.place));
+ var mem = shortMembers(s);
+ if (mem) sub.push(esc(mem));
var tip = s.place ? s.title + '(' + s.place + ')' : s.title;
- var html = '