实现数据持久化和页面交互

实现数据持久化和页面交互

经过上几次实践,我们对实现页面交互所需要的数据模型全部都已经都建好了,对可以对数据模型进行增删改查操作的业务层也已经定义好了,接下来我们要通过业务层的方法,实现数据持久化实现与页面的交互。

在这里插入图片描述

如图所示,我们需要实现上图所示统计卡片、记录列表两部分的数据持久化与页面交互。因为这两个组件都在RecordIndex文件中,所以我们在RecordIndex使用RecordService中的查询方法,做到一次查询,两个组件都可以使用。

import DateUtil from '../../common/utils/DateUtil'
import RecordService from '../../service/RecordService'
import RecordVO from '../../viewmodel/RecordVO'
import RecordList from './RecordList'
import SearchHeader from './SearchHeader'
import StatsCard from './StatsCard'
@Component
export default  struct RecordIndex {
  //接收RecordVO[]数据
  @Provide records:RecordVO[] =[]

  //获取所选择日期
  @StorageProp('selectedDate')
  @Watch('aboutToAppear')     //监控,当日期变更时,重新查询
  selectedDate:number =DateUtil.beginTimeOfDay(new Date())  //从应用存储中提取选择日期的毫秒值
  @Prop @Watch('handelPageShow') isPageShow:boolean

  handelPageShow(){
    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'))
  }
}

这部分需要注意两点,第一是因为查询需要用到所选日期,所以我们从应用存储中提取选择日期的毫秒值,并监控该值,当其变化时,执行aboutToAppear生命周期钩子,根据变化的日期重新查询数据。

第二点,如下两图,在index页,我们定义了状态变量,和onPageShow,onPageHide方法,每次进入饮食记录页,将状态传给RecordIndex页,在RecordIndex接收该状态变量,并监控该值,一旦状态表示进入饮食记录页面,说明数据有可能改变,调用生命周期钩子重新查询数据,重新渲染。

在这里插入图片描述

在这里插入图片描述

统计卡片组件数据渲染

在获得查询到的数据后,可以看到查询结果被@Provide修饰,所以,在统计卡片组件中通过@Consume获取查到的数据,并进行监控,当数据变化时,重新获得变化后统计卡片所需数据。

@Consume
@Watch('handleRecordsChange')
records:RecordVO[]      //获得父亲提供的数组
@State info:StatsInfo=new StatsInfo()

//当记录变更时,获取他
handleRecordsChange(){
  this.info=RecordService.calculateStatsInfo(this.records)
}

在获得到需要的数据info后,即可将统计卡片中原本的死数据用info中包含的信息代替

//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%')

可以看出,我们将获得的info数据传递给了热量统计和营养素组件,这两部分获得了info中其需要的部分。

注意点:

以热量统计举例,在热量统计组件中我们封装了@Builder StatsBuilder组件,然后再在build()中调用该方法,传递数据,实现组件和数据的渲染,在这里,如果我们传递值给StatsBuilder组件,在数据变化时其不会重新渲染

,所以我们应该传递其引用。这样才能在数据变化时重新渲染,在营养素统计中也是一样。如下:

@Builder StatsBuilder($$:{label:string,value:number,tips?:string}){

  Column({space:CommonConstants.SPACE_6}){
    Text($$.label)
      .fontColor($r('app.color.gray'))
      .fontWeight(CommonConstants.FONT_WEIGHT_600)
    Text($$.value.toFixed(0))
      .fontSize(20)
      .fontWeight(CommonConstants.FONT_WEIGHT_700)
    if ($$.tips){
      Text($$.tips)
        .fontSize(12)
        .fontColor($r('app.color.light_gray'))
    }

  }

}

在build()中调用该方法,传递数据时,如下:

//1.饮食摄入
this.StatsBuilder({label:'饮食摄入',value:this.intake})
 Stack(){     //层叠
   //2.还可以吃
   //2.1 进度条
   Progress({
     value:this.intake,      //达到值
     total:this.recommend,   //总共值
     type:ProgressType.Ring   //环形进度条
   })
     .width(120)
     .style({strokeWidth:CommonConstants.DEFAULT_10})  //进度环的粗细
     .color($r('app.color.primary_color'))   //进度环的颜色
   //2。2统计数据
   this.StatsBuilder({label:'还可以吃',value:this.remainCalorie(),tips:`推荐${this.recommend}`})
 }
  //3.运动消耗
  this.StatsBuilder({label:'运动消耗',value:this.expend})

记录列表组件数据渲染

在纪录列表中,同样在记录列表组件中通过@Consume获取查到的数据,并进行监控,当数据变化时,重新获得变化后记录列表所需数据。

@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(18).fontWeight(CommonConstants.FONT_WEIGHT_700)
               Text(`建议${group.type.min}~${group.type.max}千卡 `).grayText()
               Blank();
               Text(group.calorie.toFixed(0)).fontSize(14).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_6}){
                  Image(item.recordItem.image).width(50)
                  Column({space:CommonConstants.SPACE_4}){
                    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)
         }
      })
    }.width(CommonConstants.THOUSANDTH_940)
    .margin({top:10})
    .height('100%')
  }
  //列表的滑动删除按钮
  @Builder deleteButton(){
    Image($r('app.media.ic_public_delete_filled'))
      .width(20)
      .fillColor(Color.Red)
      .margin(5)

  }
}

注意点:

在上述代码中如下图部分,我们在点击分组标题时,会进入ItemIndex页,同时将分组的类型传递给ItemIndex页

在这里插入图片描述

在ItemIndex页

onPageShow(){
  //获取跳转时的参数
  let params:any=router.getParams()
  //获取点击的饮食记录类型
  this.type=params['type']
  this.iSFood=this.type.id!==RecordTypeEnum.WORKOUT

}

当页面展示时,获得路由参数,确定饮食记录类型,从而确定显示运动列表还是饮食列表。


至此,饮食记录实现效果如下:

屏幕录制 2024-06-18 212733

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值