饮食记录是运动健康App的能量管理页。用户从首页点击"饮食记录"或从个人中心菜单跳转到这里,查看今天吃了什么、摄入了多少卡路里、还剩多少热量配额。这个页面的任务是:让用户对"吃了什么"有量化的认知——不是"感觉吃多了",而是"今天已经摄入1570千卡,还剩630千卡"。

页面结构分为三部分:顶部导航栏、卡路里摘要卡片、饮食记录列表。从"总览"到"明细",信息从宏观到微观。
完整效果
在这里插入图片描述
在这里插入图片描述

主色的变化

这个页面的主色不是红色(#FF4757),而是橙色(#FF9F43):

const A: string = '#FF9F43'

整个App的主色是红色,但饮食页面切换到了橙色。这不是随意的——红色代表"运动"(步数、训练),橙色代表"食物"(卡路里、饮食)。颜色和语义绑定:用户看到红色联想到"动",看到橙色联想到"吃"。这种颜色分工在首页就有铺垫:步数环是红色,卡路里环是橙色。

导航栏的"+"按钮

Row() {
  Row() {
    SymbolGlyph($r('sys.symbol.chevron_left')).fontSize(20).fontColor([T1])
  }.width(34).height(34).borderRadius(17).backgroundColor('rgba(0,0,0,0.03)')
  .justifyContent(FlexAlign.Center).onClick(() => { router.back() })
  Text('饮食记录').fontSize(20).fontWeight(FontWeight.Bold).fontColor(T1)
    .margin({ left: 10 }).layoutWeight(1)
  Text('+').fontSize(24).fontColor(A).fontWeight(FontWeight.Bold)
}.width('100%').padding({ left: 16, right: 16, top: 12, bottom: 8 })

在这里插入图片描述

导航栏右侧是一个橙色的"+“按钮——暗示"添加新记录”。但没有绑定onClick,点击不会弹出添加表单。和社区页面的"发布"按钮、个人中心的"编辑"按钮一样,UI元素存在但交互未实现。

卡路里摘要卡片

Row() {
  Column() {
    Text(this.total.toString()).fontSize(36).fontWeight(FontWeight.Bold).fontColor(A)
    Text('已摄入/千卡').fontSize(11).fontColor(T3)
  }.alignItems(HorizontalAlign.Center).layoutWeight(1)
  Row() {}.width(1).height(40).backgroundColor('#F0EEF4')
  Column() {
    Text('630').fontSize(36).fontWeight(FontWeight.Bold).fontColor('#00B894')
    Text('剩余/千卡').fontSize(11).fontColor(T3)
  }.alignItems(HorizontalAlign.Center).layoutWeight(1)
}.width('100%').padding(20).backgroundColor('#FFFFFF').borderRadius(16)
.margin({ left: 16, right: 16, bottom: 16 })

在这里插入图片描述

摘要卡片是一个白色圆角Row,分成左右两部分:左边橙色显示已摄入热量,右边绿色显示剩余热量。中间用1px宽的灰色竖线分隔。

这种"已用/剩余"的双列设计和首页的数据环异曲同工——首页用三个环展示步数/卡路里/运动时间,饮食页用两列展示已摄入/剩余热量。都是让用户一眼看到"我做了多少"和"还差多少"。

数字的硬编码

@State total: number = 1570

已摄入热量1570千卡是@State变量,但没有动态计算——不是把四条餐饮记录的cal加起来。手动算一下:380+450+220+520=1570,数字刚好对得上。但这是因为手动凑的,不是公式算出来的。如果用户新增一条记录,total不会自动更新。

剩余630千卡是直接写死的字符串,不是变量。1570+630=2200——这暗示每日热量目标是2200千卡。但2200这个数字没有在代码中定义,藏在硬编码的减法里。实际项目应该定义一个DAILY_TARGET = 2200常量,然后remaining = DAILY_TARGET - total

饮食记录列表

数据模型

interface Meal { icon: string; name: string; cal: number; time: string }

四个字段:图标、名称、卡路里、时间。和社区页面的Post接口一样精简——没有餐次分类(早/午/晚)、没有营养成分明细(蛋白质/碳水/脂肪)、没有食物图片。原型阶段够用。

@State meals: Meal[] = [
  { icon: '🥣', name: '燕麦牛奶 + 鸡蛋', cal: 380, time: '早餐 8:00' },
  { icon: '🍗', name: '鸡胸肉沙拉', cal: 450, time: '午餐 12:30' },
  { icon: '🍌', name: '香蕉 + 蛋白粉', cal: 220, time: '加餐 16:00' },
  { icon: '🥩', name: '牛排 + 西兰花', cal: 520, time: '晚餐 19:00' },
]

在这里插入图片描述

四条记录,覆盖早餐、午餐、加餐、晚餐。时间从8:00到19:00,符合正常的饮食节奏。emoji图标和食物类型对应——🥣燕麦、🍗鸡肉、🍌香蕉、🥩牛排。

单条记录的结构

Row() {
  Text(m.icon).fontSize(28).width(48).height(48).borderRadius(12)
    .backgroundColor('#F5F5F7').textAlign(TextAlign.Center)
  Column() {
    Text(m.name).fontSize(14).fontWeight(FontWeight.Bold).fontColor(T1)
    Text(m.time).fontSize(11).fontColor(T3).margin({ top: 2 })
  }.alignItems(HorizontalAlign.Start).margin({ left: 12 }).layoutWeight(1)
  Text(m.cal + '千卡').fontSize(14).fontWeight(FontWeight.Bold).fontColor(A)
}.width('100%').padding(12).backgroundColor('#FFFFFF').borderRadius(14)

每条记录是一个白色圆角Row:左边emoji图标(48×48浅灰圆角方块)、中间食物名称和时间、右边卡路里数值。

图标容器用borderRadius(12)——不是圆形(24),而是圆角方块(12)。和社区页面的44×44圆形头像不同,饮食记录的图标是"方形带圆角"。这种差异是有意的:头像用圆形(亲和力),食物图标用圆角方块(规整感)。

卡路里数值用橙色加粗——和摘要卡片的已摄入颜色一致。用户看到橙色就联想到"热量",不管在摘要区还是列表区。

Scroll+Column+ForEach

Scroll() {
  Column({ space: 10 }) {
    ForEach(this.meals, (m: Meal) => {
      // 餐食卡片
    })
    Blank().height(20)
  }.width('100%').padding({ left: 16, right: 16 })
}.width('100%').layoutWeight(1)

和社区页面、个人中心一样的模式:Scroll包裹Column,Column用space控制间距,ForEach渲染列表,最后加Blank底部留白。这个模式在整个App中反复出现——它已经成为ArkUI列表渲染的"标准写法"。

页面间的数据关系

饮食记录和首页的卡路里环有数据关联——首页显示今天的卡路里消耗,饮食页显示今天的卡路里摄入。消耗-摄入=净热量差,这是减肥的核心公式。但当前两个页面的数据是独立的:首页的卡路里来自WEEKLY_DATA[3].calories(运动消耗),饮食页的total是硬编码的1570(食物摄入)。两者没有计算差值。

实际项目中应该有一个统一的能量管理服务:运动消耗从运动记录获取,食物摄入从饮食记录获取,净热量差=消耗-摄入。首页可以展示这个差值,让用户知道"今天运动消耗了800千卡,吃了1570千卡,净摄入770千卡"。

踩坑记录

主色的语义化选择。 饮食页用橙色(#FF9F43)作为主色A,而不是App默认的红色(#FF4757)。这导致A的含义在不同页面不同——在首页A是红色(运动),在饮食页A是橙色(食物)。如果以后要抽公共组件,不能假设A一定是红色。更好的做法是定义语义化常量:COLOR_EXERCISECOLOR_FOOD,而不是用A统一。

total和meals的数据不同步。 @State total = 1570是独立变量,不从meals数组计算。如果以后加了添加/删除餐食的功能,total需要手动更新。应该用getter动态计算:get totalCalories(): number { return this.meals.reduce((sum, m) => sum + m.cal, 0) }。但ArkUI的@Component不支持getter语法,需要在aboutToAppear中计算或用@State配合手动更新。

剩余热量的硬编码。 630千卡是直接写在build方法里的字符串,不是变量。如果每日目标变化(比如从2200改成2000),需要同时改total的初始值和这里的630。两处修改容易遗漏。应该定义常量const DAILY_TARGET = 2200,然后remaining = DAILY_TARGET - this.total

“+”"按钮的交互缺失。 导航栏右边的"+“暗示"添加”,但没有onClick。用户点击后没有反馈。真实项目应该弹出一个添加表单——选择食物、输入卡路里、选择时间。但表单组件的实现复杂度远超当前原型。

餐食卡片没有删除/编辑功能。 每条记录只能看不能改。如果用户录错了(比如把380千卡写成3800),没有修正途径。原型阶段够用,但真实产品必须有滑动删除或长按编辑。


饮食记录是运动健康App中"能量管理"的另一半——首页展示消耗,饮食页展示摄入。两者结合才能回答用户最关心的问题:"我今天吃多了还是吃少了?"从技术角度看,这个页面的亮点在于颜色语义化——橙色代表食物,和红色代表运动形成对比。这种颜色分工让页面在视觉上就有了"运动vs饮食"的区分,用户不需要读文字就能理解页面主题。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