const { secure, toast } = require("../../utils/api"); Page({ data: { pets: [], selected: [], walk: null, points: [], polyline: [], latitude: 31.23, longitude: 121.47, duration: "00:00", distance: "0.00", share: false, busy: false, locationError: "", }, onShow() { this.load(); clearInterval(this.clock); this.clock = setInterval(() => this.tick(), 1000); }, onHide() { this.stopTimers(); }, onUnload() { this.stopTimers(); }, stopTimers() { clearInterval(this.clock); clearInterval(this.tracker); this.clock = null; this.tracker = null; }, async load() { try { const [pets, walks] = await Promise.all([ secure("/pets"), secure("/walks"), ]); const selected = (wx.getStorageSync("walk_pet_ids") || []).filter((id) => pets.some((p) => p.id === id), ); const walk = walks.find((w) => w.status === "active" || w.status === "paused") || null; this.setData({ pets, selected, walk }); if (walk) { this.renderWalk(walk); if (walk.status === "active") { this.track(); this.point(); } } else { clearInterval(this.tracker); this.setData({ points: [], polyline: [], duration: "00:00", distance: "0.00", share: false }); } } catch (e) { toast(e); } }, pick(e) { if (this.data.walk) return; this.setData({ selected: e.detail.selected }); wx.setStorageSync("walk_pet_ids", e.detail.selected); }, async start() { if (this.data.busy) return; if (!this.data.selected.length) { wx.showToast({ title: "请选择同行的狗狗", icon: "none" }); return; } this.setData({ busy: true, locationError: "" }); let position; try { position = await wx.getLocation({ type: "gcj02" }); } catch (error) { this.setData({ busy: false, locationError: "请检查小程序定位权限,开启后再试" }); toast(new Error("无法取得位置,请检查定位权限")); return; } this.setData({ latitude: position.latitude, longitude: position.longitude }); try { const walk = await secure("/walks", "POST", { pet_ids: this.data.selected, }); this.setData({ walk }); this.track(); await this.savePoint(position); } catch (e) { if (this.data.walk) this.setData({ locationError: "轨迹保存失败,请点击重试" }); toast(e); } finally { this.setData({ busy: false }); } }, track() { clearInterval(this.tracker); this.tracker = setInterval(() => this.point(), 15000); }, async point() { if (!this.data.walk || this.data.walk.status !== "active" || this.locating) return; this.locating = true; try { const pos = await wx.getLocation({ type: "gcj02" }); await this.savePoint(pos); this.setData({ locationError: "" }); } catch (e) { if (this.data.walk) this.setData({ locationError: "定位更新失败,请检查定位权限和网络" }); } finally { this.locating = false; } }, async savePoint(pos) { const walkId = this.data.walk.id; const walk = await secure( "/walks/" + walkId + "/points", "POST", { latitude: pos.latitude, longitude: pos.longitude }, ); if (this.data.walk && this.data.walk.id === walkId && this.data.walk.status === "active") { this.renderWalk(walk); } }, renderWalk(walk) { const pts = (walk.points || []).map((p) => ({ latitude: p.latitude, longitude: p.longitude, })); const last = pts[pts.length - 1]; this.setData({ walk, points: pts, polyline: pts.length > 1 ? [{ points: pts, color: "#268e6a", width: 5 }] : [], latitude: last ? last.latitude : this.data.latitude, longitude: last ? last.longitude : this.data.longitude, distance: ((walk.distance_meters || 0) / 1000).toFixed(2), share: walk.share_enabled, }); this.tick(); }, tick() { const walk = this.data.walk; if (!walk) return; const end = walk.ended_at ? new Date(walk.ended_at) : walk.status === "paused" ? new Date(walk.paused_at || Date.now()) : new Date(); const sec = Math.max( 0, Math.floor((end - new Date(walk.started_at)) / 1000) - (walk.paused_seconds || 0), ); this.setData({ duration: String(Math.floor(sec / 60)).padStart(2, "0") + ":" + String(sec % 60).padStart(2, "0"), }); }, async action(e) { if (this.data.busy) return; const kind = e.currentTarget.dataset.kind; this.setData({ busy: true }); try { const walk = await secure( "/walks/" + this.data.walk.id + "/" + kind, "POST", ); this.renderWalk(walk); if (kind === "finish") { clearInterval(this.tracker); this.setData({ walk: null, points: [], polyline: [], duration: "00:00", distance: "0.00", share: false }); wx.showToast({ title: "今天的快乐已记录", icon: "success" }); } else if (kind === "resume") { this.track(); this.point(); } else { clearInterval(this.tracker); } } catch (err) { toast(err); } finally { this.setData({ busy: false }); } }, records() { wx.navigateTo({ url: "/pages/walk-records/walk-records" }); }, async shareChange(e) { try { const walk = await secure( "/walks/" + this.data.walk.id + "/share", "PUT", { enabled: e.detail.value }, ); this.renderWalk(walk); } catch (err) { toast(err); } }, onShareAppMessage() { const walk = this.data.walk; return { title: "肉垫计划 · 看看我们遛到哪儿了 🐾", path: walk && walk.share_enabled ? "/pages/share/share?token=" + walk.share_token : "/pages/home/home", }; }, });