// pages/achievements/achievements — 7 完整成就页(两入口共用,docs/05 R10) const { call } = require('../../utils/cloud.js'); const { ACHIEVEMENTS, TOTAL } = require('../../utils/achievements.js'); const { toDateStr } = require('../../utils/format.js'); Page({ data: { dogId: '', dogName: '', unlockedCount: 0, total: TOTAL, unlockedList: [], lockedList: [], }, onLoad(o) { this.dogId = o.dogId || ''; }, onShow() { if (this.dogId) this.load(); }, async load() { const res = await call('getDogDetail', { dogId: this.dogId }); if (!res.ok) return; const dog = res.data.dog; const stats = dog.stats || { totalWalks: 0, totalDistance: 0, currentStreak: 0 }; const unlockedAtMap = {}; res.data.achievements.unlocked.forEach((u) => { unlockedAtMap[u.achievementKey] = u.unlockedAt; }); const unlockedList = []; const lockedList = []; ACHIEVEMENTS.forEach((a) => { if (unlockedAtMap[a.key] != null) { unlockedList.push({ key: a.key, name: a.name, desc: a.desc, when: toDateStr(unlockedAtMap[a.key]) }); } else { lockedList.push({ key: a.key, name: a.name, desc: this.lockedDesc(a, stats) }); } }); this.setData({ dogName: dog.name, unlockedCount: unlockedList.length, total: res.data.achievements.total, unlockedList, lockedList, }); }, // 「还差 X」进度文案 lockedDesc(a, stats) { const streak = stats.currentStreak || 0; const dist = stats.totalDistance || 0; const early = stats.earlyWalks || 0; switch (a.key) { case 'week_streak': return `连续遛狗满 7 天 · 还差 ${Math.max(0, 7 - streak)} 天`; case 'month_streak': return `连续遛狗满 30 天 · 还差 ${Math.max(0, 30 - streak)} 天`; case 'hundred_km': return `累计满 100 公里 · 还差 ${Math.max(0, +(100 - dist).toFixed(1))} km`; case 'early_bird': return `7 点前遛狗 10 次 · 还差 ${Math.max(0, 10 - early)} 次`; default: return a.desc; } }, });