version 2

This commit is contained in:
2026-03-02 07:02:25 +08:00
parent 0c220d3fe2
commit 6e3f63185d
3 changed files with 242 additions and 31 deletions

View File

@@ -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;
};
// 打卡习惯

View File

@@ -65,13 +65,38 @@
<!-- 已兑换奖励 -->
<div class="claimed-rewards" v-if="claimedRewards.length > 0">
<h3 class="claimed-title">🎉 已兑换的奖励</h3>
<div class="reward-stats">
<div class="stat-item">
<span class="stat-label">已兑换:</span>
<span class="stat-value">{{ claimedRewards.length }} </span>
</div>
<div class="stat-item">
<span class="stat-label">已给孩子:</span>
<span class="stat-value">{{ givenRewards.length }} </span>
</div>
<div class="stat-item">
<span class="stat-label">待领取:</span>
<span class="stat-value">{{ claimedRewards.length - givenRewards.length }} </span>
</div>
</div>
<div class="claimed-grid">
<div
v-for="id in claimedRewards"
:key="id"
class="claimed-card"
:class="{ given: givenRewards.includes(id) }"
>
{{ getRewardById(id)?.emoji }} {{ getRewardById(id)?.name }}
<div class="claimed-content">
{{ getRewardById(id)?.emoji }} {{ getRewardById(id)?.name }}
</div>
<button
v-if="!givenRewards.includes(id)"
class="give-btn"
@click="markRewardAsGiven(id)"
>
已给孩子
</button>
<div v-else class="given-badge">已给孩子 </div>
</div>
</div>
</div>
@@ -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);
}
/* 弹窗 */

View File

@@ -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}`);