【OpenHarmony/HarmonyOs 】限时答题状态机实践:倒计时、暂停、自动提交与实况窗结束态设计

【OpenHarmony/HarmonyOs 】限时答题状态机实践:倒计时、暂停、自动提交与实况窗结束态设计

项目类型:OpenHarmony / HarmonyOS ArkTS 数学学习应用
项目名称:数学视界
对应主题:实况窗、全场景智慧生活、端侧 AI
关键词:ArkTS、倒计时、限时答题、状态机、自动提交、实况窗 ⏱️

一、这篇文章写什么?

之前已经写过答题结果页和题型渲染,这篇只聚焦“答题进行中”的状态管理:倒计时、暂停、自动提交和结束态。

数学视界的QuizPage.ets已经具备限时答题基础:

  • 记录总用时;
  • 每题限时倒计时;
  • 暂停状态;
  • 时间到自动提交;
  • 切题时重置倒计时;
  • 完成后生成结果数据。

这些状态非常适合扩展到“实况窗”思路:把正在进行的挑战展示成系统级持续状态。

二、核心状态:答题过程不是一个布尔值

答题页状态包括:

@StatecurrentIndex: number =0@StateuserAnswers: string[] = []@StatequestionStartTime: number =0@StatetotalTimeSpent: number =0@StateshowAnalysis: boolean = false@StateisSubmitting: boolean = false@StatequestionTimer: number =0@StateisPaused: boolean = false

这些状态共同描述答题过程:

  • 当前第几题;
  • 用户答案;
  • 总用时;
  • 当前题剩余时间;
  • 是否暂停;
  • 是否正在提交;
  • 是否显示解析。

这就是一个小型状态机。

三、启动计时器:总用时和题目倒计时分开

startTimer()中有两个计时器:

startTimer(): void {if(this.timerHandler >=0) { clearInterval(this.timerHandler) }this.timerHandler = setInterval(() => {if(!this.isPaused) {this.totalTimeSpent++ } },1000) }

总用时只关心整个挑战持续了多久。只要没有暂停,就每秒增加。

题目倒计时则和配置有关:

if(cfg.timeLimit !== undefined && cfg.timeLimit >0) {this.questionTimer = cfg.timeLimitthis.questionTimerHandler = setInterval(() => {if(!this.isPaused &&this.questionTimer >0) {this.questionTimer--if(this.questionTimer ===0) {this.autoSubmit() } } },1000) }

这里的设计很清楚:

  • totalTimeSpent是全局;
  • questionTimer是单题;
  • isPaused同时控制两者。

四、暂停:状态机里的关键开关

用户点击关闭按钮时,会进入确认退出状态:

.onClick(() => {this.pulseAnim('close_quiz')this.showConfirmExit =truethis.isPaused =true})

暂停后:

  • 总用时不再增加;
  • 单题倒计时不再减少;
  • 用户可以决定继续或退出。

这比简单clearInterval()更灵活,因为继续答题时不用重新创建整套状态,只要恢复isPaused

五、自动提交:时间到就是一次提交

时间归零后调用:

autoSubmit(): void { this.submitCurrentQuestion() }

提交逻辑:

submitCurrentQuestion(): void {if(this.isSubmitting)returnthis.isSubmitting =truethis.showAnalysis =trueif(this.questionTimerHandler >=0) { clearInterval(this.questionTimerHandler)this.questionTimerHandler = -1} }

这里有两个细节:

  1. isSubmitting防止重复提交;
  2. 提交后清掉单题倒计时,避免解析状态还继续倒计时。

六、下一题:重置单题状态

进入下一题时:

nextQuestion(): void {this.isSubmitting =falsethis.showAnalysis =falseif(this.currentIndex <this.questions.length -1) {this.currentIndex++this.questionStartTime = Date.now()this.userAnswers = [...this.userAnswers]if(cfg.timeLimit !== undefined && cfg.timeLimit >0) {this.questionTimer = cfg.timeLimitthis.questionTimerHandler = setInterval(() => {if(!this.isPaused &&this.questionTimer >0) {this.questionTimer--if(this.questionTimer ===0) {this.autoSubmit() } } },1000) } }else{this.finishChallenge() } }

每题都有自己的倒计时周期。进入下一题后:

  • 隐藏解析;
  • 解除提交中状态;
  • 切换题号;
  • 重新设置倒计时。

七、结束挑战:停止计时并生成结果

完成最后一题后:

finishChallenge(): void {this.stopTimer()constcorrectCount =this.calculateCorrect()consttotalTime =this.totalTimeSpentconstscore = Math.round((correctCount /this.questions.length) *100) }

然后构造结果对象:

const resultData:ChallengeResult={id:Date.now().toString(),config:this.config??{gradeId:'',knowledgeIds:[],questionCount:0},startTime:this.questionStartTime-totalTime*1000,endTime:Date.now(),questions:challengeQuestions,correctCount,totalCount:this.questions.length,totalTime,score,gradeId:this.config?.gradeId??'',knowledgeIds:this.config?.knowledgeIds??[],isCompleted:true, }

这就是从“答题中状态”到“结果页状态”的转换。

八、退出页面时清理计时器

生命周期中调用:

aboutToDisappear(){ this.stopTimer() }

清理函数:

stopTimer(): void {if(this.timerHandler >=0) { clearInterval(this.timerHandler)this.timerHandler = -1}if(this.questionTimerHandler >=0) { clearInterval(this.questionTimerHandler)this.questionTimerHandler = -1} }

这能避免页面退出后计时器还在后台运行。

九、实况窗设计:当前项目可扩展的状态模型

如果后续接入实况窗,可以从现有状态直接构造:

interfaceQuizLiveState {currentIndex:numbertotalCount:numberremainingSeconds:numbertotalTimeSpent:numberstatus:'running'|'paused'|'analysis'|'finished'}

映射关系:

  • isPaused = true:暂停;
  • showAnalysis = true:解析中;
  • questionTimer:剩余秒数;
  • currentIndex + 1:当前题号;
  • finishChallenge():结束态。

实况窗可以展示:

数学挑战 3/10 剩余12秒 当前状态:答题中

结束时可以展示:

挑战完成:80 分 用时 3分20秒

注意:当前项目还没有实际接入系统实况窗,这里是基于现有状态机的扩展设计。

十、总结

这篇文章贴合“实况窗、全场景智慧生活”主题,重点不是泛讲实况窗,而是从项目已有倒计时状态出发。

核心实现包括:

  • ⏱ 用totalTimeSpent记录总用时;
  • ⌛ 用questionTimer记录单题倒计时;
  • ⏸ 用isPaused控制暂停;
  • 🔁 用autoSubmit()处理时间到自动提交;
  • 📖 提交后进入解析状态;
  • 🧹 页面退出时清理计时器;
  • 🚀 可扩展为实况窗运行态、暂停态、解析态和结束态。

限时答题的核心不是倒计时本身,而是状态之间的流转。状态清楚,后续接入实况窗就有了基础。✨