news 2026/2/24 21:08:44

小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/2/23 17:34:54

GraphQL Editor性能优化实战:5大策略应对大规模Schema挑战

GraphQL Editor性能优化实战:5大策略应对大规模Schema挑战 【免费下载链接】graphql-editor 📺 Visual Editor & GraphQL IDE. 项目地址: https://gitcode.com/gh_mirrors/gr/graphql-editor 在处理日益复杂的GraphQL项目时,Sche…

作者头像 李华
网站建设 2026/2/21 11:23:13

8B参数如何超越GPT-4o?揭秘MiniCPM-V 4.5的部署实战

8B参数如何超越GPT-4o?揭秘MiniCPM-V 4.5的部署实战 【免费下载链接】OmniLMM 项目地址: https://gitcode.com/gh_mirrors/om/OmniLMM 你是否曾想过,一个仅有8B参数的开源模型竟然能在多项基准测试中超越GPT-4o-latest这样的顶级闭源模型&#x…

作者头像 李华
网站建设 2026/2/18 16:12:18

21、深入解析RAC数据库的跟踪与诊断技术

深入解析RAC数据库的跟踪与诊断技术 1. 引言 在RAC(Real Application Clusters)数据库环境中,获取跟踪和诊断信息对于解决性能问题、排查故障至关重要。本文将详细介绍获取这些信息的方法,包括跟踪文件位置、DBMS_MONITOR包、ORADEBUG工具以及LKDEBUG实用程序等内容。 2…

作者头像 李华