黑马健康6(饮食页面的业务记录以及数据持久化和页面交互)

饮食页面的业务记录:

作用

实现数据的增删改查,把在数据库中查到的数据变成页面上的数据,实现新增饮食记录,根据Id删除饮食记录,根据id查询饮食记录列表的功能。将查到的饮食录列表转换成统计信息和分组记录信息。

代码

RecordService.ets

import RecordPO from '../common/bean/RecordPO'
import DateUtil from '../common/utils/DateUtil'
import ItemModel from '../model/ItemModel'
import RecordModel from '../model/RecordModel'
import { RecordTypeEnum, RecordTypes } from '../model/RecordTypeModel'
import GroupInfo from '../viewmodel/GroupInfo'
import RecordType from '../viewmodel/RecordType'
import RecordVO from '../viewmodel/RecordVO'
import StatsInfo from '../viewmodel/StatsInfo'
class RecordService
{
  //1.新增
  insert(typeId:number,itemId:number,amount:number){
    //1.1获取时间
    let createTime = (AppStorage.Get('selectedDate')  || DateUtil.beginTimeOfDay(new Date())) as number
    //1.2
         return  RecordModel.insert({typeId,itemId,amount,createTime})
  }
  deleteById(id:number)
  {
    return RecordModel.deleteById(id)
  }
  //查询 先查询出 然后交给两个不同的部分去封装成他们想要的格式
async queryRecordByDate(date:number):Promise<RecordVO[]>
{
  //1.查询数据库的RecordPo
  let rps = await RecordModel.listByDate(date)
  //2.将RecordPo 转为 RecordVo
  return rps.map(rp=>{
    //2.1获取po中的基本属性
    let rv = {id:rp.id,typeId:rp.typeId,amount:rp.amount} as RecordVO
    //2.2查询记录项目
    rv.recordItem = ItemModel.getById(rp.itemId,rp.typeId!==RecordTypeEnum.WORKOUT) //PO里面的item查到 就能查到recordItem  赋值vo
    //2.3计算热量
    rv.calorie = rp.amount * rv.recordItem.calorie
    return rv
  })
}
  calculateStatsInfo(records:RecordVO[]):StatsInfo
  {
    //1.准备结果
  let info = new StatsInfo
  if (!records || records.length<=0)
  {
  return info
}
//2.计算统计数据
    records.forEach(r=>
    {
      if (r.typeId === RecordTypeEnum.WORKOUT)
      {//运动累加消耗热量
        info.expend += r.calorie
      }else
      {//食物,累加摄入热量,蛋白质,碳水,脂肪
        info.protein+=r.calorie
        info.carbon+=r.recordItem.carbon
        info.protein+=r.recordItem.protein
        info.fat+=r.recordItem.fat
      }
    })
//3.返回
return info
  }
  calculateGroupInfo(records:RecordVO[]):GroupInfo<RecordType,RecordVO>[]{
//1.创建空的记录类型分组
    let groups = RecordTypes.map(RecordType=>new GroupInfo(RecordType,[]))
    if (!records || records.length<=0)
    {
      return groups//空数组
    }
    //2.遍历所有的饮食记录,
    records.forEach(record=>
    {
      //2.1把每个记录存入其相应类型的分组中
      groups[record.typeId]/*找到组*/.items/*组织里面的数组*/.push(record)
      //2.2计算该组的总热量 累加
      groups[record.typeId].calorie+=record.calorie
    })
    return groups
  }

}
let recordService = new RecordService()
export default recordService as RecordService

RecordPO.ets:

//记录数据持久化的一个数据库字段
export default class RecordPO{
  /**
   * 记录id
   */
  id?: number
  /**
   * 饮食记录类型
   */
  typeId: number
  /**
   * 记录中的食物或运动信息
   */
  itemId: number

  /**
   * 食物数量或运动时长,如果是运动信息则无
   */
  amount: number
  /**
   * 记录的日期
   */
  createTime: number//页面变量要用基本类型不能用对象类型
}

GroupInfo.ets

export default class GroupInfo<TYPE, ELEMENT> {
  //《》泛型  变成通用的
  //分组类型 主食。。。
  type: TYPE
  //组内数据集合

  items: ELEMENT[]

  //组内记录的总热量

  calorie: number = 0

  constructor(type: TYPE, items: ELEMENT[]) {
    this.type = type
    this.items = items
  }
}

StatsInfo.ets

export default class StatsInfo{
  /**
   * 当日摄入卡路里总量
   */
  intake: number = 0
  /**
   * 当日运动消耗能量
   */
  expend: number = 0
  /**
   * 当日摄入碳水总量
   */
  carbon: number = 0
  /**
   * 当日摄入蛋白总量
   */
  protein: number = 0
  /**
   * 当日摄入脂肪总量
   */
  fat: number = 0

  constructor(intake: number = 0, expend: number = 0, carbon: number = 0, protein: number = 0, fat: number = 0) {
    this.intake = intake
    this.expend = expend
    this.carbon = carbon
    this.protein = protein
    this.fat = fat
  }
}

