0.0.0
This commit is contained in:
59
miniprogram/pages/dog-detail/dog-detail.js
Normal file
59
miniprogram/pages/dog-detail/dog-detail.js
Normal file
@@ -0,0 +1,59 @@
|
||||
// pages/dog-detail/dog-detail — 4 狗狗主页
|
||||
// 累计只用 次数/公里/连续天数;成就墙露灰锁(docs/05 R8, R10)
|
||||
const { call } = require('../../utils/cloud.js');
|
||||
const { ACHIEVEMENTS, TOTAL } = require('../../utils/achievements.js');
|
||||
const { formatDurationCN, formatDistance, toDateStr } = require('../../utils/format.js');
|
||||
|
||||
const GENDER_TEXT = { male: '公', female: '母' };
|
||||
|
||||
Page({
|
||||
data: {
|
||||
loading: true,
|
||||
dog: null,
|
||||
stats: null,
|
||||
metaText: '',
|
||||
wall: [],
|
||||
unlockedCount: 0,
|
||||
total: TOTAL,
|
||||
recent: [],
|
||||
},
|
||||
|
||||
onLoad(o) { this.dogId = o.id; },
|
||||
onShow() { if (this.dogId) this.load(); },
|
||||
|
||||
async load() {
|
||||
const res = await call('getDogDetail', { dogId: this.dogId });
|
||||
if (!res.ok) return;
|
||||
const { dog, achievements, recentWalks } = res.data;
|
||||
|
||||
const metaParts = [dog.breed];
|
||||
if (dog.ageText) metaParts.push(dog.ageText);
|
||||
if (GENDER_TEXT[dog.gender]) metaParts.push(GENDER_TEXT[dog.gender]);
|
||||
|
||||
const unlockedKeys = new Set(achievements.unlocked.map((u) => u.achievementKey));
|
||||
const wall = ACHIEVEMENTS.map((a) => ({
|
||||
key: a.key, name: a.name, unlocked: unlockedKeys.has(a.key),
|
||||
}));
|
||||
|
||||
const recent = recentWalks.map((w) => ({
|
||||
_id: w._id,
|
||||
title: `${toDateStr(w.startAt)}${w.isManual ? ' · 补记' : ''}`,
|
||||
sub: `${formatDurationCN(w.duration)} · ${formatDistance(w.distance)}` +
|
||||
(w.dogSteps != null ? ` · ${w.dogSteps}狗步` : ''),
|
||||
}));
|
||||
|
||||
this.setData({
|
||||
loading: false,
|
||||
dog,
|
||||
stats: dog.stats || {},
|
||||
metaText: metaParts.join(' · '),
|
||||
wall,
|
||||
unlockedCount: achievements.unlocked.length,
|
||||
total: achievements.total,
|
||||
recent,
|
||||
});
|
||||
},
|
||||
|
||||
goEdit() { wx.navigateTo({ url: `/pages/dog-edit/dog-edit?id=${this.dogId}` }); },
|
||||
goAchievements() { wx.navigateTo({ url: `/pages/achievements/achievements?dogId=${this.dogId}` }); },
|
||||
});
|
||||
4
miniprogram/pages/dog-detail/dog-detail.json
Normal file
4
miniprogram/pages/dog-detail/dog-detail.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"usingComponents": {},
|
||||
"navigationBarTitleText": "狗狗主页"
|
||||
}
|
||||
42
miniprogram/pages/dog-detail/dog-detail.wxml
Normal file
42
miniprogram/pages/dog-detail/dog-detail.wxml
Normal file
@@ -0,0 +1,42 @@
|
||||
<view wx:if="{{!loading}}" class="page">
|
||||
<!-- 头部 -->
|
||||
<view class="header">
|
||||
<view class="edit" bindtap="goEdit">编辑</view>
|
||||
<image wx:if="{{dog.avatar}}" class="dog-avatar" src="{{dog.avatar}}" mode="aspectFill" />
|
||||
<view wx:else class="dog-avatar">🐶</view>
|
||||
<view class="dog-name">{{dog.name}}</view>
|
||||
<view class="dog-meta">{{metaText}}</view>
|
||||
</view>
|
||||
|
||||
<!-- 累计:次数/公里/连续 -->
|
||||
<view class="grid3">
|
||||
<view class="g"><view class="g-v">{{stats.totalWalks || 0}}</view><view class="g-k">总次数</view></view>
|
||||
<view class="g"><view class="g-v">{{stats.totalDistance || 0}}</view><view class="g-k">总公里</view></view>
|
||||
<view class="g"><view class="g-v streak">{{stats.currentStreak || 0}}</view><view class="g-k">连续天数</view></view>
|
||||
</view>
|
||||
|
||||
<!-- 成就墙 -->
|
||||
<view class="row-head" bindtap="goAchievements">
|
||||
<text class="rh-title">成就墙</text>
|
||||
<text class="rh-more">{{unlockedCount}} / {{total}} ›</text>
|
||||
</view>
|
||||
<view class="wall">
|
||||
<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"><text class="rh-title">近期记录</text></view>
|
||||
<view wx:if="{{recent.length}}" class="recent">
|
||||
<view wx:for="{{recent}}" wx:key="_id" class="walk-row">
|
||||
<view class="wr-ico">🐾</view>
|
||||
<view class="wr-body">
|
||||
<view class="wr-title">{{item.title}}</view>
|
||||
<view class="wr-sub">{{item.sub}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view wx:else class="empty">还没有遛狗记录,点底部按钮带它出门吧</view>
|
||||
</view>
|
||||
33
miniprogram/pages/dog-detail/dog-detail.wxss
Normal file
33
miniprogram/pages/dog-detail/dog-detail.wxss
Normal file
@@ -0,0 +1,33 @@
|
||||
.page { min-height: 100vh; padding-bottom: calc(180rpx + env(safe-area-inset-bottom)); }
|
||||
|
||||
.header { position: relative; text-align: center; padding: 32rpx; border-bottom: 1rpx solid var(--line); }
|
||||
.edit { position: absolute; top: 28rpx; right: 32rpx; font-size: 26rpx; color: var(--accent-deep); }
|
||||
.dog-avatar { width: 124rpx; height: 124rpx; border-radius: 50%; background: var(--surface-2); display: flex; align-items: center; justify-content: center; font-size: 60rpx; margin: 0 auto 14rpx; }
|
||||
.dog-name { font-size: 34rpx; font-weight: 600; }
|
||||
.dog-meta { font-size: 24rpx; color: var(--ink-2); margin-top: 6rpx; }
|
||||
|
||||
.grid3 { display: flex; padding: 28rpx 12rpx; border-bottom: 1rpx solid var(--line); }
|
||||
.g { flex: 1; text-align: center; }
|
||||
.g-v { font-size: 40rpx; font-weight: 600; }
|
||||
.g-v.streak { color: var(--success); }
|
||||
.g-k { font-size: 22rpx; color: var(--ink-2); margin-top: 4rpx; }
|
||||
|
||||
.row-head { display: flex; align-items: center; justify-content: space-between; padding: 28rpx 32rpx 12rpx; }
|
||||
.rh-title { font-size: 26rpx; font-weight: 600; }
|
||||
.rh-more { font-size: 22rpx; color: var(--accent-deep); }
|
||||
|
||||
.wall { display: flex; flex-wrap: wrap; padding: 0 24rpx 20rpx; border-bottom: 1rpx solid var(--line); }
|
||||
.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); }
|
||||
|
||||
.recent { padding: 0 32rpx; 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); }
|
||||
.wr-ico { width: var(--icon-size); font-size: var(--icon-size); line-height: 1; text-align: center; }
|
||||
.wr-body { flex: 1; }
|
||||
.wr-title { font-size: 26rpx; }
|
||||
.wr-sub { font-size: 22rpx; color: var(--ink-2); margin-top: 4rpx; }
|
||||
|
||||
.empty { text-align: center; font-size: 24rpx; color: var(--ink-3); padding: 40rpx 32rpx; }
|
||||
Reference in New Issue
Block a user