OpenHarmony 英语学习 App 实战:学习成就系统与数据可视化面板设计

发布时间:2026/7/2 1:58:04
OpenHarmony 英语学习 App 实战:学习成就系统与数据可视化面板设计 OpenHarmony 英语学习 App 实战学习成就系统与数据可视化面板设计摘要学习 App 要让用户坚持除了内容本身还需要持续反馈。用户今天学了多少、连续坚持了几天、解锁了哪些成就这些都能形成正向激励。本文以「英语视界 YingYu」项目为例分享如何在 OpenHarmony/HarmonyOS 中设计学习成就系统和数据可视化面板。相关文件包括entry/src/main/ets/utils/UserDataManager.ts entry/src/main/ets/pages/ProfileContent.ets entry/src/main/ets/model/DataModels.ts一、为什么需要成就系统英语学习是长期任务很难每天都有明显进步。成就系统的价值在于给用户阶段性反馈强化连续学习行为让学习数据更可见提升打卡动力鼓励探索不同功能模块。「英语视界」中的成就覆盖多个维度第一个单词10 个、50 个、100 个单词连续学习连续打卡完成每日目标趣味英语探索。二、成就数据模型项目中使用Achievement描述成就exportinterfaceAchievement{ id:stringtitle:stringdescription:stringicon:stringunlocked:boolean unlockedDate?:string}字段含义id唯一标识title成就名称description解锁条件icon展示图标unlocked是否解锁unlockedDate解锁日期。这个模型很轻量但足够支撑列表、徽章墙、个人中心统计等展示。三、默认成就列表项目在UserDataManager.ts中定义默认成就const defaultAchievements: Achievement[] [ {id:first_word, title:初学者, description:学习第一个单词, icon:, unlocked:false}, {id:ten_words, title:小试牛刀, description:学习10个单词, icon:, unlocked:false}, {id:fifty_words, title:词汇达人, description:学习50个单词, icon:, unlocked:false}, {id:hundred_words, title:词汇专家, description:学习100个单词, icon:⭐, unlocked:false}, {id:first_week, title:一周坚持, description:连续学习7天, icon:, unlocked:false}, {id:first_month, title:一月坚持, description:连续学习30天, icon:, unlocked:false}, {id:daily_streak_3, title:学习新星, description:连续3天打卡, icon:✨, unlocked:false}, {id:daily_streak_7, title:学习达人, description:连续7天打卡, icon:, unlocked:false}, {id:daily_streak_30, title:学习传奇, description:连续30天打卡, icon:, unlocked:false}, {id:perfect_day, title:完美一天, description:完成每日学习目标, icon:, unlocked:false} ]成就命名不只是技术问题也影响用户感受。对于学生用户文案要积极、轻松、有鼓励感。四、合并默认成就和本地存储应用升级后可能会新增成就。如果直接读取旧数据新增成就可能丢失。因此项目使用mergeAchievementsFromStorage()合并默认成就和本地成就状态。functionmergeAchievementsFromStorage(): Achievement[]{constbyId newMapstring, Achievement()conststored yingyuPrefGet(STORAGE_KEY_ACHIEVEMENTS)if(stored) {constparsed JSON.parse(stored)asAchievement[]for(leti 0; i parsed.length; i){ byId.set(parsed[i].id, parsed[i]) } }constresult: Achievement[] []for(leti 0; i defaultAchievements.length; i) {constdef defaultAchievements[i]constexisting byId.get(def.id) result.push({ id: def.id, title: def.title, description: def.description, icon: def.icon, unlocked: existing ? existing.unlocked :false, unlockedDate: existing?.unlockedDate }) }returnresult }这个设计非常实用默认配置可以升级用户解锁状态不会丢。五、解锁成就解锁逻辑封装为unlockAchievement()exportfunctionunlockAchievement(achievementId:string):boolean{try{constachievements mergeAchievementsFromStorage()constachievement achievements.find(aa.id achievementId)if(achievement !achievement.unlocked) { achievement.unlockedtrueachievement.unlockedDategetTodayDateKey()persistAchievements(achievements)returntrue} }catch(e) {console.error(Failed to unlock achievement:, e) }returnfalse}函数返回boolean页面可以据此决定是否弹出“新成就解锁”的提示。六、词汇数量成就当用户学习新单词时会检查词汇成就functioncheckWordAchievements(learnedCount:number):void{if(learnedCount1) {unlockAchievement(first_word)}if(learnedCount10) {unlockAchievement(ten_words)}if(learnedCount50) {unlockAchievement(fifty_words)}if(learnedCount100) {unlockAchievement(hundred_words)} }对应调用点exportfunctionaddLearnedWord(wordId:number): boolean { const learned getLearnedWords()if(!learned.includes(wordId)) { learned.push(wordId) yingyuPrefSet(STORAGE_KEY_LEARNED_WORDS, JSON.stringify(learned)) checkWordAchievements(learned.length)returntrue} returnfalse}这样用户每学一个新词系统都会自动判断是否达到里程碑。七、连续学习和打卡成就项目中区分了两种连续性学习连续当天学过单词打卡连续当天完成目标。连续学习天数计算function getLearningStreakDays(): number { const map buildProgressDateMap() let streak 0 constcheck newDate()check.setHours(0, 0, 0, 0)while(true) { constkey ${check.getFullYear()}-${(check.getMonth() 1).toString().padStart(2,0)}-${check.getDate().toString().padStart(2,0)} const rec map.get(key)if(recrec.wordsLearned 0) { streakcheck.setDate(check.getDate() - 1) }else{ break } }returnstreak }成就同步functionsyncStreakAndMiscAchievements():void{constlearnStreakgetLearningStreakDays()if(learnStreak7) {unlockAchievement(first_week)}if(learnStreak30) {unlockAchievement(first_month)}constcheckStreakgetCheckInStreakDays()if(checkStreak3) {unlockAchievement(daily_streak_3)}if(checkStreak7) {unlockAchievement(daily_streak_7)} }这种设计让“学习过”和“完成目标”都有激励。八、记录今日学习当用户完成学习行为后调用recordTodayLearning()exportfunctionrecordTodayLearning(wordsLearned: number):void{constprogress getLearningProgress()consttoday getTodayDateKey()constsettings getUserSettings()constgoal settings.dailyGoalconsttodayRecord progress.find(pp.date today)if(todayRecord) { todayRecord.wordsLearned wordsLearned todayRecord.completed goal 0 todayRecord.wordsLearned goal }else{ progress.push({date: today,wordsLearned: wordsLearned,minutesSpent:0,completed: goal 0 wordsLearned goal }) }saveProgressList(progress)syncStreakAndMiscAchievements() }这个函数同时完成今日学习数累加判断是否完成每日目标保存进度同步连续学习成就。九、个人中心数据面板ProfileContent.ets中展示用户学习数据loadData() {this.achievements getAchievements()this.progress getLearningProgress()conststats getStatistics()this.totalWords stats.totalWordsthis.totalDays stats.totalDaysthis.consecutiveDays stats.consecutiveDaysthis.achievementsCount stats.achievementsCountconstsettings getUserSettings()this.dailyGoal settings.dailyGoalthis.recentProgress getRecentProgressSorted(7) }页面层只获取统计结果不直接计算所有业务逻辑。十、今日进度卡片个人中心展示今日目标进度getProgressPercentage(): number {if(this.dailyGoal 0)return0returnMath.min(100, Math.round((this.todayProgress /this.dailyGoal) *100)) }进度条Row(){Column().width(this.getProgressPercentage().toString()%) .height(this.isTabletDevice ?14:12) .backgroundColor($r(app.color.primary_color)) .borderRadius(6)} .width(100%) .backgroundColor($r(app.color.divider_color)) .borderRadius(6)这类进度条比单纯数字更直观适合个人中心和首页。十一、学习统计卡片统计卡展示四个核心数字this.StatColumn(this.totalWords.toString(),已学单词)this.StatColumn(this.totalDays.toString(),学习天数)this.StatColumn(this.consecutiveDays.toString(),连续打卡)this.StatColumn(this.achievementsCount.toString(),已获成就)对学习 App 来说这四个指标很有代表性总量时间连续性成就。十二、小结本文结合「英语视界 YingYu」项目拆解了学习成就系统和数据面板使用Achievement表示成就默认成就和本地状态合并支持后续升级词汇数量、连续学习、每日目标都能触发成就recordTodayLearning()统一记录学习行为个人中心展示今日进度、学习统计和成就数量通过进度条和卡片提升数据可读性。学习坚持需要反馈反馈需要数据数据需要被设计成用户愿意看的样子。成就系统不是花哨功能而是长期学习产品的激励引擎。