Complete core mini program flows
ci-cd / verify (push) Successful in 5s
ci-cd / deploy (push) Skipped

This commit is contained in:
2026-10-04 09:26:58 +08:00
parent 5688db4d63
commit c72cf24f56
28 changed files with 490 additions and 65 deletions
@@ -0,0 +1,47 @@
const { secure, toast } = require("../../utils/api");
function duration(seconds) {
const minutes = Math.floor((seconds || 0) / 60);
return `${minutes} 分 ${String((seconds || 0) % 60).padStart(2, "0")} 秒`;
}
Page({
data: { records: [], selected: null, loading: true },
onLoad() {
this.load();
},
async load() {
try {
const [walks, pets] = await Promise.all([secure("/walks"), secure("/pets")]);
const names = Object.fromEntries(pets.map((pet) => [pet.id, pet.name]));
this.setData({
records: walks.filter((walk) => walk.status === "finished").map((walk) => ({
...walk,
date: new Date(walk.started_at).toLocaleString("zh-CN"),
duration: duration(walk.duration_seconds),
distance: ((walk.distance_meters || 0) / 1000).toFixed(2),
petNames: (walk.pet_ids || []).map((id) => names[id] || "已归档宠物").join("、"),
polyline: walk.points && walk.points.length > 1 ? [{
points: walk.points.map((point) => ({ latitude: point.latitude, longitude: point.longitude })),
color: "#268e6a",
width: 5,
}] : [],
lastPoint: walk.points && walk.points.length ? walk.points[walk.points.length - 1] : null,
})),
loading: false,
});
} catch (error) {
this.setData({ loading: false });
toast(error);
}
},
open(e) {
this.setData({ selected: this.data.records[e.currentTarget.dataset.index] });
},
close() {
this.setData({ selected: null });
},
back() {
wx.navigateBack();
},
});
@@ -0,0 +1 @@
{}
@@ -0,0 +1,29 @@
<view class="screen">
<text wx:if="{{selected}}" class="back" bindtap="close">‹ 全部记录</text>
<text wx:else class="back" bindtap="back">‹ 返回</text>
<view class="eyebrow">OUR LITTLE FOOTPRINTS</view>
<view class="title">{{selected?'这次的脚印':'遛狗记录'}} 🐾</view>
<view wx:if="{{selected}}">
<view class="card detail">
<strong>{{selected.date}}</strong>
<text>{{selected.petNames}}</text>
<view class="metrics">
<view><strong>{{selected.duration}}</strong><text>时长</text></view>
<view><strong>{{selected.distance}} km</strong><text>距离</text></view>
</view>
</view>
<map wx:if="{{selected.lastPoint}}" class="route-map"
latitude="{{selected.lastPoint.latitude}}"
longitude="{{selected.lastPoint.longitude}}"
polyline="{{selected.polyline}}" scale="15" />
<view wx:else class="empty">这次没有留下定位轨迹</view>
</view>
<view wx:else>
<view wx:for="{{records}}" wx:key="id" class="card record" data-index="{{index}}" bindtap="open">
<view class="row"><strong>{{item.date}}</strong><text>›</text></view>
<view class="pet-names">🐕 {{item.petNames}}</view>
<view class="record-stats">{{item.duration}} · {{item.distance}} km · {{item.points.length}} 个足迹</view>
</view>
<view wx:if="{{!loading&&!records.length}}" class="empty">完成一次遛狗,脚印就会出现在这里 🐾</view>
</view>
</view>
@@ -0,0 +1,10 @@
.back { display: block; color: #327c5e; margin-bottom: 34rpx; font-size: 26rpx; }
.record strong, .detail strong { font-size: 28rpx; }
.record .row text { font-size: 38rpx; color: #8da89a; }
.pet-names, .detail > text { display: block; color: #547b65; margin-top: 16rpx; }
.record-stats { color: #91a59a; font-size: 24rpx; margin-top: 12rpx; }
.metrics { display: flex; gap: 28rpx; margin-top: 28rpx; border-top: 1rpx solid #e8efe9; padding-top: 22rpx; }
.metrics view { flex: 1; }
.metrics strong, .metrics text { display: block; }
.metrics text { color: #8da89a; font-size: 22rpx; margin-top: 7rpx; }
.route-map { width: 100%; height: 600rpx; border-radius: 30rpx; overflow: hidden; }