【OpenHarmony/HarmonyOs 】知识挑战模块拆解:题库筛选、限时模式、暂停续答与成绩统计
本文基于我的 OpenHarmony/HarmonyOS 项目「物理视界 PhysicsVision」整理。项目中的「物理挑战」模块包含本地题库、年级筛选、分类筛选、题目数量选择、限时模式、暂停续答、连对统计和成绩记录。
这一篇单独拆解挑战模块的完整交互链路。🎯
一、挑战模块的定位
物理模型负责“理解”,公式计算器负责“验证”,挑战模块则负责“巩固”。
学习 App 如果只有内容展示,很容易停留在浏览;加入挑战后,用户才会产生反馈:
- 我掌握了吗?
- 哪个分类容易错?
- 当前正确率如何?
- 是否需要回到模型继续学习?
所以挑战模块是学习闭环中的关键一环。
二、题目数据结构
项目先定义了题目接口:
interfaceQuizQuestion{ question:stringoptionA:stringoptionB:stringoptionC:stringoptionD:stringanswer: number category:stringgrade:stringexplanation:string}每道题包含:
- 题干;
- A/B/C/D 四个选项;
- 正确答案索引;
- 所属分类;
- 年级;
- 解析。
这套结构足够支撑筛选、答题、解析、统计和后续错题本。
三、挑战状态管理
页面使用多个@State管理答题流程:
@StatequizState: string ='home'@StatecurrentQ: number =0@Statescore: number =0@StateselectedAnswer: number = -1@Stateanswered: boolean = false@StatetotalQuestions: number =10@Statestreak: number =0@StatemaxStreak: number =0@StatetimeLimit: number =0@StatetimeLeft: number =0其中quizState控制页面状态:
home:挑战首页;playing:答题中;result:结果页。
这种状态机思路很适合复杂交互页面。
四、年级与分类筛选
题库支持两个筛选维度:
@StateselectedGrade: string ='全部'@StateselectedCategory: string ='全部'privategrades: string[] = ['全部','初中','高一','高二','高三'] privatecategories: string[] = ['全部','力学','电磁学','光学','热学','波动']筛选函数如下:
getFilteredQuestions(): QuizQuestion[] {constfiltered: QuizQuestion[] = []for(let i =0; i <this.allQuestions.length; i++) {constq =this.allQuestions[i]constgradeMatch =this.selectedGrade ==='全部'|| q.grade ===this.selectedGradeconstcategoryMatch =this.selectedCategory ==='全部'|| q.category ===this.selectedCategoryif(gradeMatch && categoryMatch) { filtered.push(q) } }returnfiltered }这样学生可以选择“高一 + 力学”或“高三 + 电磁学”等组合,复习更有针对性。
五、随机抽题:打乱后取前 N 道
开始挑战前,项目会先筛选题目,再随机打乱:
shuffleAndPick():void{constfiltered =this.getFilteredQuestions()constshuffled:QuizQuestion[] = []for(leti =0; i < filtered.length; i++) { shuffled.push(filtered[i]) }for(leti = shuffled.length-1; i >0; i--) {constj =Math.floor(Math.random() * (i +1))consttemp = shuffled[i] shuffled[i] = shuffled[j] shuffled[j] = temp }this.currentQuestions= []constcount =Math.min(this.totalQuestions, shuffled.length)for(leti =0; i < count; i++) {this.currentQuestions.push(shuffled[i]) } }这里使用了经典洗牌算法。
如果题库数量不足,则取实际可用数量,避免越界。
六、开始挑战
startQuiz(): void {this.shuffleAndPick()if(this.currentQuestions.length ===0)returnthis.currentQ =0this.score =0this.selectedAnswer = -1this.answered =falsethis.cardAnim =falsethis.hasPausedQuiz =falsethis.streak =0this.maxStreak =0this.quizState ='playing'setTimeout(() => { animateTo({ duration:600, curve: curves.springCurve(0,1,328,28) }, () => {this.cardAnim =true})this.startQuestionTimer() },100) }开始时会重置答题状态、得分、连对数,并切换到答题页。
动画结束后启动计时器。
七、限时模式
挑战页支持每题不限时、30 秒、15 秒:
ForEach([0,30,15],(sec: number)=>{ Text(sec ===0?'无限制': sec.toString() +'s') .onClick(()=>{ this.timeLimit = sec }) })计时器逻辑:
startQuestionTimer(): void {this.stopQuestionTimer()if(this.timeLimit <=0)returnthis.timeLeft =this.timeLimitthis.questionTimerId = setInterval(() => {this.timeLeft--if(this.timeLeft <=0) {this.stopQuestionTimer()if(!this.answered) {this.answered =truethis.selectedAnswer = -1this.streak =0} } },1000) }超时后自动标记为已答,选项为-1,连对清零。
八、暂停与继续
挑战模块支持暂停:
pauseQuiz(): void {this.stopQuestionTimer()this.hasPausedQuiz =truethis.quizState ='home'}继续答题:
resumeQuiz(): void {this.hasPausedQuiz =falsethis.cardAnim =falsethis.quizState ='playing'if(this.answered) {// 保持已答状态,让用户点下一题}else{this.selectedAnswer = -1} }这个设计很贴近真实使用场景。
学生可能中途切出去看公式、查模型或处理别的事,回来后可以继续挑战。
九、选择答案与连对统计
selectOption(index: number): void {if(this.answered)returnthis.stopQuestionTimer()this.selectedAnswer = indexthis.answered =trueconstq =this.currentQuestions[this.currentQ]if(index === q.answer) {this.score++this.streak++if(this.streak >this.maxStreak)this.maxStreak =this.streak }else{this.streak =0} }逻辑很直接:
- 已答过就不能重复选择;
- 选中后停止计时;
- 答对加分和连对;
- 答错连对清零。
这类即时反馈能增强挑战感。
十、完成挑战与最高分记录
最后一道题完成后:
this.totalPlayed++constpct =this.currentQuestions.length >0? Math.round(this.score /this.currentQuestions.length *100) :0if(pct >this.highScore) {this.highScore = pct }this.quizState ='result'这里使用@StorageLink保存挑战次数和最高正确率:
@StorageLink('highScore')highScore: number =0@StorageLink('totalPlayed')totalPlayed: number =0这些数据还会被设置页、成就页读取,形成全局学习状态。
总结
挑战模块看起来只是答题,但实际上包含一条完整链路:
筛选题库 → 随机抽题 → 开始挑战 → 限时答题 → 暂停续答 → 判断正误 → 统计成绩 → 更新学习状态。
对 OpenHarmony/HarmonyOS 应用开发来说,这类模块很适合用状态驱动实现。
只要把答题流程拆清楚,ArkUI 的响应式状态就能让页面自然跟随数据变化。🎯