From ae62c7da9d567824917f2c0a7c30d6df3cf940ed Mon Sep 17 00:00:00 2001 From: guoyoudu Date: Mon, 2 Mar 2026 22:40:49 +0800 Subject: [PATCH] version 3 --- src/App.vue | 138 +++- src/components/AdventureArea.vue | 25 +- src/components/AdventureMap.vue | 93 +-- src/components/CraftArea.vue | 18 +- src/components/ExternalLearningArea.vue | 819 ++++++++++++++++++++++++ src/components/HabitArea.vue | 84 ++- src/components/KnowledgeArea.vue | 414 +++++++++--- src/components/LogicArea.vue | 12 +- src/components/ParentArea.vue | 15 +- src/components/RewardArea.vue | 35 +- src/components/WelcomeArea.vue | 54 +- src/stores/starEnergy.ts | 8 +- 12 files changed, 1499 insertions(+), 216 deletions(-) create mode 100644 src/components/ExternalLearningArea.vue diff --git a/src/App.vue b/src/App.vue index f8e940e..05fdd20 100644 --- a/src/App.vue +++ b/src/App.vue @@ -171,6 +171,17 @@ + + @@ -116,7 +116,7 @@

星星收集

帮助火箭收集星星,避开陨石

-
⭐ +20 能量
+
⭐ +1 能量
开始 →
@@ -127,7 +127,7 @@

宇宙拼图

拖动拼图块,完成宇宙图案

-
⭐ +18 能量
+
⭐ +1 能量
开始 →
@@ -138,7 +138,7 @@

月球漫步

在月球上漫步,收集月球石

-
⭐ +25 能量
+
⭐ +1 能量
开始 →
@@ -149,7 +149,7 @@

星座连线

按顺序连接星星,组成星座

-
⭐ +22 能量
+
⭐ +1 能量
开始 →
@@ -343,7 +343,7 @@

云朵迷宫

在云朵中找到正确的路径

-
⭐ +15 能量
+
⭐ +1 能量
开始 →
@@ -354,7 +354,7 @@

小鸟学飞

帮助小鸟学习飞行,收集字母

-
⭐ +18 能量
+
⭐ +1 能量
开始 →
@@ -365,7 +365,7 @@

彩虹画画

给彩虹涂上美丽的颜色

-
⭐ +20 能量
+
⭐ +1 能量
开始 →
@@ -376,7 +376,7 @@

彩云拼图

拼出可爱的云朵形状

-
⭐ +16 能量
+
⭐ +1 能量
开始 →
@@ -538,7 +538,7 @@

海底寻宝

在海底寻找宝藏

-
⭐ +18 能量
+
⭐ +1 能量
开始 →
@@ -549,7 +549,7 @@

鱼儿认识

认识各种海洋生物

-
⭐ +15 能量
+
⭐ +1 能量
开始 →
@@ -560,7 +560,7 @@

珊瑚涂色

给美丽的珊瑚涂色

-
⭐ +20 能量
+
⭐ +1 能量
开始 →
@@ -571,7 +571,7 @@

海洋拼图

拼出海底世界

-
⭐ +22 能量
+
⭐ +1 能量
开始 →
@@ -712,7 +712,7 @@

矿石开采

开采各种珍贵的矿石

-
⭐ +16 能量
+
⭐ +1 能量
开始 →
@@ -723,7 +723,7 @@

地鼠打洞

帮助地鼠找到正确的洞穴

-
⭐ +15 能量
+
⭐ +1 能量
开始 →
@@ -734,7 +734,7 @@

洞穴探险

在黑暗的洞穴中探险

-
⭐ +20 能量
+
⭐ +1 能量
开始 →
@@ -843,7 +843,7 @@

动物朋友

认识森林里的动物朋友

-
⭐ +15 能量
+
⭐ +1 能量
开始 →
@@ -854,7 +854,7 @@

植物生长

帮助植物从种子长成大树

-
⭐ +18 能量
+
⭐ +1 能量
开始 →
@@ -865,7 +865,7 @@

树叶收集

收集各种美丽的树叶

-
⭐ +16 能量
+
⭐ +1 能量
开始 →
@@ -876,7 +876,7 @@

森林拼图

拼出美丽的森林景象

-
⭐ +20 能量
+
⭐ +1 能量
开始 →
@@ -1021,7 +1021,7 @@

九九八十一难

和唐僧师徒一起经历九九八十一难,学习各种知识

-
⭐ +50 能量
+
⭐ +10 能量
开始 →
@@ -1154,7 +1154,7 @@

情感教育与习惯养成

和哪吒一起学习情感管理、好习惯养成和价值观培养

