Files
FamilyBook/templates/trips/_map_body.php
T

80 lines
4.9 KiB
PHP
Raw Normal View History

<?php
/**
* 旅程**地图视图**的主体(v54 用户 2026-10-16 第 3 条)。
*
* 用户原话:「你去读一下 TripLog 的代码,在旅程这个模块,**可以在地图上显示旅程数据**。
* 1. 先读一下它之前那个代码是怎么实现的,**在我们的旅程模块也实现一下**。」
*
* ── TripLog 那套是怎么做的(读了它的 `templates/activities/index.php`)──
* · 用**高德 JS API**(`AMap.Map` + `AMap.Marker`);
* · 每个活动一枚标记;标记的 `content` 是自绘的"小卡片"(封面图 + 标题 + 地点),
* 同城/邻近的卡片会**环形扇开**+画引线,避免叠在一起;
* · `map.setFitView()` 自动框住所有点,但**上限 11 级**(不然只有一趟时会被
* 一路放大到街道级,那是导航视角不是"足迹"视角)。
*
* ── 本仓怎么落地(同一套思路,但数据源更规整)──
* · 坐标来源:`Trip::attachSpots()` —— 旅程下所有节点的 from/to 坐标**取重心**;
* 没有坐标就退到城市(内置离线城市表,**不发网络请求**,见那里的说明)。
* · 每趟旅程一枚标记,点它进 `trips/show`。
* · 🔴 v63(用户 2026-10-19 第 3 条):「旅程地图模式,应该显示旅程的题图。」
* ⇒ 卡片左侧加一枚**题图小方图**(`trip_details.cover_key`,与列表页同一个来源、
* 同一套 `media_url()` 出口)。没有题图就只显示文字,不留灰框。
* ⚠️ URL 在**服务端**算好塞进 JSON:`media_url()` 依赖站点路径与代理前缀
* (见那个函数的说明),端上拼不出来。
* · 视野上限同样 11 级、贴合全部标记。
*
* ⚠️ **合规**:地图用高德(腾讯/高德/百度/天地图是合规白名单内),
* 绝不用 Google/OSM 等境外瓦片。key 走本仓既有配置(`App\Geo::jsConfig()`),
* 由 `layout.php` 的 `fb-amap` 按需加载 —— 本页不自己拼 SDK 地址。
* ⚠️ 容器必须有**固定高度**(这里是 `.trip-map` 的 CSS 高度):
* 高德地图在"高度为 0 的容器"里渲染出来是空白,这是最常见的坑。
* ⚠️ 没有坐标的旅程**不画**(数据里说了 `trip_spot=false`);
* 若**一个点都没有**,不渲染地图容器,直接给一句说明(免得用户对着白框发呆)。
*
* 变量:$rows(Trip::all + attachDerived + attachSpots 之后)、$total
*/
$mapPoints = [];
foreach ((array)($rows ?? []) as $row) {
if (empty($row['trip_spot'])) {
continue;
}
$mapPoints[] = [
'id' => (int)$row['id'],
'title' => (string)$row['title'],
'lng' => (float)$row['trip_lng'],
'lat' => (float)$row['trip_lat'],
'when' => \App\Trip::activitySpan($row['starts_at'] ?? null, $row['ends_at'] ?? null),
'city' => (string)($row['city'] ?? ''),
'url' => url('trips/show', ['id' => (int)$row['id']]),
// 🔴 v63 第 3 条:题图。⚠️ 用 `media_url()` 而不是直接拼 storage 路径 ——
// 图片统一走 media.php 代理(桶不必开公共读),且**必须带版本号**,
// 否则换了题图缓存里还是老图。
'cover' => !empty($row['cover_key']) ? media_url((string)$row['cover_key']) : '',
];
}
?>
<?php if (!$mapPoints): ?>
<?php /* 一个坐标都没有 —— 如实说清楚"为什么没有",而不是给个空地图。
常见原因:行程里没填出发/到达地,或填了但(当时没配高德)没解析出坐标。 */ ?>
<div class="card card-pad empty" id="tripMapEmpty">
<b>还没有能在地图上显示的旅程</b>
地图要靠行程里的**出发地 / 到达地**(有坐标或能认出城市)。
去某趟旅程里加一段行程、把出发到达填上,它就会出现在这里。
</div>
<?php else: ?>
<?php /* 🔴 高度写在内联 style 上不够稳(有的主题会覆盖),
所以主要靠 `.trip-map` 的 CSS;这里只在**无 JS** 时兜一个提示。 */ ?>
<div class="trip-map-wrap">
<div class="trip-map" id="tripMapCanvas"
data-points="<?= e(json_encode($mapPoints, JSON_UNESCAPED_UNICODE | JSON_HEX_APOS | JSON_HEX_QUOT)) ?>">
<noscript><p class="trip-map-fallback">地图需要 JavaScript;用左侧的「列表」看旅程。</p></noscript>
</div>
<?php if (count($mapPoints) < (int)($total ?? 0)): ?>
<?php /* 有旅程不在图上 —— 说清楚,免得用户以为"少了几趟"。 */ ?>
<p class="small muted trip-map-hint">
有 <?= (int)($total ?? 0) - count($mapPoints) ?> 趟旅程没有可用位置(行程里没填出发/到达,或认不出城市),
没有画在地图上 —— 它们在「列表」里照常显示。
</p>
<?php endif; ?>
</div>
<?php endif; ?>