当前位置: 首页 > news >正文

【HarmonyOS 6】个人中心数据可视化实战

一、案例背景

在健康管理类应用中,用户希望在“个人中心”快速查看周期性的健康汇总。相比单一数据,健康报告弹窗能在一个页面中集中展示平均分、每日评分、分项进度与健康建议,阅读效率更高。

本案例面向 HarmonyOS 6 初学者,聚焦一个移动端功能点:个人中心菜单入口 + 健康报告弹窗 + 分项进度展示

你将学到:

  • 如何在“更多功能”中设置报告入口
  • 如何控制弹窗显示/隐藏
  • 如何用环形进度展示综合评分
  • 如何用线性进度展示分项平均分

二、完整代码实现

本功能主要由两部分组成:

  1. 个人中心的“健康报告”入口
  2. 健康报告弹窗的内容结构与进度展示

2.1 菜单入口(点击打开报告弹窗)

Column(){CommonCard({title:'更多功能'}){Column(){this.MenuRow('📊 健康报告',()=>{this.showReportDialog=true;this.loadReportData(this.reportPeriod);})Divider().margin({top:this.getDividerMargin(),bottom:this.getDividerMargin()}asPadding)this.MenuRow('📚 养生知识',()=>{this.selectedArticleIndex=-1;this.showKnowledgeDialog=true;})Divider().margin({top:this.getDividerMargin(),bottom:this.getDividerMargin()}asPadding)this.MenuRow('⚙️ 设置',()=>{this.tempWaterTarget=this.dailyWaterTarget;this.tempExerciseTarget=this.weeklyExerciseTarget;this.tempSleepHours=this.sleepTargetHours;this.loadReminderSettings();this.showSettingsDialog=true;})Divider().margin({top:this.getDividerMargin(),bottom:this.getDividerMargin()}asPadding)this.MenuRow('ℹ️ 关于',()=>{this.showAboutDialog=true;})}}}.margin({left:this.getCardMargin(),right:this.getCardMargin(),bottom:this.getCardMargin()}asPadding)

入口逻辑很清晰:

  • 点击“📊 健康报告”
  • 打开弹窗showReportDialog = true
  • 同步加载本周/本月数据

2.2 健康报告弹窗结构

弹窗包含三个核心区域:综合评分、每日评分、各项平均分。

// 健康报告弹窗if(this.showReportDialog){Column(){Column(){// 头部Row(){Text('健康报告').fontSize(this.getDialogTitleSize()).fontWeight(FontWeight.Bold).fontColor($r('app.color.text_primary'))Blank()// 周/月切换Row(){Text('周').fontSize(this.getSmallTextSize()).fontColor(this.reportPeriod==='week'?Color.White:$r('app.color.text_primary')).padding({left:this.getDividerMargin(),right:this.getDividerMargin(),top:this.getChipPaddingVertical(),bottom:this.getChipPaddingVertical()}asPadding).backgroundColor(this.reportPeriod==='week'?$r('app.color.primary_color'):$r('app.color.input_background')).borderRadius(this.getDividerMargin()).onClick(()=>{if(this.reportPeriod!=='week'){this.reportPeriod='week';this.loadReportData('week');}})Text('月').fontSize(this.getSmallTextSize()).fontColor(this.reportPeriod==='month'?Color.White:$r('app.color.text_primary')).padding({left:this.getDividerMargin(),right:this.getDividerMargin(),top:this.getChipPaddingVertical(),bottom:this.getChipPaddingVertical()}asPadding).backgroundColor(this.reportPeriod==='month'?$r('app.color.primary_color'):$r('app.color.input_background')).borderRadius(this.getDividerMargin()).margin({left:this.getItemGap()}asPadding).onClick(()=>{if(this.reportPeriod!=='month'){this.reportPeriod='month';this.loadReportData('month');}})}Text('×').fontSize(this.getCloseLargeSize()).fontColor($r('app.color.text_secondary')).margin({left:this.getSectionGap()}asPadding).onClick(()=>{this.showReportDialog=false;})}.width('100%')

头部结构包含:

  • 左侧标题
  • 中部周/月切换
  • 右侧关闭按钮

2.3 综合评分(环形进度)

// 综合评分区Column(){Stack(){Progress({value:100,total:100,type:ProgressType.Ring}).width(this.getReportRingSize()).height(this.getReportRingSize()).color($r('app.color.divider_color')).style({strokeWidth:this.getReportRingStrokeWidth()})Progress({value:this.avgTotalScore,total:100,type:ProgressType.Ring}).width(this.getReportRingSize()).height(this.getReportRingSize()).color(this.getScoreColor(this.avgTotalScore)).style({strokeWidth:this.getReportRingStrokeWidth()})Column(){Text(this.avgTotalScore.toString()).fontSize(this.getReportScoreSize()).fontWeight(FontWeight.Bold).fontColor(this.getScoreColor(this.avgTotalScore))Text('平均分').fontSize(this.getReportLabelSize()).fontColor($r('app.color.text_secondary'))}}Text(`${this.getCurrentReportOffset()===0?(this.reportPeriod==='week'?'本周':'本月'):this.getReportRangeLabel()}健康评分`).fontSize(this.getSmallTextSize()).fontColor($r('app.color.text_secondary')).margin({top:this.getItemGap()}asPadding)}.width('100%').padding({top:this.getSectionGap(),bottom:this.getSectionGap()}asPadding)

这里使用两个环形进度叠加:

  • 底层灰色圆环是背景
  • 顶层彩色圆环表示平均分

2.4 各项平均分(线性进度)

