This commit is contained in:
2026-06-25 18:24:29 +08:00
commit a1190d7d9a
90 changed files with 5459 additions and 0 deletions

View File

@@ -0,0 +1,62 @@
// pages/records/records — 6/6b 记录Tab含空状态
const { call } = require('../../utils/cloud.js');
const store = require('../../utils/store.js');
const { ACHIEVEMENTS, TOTAL } = require('../../utils/achievements.js');
const { formatDurationCN, formatDistance, toDateStr } = require('../../utils/format.js');
Page({
data: {
isEmpty: false,
dogName: '豆豆',
primaryDogId: '',
wall: [],
unlockedCount: 0,
total: TOTAL,
walks: [],
dogs: [],
},
onShow() {
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
this.getTabBar().setData({ selected: 0, walkState: (store.getWalkSession() || {}).status || 'idle' });
}
this.load();
},
async load() {
const dogs = store.getState().dogs || [];
const primaryDogId = store.getState().currentDogId || (dogs[0] && dogs[0]._id) || '';
this.setData({ dogs, primaryDogId, dogName: (dogs[0] && dogs[0].name) || '狗狗' });
// 历史
const hist = await call('getHistory', {});
const walks = (hist.ok ? hist.data.walks : []).slice(0, 8).map((w) => ({
_id: w._id,
title: `${toDateStr(w.startAt)} · ${(w.dogNames || []).join('、')}`,
sub: `${formatDurationCN(w.duration)} · ${formatDistance(w.distance)}`,
isManual: w.isManual,
}));
// 成就概览(取前 4基于主狗解锁状态
let wall = ACHIEVEMENTS.slice(0, 4).map((a) => ({ key: a.key, name: a.name, unlocked: false }));
let unlockedCount = 0;
if (primaryDogId) {
const dd = await call('getDogDetail', { dogId: primaryDogId });
if (dd.ok) {
const keys = new Set(dd.data.achievements.unlocked.map((u) => u.achievementKey));
unlockedCount = dd.data.achievements.unlocked.length;
wall = ACHIEVEMENTS.slice(0, 4).map((a) => ({ key: a.key, name: a.name, unlocked: keys.has(a.key) }));
}
}
this.setData({ isEmpty: walks.length === 0, walks, wall, unlockedCount });
},
goAch() {
const q = this.data.primaryDogId ? `?dogId=${this.data.primaryDogId}` : '';
wx.navigateTo({ url: `/pages/achievements/achievements${q}` });
},
startWalk() { wx.navigateTo({ url: '/pages/walking/walking' }); },
openAdd() { this.selectComponent('#addSheet').open(); },
onSaved() { this.load(); },
});

View File

@@ -0,0 +1,6 @@
{
"usingComponents": {
"manual-add-sheet": "/components/manual-add-sheet/index"
},
"navigationBarTitleText": "记录"
}

View File

