// pages/main/journal-map/index.ts import config from "../../../config/index"; import Time from "../../../utils/Time"; import { Journal, JournalPageType } from "../../../types/Journal"; import Toolkit from "../../../utils/Toolkit"; import { MapMarker } from "../../../types/UI"; interface LocationMarker { locationKey: string; // 位置键 "lat,lng" lat: number; lng: number; location?: string; journalIds: number[]; // 该位置的所有日记 ID count: number; // 日记数量 date: string; previewThumb?: string; // 预览缩略图 } interface JournalMapData { centerLat: number; centerLng: number; scale: number; markers: MapMarker[]; locations: LocationMarker[]; // 位置标记列表 customCalloutMarkerIds: string[]; // 改为 string[] 以支持 locationKey includePoints: Array<{ latitude: number; longitude: number }>; // 缩放视野以包含所有点 popupIds: number[]; popupVisible: boolean; isLoading: boolean; } Page({ data: { centerLat: 39.908823, centerLng: 116.397470, scale: 13, markers: [], locations: [], customCalloutMarkerIds: [], includePoints: [], selectedLocation: null, popupIds: [], popupVisible: false, isLoading: true, }, async onLoad() { await this.loadJournals(); }, /** 加载所有记录 */ async loadJournals() { this.setData({ isLoading: true }); try { const list: Journal[] = await new Promise((resolve, reject) => { wx.request({ url: `${config.url}/journal/list`, method: "POST", header: { Key: wx.getStorageSync("key") }, data: { page: 0, size: 9007199254740992, type: JournalPageType.PREVIEW }, success: (resp: any) => { if (resp.data.code === 20000) { resolve(resp.data.data.list); } else { reject(new Error(resp.data.message || "加载失败")); } }, fail: reject }); }) || []; // 过滤有位置信息的记录,并按位置分组 const locationMap = new Map(); list.filter((journal: any) => journal.lat && journal.lng).forEach((journal: any) => { // 保留 6 位小数作为位置键,约等于 0.1 米精度 const lat = Number(journal.lat.toFixed(6)); const lng = Number(journal.lng.toFixed(6)); const locationKey = `${lat},${lng}`; if (!locationMap.has(locationKey)) { const thumbItem = journal.items.find((item: any) => item.attachType === "THUMB"); locationMap.set(locationKey, { locationKey, lat, lng, location: journal.location, journalIds: [], count: 0, date: Time.toPassedDate(journal.createdAt), previewThumb: thumbItem ? `${config.url}/attachment/read/${thumbItem.mongoId}` : undefined }); } const marker = locationMap.get(locationKey)!; marker.journalIds.push(journal.id); marker.count++; // 如果还没有预览图,尝试从当前日记获取 if (!marker.previewThumb) { const thumbItem = journal.items.find((item: any) => item.attachType === "THUMB"); if (thumbItem) { marker.previewThumb = `${config.url}/attachment/read/${thumbItem.mongoId}`; } } }); const locations = Array.from(locationMap.values()); if (locations.length === 0) { wx.showToast({ title: "暂无位置记录", icon: "none" }); this.setData({ isLoading: false }); return; } // 生成地图标记 const markers: MapMarker[] = locations.map((location, index) => ({ id: index, latitude: location.lat, longitude: location.lng, width: 24, height: 30, customCallout: { anchorY: -2, // 随机错位避免近距离重叠 anchorX: Toolkit.random(-10, 10), display: "ALWAYS" } })); // 所有标记的 locationKey 列表 const customCalloutMarkerIds = locations.map(l => l.locationKey); // 计算中心点(所有标记的平均位置) const centerLat = locations.reduce((sum, l) => sum + l.lat, 0) / locations.length; const centerLng = locations.reduce((sum, l) => sum + l.lng, 0) / locations.length; // 缩放视野以包含所有标记点 const includePoints = locations.map((l) => ({ latitude: l.lat, longitude: l.lng })); this.setData({ locations, markers, customCalloutMarkerIds, centerLat, centerLng, includePoints, isLoading: false }); } catch (err: any) { wx.showToast({ title: err.message || "加载失败", icon: "error" }); this.setData({ isLoading: false }); } }, /** 标记点击事件 */ onMarkerTap(e: any) { const markerId = e.detail.markerId || e.markerId; this.loadLocationDetail(markerId); }, /** 气泡点击事件 */ onCalloutTap(e: any) { const markerId = e.detail.markerId || e.markerId; this.loadLocationDetail(markerId); }, /** 加载位置详情(该位置的所有日记) */ async loadLocationDetail(markerId: number) { const location = this.data.locations[markerId]; if (!location) { return; } this.setData({ popupIds: location.journalIds, popupVisible: true }); }, /** 关闭详情 */ async closeDetail() { this.setData({ popupVisible: false, selectedLocation: null }); }, });