From 6e3f63185ded49229f700eba671edcbd3cd7bb2c Mon Sep 17 00:00:00 2001 From: guoyoudu Date: Mon, 2 Mar 2026 07:02:25 +0800 Subject: [PATCH] version 2 --- src/components/HabitArea.vue | 7 +- src/components/RewardArea.vue | 132 ++++++++++++++++++++++++++++++--- src/stores/starEnergy.ts | 134 +++++++++++++++++++++++++++++----- 3 files changed, 242 insertions(+), 31 deletions(-) diff --git a/src/components/HabitArea.vue b/src/components/HabitArea.vue index 83f9c73..e135b35 100644 --- a/src/components/HabitArea.vue +++ b/src/components/HabitArea.vue @@ -158,7 +158,8 @@ const habits = { noPickyEating: { name: '不挑食', icon: '🥦' }, washHands: { name: '勤洗手', icon: '🧼' }, homeworkFast: { name: '写作业快', icon: '📝' }, - earlySleep: { name: '早睡', icon: '🌙' } + earlySleep: { name: '早睡', icon: '🌙' }, + cleanRoom: { name: '打扫卫生', icon: '🧹' } }; // 坏习惯数据 @@ -174,7 +175,7 @@ const monsters = { boss: { name: '坏习惯大魔王', health: 500, - habits: ['brushTeeth', 'noBedLate', 'noPickyEating', 'washHands', 'homeworkFast', 'earlySleep'] + habits: ['brushTeeth', 'noBedLate', 'noPickyEating', 'washHands', 'homeworkFast', 'earlySleep', 'cleanRoom'] } }; @@ -190,7 +191,7 @@ const badges = computed(() => starEnergyStore.badges); // 检查习惯是否已打卡 const isChecked = (habit: keyof typeof habitData.value) => { const today = new Date().toISOString().split('T')[0]; - return habitData.value[habit].lastChecked === today; + return habitData.value[habit]?.lastChecked === today; }; // 打卡习惯 diff --git a/src/components/RewardArea.vue b/src/components/RewardArea.vue index 9ececd3..77046ba 100644 --- a/src/components/RewardArea.vue +++ b/src/components/RewardArea.vue @@ -65,13 +65,38 @@

🎉 已兑换的奖励