@@ -0,0 +1,48 @@
<view class="page">
<!-- ===== 空状态 6b ===== -->
<block wx:if="{{isEmpty}}">
<view class="empty-hero">
<view class="paw">🐾</view>
<view class="empty-title">{{dogName}}已经在门口等了</view>
<view class="empty-sub">第一次遛狗,从下面那个橙色按钮开始。走完一程,这里就有它的第一条记录啦。</view>
<button class="btn primary start-btn" bindtap="startWalk"><text class="btn-ico">🐾</text>带{{dogName}}出门</button>
</view>
<view class="row-head"><text class="rh-title">成就墙</text><text class="rh-more">0 / {{total}} 待解锁</text></view>
<view class="wall">
<view wx:for="{{wall}}" wx:key="key" class="medal-item locked">
<view class="medal">🔒</view>
<view class="medal-name">{{item.name}}</view>
</view>
</view>
</block>
<!-- ===== 正常态 6 ===== -->
<block wx:else>
<view class="row-head" bindtap="goAch">
<text class="rh-title">成就墙</text>
<text class="rh-more">全部 {{unlockedCount}}/{{total}} </text>
</view>
<view class="wall" bindtap="goAch">
<view wx:for="{{wall}}" wx:key="key" class="medal-item {{item.unlocked?'':'locked'}}">
<view class="medal">{{item.unlocked ? '🏅' : '🔒'}}</view>
<view class="medal-name">{{item.name}}</view>
</view>
</view>
<view class="row-head bordered">
<text class="rh-title">遛狗历史</text>
<text class="rh-more" catchtap="openAdd"> 补记</text>
</view>
<view class="rows">
<view wx:for="{{walks}}" wx:key="_id" class="walk-row {{item.isManual?'manual':''}}">
<view class="wr-ico">{{item.isManual ? '✎' : '🐾'}}</view>
<view class="wr-body">
<view class="wr-title">{{item.title}}<text wx:if="{{item.isManual}}" class="tag">补记</text></view>
<view class="wr-sub">{{item.sub}}</view>
</view>
</view>
</view>
</block>
<manual-add-sheet id="addSheet" dogs="{{dogs}}" bind:saved="onSaved" />
</view>

View File

@@ -0,0 +1,33 @@
.page { min-height: 100vh; padding: 24rpx 32rpx calc(180rpx + env(safe-area-inset-bottom)); }
/* 空状态 */
.empty-hero { text-align: center; padding: 56rpx 40rpx 40rpx; display: flex; flex-direction: column; align-items: center; }
.paw { width: 168rpx; height: 168rpx; border-radius: 50%; background: var(--accent-soft); display: flex; align-items: center; justify-content: center; font-size: 80rpx; line-height: 1; margin-bottom: 28rpx; }
.empty-title { font-size: 32rpx; font-weight: 600; margin-bottom: 14rpx; }
.empty-sub { font-size: 26rpx; color: var(--ink-2); line-height: 1.6; max-width: 440rpx; }
.start-btn { width: auto; padding: 0 48rpx; margin-top: 36rpx; }
/* 区块标题 */
.row-head { display: flex; align-items: center; justify-content: space-between; padding: 24rpx 4rpx 14rpx; }
.row-head.bordered { border-top: 1rpx solid var(--line); margin-top: 16rpx; }
.rh-title { font-size: 26rpx; font-weight: 600; }
.rh-more { font-size: 22rpx; color: var(--accent-deep); }
/* 成就墙 */
.wall { display: flex; flex-wrap: wrap; padding-bottom: 8rpx; }
.medal-item { width: 25%; display: flex; flex-direction: column; align-items: center; gap: 8rpx; padding: 12rpx 0; }
.medal { width: 84rpx; height: 84rpx; border-radius: 50%; background: var(--surface-2); display: flex; align-items: center; justify-content: center; font-size: var(--icon-size); line-height: 1; }
.medal-item.locked .medal { opacity: 0.55; }
.medal-name { font-size: 20rpx; color: var(--ink-2); }
.medal-item.locked .medal-name { color: var(--ink-3); }
/* 历史 */
.rows { display: flex; flex-direction: column; gap: 16rpx; }
.walk-row { display: flex; align-items: center; gap: 18rpx; padding: 22rpx; background: var(--surface-2); border-radius: var(--radius-sm); }
.walk-row.manual { background: transparent; border: 1rpx dashed var(--line-2); }
.wr-ico { width: var(--icon-size); font-size: var(--icon-size); line-height: 1; text-align: center; }
.walk-row.manual .wr-ico { color: var(--accent-deep); }
.wr-body { flex: 1; }
.wr-title { font-size: 26rpx; }
.wr-sub { font-size: 22rpx; color: var(--ink-2); margin-top: 4rpx; }
.tag { font-size: 20rpx; color: var(--ink-2); background: var(--surface-2); border: 1rpx solid var(--line-2); border-radius: var(--radius-pill); padding: 2rpx 12rpx; margin-left: 12rpx; }