// 模块统计Column(){Text('各项平均').fontSize(this.getSectionTitleSize()).fontWeight(FontWeight.Medium).fontColor($r('app.color.text_primary')).width('100%')// 打卡Row(){Text('✅ 打卡').fontSize(this.getBodyTextSize()).fontColor($r('app.color.text_primary'))Blank()Text(`${this.avgCheckInScore}`).fontSize(this.getBodyTextSize()).fontWeight(FontWeight.Medium).fontColor(this.getScoreColor(this.avgCheckInScore))}.width('100%').margin({top:this.getDividerMargin()}asPadding)Progress({value:this.avgCheckInScore,total:100,type:ProgressType.Linear}).height(this.getProgressHeight()).color($r('app.color.primary_color')).backgroundColor($r('app.color.divider_color')).borderRadius(this.getProgressHeight()/2).margin({top:this.getSmallGap()}asPadding)// 饮水Row(){Text('💧 饮水').fontSize(this.getBodyTextSize()).fontColor($r('app.color.text_primary'))Blank()Text(`${this.avgWaterScore}`).fontSize(this.getBodyTextSize()).fontWeight(FontWeight.Medium).fontColor(this.getScoreColor(this.avgWaterScore))}.width('100%').margin({top:this.getDividerMargin()}asPadding)Progress({value:this.avgWaterScore,total:100,type:ProgressType.Linear}).height(this.getProgressHeight()).color($r('app.color.water_blue')).backgroundColor($r('app.color.divider_color')).borderRadius(this.getProgressHeight()/2).margin({top:this.getSmallGap()}asPadding)// 运动Row(){Text('🏃 运动').fontSize(this.getBodyTextSize()).fontColor($r('app.color.text_primary'))Blank()Text(`${this.avgExerciseScore}`).fontSize(this.getBodyTextSize()).fontWeight(FontWeight.Medium).fontColor(this.getScoreColor(this.avgExerciseScore))}.width('100%').margin({top:this.getDividerMargin()}asPadding)Progress({value:this.avgExerciseScore,total:100,type:ProgressType.Linear}).height(this.getProgressHeight()).color($r('app.color.exercise_orange')).backgroundColor($r('app.color.divider_color')).borderRadius(this.getProgressHeight()/2).margin({top:this.getSmallGap()}asPadding)// 睡眠Row(){Text('😴 睡眠').fontSize(this.getBodyTextSize()).fontColor($r('app.color.text_primary'))Blank()Text(`${this.avgSleepScore}`).fontSize(this.getBodyTextSize()).fontWeight(FontWeight.Medium).fontColor(this.getScoreColor(this.avgSleepScore))}.width('100%').margin({top:this.getDividerMargin()}asPadding)Progress({value:this.avgSleepScore,total:100,type:ProgressType.Linear}).height(this.getProgressHeight()).color($r('app.color.sleep_purple')).backgroundColor($r('app.color.divider_color')).borderRadius(this.getProgressHeight()/2).margin({top:this.getSmallGap()}asPadding)}

四条进度条分别代表四个维度的平均分,颜色区分非常直观:

  • 打卡:主色
  • 饮水:蓝色
  • 运动:橙色
  • 睡眠:紫色

三、总结

本案例完整展示了一个移动端健康报告弹窗的实现方式,核心要点包括:

  1. 菜单入口触发弹窗显示
  2. 周/月切换控制数据周期
  3. 环形进度展示综合评分
  4. 线性进度展示分项平均分

掌握这个案例后,你可以继续扩展更多报告内容,例如:

  • 数据趋势折线图
  • 日历高亮
  • 评分等级标签
http://www.jsqmd.com/news/482449/

相关文章:

  • PAT 乙级 1113
  • React15 - 在ruducer中以对象映射替代switch语句
  • 第10章 矩阵分解:拆解复杂矩阵,简化工程应用
  • Hadoop生态中的数据生命周期管理技术深度剖析
  • **图神经网络实战:用PyTorch Geometric构建社交关系预测模型**在当前人工
  • UV 使用指南
  • **发散创新:基于Python的伦理黑客实战演练——从漏洞探测到防御加固全流程解
  • AI 工程化实战:5分钟带你快速掌握 Function Calling!
  • React15 - 在redux应用中数据存储位置探讨
  • 魔术橡皮 3.1.17 | 无限次AI生图,AI橡皮,图片AI编辑修改
  • 《C++实战项目-高并发内存池》7.大块内存的申请与释放
  • 实战|AI应用架构师用GNN构建智能客服的意图识别
  • Qt Creator + MSVC 2022 64bit 配置 Dump 文件生成与分析流程
  • IBM助力实现费曼量子模拟愿景
  • 微软在Windows 11中新增Xbox模式
  • Redux - 在ruducer中以对象映射替代switch语句
  • 洛谷 B4500:[GESP202603 三级] 凯撒密码 ← 字符串
  • 【原】Python+AI学习笔记(01)大模型调用准备工作 与 OpenAI库基础使用
  • Omsk Metro的题解
  • 东华OJ-进阶题-10-分解质因数(C++)
  • 设计模式2-结构性
  • 一行命令搞定驱动安装!MicroPython 开发有了自己的 “PyPI”包管理平台!
  • Problems(2026/02 ~ 2026/03)
  • React15 - redux中combineReducer的作用
  • 图像拼接对齐
  • Problems(2026/01 ~ 2026/03)
  • 音乐会节目单
  • 「NOI2005」聪聪和可可 的 题解
  • 三角函数 - 重制版
  • Problems(2025 年及更早)