-
⭐ +40 能量
+
⭐ +10 能量
开始 →
@@ -2516,7 +2516,18 @@ const initJourneyLevel = () => { // 加载关卡题目 const loadJourneyQuestion = () => { if (journeyLevel.value <= journeyQuestions.length) { - currentJourneyQuestion.value = journeyQuestions[journeyLevel.value - 1]; + const originalQuestion = journeyQuestions[journeyLevel.value - 1]; + // 复制问题对象 + const questionCopy = { ...originalQuestion }; + // 随机化选项顺序 + if (questionCopy.options) { + questionCopy.options = [...questionCopy.options]; + for (let i = questionCopy.options.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [questionCopy.options[i], questionCopy.options[j]] = [questionCopy.options[j], questionCopy.options[i]]; + } + } + currentJourneyQuestion.value = questionCopy; showJourneyFeedback.value = false; } else { journeyComplete.value = true; @@ -2527,7 +2538,7 @@ const loadJourneyQuestion = () => { const checkJourneyAnswer = (option: string) => { isJourneyCorrect.value = option === currentJourneyQuestion.value.answer; journeyFeedbackMessage.value = isJourneyCorrect.value - ? `太棒了!回答正确!正确答案是 ${currentJourneyQuestion.value.answer}。获得 5 颗星星能量!` + ? `太棒了!回答正确!正确答案是 ${currentJourneyQuestion.value.answer}。获得 2 颗星星能量!` : `再试一次,加油!你选择的是 ${option}。`; showJourneyFeedback.value = true; if (isJourneyCorrect.value) { @@ -2538,9 +2549,6 @@ const checkJourneyAnswer = (option: string) => { // 下一关 const nextJourneyLevel = () => { if (isJourneyCorrect.value) { - // 增加星星能量 - starEnergyStore.addEnergy(5, 'knowledge'); - // 进入下一关 journeyLevel.value++; @@ -2594,7 +2602,18 @@ const initNezhaLevel = () => { // 加载哪吒闹海题目 const loadNezhaQuestion = () => { if (nezhaLevel.value <= nezhaQuestions.length) { - currentNezhaQuestion.value = nezhaQuestions[nezhaLevel.value - 1]; + const originalQuestion = nezhaQuestions[nezhaLevel.value - 1]; + // 复制问题对象 + const questionCopy = { ...originalQuestion }; + // 随机化选项顺序 + if (questionCopy.options) { + questionCopy.options = [...questionCopy.options]; + for (let i = questionCopy.options.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [questionCopy.options[i], questionCopy.options[j]] = [questionCopy.options[j], questionCopy.options[i]]; + } + } + currentNezhaQuestion.value = questionCopy; showNezhaFeedback.value = false; } else { nezhaComplete.value = true; @@ -2605,7 +2624,7 @@ const loadNezhaQuestion = () => { const checkNezhaAnswer = (option: string) => { isNezhaCorrect.value = option === currentNezhaQuestion.value.answer; nezhaFeedbackMessage.value = isNezhaCorrect.value - ? `太棒了!回答正确!正确答案是 ${currentNezhaQuestion.value.answer}。获得 5 颗星星能量!` + ? `太棒了!回答正确!正确答案是 ${currentNezhaQuestion.value.answer}。获得 2 颗星星能量!` : `再试一次,加油!你选择的是 ${option}。`; showNezhaFeedback.value = true; if (isNezhaCorrect.value) { @@ -2616,9 +2635,6 @@ const checkNezhaAnswer = (option: string) => { // 哪吒闹海下一关 const nextNezhaLevel = () => { if (isNezhaCorrect.value) { - // 增加星星能量 - starEnergyStore.addEnergy(5, 'knowledge'); - // 进入下一关 nezhaLevel.value++; @@ -2773,17 +2789,8 @@ const startActivity = (activity: string) => { // 完成活动 const completeActivity = () => { - const rewards: Record = { - 'planet-explorer': 15, 'star-collector': 20, 'space-puzzle': 18, 'moon-walk': 25, 'constellation': 22, - 'cloud-maze': 15, 'bird-flight': 18, 'rainbow-paint': 20, 'cloud-puzzle': 16, - 'treasure-hunt': 18, 'fish-match': 15, 'coral-color': 20, 'ocean-puzzle': 22, - 'mining': 16, 'mole-whack': 15, 'cave-explore': 20, - 'animal-friends': 15, 'plant-grow': 18, 'leaf-collect': 16, 'forest-puzzle': 20, - 'journey-to-west': 50, - 'nezha-sea': 40 - }; - - const reward = rewards[currentActivity.value || ''] || 10; + // 西游记和哪吒闹海奖励10颗星星,其他活动奖励1颗星星 + const reward = currentScene.value === 'journey' || currentScene.value === 'nezha' ? 10 : 1; starEnergyStore.addEnergy(reward, 'adventure'); playAudio('活动完成!获得' + reward + '颗星星能量!'); alert('🎉 活动完成!获得 ' + reward + ' 颗星星能量!'); diff --git a/src/components/CraftArea.vue b/src/components/CraftArea.vue index 8bacae3..0a7813e 100644 --- a/src/components/CraftArea.vue +++ b/src/components/CraftArea.vue @@ -150,21 +150,21 @@ const tasks = ref({ { title: '卡纸花朵', description: '跟着 "手工兔" 学做卡纸花朵,步骤有图片 + 语音讲解', - reward: 8, + reward: 1, completed: false, icon: '🌸' }, { title: '瓶盖拼图', description: '用瓶盖制作创意拼图,发挥想象力', - reward: 8, + reward: 1, completed: false, icon: '🧩' }, { title: '纸盘手偶', description: '用纸盘制作可爱的手偶,自己设计表情', - reward: 8, + reward: 1, completed: false, icon: '🤹' } @@ -173,21 +173,21 @@ const tasks = ref({ { title: '叠袜子', description: '3-4 岁:学习叠袜子,整齐摆放', - reward: 10, + reward: 1, completed: false, icon: '🧦' }, { title: '摆碗筷', description: '5-6 岁:帮助摆放碗筷,准备吃饭', - reward: 10, + reward: 1, completed: false, icon: '🍽️' }, { title: '整理书包', description: '7-8 岁:自己整理书包,准备第二天的学习用品', - reward: 10, + reward: 1, completed: false, icon: '🎒' } @@ -196,21 +196,21 @@ const tasks = ref({ { title: '蚂蚁搬家', description: '拍蚂蚁搬家的照片,观察它们的行为', - reward: 8, + reward: 1, completed: false, icon: '🐜' }, { title: '树叶脉络', description: '收集不同形状的树叶,观察它们的脉络', - reward: 8, + reward: 1, completed: false, icon: '🍃' }, { title: '花朵写生', description: '在户外观察花朵,用画笔记录它们的样子', - reward: 8, + reward: 1, completed: false, icon: '🖌️' } diff --git a/src/components/ExternalLearningArea.vue b/src/components/ExternalLearningArea.vue new file mode 100644 index 0000000..b2e0fad --- /dev/null +++ b/src/components/ExternalLearningArea.vue @@ -0,0 +1,819 @@ + + + + + diff --git a/src/components/HabitArea.vue b/src/components/HabitArea.vue index e135b35..6bedafd 100644 --- a/src/components/HabitArea.vue +++ b/src/components/HabitArea.vue @@ -196,8 +196,20 @@ const isChecked = (habit: keyof typeof habitData.value) => { // 打卡习惯 const checkHabit = (habit: keyof typeof habitData.value) => { + const habitInfo = starEnergyStore.habits[habit]; + if (!habitInfo) { + console.error('习惯数据不存在:', habit); + return; + } + + const today = new Date().toISOString().split('T')[0]; + if (habitInfo.lastChecked === today) { + alert('今天已经打卡过了,明天再来吧!💪'); + return; + } + starEnergyStore.checkHabit(habit); - alert(`打卡成功!获得 10 颗星星能量!`); + alert(`打卡成功!获得 1 颗星星能量!💫`); }; // 应用坏习惯扣分 @@ -502,19 +514,40 @@ const getMonsterEmoji = (monster: keyof typeof monsterData.value) => { left: -50%; width: 200%; height: 200%; - background: linear-gradient(45deg, transparent, rgba(255, 255, 255, 0.3), transparent); + background: linear-gradient(45deg, transparent, rgba(255, 255, 255, 0.4), transparent); transform: rotate(45deg); animation: shine 3s ease-in-out infinite; } +.habit-card::after { + content: '⭐'; + position: absolute; + top: 8px; + right: 8px; + font-size: 18px; + animation: sparkle 2s ease-in-out infinite; + opacity: 0.8; +} + @keyframes shine { 0% { transform: translateX(-100%) rotate(45deg); } 100% { transform: translateX(100%) rotate(45deg); } } +@keyframes sparkle { + 0%, 100% { + opacity: 0.8; + transform: scale(1) rotate(0deg); + } + 50% { + opacity: 1; + transform: scale(1.3) rotate(10deg); + } +} + .habit-card:hover { - transform: translateY(-8px) scale(1.05); - box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15); + transform: translateY(-12px) scale(1.08) rotate(2deg); + box-shadow: 0 15px 35px rgba(0, 0, 0, 0.2), 0 0 30px rgba(255, 182, 193, 0.3); } .habit-icon { @@ -546,31 +579,60 @@ const getMonsterEmoji = (monster: keyof typeof monsterData.value) => { } .check-btn { - background: linear-gradient(135deg, #98FB98, #90EE90); + background: linear-gradient(135deg, #98FB98, #90EE90, #32CD32); + background-size: 200% 200%; color: #006400; border: none; - padding: 10px 20px; - border-radius: 20px; + padding: 12px 24px; + border-radius: 25px; font-size: 14px; font-weight: bold; cursor: pointer; transition: all 0.3s ease; - box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); + box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15); display: flex; align-items: center; justify-content: center; - gap: 5px; + gap: 6px; margin: 0 auto; width: fit-content; font-family: var(--cartoon-font); + animation: btnPulse 3s ease-in-out infinite; + position: relative; + overflow: hidden; +} + +@keyframes btnPulse { + 0%, 100% { background-position: 0% 50%; } + 50% { background-position: 100% 50%; } +} + +.check-btn::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent); + animation: btnShine 2s ease-in-out infinite; +} + +@keyframes btnShine { + 0% { left: -100%; } + 50%, 100% { left: 100%; } } .check-btn:hover:not(:disabled) { - transform: translateY(-2px); - box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15); + transform: translateY(-4px) scale(1.05); + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2); background: linear-gradient(135deg, #90EE90, #32CD32); } +.check-btn:active:not(:disabled) { + transform: translateY(-2px) scale(1.02); +} + .check-btn:disabled { background: linear-gradient(135deg, #FFD700, #FFA500); color: white; diff --git a/src/components/KnowledgeArea.vue b/src/components/KnowledgeArea.vue index 65c906a..8434ecf 100644 --- a/src/components/KnowledgeArea.vue +++ b/src/components/KnowledgeArea.vue @@ -29,7 +29,7 @@
🐿️

数学魔法课

和数字松鼠一起冒险

-
⭐ +15 能量
+
⭐ +1 能量
@@ -38,7 +38,7 @@
🧚

语文魔法课

帮汉字小精灵拼偏旁

-
⭐ +15 能量
+
⭐ +1 能量
@@ -47,7 +47,7 @@
🐦

英语魔法课

和字母小鸟走迷宫

-
⭐ +15 能量
+
⭐ +1 能量
@@ -56,7 +56,7 @@
🌱

科学魔法课

在植物实验室做实验

-
⭐ +15 能量
+
⭐ +1 能量
@@ -65,27 +65,36 @@
🌸

日语魔法课

学习简单的日语和50音图

-
⭐ +15 能量
+
⭐ +1 能量
+ + + +
+
🗺️
+
🌏
+

地理魔法课

+

学习中国地理常识和南北方差异

+
⭐ +1 能量
-

{{ subjectTitles[currentSubject] }}

+

{{ currentSubject ? subjectTitles[currentSubject as keyof typeof subjectTitles] : '' }}

-
+

今日进度

-
+
- {{ dailyProgress.completed[currentSubject] }}/{{ dailyLimits[currentSubject] }} 题 + {{ dailyProgress.completed[currentSubject as keyof typeof dailyProgress.completed] || 0 }}/{{ dailyLimits[currentSubject as keyof typeof dailyLimits] }} 题
-
+
{{ task.icon }}

{{ task.title }}

@@ -95,8 +104,8 @@ {{ task.reward }} 颗星星能量
-
@@ -267,6 +276,33 @@
+ + +
+
+
+

{{ currentGame.question }}

+ +
+
+
+ {{ option }} +
🔊
+
+
+
+
+
+ {{ feedbackMessage }} +
+ +
+
@@ -297,7 +333,8 @@ const dailyLimits = { chinese: 25, english: 25, science: 25, - japanese: 25 + japanese: 25, + geography: 25 }; // 题目进度跟踪 @@ -333,6 +370,11 @@ const progress = ref({ directions: 0, items: 0, greetings: 0 + }, + geography: { + map: 0, + basics: 0, + northSouth: 0 } }); @@ -344,13 +386,18 @@ const dailyProgress = ref({ chinese: 0, english: 0, science: 0, - japanese: 0 + japanese: 0, + geography: 0 } }); // 检查是否达到每日上限 const isDailyLimitReached = (subject: string) => { - return dailyProgress.value.completed[subject as keyof typeof dailyProgress.value.completed] >= dailyLimits[subject as keyof typeof dailyLimits]; + console.log('检查每日上限:', subject); + const completed = dailyProgress.value.completed[subject as keyof typeof dailyProgress.value.completed] || 0; + const limit = dailyLimits[subject as keyof typeof dailyLimits] || 25; + console.log('已完成:', completed, '上限:', limit); + return completed >= limit; }; // 初始化时加载语音合成API的voices和进度数据 @@ -410,7 +457,8 @@ const checkDateUpdate = () => { chinese: 0, english: 0, science: 0, - japanese: 0 + japanese: 0, + geography: 0 } }; @@ -427,7 +475,8 @@ const subjectTitles = { chinese: '语文魔法课', english: '英语魔法课', science: '科学魔法课', - japanese: '日语魔法课' + japanese: '日语魔法课', + geography: '地理魔法课' }; // 任务数据 @@ -595,11 +644,64 @@ const tasks = ref({ completed: false, icon: '👋' } + ], + geography: [ + { + title: '中国地图认知', + description: '认识中国地图的形状和主要省份', + reward: 5, + completed: false, + icon: '🗺️' + }, + { + title: '中国地理常识', + description: '学习中国的地理位置、面积等基本常识', + reward: 5, + completed: false, + icon: '🌏' + }, + { + title: '南北方差异', + description: '了解中国南北方的地理文化差异', + reward: 5, + completed: false, + icon: '🌵' + } ] }); // 游戏数据 const gameData = { + geography: { + // 中国地图认知 + map: [ + { question: '中国地图的形状像什么?', options: ['公鸡', '兔子', '大象', '龙'], answer: '公鸡' }, + { question: '中国的首都是哪里?', options: ['上海', '北京', '广州', '深圳'], answer: '北京' }, + { question: '中国最大的岛屿是哪个?', options: ['海南岛', '台湾岛', '崇明岛', '舟山群岛'], answer: '台湾岛' }, + { question: '中国最长的河流是哪条?', options: ['黄河', '长江', '珠江', '松花江'], answer: '长江' }, + { question: '中国的母亲河是哪条?', options: ['长江', '黄河', '珠江', '淮河'], answer: '黄河' } + ], + // 中国地理常识 + basics: [ + { question: '中国位于哪个大洲?', options: ['欧洲', '亚洲', '非洲', '北美洲'], answer: '亚洲' }, + { question: '中国的土地面积大约是多少?', options: ['960万平方公里', '860万平方公里', '760万平方公里', '660万平方公里'], answer: '960万平方公里' }, + { question: '中国的邻国中,面积最大的是哪个国家?', options: ['俄罗斯', '印度', '蒙古', '朝鲜'], answer: '俄罗斯' }, + { question: '中国的四大直辖市是北京、上海、天津和哪里?', options: ['重庆', '广州', '深圳', '成都'], answer: '重庆' }, + { question: '中国的地势特点是什么?', options: ['东高西低', '西高东低', '南高北低', '北高南低'], answer: '西高东低' }, + { question: '中国最大的高原是哪个?', options: ['青藏高原', '内蒙古高原', '黄土高原', '云贵高原'], answer: '青藏高原' }, + { question: '中国最大的平原是哪个?', options: ['东北平原', '华北平原', '长江中下游平原', '关中平原'], answer: '东北平原' }, + { question: '中国的南海诸岛属于哪个省级行政区?', options: ['广东省', '海南省', '福建省', '台湾省'], answer: '海南省' }, + { question: '中国的五岳中,位于山东省的是哪座山?', options: ['泰山', '华山', '衡山', '恒山'], answer: '泰山' } + ], + // 南北方差异 + northSouth: [ + { question: '中国南北方的分界线是什么?', options: ['秦岭-淮河', '长江', '黄河', '长城'], answer: '秦岭-淮河' }, + { question: '北方地区的主要粮食作物是什么?', options: ['小麦', '水稻', '玉米', '高粱'], answer: '小麦' }, + { question: '南方地区的主要粮食作物是什么?', options: ['水稻', '小麦', '玉米', '高粱'], answer: '水稻' }, + { question: '北方人喜欢吃的面食是?', options: ['面条', '米饭', '馒头', '饺子'], answer: '面条' }, + { question: '南方人喜欢吃的主食是?', options: ['米饭', '面条', '馒头', '饺子'], answer: '米饭' } + ] + }, math: { // 数数练习(1-100 认知) counting: [ @@ -2241,89 +2343,163 @@ const playAudio = (text: string) => { // 选择学科 const selectSubject = (subject: string) => { + console.log('=== 选择学科开始 ==='); + console.log('参数 subject:', subject); + console.log('currentSubject.value 修改前:', currentSubject.value); currentSubject.value = subject; currentGame.value = null; + console.log('currentSubject.value 修改后:', currentSubject.value); + console.log('currentGame.value:', currentGame.value); + console.log('=== 选择学科结束 ==='); }; // 开始游戏 const startGame = (subject: string, taskIndex: number) => { - console.log('开始游戏:', subject, taskIndex); + try { + console.log('开始游戏函数被调用:', subject, taskIndex); + console.log('类型检查 - subject:', typeof subject, 'taskIndex:', typeof taskIndex); - // 检查是否达到每日上限 - if (isDailyLimitReached(subject)) { - const subjectName = subject === 'math' ? '数学' : subject === 'chinese' ? '语文' : subject === 'english' ? '英语' : subject === 'japanese' ? '日语' : '科学'; - const limit = dailyLimits[subject as keyof typeof dailyLimits]; - alert(`今日${subjectName}题目已达上限(${limit}题),明天再来挑战吧!`); - return; - } + // 检查是否达到每日上限 + if (isDailyLimitReached(subject)) { + const subjectName = subject === 'math' ? '数学' : subject === 'chinese' ? '语文' : subject === 'english' ? '英语' : subject === 'japanese' ? '日语' : subject === 'geography' ? '地理' : '科学'; + const limit = dailyLimits[subject as keyof typeof dailyLimits]; + alert(`今日${subjectName}题目已达上限(${limit}题),明天再来挑战吧!`); + return; + } - // 根据学科和任务索引选择不同的题目集 - let taskType = ''; - let gameDataSubject: any = null; - let startIndex = 0; + // 根据学科和任务索引选择不同的题目集 + let taskType = ''; + let gameDataSubject: any = null; + let startIndex = 0; - switch (subject) { - case 'math': - const mathTasks = ['counting', 'shapes', 'calculation']; - taskType = mathTasks[taskIndex] || 'counting'; - gameDataSubject = gameData.math[taskType as keyof typeof gameData.math]; - startIndex = progress.value.math[taskType as keyof typeof progress.value.math]; - break; - case 'chinese': - const chineseTasks = ['radicals', 'pinyin', 'stories']; - taskType = chineseTasks[taskIndex] || 'radicals'; - gameDataSubject = gameData.chinese[taskType as keyof typeof gameData.chinese]; - startIndex = progress.value.chinese[taskType as keyof typeof progress.value.chinese]; - break; - case 'english': - const englishTasks = ['maze', 'animal', 'dialogue']; - taskType = englishTasks[taskIndex] || 'maze'; - gameDataSubject = gameData.english[taskType as keyof typeof gameData.english]; - startIndex = progress.value.english[taskType as keyof typeof progress.value.english]; - break; - case 'science': - const scienceTasks = ['plants', 'buoyancy', 'colors', 'chemistry', 'physics', 'experiments']; - taskType = scienceTasks[taskIndex] || 'plants'; - gameDataSubject = gameData.science[taskType as keyof typeof gameData.science]; - startIndex = progress.value.science[taskType as keyof typeof progress.value.science]; - break; - case 'japanese': - const japaneseTasks = ['hiragana', 'animals', 'fruits', 'body', 'directions', 'items', 'greetings']; - taskType = japaneseTasks[taskIndex] || 'hiragana'; - gameDataSubject = gameData.japanese[taskType as keyof typeof gameData.japanese]; - startIndex = progress.value.japanese[taskType as keyof typeof progress.value.japanese]; - break; - default: - gameDataSubject = gameData[subject as keyof typeof gameData]; - } + switch (subject) { + case 'math': + const mathTasks = ['counting', 'shapes', 'calculation']; + taskType = mathTasks[taskIndex] || 'counting'; + gameDataSubject = gameData.math[taskType as keyof typeof gameData.math]; + startIndex = progress.value.math[taskType as keyof typeof progress.value.math] || 0; + break; + case 'chinese': + const chineseTasks = ['radicals', 'pinyin', 'words']; + taskType = chineseTasks[taskIndex] || 'radicals'; + gameDataSubject = gameData.chinese[taskType as keyof typeof gameData.chinese]; + startIndex = progress.value.chinese[taskType as keyof typeof progress.value.chinese] || 0; + break; + case 'english': + const englishTasks = ['maze', 'animal', 'dialogue']; + taskType = englishTasks[taskIndex] || 'maze'; + gameDataSubject = gameData.english[taskType as keyof typeof gameData.english]; + startIndex = progress.value.english[taskType as keyof typeof progress.value.english] || 0; + break; + case 'science': + const scienceTasks = ['plants', 'buoyancy', 'colors', 'chemistry', 'physics', 'experiments']; + taskType = scienceTasks[taskIndex] || 'plants'; + gameDataSubject = gameData.science[taskType as keyof typeof gameData.science]; + startIndex = progress.value.science[taskType as keyof typeof progress.value.science] || 0; + break; + case 'japanese': + const japaneseTasks = ['hiragana', 'animals', 'fruits', 'body', 'directions', 'items', 'greetings']; + taskType = japaneseTasks[taskIndex] || 'hiragana'; + gameDataSubject = gameData.japanese[taskType as keyof typeof gameData.japanese]; + startIndex = progress.value.japanese[taskType as keyof typeof progress.value.japanese] || 0; + break; + case 'geography': + const geographyTasks = ['map', 'basics', 'northSouth']; + taskType = geographyTasks[taskIndex] || 'map'; + gameDataSubject = gameData.geography[taskType as keyof typeof gameData.geography]; + startIndex = progress.value.geography[taskType as keyof typeof progress.value.geography] || 0; + break; + default: + gameDataSubject = gameData[subject as keyof typeof gameData]; + startIndex = 0; + } - console.log('选择的任务类型:', taskType); - console.log('游戏数据:', gameDataSubject); - console.log('开始索引:', startIndex); + console.log('选择的任务类型:', taskType); + console.log('游戏数据:', gameDataSubject); + console.log('开始索引:', startIndex); - if (gameDataSubject && gameDataSubject[0]) { - // 确保startIndex不超过题目总数 - const effectiveIndex = Math.min(startIndex, gameDataSubject.length - 1); + console.log('gameDataSubject类型:', typeof gameDataSubject); + console.log('gameDataSubject是否为数组:', Array.isArray(gameDataSubject)); + console.log('gameDataSubject长度:', gameDataSubject ? gameDataSubject.length : 'N/A'); - currentGame.value = { - subject, - taskIndex, - taskType, - question: gameDataSubject[effectiveIndex].question, - options: gameDataSubject[effectiveIndex].options, - parts: gameDataSubject[effectiveIndex].parts, - items: gameDataSubject[effectiveIndex].items, - answer: gameDataSubject[effectiveIndex].answer, - audio: gameDataSubject[effectiveIndex].audio - }; - console.log('当前游戏设置:', currentGame.value); - } else { - console.error('游戏数据未找到:', subject, taskType); + // 确保gameDataSubject是数组且有数据 + if (gameDataSubject && Array.isArray(gameDataSubject) && gameDataSubject.length > 0) { + // 确保startIndex不超过题目总数 + const effectiveIndex = Math.min(startIndex, gameDataSubject.length - 1); + console.log('effectiveIndex:', effectiveIndex); + + // 获取题目数据 + const questionData = gameDataSubject[effectiveIndex]; + console.log('questionData:', questionData); + + if (!questionData) { + console.error('题目数据未找到:', subject, taskType, effectiveIndex); + // 即使没有题目数据,也设置currentGame.value + currentGame.value = { + subject, + taskIndex, + taskType, + question: '暂无题目数据', + options: [], + parts: [], + items: [], + answer: '', + audio: '' + }; + return; + } + + let shuffledOptions = null; + let shuffledAnswer = questionData.answer; + + // 如果有选项,随机打乱顺序 + if (questionData.options) { + // 创建选项的副本并打乱 + shuffledOptions = [...questionData.options]; + for (let i = shuffledOptions.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [shuffledOptions[i], shuffledOptions[j]] = [shuffledOptions[j], shuffledOptions[i]]; + } + } + + // 设置currentGame.value + currentGame.value = { + subject, + taskIndex, + taskType, + question: questionData.question || '暂无题目', + options: shuffledOptions || questionData.options || [], + parts: questionData.parts || [], + items: questionData.items || [], + answer: shuffledAnswer || '', + audio: questionData.audio || '' + }; + console.log('当前游戏设置:', currentGame.value); + console.log('currentGame.value设置后:', currentGame.value); + } else { + console.error('游戏数据未找到或为空:', subject, taskType, gameDataSubject); + // 即使没有游戏数据,也设置currentGame.value,避免界面无反应 + currentGame.value = { + subject, + taskIndex, + taskType, + question: '暂无题目数据', + options: [], + parts: [], + items: [], + answer: '', + audio: '' + }; + } + currentQuestionIndex.value = startIndex; + showFeedback.value = false; + currentAnswer.value = ''; + experimentResult.value = ''; + console.log('函数执行完毕,currentGame.value:', currentGame.value); + } catch (error) { + console.error('开始游戏时发生错误:', error); + alert('开始游戏时发生错误,请重试'); } - currentQuestionIndex.value = startIndex; - showFeedback.value = false; - currentAnswer.value = ''; - experimentResult.value = ''; }; // 检查数学答案 @@ -2437,6 +2613,25 @@ const checkJapaneseAnswer = (option: string) => { } }; +// 检查地理答案 +const checkGeographyAnswer = (option: string) => { + isCorrect.value = option === currentGame.value.answer; + const task = tasks.value[currentGame.value.subject as keyof typeof tasks.value][currentGame.value.taskIndex]; + let starsPerQuestion = 1; + if (currentGame.value.subject === 'math' || currentGame.value.subject === 'chinese' || currentGame.value.subject === 'english' || currentGame.value.subject === 'japanese' || currentGame.value.subject === 'geography') { + starsPerQuestion = Math.ceil(task.reward / 20); + } else if (currentGame.value.subject === 'science') { + starsPerQuestion = Math.ceil(task.reward / 10); + } + feedbackMessage.value = isCorrect.value + ? `太棒了!回答正确!正确答案是 ${currentGame.value.answer}。获得 ${starsPerQuestion} 颗星星能量!` + : `再试一次,加油!你选择的是 ${option}。`; + showFeedback.value = true; + if (isCorrect.value) { + playAudio(`太棒了,正确答案是 ${currentGame.value.answer}`); + } +}; + // 选择语文偏旁 const selectPart = (part: string) => { currentAnswer.value += part; @@ -2454,12 +2649,16 @@ const nextQuestion = () => { const taskIndex = currentGame.value.taskIndex; const task = tasks.value[subject as keyof typeof tasks.value][taskIndex]; - // 计算每题的星星奖励(总奖励除以题目数量) + // 计算每题的星星奖励(降低获得量) let starsPerQuestion = 1; - if (subject === 'math' || subject === 'chinese' || subject === 'english') { + if (subject === 'math' || subject === 'chinese' || subject === 'english' || subject === 'japanese' || subject === 'geography') { + // 降低数学、语文、英语、日语、地理的奖励获得量 + // 总奖励为5,分成20题,每题0.25颗,向上取整为1颗 starsPerQuestion = Math.ceil(task.reward / 20); // 20题任务 } else if (subject === 'science') { - starsPerQuestion = Math.ceil(task.reward / 10); // 10题任务 + // 降低科学的奖励获得量 + // 总奖励为8-12,分成10题,每题0.8-1.2颗,向上取整为1颗 + starsPerQuestion = Math.ceil(task.reward / 15); // 15题任务的计算方式,降低获得量 } // 添加星星能量 @@ -2485,6 +2684,9 @@ const nextQuestion = () => { case 'japanese': progress.value.japanese[taskType as keyof typeof progress.value.japanese]++; break; + case 'geography': + progress.value.geography[taskType as keyof typeof progress.value.geography]++; + break; } // 更新每日完成记录 @@ -2495,7 +2697,7 @@ const nextQuestion = () => { // 检查是否达到每日上限 if (isDailyLimitReached(subject)) { - const subjectName = subject === 'math' ? '数学' : subject === 'chinese' ? '语文' : subject === 'english' ? '英语' : subject === 'japanese' ? '日语' : '科学'; + const subjectName = subject === 'math' ? '数学' : subject === 'chinese' ? '语文' : subject === 'english' ? '英语' : subject === 'japanese' ? '日语' : subject === 'geography' ? '地理' : '科学'; const limit = dailyLimits[subject as keyof typeof dailyLimits]; alert(`今日${subjectName}题目已达上限(${limit}题),明天再来挑战吧!`); currentGame.value = null; @@ -2522,21 +2724,38 @@ const nextQuestion = () => { case 'japanese': gameDataSubject = gameData.japanese[taskType as keyof typeof gameData.japanese]; break; + case 'geography': + gameDataSubject = gameData.geography[taskType as keyof typeof gameData.geography]; + break; default: gameDataSubject = gameData[subject as keyof typeof gameData]; } if (gameDataSubject && currentQuestionIndex.value < gameDataSubject.length) { + const questionData = gameDataSubject[currentQuestionIndex.value]; + let shuffledOptions = null; + let shuffledAnswer = questionData.answer; + + // 如果有选项,随机打乱顺序 + if (questionData.options) { + // 创建选项的副本并打乱 + shuffledOptions = [...questionData.options]; + for (let i = shuffledOptions.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [shuffledOptions[i], shuffledOptions[j]] = [shuffledOptions[j], shuffledOptions[i]]; + } + } + currentGame.value = { subject, taskIndex, taskType, - question: gameDataSubject[currentQuestionIndex.value].question, - options: gameDataSubject[currentQuestionIndex.value].options, - parts: gameDataSubject[currentQuestionIndex.value].parts, - items: gameDataSubject[currentQuestionIndex.value].items, - answer: gameDataSubject[currentQuestionIndex.value].answer, - audio: gameDataSubject[currentQuestionIndex.value].audio + question: questionData.question, + options: shuffledOptions || questionData.options, + parts: questionData.parts, + items: questionData.items, + answer: shuffledAnswer, + audio: questionData.audio }; showFeedback.value = false; currentAnswer.value = ''; @@ -2745,6 +2964,7 @@ const nextQuestion = () => { .subject-card.chinese::before { background: linear-gradient(90deg, #9370DB, #DDA0DD); } .subject-card.english::before { background: linear-gradient(90deg, #87CEEB, #4682B4); } .subject-card.science::before { background: linear-gradient(90deg, #98FB98, #32CD32); } +.subject-card.geography::before { background: linear-gradient(90deg, #FFA500, #FFD700); } .subject-icon { font-size: 50px; diff --git a/src/components/LogicArea.vue b/src/components/LogicArea.vue index 8001e99..87408d4 100644 --- a/src/components/LogicArea.vue +++ b/src/components/LogicArea.vue @@ -145,14 +145,14 @@ const selectGame = (game: string) => { // 完成迷宫 const completeMaze = () => { - starEnergyStore.addEnergy(5, 'logic'); - alert('迷宫挑战成功!获得 5 颗星星能量!'); + starEnergyStore.addEnergy(1, 'logic'); + alert('迷宫挑战成功!获得 1 颗星星能量!'); }; // 检查找不同答案 const checkDiff = () => { // 这里可以添加答案检查逻辑 - starEnergyStore.addEnergy(5, 'logic'); + starEnergyStore.addEnergy(1, 'logic'); alert('找不同挑战成功!获得 5 颗星星能量!'); }; @@ -165,14 +165,14 @@ const clearCanvas = () => { // 完成涂鸦 const completeDoodle = () => { - starEnergyStore.addEnergy(8, 'logic'); - alert('涂鸦完成!星宝把你的画变成了动画!获得 8 颗星星能量!'); + starEnergyStore.addEnergy(1, 'logic'); + alert('涂鸦完成!星宝把你的画变成了动画!获得 1 颗星星能量!'); }; // 创建故事 const createStory = () => { if (storyText.value) { - starEnergyStore.addEnergy(8, 'logic'); + starEnergyStore.addEnergy(1, 'logic'); alert('故事创编成功!星宝把你的故事做成了动画!获得 8 颗星星能量!'); } else { alert('请先编写故事内容!'); diff --git a/src/components/ParentArea.vue b/src/components/ParentArea.vue index df744d6..3ba2691 100644 --- a/src/components/ParentArea.vue +++ b/src/components/ParentArea.vue @@ -41,7 +41,7 @@
- +
@@ -208,10 +208,19 @@ const readFeedback = (content?: string) => { } try { + // 检查浏览器是否支持语音合成 + if (!('speechSynthesis' in window)) { + alert('您的设备不支持语音朗读功能'); + return; + } + + // 停止当前正在播放的语音 + window.speechSynthesis.cancel(); + const speech = new SpeechSynthesisUtterance(text); speech.lang = 'zh-CN'; speech.volume = 1; - speech.rate = 1; + speech.rate = 1.1; speech.pitch = 1.2; // 尝试选择更适合儿童的中文语音 @@ -254,6 +263,8 @@ const readFeedback = (content?: string) => { } } catch (error) { console.error('音频播放失败:', error); + // 语音合成失败时的备用提示 + alert('语音播放失败,请检查设备音频设置'); } }; diff --git a/src/components/RewardArea.vue b/src/components/RewardArea.vue index 77046ba..02313b2 100644 --- a/src/components/RewardArea.vue +++ b/src/components/RewardArea.vue @@ -347,35 +347,38 @@ const getButtonText = (reward: any) => { // 兑换奖励 const redeemReward = (reward: any) => { + // 检查是否已兑换 + if (claimedRewards.value.includes(reward.id)) { + alert('这个奖励已经兑换过了!'); + return; + } + // 检查是否是BOSS奖励 if (reward.type === 'boss_defeat') { - if (starEnergyStore.getBossDefeats < reward.cost) { - insufficientStars.value = reward.cost - starEnergyStore.getBossDefeats; + const bossDefeats = starEnergyStore.getBossDefeats; + if (bossDefeats < reward.cost) { + insufficientStars.value = reward.cost - bossDefeats; selectedReward.value = reward; showInsufficientModal.value = true; return; } } else { // 普通奖励使用星星能量 - if (starEnergyStore.getTotalEnergy < reward.cost) { - insufficientStars.value = reward.cost - starEnergyStore.getTotalEnergy; + const totalEnergy = starEnergyStore.getTotalEnergy; + if (totalEnergy < reward.cost) { + insufficientStars.value = reward.cost - totalEnergy; selectedReward.value = reward; showInsufficientModal.value = true; return; } } - if (claimedRewards.value.includes(reward.id)) { - return; - } - selectedReward.value = reward; let success = false; if (reward.type === 'boss_defeat') { - // BOSS奖励使用击败次数兑换 - // 这里可以添加专门的BOSS奖励兑换逻辑 - success = starEnergyStore.redeemReward(reward.id, 0); // 不消耗星星 + // BOSS奖励使用击败次数兑换,不消耗星星 + success = starEnergyStore.redeemReward(reward.id, 0); } else { // 普通奖励使用星星能量 success = starEnergyStore.redeemReward(reward.id, reward.cost); @@ -383,18 +386,20 @@ const redeemReward = (reward: any) => { if (success) { showSuccessModal.value = true; - + // 播放音效 try { const speech = new SpeechSynthesisUtterance('恭喜你,兑换成功!'); speech.lang = 'zh-CN'; speech.volume = 1; - speech.rate = 1; + speech.rate = 1.1; speech.pitch = 1.2; window.speechSynthesis.speak(speech); } catch (error) { console.error('音频播放失败:', error); } + } else { + alert('兑换失败,请重试!'); } }; @@ -578,8 +583,8 @@ const closeModal = () => { } .reward-card:hover:not(.disabled):not(.claimed) { - transform: translateY(-5px); - box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15); + transform: translateY(-8px) scale(1.05); + box-shadow: 0 12px 35px rgba(0, 0, 0, 0.2), 0 0 30px rgba(152, 251, 152, 0.3); border-color: #32CD32; } diff --git a/src/components/WelcomeArea.vue b/src/components/WelcomeArea.vue index 68c2a9b..cf7ac5d 100644 --- a/src/components/WelcomeArea.vue +++ b/src/components/WelcomeArea.vue @@ -97,7 +97,11 @@ 🚀 开始冒险 -
💡 点击按钮开启你的冒险之旅!
+ +
💡 选择一个按钮开启你的冒险之旅!
@@ -170,6 +174,13 @@ const startAdventure = () => { // 触发导航事件,显示冒险地图 emit('navigate', 'adventure'); }; + +const goToExternal = () => { + // 播放课外学习的语音 + playAudio('让我们去课外学习天地,探索更多有趣的知识吧!'); + // 触发导航事件,显示课外学习区域 + emit('navigate', 'external'); +};