news 2026/9/2 6:37:07

小V健身助手开发手记(四):打造专属健康空间——以 PersonContent构建统一风格的个人中心

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
小V健身助手开发手记(四):打造专属健康空间——以 PersonContent构建统一风格的个人中心

  • 个人首页: VON

  • 鸿蒙系列专栏: 鸿蒙开发小型案例总结

  • 综合案例 :鸿蒙综合案例开发

  • 鸿蒙6.0:从0开始的开源鸿蒙6.0.0

  • 鸿蒙5.0:鸿蒙5.0零基础入门到项目实战

  • Electron适配开源鸿蒙专栏:Electron for OpenHarmony

  • Flutter 适配开源鸿蒙专栏:Flutter for OpenHarmony

  • 本文所属专栏:鸿蒙综合案例开发

  • 本文atomgit地址:小V健身

小V健身助手开发手记(四)

  • 打造专属健康空间——以 `PersonContent` 构建统一风格的个人中心
    • 🧩 一、为什么是 `PersonContent` 而非 `PersonalPage`?
    • 🖼️ 二、UI 结构与视觉语言
      • 设计细节:
    • 💻 三、核心代码解析
      • 1. 组件定义与状态管理
      • 2. 用户信息区(`buildUserInfo`)
      • 3. 功能列表(基于 `List` + `PersonalItem`)
    • 🔗 四、路由与集成
    • 🛠️ 五、扩展性与未来演进
    • ✅ 六、结语
    • 代码总结
      • PersonalItem
      • PersonContent

打造专属健康空间——以PersonContent构建统一风格的个人中心

在移动应用的用户体验地图中,个人中心往往被低估。它不像首页那样高频曝光,也不如成就页那样充满激励感,但它却是用户建立“归属感”与“掌控感”的关键节点。一个设计良好的个人中心,能让用户感受到:“这是我的空间,一切尽在掌握。”

在「小V健身助手」的开发进程中,我们始终坚持组件化、一致性与轻量化三大原则。继首页、成就页之后,我们以同样的架构思想,构建了名为PersonContent的个人中心视图组件——它不是独立页面,而是可嵌入、可复用、风格统一的 UI 模块。

本文将详解PersonContent的设计思路、代码实现与工程价值。


🧩 一、为什么是PersonContent而非PersonalPage

在 HarmonyOS 的 Stage 模型中,页面通常由@Entry装饰的组件作为入口。但在 Tab 导航或多场景复用的场景下,将内容逻辑与路由入口解耦是更优的选择。

参考我们已有的AchievementContent

@Componentexportdefaultstruct AchievementContent{...}

它不带@Entry,仅负责渲染成就区域的内容,可被主页面按需嵌入。

同理,我们将个人中心也抽象为PersonContent

  • 职责单一:只管 UI 渲染,不处理路由跳转逻辑(除内部子跳转);
  • 高度复用:未来可用于侧边栏、设置弹窗等场景;
  • 风格统一:与AchievementContent共享布局规范、间距系统与交互反馈。

这是一种“页面即组件”的现代前端思维。


🖼️ 二、UI 结构与视觉语言

PersonContent采用经典的“头像区 + 功能列表”布局:

[ 头像 + 昵称 + 日期 ] ────────────────────── [ 目标设置 → ] [ 历史记录 → ] [ 我的成就 → ] [ 隐私与数据 → ] [ 关于小V v1.0.0 ] [ 退出应用(红色)]

设计细节:

  • 背景色:使用浅灰#f5f5f5,区别于首页的深色运动氛围,营造“后台管理”的冷静感;
  • 列表项:白底圆角卡片,内含图标、标题、描述与箭头,信息层级清晰;
  • 退出按钮:单独高亮为红色文字,避免误触,同时传递“重要操作”信号;
  • 日期同步:顶部展示当前选中日期(来自全局AppStorage),与首页、成就页保持上下文一致。

💻 三、核心代码解析

1. 组件定义与状态管理

