// pages/main/journal-map/index.ts import config from "../../../config/index"; import Time from "../../../utils/Time"; import { Journal, JournalPageType } from "../../../types/Journal"; import { MediaAttachExt, MediaAttachType } from "../../../types/Attachment"; interface MapMarker { id: number; latitude: number; longitude: number; width: number; height: number; customCallout: { anchorY: number; anchorX: number; display: string; }; } interface JournalMarker { id: number; date: string; location?: string; lat: number; lng: number; idea?: string; items: Array<{ type: number; thumbURL: string; sourceURL: string; mongoId: string; }>; } interface JournalMapData { centerLat: number; centerLng: number; scale: number; markers: MapMarker[]; journals: JournalMarker[]; customCalloutMarkerIds: number[]; selectedMarker: JournalMarker | null; isLoading: boolean; detailHeight: number; } Page({ data: { centerLat: 39.908823, centerLng: 116.397470, scale: 13, markers: [], journals: [], customCalloutMarkerIds: [], selectedMarker: null, isLoading: true, detailHeight: 0 }, async onLoad() { await this.loadJournals(); // 获取窗口高度,固定详情面板为 1/2 高度 const windowInfo = wx.getWindowInfo(); const windowHeight = windowInfo.windowHeight; this.setData({ detailHeight: windowHeight * 0.5 }); }, /** 加载所有记录 */ 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 journals: JournalMarker[] = list .filter((journal: any) => journal.lat && journal.lng) .map((journal: any) => ({ id: journal.id, date: Time.toPassedDateTime(journal.createdAt), location: journal.location, lat: journal.lat, lng: journal.lng, idea: journal.idea, items: journal.items.filter((item: any) => item.attachType === "THUMB") .map((item: any) => { const ext = JSON.parse(item.ext); return { type: ext.isVideo ? 1 : 0, thumbURL: `${config.url}/attachment/read/${item.mongoId}`, mongoId: item.mongoId, source: journal.items.find((source: any) => source.id === ext.sourceId) }; }) })); if (journals.length === 0) { wx.showToast({ title: "暂无位置记录", icon: "none" }); this.setData({ isLoading: false }); return; } // 生成地图标记 const markers: MapMarker[] = journals.map((journal) => ({ id: journal.id, latitude: journal.lat, longitude: journal.lng, width: 24, height: 30, customCallout: { anchorY: -2, anchorX: 0, display: "ALWAYS" } })); // 所有标记的 ID 列表 const customCalloutMarkerIds = journals.map((j) => j.id); // 计算中心点(所有标记的平均位置) const centerLat = journals.reduce((sum, j) => sum + j.lat, 0) / journals.length; const centerLng = journals.reduce((sum, j) => sum + j.lng, 0) / journals.length; this.setData({ journals, markers, customCalloutMarkerIds, centerLat, centerLng, 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.loadMarkerDetail(markerId); }, /** 气泡点击事件 */ onCalloutTap(e: any) { const markerId = e.detail.markerId || e.markerId; this.loadMarkerDetail(markerId); }, /** 加载标记详情 */ async loadMarkerDetail(markerId: number) { wx.showLoading({ title: "加载中...", mask: true }); try { const journal: any = await new Promise((resolve, reject) => { wx.request({ url: `${config.url}/journal/${markerId}`, method: "POST", header: { Key: wx.getStorageSync("key") }, success: (res: any) => { if (res.data.code === 20000) { resolve(res.data.data); } else { reject(new Error(res.data.message || "加载失败")); } }, fail: reject }); }); const items = journal.items || []; const thumbItems = items.filter((item: any) => item.attachType === MediaAttachType.THUMB); const mediaItems = thumbItems.map((thumbItem: any) => { const ext = JSON.parse(thumbItem.ext) as MediaAttachExt; return { type: ext.isVideo ? 1 : 0, thumbURL: `${config.url}/attachment/read/${thumbItem.mongoId}`, sourceURL: `${config.url}/attachment/read/${ext.sourceMongoId}`, mongoId: thumbItem.mongoId, }; }); this.setData({ selectedMarker: { id: journal.id, date: Time.toPassedDateTime(journal.createdAt), location: journal.location, lat: journal.lat, lng: journal.lng, idea: journal.idea, items: mediaItems } }); wx.hideLoading(); } catch (err: any) { wx.hideLoading(); wx.showToast({ title: err.message || "加载失败", icon: "error" }); } }, /** 关闭详情 */ closeDetail() { this.setData({ selectedMarker: null }); }, /** 预览媒体 */ previewMedia(e: WechatMiniprogram.BaseEvent) { const index = e.currentTarget.dataset.index; if (!this.data.selectedMarker) return; const sources = this.data.selectedMarker.items.map((item: any) => ({ url: item.sourceURL, type: item.type === 0 ? "image" : "video" })); wx.previewMedia({ current: index, sources: sources as WechatMiniprogram.MediaSource[] }); }, });