期末项目黑马健康5

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

综合运用本学期所学内容及个人自学知识,使用HarmonyOS 4.0 及以上版本开发一款具有实用性和创新性的移动应用软件。


一、项目名称:黑马健康运动

黑马健康是一款流行的运动健身软件,旨在帮助用户保持健康和锻炼,致力于提供健身教学、跑步、骑行、交友及健身饮食指导一站式运动解决方案。健身、跑步、骑行,运动品类更丰富,个性化推荐更加精细。

二、应用运行过程5

1.数据模型—通用DB工具

代码如下(示例):
EntryAbility.ets

import UIAbility from '@ohos.app.ability.UIAbility';
import hilog from '@ohos.hilog';
import window from '@ohos.window';
import { CommonConstants } from '../common/constants/CommonConstants';
import DateUtil from '../common/utils/DateUtil';
import DbUtil from '../common/utils/DbUtil';
import PreferenceUtil from '../common/utils/PreferenceUtil';
import RecordModel from '../model/RecordModel';

export default class EntryAbility extends UIAbility {
  async  onCreate(want, launchParam) {
    //1.加载用户首选项
    PreferenceUtil.loadPreference(this.context)
    //2.初始化日期
    AppStorage.SetOrCreate(CommonConstants.RECORD_DATE, DateUtil.beginTimeOfDay(new Date()))
    //3.初始化RDB工具
    await DbUtil.initDB(this.context)
    //4.创建record表
    DbUtil.createTable(RecordModel.getCreateTableSql())

    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
  }

  onDestroy() {
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
  }

  onWindowStageCreate(windowStage: window.WindowStage) {
    // Main window is created, set main page for this ability
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');

    //默认加载页面
    windowStage.loadContent('pages/WelcomePage', (err, data) => {
      if (err.code) {
        hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
        return;
      }
      hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
    });
  }

  onWindowStageDestroy() {
    // Main window is destroyed, release UI related resources
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
  }

  onForeground() {
    // Ability has brought to foreground
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
  }

  onBackground() {
    // Ability has back to background
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
  }
}

2.饮食记录业务层开发

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 {
  insert(typeId: number,itemId: number,amount: number){
    //1.获取时间
    let createTime = (AppStorage.Get('selectedDate') || DateUtil.beginTimeOfDay(new Date())) as number
    //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: RecordPO) => {
      //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)
      //2.3.计算热量
      rv.calorie = rp.amount * rv.recordItem.calorie
      return rv
    })
  }

  //作用:把RecordVo数组转成StatsInfo和GroupInfo
  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.intake += r.calorie
        info.carbon += r.recordItem.carbon
        info.protein += r.recordItem.protein
        info.fat += r.recordItem.fat
      }
    })
    //3.返回
    return info
  }

  /**
   * 将记录列表按照记录类型分组
   * @param records 记录列表
   * @returns 分组记录信息
   */

  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

该处使用的url网络请求的数据。

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

运行截图:
在这里插入图片描述
在这里插入图片描述ItemIndex.ets
代码如下:

import router from '@ohos.router'
import { CommonConstants } from '../common/constants/CommonConstants'
import { RecordTypeEnum, RecordTypes } from '../model/RecordTypeModel'
import RecordService from '../service/RecordService'
import ItemCard from '../view/item/ItemCard'
import ItemList from '../view/item/ItemList'
import ItemPanelHeader from '../view/item/ItemPanelHeader'
import NUmberKeyboard from '../view/item/NumberKeyboard'
import RecordItem from '../viewmodel/RecordItem'
import RecordType from '../viewmodel/RecordType'
@Entry
@Component
struct ItemIndex {
  @State amount:number = 1
  @State value: string = ''//按键记录
  @State showPanel: boolean = false
  @State item: RecordItem = null
  @State type: RecordType = RecordTypes[0]
  @State isFood: boolean = true

  onPanelShow(item: RecordItem){//定义函数,当panel要展示时做一些事情
    this.amount = 1
    this.value = ''
    this.item = item
    this.showPanel = true//控制弹窗展示
  }

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

  build() {
    Column() {
      //1.头部导航
      this.Header()
      //2.列表
      ItemList({showPanel: this.onPanelShow.bind(this), isFood: this.isFood})
      .layoutWeight(1)
      //3.底部面板
      Panel(this.showPanel) { //使用时需要传入一个布尔类型的参数
        //3.1.顶部日期
        ItemPanelHeader()
        //3.2.记录项卡片
        if (this.item) {
          ItemCard({ amount: this.amount, item: $item })
        }
        //3.3.数字键盘
        NUmberKeyboard({ amount: $amount, value: $value })
        //3.4.按钮
        this.PanelButton()
      }
      .mode(PanelMode.Full)
      .dragBar(false)
      .backgroundMask($r('app.color.light_gray'))
      .backgroundColor(Color.White)
    }
    .width('100%')
    .height('100%')
  }

  @Builder PanelButton(){
    Row({space:CommonConstants.SPACE_6}){
      Button('取消')
        .width(120)
        .backgroundColor($r('app.color.light_gray'))
        .type(ButtonType.Normal)//样式
        .borderRadius(6)//边角
        .onClick(() => this.showPanel = false)
      Button('提交')
        .width(120)
        .backgroundColor($r('app.color.primary_color'))
        .type(ButtonType.Normal)//样式
        .borderRadius(6)//边角
        .onClick(() => {
          //1.持久化保存
          RecordService.insert(this.type.id, this.item.id, this.amount)
            .then(() => {
              //2.关闭弹窗
              this.showPanel = false
            })
        })
    }
    .margin({top: 10})
  }

  @Builder Header(){
    Row(){
      Image($r('app.media.ic_public_back'))
        .width(24)
        .onClick(() => router.back())//点击事件 返回
      Blank()
      Text(this.type.name).fontSize(18).fontWeight(CommonConstants.FONT_WEIGHT_600)
    }
    .width(CommonConstants.THOUSANDTH_940)
    .height(32)
  }
}

总结

1.数据模型—通用DB工具

在这里插入图片描述
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.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
  }
}

2.饮食记录业务层开发

  1. 新增饮食记录
    @param typeId 记录类型id
    @param itemId 记录项id
    @param amount 记录项数量(食物量、运动时长)
    @returns 新增数量

2.根据id删除饮食记录
@param id 记录id
@returns 删除条数

  1. 根据日期查询饮食记录列表
    @param date 查询的日期
    @returns 记录列表

  2. 根据记录列表信息统计出热量、营养素信息
    @param records 饮食记录列表
    @returns 热量、营养素信息

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

实现页面交互时,添加饮食和运动为什么添加不进去

解决方案:在RecordIndex页面中的aboutToAppear在组件构建时build之前触发,在进入首页之前已经触发,这个时候没有数据,在index页面定义
@State isPagesShow: boolean = false
来通知RrcordIndex
在RecordIndex.ets写

@Prop @Watch('handlePageShow') isPageShow: boolean

handlePageShow(){
  if (this.isPageShow) {
    this.aboutToAppear()
  }
}

在这里插入图片描述

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值