@Componentexportstruct PersonContent{// 双向绑定全局日期,确保与其他页面同步@StorageLink('date')date:number=DateUtil.beginTimeOfDay(newDate())// 本地状态:今日目标(后续可接入持久化)@State dailyTarget:number=2000build(){/* ... */}}

使用@StorageLink而非@StorageProp,是因为我们希望个人中心也能响应日期变更(例如从日历选择新日期后,顶部自动刷新)。

2. 用户信息区(buildUserInfo

@BuilderbuildUserInfo(){Row(){Image($r('app.media.ic_default_avatar')).width(60).height(60).borderRadius(30)Column(){Text('小V用户').fontSize(18).fontWeight(600)Text(DateUtil.formatDate(this.date)).fontSize(12).fontColor('#888')}.margin({left:15})Blank()// 推动右侧对齐}.padding({bottom:25})}

简洁、克制,突出身份认同而非社交属性(当前无账号体系)。

3. 功能列表(基于List+PersonalItem

我们封装了可复用的PersonalItem组件:

// component/PersonalItem.ets@Componentexportstruct PersonalItem{icon:ResourceStr title:stringdesc:stringshowArrow:booleanonClick:()=>voidbuild(){Row(){Image(this.icon).width(24).height(24).margin({right:15})Column(){Text(this.title).fontSize(16).fontWeight(500)if(this.desc)Text(this.desc).fontSize(12).opacity(0.6)}Blank()if(this.showArrow)Image($r('app.media.arrow_right')).width(16)}.backgroundColor(Color.White).borderRadius(12).padding({horizontal:16,vertical:12}).onClick(this.onClick)}}

通过组合PersonalItemPersonContent的列表代码变得极其简洁且语义清晰。


🔗 四、路由与集成

在主页面中,只需一行即可嵌入:

// MainIndexPage.etsif(tabIndex===2){PersonContent()}

内部跳转使用标准 ArkTS 路由:

router.pushUrl({url:'pages/AchievementPage'})

注意:AchievementPage应是一个带@Entry的完整页面,而AchievementContent是其内部的内容组件——这种“页面壳 + 内容体”模式,是我们推荐的分层方式。


🛠️ 五、扩展性与未来演进

当前PersonContent是 MVP 版本,但已预留扩展路径:

功能实现方式
动态昵称/头像preferences读取,支持编辑弹窗
目标持久化dailyTarget存入AppStoragedata_preferences
缓存清理在“隐私与数据”页调用preferences.clear()
深色模式适配使用@Watch监听系统主题,动态切换颜色

所有这些,都不会破坏现有组件结构。


✅ 六、结语

PersonContent的诞生,标志着「小V健身助手」完成了从“功能驱动”到“体验驱动”的一次跃迁。它不再只是一个功能列表,而是一个有温度、有秩序、有控制感的个人健康空间

更重要的是,它延续了我们对代码可维护性的坚持:

好的 UI,不仅是看起来舒服,更是写起来清晰、改起来安全。

代码总结

此次更新只有这两部分代码,这里路由部分没有实现

PersonalItem

// component/PersonalItem.etsinterfacePersonalItemFace{icon:ResourceStr title:stringdesc?:stringshowArrow?:boolean}@Componentexportdefaultstruct PersonalItem{@Prop icon:ResourceStr;@Prop title:string;@Prop desc:string='';@Prop showArrow:boolean=true;// @Prop onClick: (event?: ClickEvent) => void;build(){Row(){Image(this.icon).width(24).height(24).margin({right:15})Column(){Text(this.title).fontSize(16).fontWeight(500).alignSelf(ItemAlign.Start)if(this.desc){Text(this.desc).fontSize(12).opacity(0.6).alignSelf(ItemAlign.Start).margin({top:3})}}Blank()if(this.showArrow){Image($r('app.media.arrow_right')).width(16).height(16).opacity(0.5)}}.width('100%').height(60).backgroundColor(Color.White).borderRadius(12).padding({left:16,right:16})// .onClick(this.onClick)}}

PersonContent

// view/PersonContent.etsimport{router}from'@kit.ArkUI'importDateUtilfrom'../../util/DateUtil'importPersonalItemfrom'../../component/PersonalItem'@Componentexportstruct PersonContent{// 从全局 Storage 获取当前日期(用于展示)@StorageLink('date')date:number=DateUtil.beginTimeOfDay(newDate())// 当前用户的每日目标(实际项目中应从持久化存储读取)@State dailyTarget:number=2000build(){Column(){// 用户信息区域this.buildUserInfo()// 功能列表List({space:12}){// 目标设置ListItem(){PersonalItem({icon:$r('app.media.ic_target'),title:'今日目标',desc:`${this.dailyTarget}千卡`,showArrow:true,// onClick: () => {// // TODO: 弹出目标设置弹窗(后续可扩展)// console.log('打开目标设置')// }})}// 历史记录ListItem(){PersonalItem({icon:$r('app.media.ic_history'),title:'历史记录',desc:'',showArrow:true,// onClick: () => {// router.pushUrl({ url: 'pages/HistoryPage' })// }})}// 成就系统ListItem(){PersonalItem({icon:$r('app.media.ic_achieve'),title:'我的成就',desc:'',showArrow:true,// onClick: () => {// router.pushUrl({ url: 'pages/AchievementPage' })// }})}// 隐私与数据ListItem(){PersonalItem({icon:$r('app.media.ic_privacy'),title:'隐私与数据',desc:'',showArrow:true,// onClick: () => {// router.pushUrl({ url: 'pages/PrivacyDataPage' })// }})}// 关于小VListItem(){PersonalItem({icon:$r('app.media.ic_about'),title:'关于小V',desc:'v1.0.0',showArrow:false,// onClick: () => {}})}// 退出应用ListItem(){Button('退出应用').width('100%').height(50).fontSize(16).fontColor('#ff3b30').backgroundColor(Color.Transparent).onClick(()=>{router.clear()})}}.width('100%').alignListItem(ListItemAlign.Start)}.width('100%').height('100%').padding({top:20,left:20,right:20}).backgroundColor('#f5f5f5')}@BuilderbuildUserInfo(){Row(){Image($r('app.media.ic_default_avatar')).width(60).height(60).borderRadius(30)Column(){Text('小V用户').fontSize(18).fontWeight(600).fontColor(Color.Black)Text(DateUtil.formatDate(this.date)).fontSize(12).fontColor('#888888')}.alignItems(HorizontalAlign.Start).margin({left:15})Blank()}.width('100%').padding({bottom:25})}}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/2 22:17:55

【Linux】进程等待与资源回收:父进程的责任

文章目录进程等待与资源回收:父进程的责任一、进程终止方式回顾与深化1.1 回顾:为什么需要进程等待1.2 进程退出的三种方式1.2.1 return退出1.2.2 exit()函数1.2.3 _exit()函数1.3 三种方式的关键区别:缓冲区刷新1.4 退出码的含义二、进程等待…

作者头像 李华
网站建设 2026/9/3 2:15:10

Android16 EDLA 认证测试CTS过程介绍

Android EDLA 认证测试CTS过程介绍 文章目录 Android EDLA 认证测试CTS过程介绍一、前言二、基本环境搭建1、环境说明核心选择原则(优先级从高到低)2、CTS常用测试命令3、测试结果(1)CTS完整报告示例(2)单项…

作者头像 李华
网站建设 2026/9/2 11:46:57

重新定义音乐体验:HyPlayer如何成为网易云音乐的完美替代方案

重新定义音乐体验:HyPlayer如何成为网易云音乐的完美替代方案 【免费下载链接】HyPlayer 仅供学习交流使用 | 第三方网易云音乐播放器 | A Netease Cloud Music Player 项目地址: https://gitcode.com/gh_mirrors/hy/HyPlayer 在数字音乐时代,我们…

作者头像 李华
网站建设 2026/9/2 18:43:48

5步实现基于openpi框架的UR5机械臂AI控制完整指南

5步实现基于openpi框架的UR5机械臂AI控制完整指南 【免费下载链接】openpi 项目地址: https://gitcode.com/GitHub_Trending/op/openpi 工业机器人控制正迎来AI技术革命,传统示教编程难以应对复杂任务变化,而专用AI方案开发周期长、兼容性差。op…

作者头像 李华
网站建设 2026/9/2 20:10:13

终极指南:高效掌握二进制解析的核心技巧

终极指南:高效掌握二进制解析的核心技巧 【免费下载链接】binary-parser A blazing-fast declarative parser builder for binary data 项目地址: https://gitcode.com/gh_mirrors/bi/binary-parser 在现代数据处理领域,二进制解析已经成为处理复…

作者头像 李华
网站建设 2026/9/2 19:10:22

竖屏视频变横屏不损失画质的方法,新手1分钟改变画框

刷到好看的竖屏视频想转横屏视频?用传统工具折腾半天,要么画质糊成马赛克,要么画面比例畸形,甚至裁剪半天还留黑边。光是调整参数、修复画质就要半小时,新手直接被劝退!但用影忆,竖屏转横屏全程…

作者头像 李华