运行截图:

总结

引入了groupInfo来实现数据的分组功能。通过分组,用户能够更直观地查看和管理自己的饮食记录,引用StatsInfo模块来统计和展示用户当日的卡路里摄入、运动消耗。实现 了饮食列表中业务的记录。

数据持久化和页面交互

作用

实现数据的可持久化,实现页面交互

代码

RecordIndex.ets

import DateUtil from '../../common/utils/DateUtil'
import RecordService from '../../RecordService/RecordService'
import RecordVO from '../../viewmodel/RecordVO'
import RecordList from './RecordList'
import SearchHeader from './SearchHeader'
import StatsCard from './StatsCard'
@Component
export default struct RecordIndex {
  @StorageProp('selectedDate')
  @Watch('aboutToAppear')//监控日期变更
  selectedDate:number = DateUtil.beginTimeOfDay(new Date())
  @Provide records:RecordVO[] = []
  @Prop @Watch('handlePageShow') isPageShow:boolean
  handlePageShow(){
    if (this.isPageShow) {
      this.aboutToAppear()
    }
  }
  async aboutToAppear()
  {
    this.records = await RecordService.queryRecordByDate(this.selectedDate)
  }
  build() {
    Column(){
      //1.头部搜索栏
     SearchHeader()
      //2.统计卡片
      StatsCard()
      //3.记录列表
      RecordList()
  .layoutWeight(1)//占据剩下的所有的高度
    }
    .width('100%')
    .height('100%')
    .backgroundColor($r('app.color.index_page_background'))
  }
}

 StatsCard.ets


import BreakpointType from '../../common/bean/BreanpointType'
import BreakpointConstants from '../../common/constants/BreakpointConstants'
import { CommonConstants } from '../../common/constants/CommonConstants'
import DateUtil from '../../common/utils/DateUtil'
import RecordService from '../../RecordService/RecordService'
import RecordVO from '../../viewmodel/RecordVO'
import StatsInfo from '../../viewmodel/StatsInfo'
import CalorieStats from './CalorieStats'
import DatePickDialog from './DatePickDialog'
import NutrientStats from './NutrientStats'

@Component
export default struct StatsCard {
  //读取日期
@StorageProp('selectedDate')selectedDate:number = DateUtil.beginTimeOfDay(new Date())
@StorageProp('currentBreakpoint')currentBreakpoint:string = BreakpointConstants.BREAKPOINT_SM
  @Consume @Watch('handleRecordsChange') records:RecordVO[]
  @State info:StatsInfo = new StatsInfo()
  handleRecordsChange(){
    this.info = RecordService.calculateStatsInfo(this.records)
  }
  controller:CustomDialogController = new CustomDialogController(
    {builder:DatePickDialog({selectedDate:new Date(this.selectedDate)})
    }
  )
  build() {
    Column()
    {
      // 1.日期信息
      Row()
      {
        Text(DateUtil.formatDate(this.selectedDate))
          .fontColor($r('app.color.secondary_color'))
        Image($r('app.media.ic_public_spinner'))
          .fillColor($r('app.color.secondary_color'))
          .width(20)//不添加的话就会铺满整个屏幕
          .padding(CommonConstants.SPACE_12)//内边距
          .onClick(()=>this.controller.open())
      }
      // 2.统计信息
      Swiper()
      {
        //2.1热量统计
        CalorieStats({intake:this.info.intake,expend:this.info.expend})
        //2.2营养素统计
 NutrientStats({carbon:this.info.carbon,protein:this.info.protein,fat:this.info.fat})
      }
      .width('100%')
      .backgroundColor(Color.White)
      .borderRadius(CommonConstants.DEFAULT_18)
      .indicatorStyle({selectedColor:$r('app.color.primary_color')})
      .displayCount(new BreakpointType({
        sm:1,
        md:1,
        lg:2
      }).getValue(this.currentBreakpoint))
      }
    .width(CommonConstants.THOUSANDTH_940)
    .backgroundColor($r('app.color.stats_title_bgc'))
    .borderRadius(CommonConstants.DEFAULT_18)

  }
}

 RecordList.ets