+
+
+ 已兑换: + {{ claimedRewards.length }} 个 +
+
+ 已给孩子: + {{ givenRewards.length }} 个 +
+
+ 待领取: + {{ claimedRewards.length - givenRewards.length }} 个 +
+
- {{ getRewardById(id)?.emoji }} {{ getRewardById(id)?.name }} +
+ {{ getRewardById(id)?.emoji }} {{ getRewardById(id)?.name }} +
+ +
已给孩子 ✓
@@ -110,6 +135,9 @@ const currentCategory = ref('all'); // 已兑换奖励ID列表 const claimedRewards = computed(() => starEnergyStore.claimedRewards); +// 已给孩子的奖励ID列表 +const givenRewards = computed(() => starEnergyStore.getGivenRewards || []); + // 弹窗状态 const showSuccessModal = ref(false); const showInsufficientModal = ref(false); @@ -166,7 +194,7 @@ const rewards = [ id: 'park_50', name: '去公园玩', description: '可以和爸爸妈妈去公园玩!', - cost: 50, + cost: 100, emoji: '🎡', category: 'activity' }, @@ -191,7 +219,7 @@ const rewards = [ id: 'zoo_100', name: '去动物园', description: '可以去动物园看小动物!', - cost: 100, + cost: 500, emoji: '🦁', category: 'activity' }, @@ -200,7 +228,7 @@ const rewards = [ id: 'lego_200', name: '乐高积木套装', description: '可以获得一套乐高积木!', - cost: 200, + cost: 400, emoji: '🧱', category: 'toy' }, @@ -224,7 +252,7 @@ const rewards = [ id: 'amusement_200', name: '游乐园一日游', description: '可以和爸爸妈妈去游乐园玩一整天!', - cost: 200, + cost: 500, emoji: '🎢', category: 'activity' }, @@ -232,7 +260,7 @@ const rewards = [ id: 'bike_200', name: '自行车一辆', description: '可以获得一辆全新的自行车!', - cost: 200, + cost: 1000, emoji: '🚲', category: 'toy' }, @@ -250,7 +278,7 @@ const rewards = [ id: 'family_trip_500', name: '家庭旅行', description: '可以和家人一起去旅行!', - cost: 500, + cost: 1000, emoji: '✈️', category: 'activity' }, @@ -258,7 +286,7 @@ const rewards = [ id: 'game_console_500', name: '游戏机', description: '可以获得一台游戏机!', - cost: 500, + cost: 2000, emoji: '🎮', category: 'toy' }, @@ -372,7 +400,12 @@ const redeemReward = (reward: any) => { // 根据ID获取奖励 const getRewardById = (id: string) => { - return rewards.find(reward => reward.id === id); + return rewards.find(reward => reward.id === id) || { emoji: '🎁', name: '未知奖励' }; +}; + +// 标记奖励为已给孩子 +const markRewardAsGiven = (rewardId: string) => { + starEnergyStore.markRewardAsGiven(rewardId); }; // 关闭弹窗 @@ -661,9 +694,41 @@ const closeModal = () => { animation: bounce 2s ease-in-out infinite; } +/* 奖励统计 */ +.reward-stats { + display: flex; + justify-content: space-around; + margin-bottom: 20px; + padding: 15px; + background: linear-gradient(135deg, #98FB98, #90EE90); + border-radius: 20px; + border: 3px solid #32CD32; +} + +.stat-item { + display: flex; + flex-direction: column; + align-items: center; + gap: 5px; +} + +.stat-label { + font-size: 14px; + color: #006400; + font-weight: bold; + font-family: var(--cartoon-font); +} + +.stat-value { + font-size: 18px; + color: #006400; + font-weight: bold; + font-family: var(--cartoon-font); +} + .claimed-grid { display: grid; - grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); + grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 15px; } @@ -677,6 +742,53 @@ const closeModal = () => { color: white; box-shadow: 0 4px 10px rgba(255, 215, 0, 0.4); animation: bounce 2s ease-in-out infinite; + display: flex; + flex-direction: column; + gap: 10px; + min-height: 100px; +} + +.claimed-card.given { + background: linear-gradient(135deg, #98FB98, #32CD32); + border: 3px solid #228B22; +} + +.claimed-content { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + gap: 10px; +} + +.give-btn { + background: rgba(255, 255, 255, 0.9); + color: #8B4513; + border: none; + padding: 8px 16px; + border-radius: 12px; + font-size: 12px; + font-weight: bold; + cursor: pointer; + transition: all 0.3s ease; + font-family: var(--cartoon-font); +} + +.give-btn:hover { + background: white; + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); +} + +.given-badge { + background: rgba(255, 255, 255, 0.3); + color: white; + padding: 6px 12px; + border-radius: 10px; + font-size: 12px; + font-weight: bold; + font-family: var(--cartoon-font); + border: 2px solid rgba(255, 255, 255, 0.5); } /* 弹窗 */ diff --git a/src/stores/starEnergy.ts b/src/stores/starEnergy.ts index 74aef13..cb0a4d1 100644 --- a/src/stores/starEnergy.ts +++ b/src/stores/starEnergy.ts @@ -33,9 +33,45 @@ export const useStarEnergyStore = defineStore('starEnergy', { // 尝试从 localStorage 加载保存的数据 const savedData = loadFromLocalStorage(); + // 获取当前日期 + const today = new Date().toDateString(); + + // 检查是否需要重置怪兽状态和已兑换奖励 + let monsters = savedData?.monsters; + let lastResetDate = savedData?.lastResetDate; + let claimedRewards = savedData?.claimedRewards; + let givenRewards = savedData?.givenRewards; + let bossDefeats = savedData?.bossDefeats; + + // 防御性检查 + if (!Array.isArray(claimedRewards)) { + claimedRewards = []; + } + if (!Array.isArray(givenRewards)) { + givenRewards = []; + } + if (typeof bossDefeats !== 'number') { + bossDefeats = 0; + } + + // 如果是新的一天,重置怪兽状态和已兑换奖励 + if (!lastResetDate || lastResetDate !== today) { + monsters = { + boss: { + name: '坏习惯大魔王', + health: 500, + defeated: false, + habits: ['brushTeeth', 'noBedLate', 'noPickyEating', 'washHands', 'homeworkFast', 'earlySleep', 'cleanRoom'] + } + }; + claimedRewards = []; // 重置已兑换奖励 + givenRewards = []; // 重置已给孩子的奖励 + lastResetDate = today; + } + return { // 星星能量总数 - total: savedData?.total || 20, + total: typeof savedData?.total === 'number' ? savedData.total : 20, // 各区域的星星能量 areas: savedData?.areas || { knowledge: 0, @@ -45,59 +81,76 @@ export const useStarEnergyStore = defineStore('starEnergy', { parent: 0 }, // 习惯打卡记录 - habits: savedData?.habits || { + habits: { brushTeeth: { name: '刷牙', count: 0, streak: 0, - lastChecked: null as string | null + lastChecked: null as string | null, + ...savedData?.habits?.brushTeeth }, noBedLate: { name: '不赖床', count: 0, streak: 0, - lastChecked: null as string | null + lastChecked: null as string | null, + ...savedData?.habits?.noBedLate }, noPickyEating: { name: '不挑食', count: 0, streak: 0, - lastChecked: null as string | null + lastChecked: null as string | null, + ...savedData?.habits?.noPickyEating }, washHands: { name: '勤洗手', count: 0, streak: 0, - lastChecked: null as string | null + lastChecked: null as string | null, + ...savedData?.habits?.washHands }, homeworkFast: { name: '写作业快', count: 0, streak: 0, - lastChecked: null as string | null + lastChecked: null as string | null, + ...savedData?.habits?.homeworkFast }, earlySleep: { name: '早睡', count: 0, streak: 0, - lastChecked: null as string | null + lastChecked: null as string | null, + ...savedData?.habits?.earlySleep + }, + cleanRoom: { + name: '打扫卫生', + count: 0, + streak: 0, + lastChecked: null as string | null, + ...savedData?.habits?.cleanRoom } }, // 怪兽状态 - monsters: { + monsters: monsters || { boss: { name: '坏习惯大魔王', - health: (savedData?.monsters && 'boss' in savedData.monsters && !savedData.monsters.boss.defeated) ? savedData.monsters.boss.health : 500, - defeated: (savedData?.monsters && 'boss' in savedData.monsters) ? savedData.monsters.boss.defeated : false, - habits: ['brushTeeth', 'noBedLate', 'noPickyEating', 'washHands', 'homeworkFast', 'earlySleep'] + health: 500, + defeated: false, + habits: ['brushTeeth', 'noBedLate', 'noPickyEating', 'washHands', 'homeworkFast', 'earlySleep', 'cleanRoom'] } }, // 勋章 - badges: savedData?.badges || [] as string[], + badges: Array.isArray(savedData?.badges) ? savedData.badges : [] as string[], // 已兑换奖励 - claimedRewards: savedData?.claimedRewards || [] as string[], + claimedRewards: claimedRewards, + // 已给孩子的奖励 + givenRewards: givenRewards, // 击败BOSS次数 - bossDefeats: savedData?.bossDefeats || 0 + bossDefeats: bossDefeats, + // 上次重置日期 + lastResetDate: lastResetDate || today }; }, @@ -120,6 +173,9 @@ export const useStarEnergyStore = defineStore('starEnergy', { // 获取已兑换奖励 getClaimedRewards: (state) => state.claimedRewards, + // 获取已给孩子的奖励 + getGivenRewards: (state) => state.givenRewards, + // 获取击败BOSS次数 getBossDefeats: (state) => state.bossDefeats }, @@ -133,7 +189,10 @@ export const useStarEnergyStore = defineStore('starEnergy', { habits: this.habits, monsters: this.monsters, badges: this.badges, - claimedRewards: this.claimedRewards + claimedRewards: this.claimedRewards, + givenRewards: this.givenRewards, + bossDefeats: this.bossDefeats, + lastResetDate: this.lastResetDate }; saveToLocalStorage(data); }, @@ -160,11 +219,19 @@ export const useStarEnergyStore = defineStore('starEnergy', { const today = new Date().toISOString().split('T')[0]; const habitData = this.habits[habit]; + // 检查习惯数据是否存在 + if (!habitData) { + return; + } + // 防止重复打卡 if (habitData.lastChecked === today) { return; } + // 保存原来的lastChecked值用于连续打卡计算 + const originalLastChecked = habitData.lastChecked; + // 更新打卡记录 habitData.count++; habitData.lastChecked = today; @@ -174,7 +241,7 @@ export const useStarEnergyStore = defineStore('starEnergy', { yesterday.setDate(yesterday.getDate() - 1); const yesterdayStr = yesterday.toISOString().split('T')[0]; - if (habitData.lastChecked === yesterdayStr) { + if (originalLastChecked === yesterdayStr) { habitData.streak++; } else { habitData.streak = 1; @@ -187,6 +254,9 @@ export const useStarEnergyStore = defineStore('starEnergy', { if (habitData.streak % 3 === 0) { this.addEnergy(5, 'habit'); } + + // 保存数据 + this.$persist(); }, // 攻击怪兽 @@ -259,6 +329,27 @@ export const useStarEnergyStore = defineStore('starEnergy', { return true; }, + // 标记奖励为已给孩子 + markRewardAsGiven(rewardId: string) { + if (!this.givenRewards.includes(rewardId)) { + this.givenRewards.push(rewardId); + this.$persist(); + return true; + } + return false; + }, + + // 取消标记奖励为已给孩子 + unmarkRewardAsGiven(rewardId: string) { + const index = this.givenRewards.indexOf(rewardId); + if (index > -1) { + this.givenRewards.splice(index, 1); + this.$persist(); + return true; + } + return false; + }, + // 重置所有数据(清除游戏进度) resetAll() { this.total = 0; @@ -305,6 +396,12 @@ export const useStarEnergyStore = defineStore('starEnergy', { count: 0, streak: 0, lastChecked: null + }, + cleanRoom: { + name: '打扫卫生', + count: 0, + streak: 0, + lastChecked: null } }; this.monsters = { @@ -312,11 +409,12 @@ export const useStarEnergyStore = defineStore('starEnergy', { name: '坏习惯大魔王', health: 500, defeated: false, - habits: ['brushTeeth', 'noBedLate', 'noPickyEating', 'washHands', 'homeworkFast', 'earlySleep'] + habits: ['brushTeeth', 'noBedLate', 'noPickyEating', 'washHands', 'homeworkFast', 'earlySleep', 'cleanRoom'] } }; this.badges = []; this.claimedRewards = []; + this.givenRewards = []; this.bossDefeats = 0; const user = getCurrentUser(); localStorage.removeItem(`starEnergyData_${user}`);