import router from '@ohos.router'
import { CommonConstants } from '../../common/constants/CommonConstants'
import RecordService from '../../RecordService/RecordService'
import GroupInfo from '../../viewmodel/GroupInfo'
import RecordType from '../../viewmodel/RecordType'
import RecordVO from '../../viewmodel/RecordVO'
@Extend(Text) function grayText(){
  .fontSize(14)
  .fontColor($r('app.color.light_gray'))
}
@Component
//列表LIST
export default struct RecordList {
  @Consume @Watch('handleRecordsChange') records:RecordVO[]
  @State groups:GroupInfo<RecordType,RecordVO>[] = []
  handleRecordsChange(){
    this.groups = RecordService.calculateGroupInfo(this.records)
  }
  build() {
    List({space:CommonConstants.SPACE_10})
    {
      ForEach(this.groups,(group:GroupInfo<RecordType,RecordVO>)=>{
        ListItem()
        {
          Column()
          {
            //1.分组的标题
            Row({space:CommonConstants.SPACE_4})
            {
              Image(group.type.icon).width(24)
              Text(group.type.name).fontSize(16).fontWeight(CommonConstants.FONT_WEIGHT_700)
              Text(`建议${group.type.min}-${group.type.max}千卡`).grayText()
              Blank()
              Text(group.calorie.toFixed(0)).fontSize(12).fontColor($r('app.color.primary_color'))
              Text('千卡').grayText()
              Image($r('app.media.ic_public_add_norm_filled'))
                .width(20)
                .fillColor($r('app.color.primary_color'))
            }
            .width('100%')
            .onClick(()=>
            {
              router.pushUrl({
                url:'pages/ItemIndex',
                params:{type:group.type}//跳转信息
              })
            })
            //2.组内记录列表
            List()
            {
              ForEach(group.items/*组内数据*/,(item:RecordVO)=>{
                ListItem(){
                  Row({space:CommonConstants.SPACE_4}){
                    Image(item.recordItem.image).width(50)
                    Column()
                    {/*记录项的名字*/
                      Text(item.recordItem.name).fontWeight(CommonConstants.FONT_WEIGHT_500)
                      Text(`${item.amount}${item.recordItem.unit}`).grayText()
                    }
                    Blank()
                    Text(`${item.calorie.toFixed(0)}千卡`).grayText()
                  }
                  .width('100%')
                  .padding(CommonConstants.SPACE_6)
                }.swipeAction({end:this.deleteButton.bind(this)})
              })
            }
            .width('100%')
          }.width('100%')
          .backgroundColor(Color.White)
          .borderRadius(CommonConstants.DEFAULT_18)
          .padding(CommonConstants.SPACE_12)
        }
      })
    }
    .height('100%')
    .width(CommonConstants.THOUSANDTH_940)//宽度 加一个百分之94
    .margin({top:10})
  }
  @Builder deleteButton(){//侧滑产生的图标是啥样式
    Image($r('app.media.ic_public_delete_filled'))
      .width(20)
      .fillColor(Color.Red)
      .margin(5)
  }
}

Index.ets

import BreakpointType from '../common/bean/BreanpointType'
import BreakpointConstants from '../common/constants/BreakpointConstants'
import { CommonConstants } from '../common/constants/CommonConstants'
import BreakpointSystem from '../common/utils/BreakpointSystem'
import RecordIndex from '../view/record/RecordIndex'
@Entry
@Component
struct Index {
  @State currentIndex:number = 0//状态变量 记录切换到谁
  private  breakpointSystem:BreakpointSystem = new BreakpointSystem()
  //因为@所以必须初始化
  @StorageProp('currentBreakpoint')currentBreakpoint:string = BreakpointConstants.BREAKPOINT_SM

  @State isPageShow:boolean = false
  onPageShow(){this.isPageShow = true}
  onPageHide(){this.isPageShow = false}
@Builder TabBarBuilder(title:ResourceStr,image:ResourceStr,index:number){//自定义文本样式 builder
  Column({space:CommonConstants.SPACE_8}){
    Image(image)
      .width(22)
      .fillColor(this.selectColor(index))//图片必须是svg格式
    Text(title)
      .fontSize(14)
      .fontColor(this.selectColor(index))
  }
}
  aboutToAppear(){
    this.breakpointSystem.register()//完成回调函数的注册
  }
  aboutToDisappear(){
    this.breakpointSystem.unregister()//取消注册
  }
  selectColor(index:number)
  {
     return this.currentIndex===index?$r('app.color.primary_color') :$r('app.color.light_gray' )
  }
  chooseBarPosition(){//为了适应手机 平板
   return new BreakpointType(
     {
       sm:BarPosition.End,
       md:BarPosition.Start,
       lg:BarPosition.Start,
     }
   ).getValue(this.currentBreakpoint)
  }
  build() {
    Tabs({barPosition:BreakpointConstants.BAR_POSITION.getValue(this.currentBreakpoint)})
    {
TabContent(){
    RecordIndex({isPageShow:this.isPageShow})
    }
      .tabBar(this.TabBarBuilder($r('app.string.tab_record'),$r('app.media.ic_calendar'),0))
      TabContent(){
        Text('发现页面')
      }
      .tabBar(this.TabBarBuilder($r('app.string.tab_discover'),$r('app.media.discover'),1))

      TabContent(){
        Text('我的主页')
      }

      .tabBar(this.TabBarBuilder($r('app.string.tab_user'),$r('app.media.ic_user_portrait'),2))

    }


      .width('100%')

    .height('100%')
    .onChange(index=>this.currentIndex = index)
    .vertical(new BreakpointType(
      {
        sm:false,
        md:true,
        lg:true
      }
    ).getValue(this.currentBreakpoint)

   )
  }
}

总结 

实现了一个具有良好结构和功能的应用程序界面,包括数据展示、用户交互和适配不同设备的布局。并且遵循了HarmonyOS应用开发的最佳实践,使用了组件化和响应式设计原则。

  • 10
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值