一、饮食记录
主要实现功能:
记录吃了什么,做了什么运动,需要有饮食记录的模型和记录分组模型,记录本身模型信息已经存在了,要引用一下之前的模型信息。
代码:
RecordType.ets
export default class RecordType{
/**
* 类型id
*/
id: number
//类型名称
name: ResourceStr
//图标
icon: ResourceStr
//类型推荐最小卡路里
min: number
max: number
constructor(id: number, name: ResourceStr, icon: ResourceStr, min: number = 0, max: number = 0) {
this.id = id
this.name = name
this.icon = icon
this.min = min
this.max = max
}
}
RecordVO.ets
import RecordItem from './RecordItem'
/**
* 饮食记录的页面数据模型
*/
export default class RecordVO {
/**
* 记录id
*/
id: number
/**
* 饮食记录类型,RecordType的类型
*/
typeId: number
/**
* 卡路里总数
*/
calorie: number
//引用的信息
recordItem: RecordItem
//食物的数量
amount: number = 0
}
RecordPO.ets
//数据库对象
export default class RecordPO{
/**
* 记录id
*/
id?: number
/**
* 饮食记录类型
*/
typeId: number
/**
* 记录中的食物或运动信息
*/
itemId: number
/**
* 食物数量或运动时长,如果是运动信息则无
*/
amount: number
/**
* 记录的日期
*/
createTime: number
}
RecordTypeModel.ets
import RecordType from '../viewmodel/RecordType'
enum RecordTypeEnum {
/**
* 早餐
*/
BREAKFAST,
/**
* 午餐
*/
LUNCH,
/**
* 晚餐
*/
DINNER,
/**
* 加餐
*/
EXTRA_MEAL,
/**
* 运动
*/
WORKOUT
}
/**
* 记录类型常量,每个枚举对应的数据
*/
const RecordTypes: RecordType[] = [
new RecordType(0, $r("app.string.breakfast"), $r("app.media.ic_breakfast"), 423, 592),
new RecordType(1, $r("app.string.lunch"), $r("app.media.ic_lunch"), 592, 761),
new RecordType(2, $r("app.string.dinner"), $r("app.media.ic_dinner"), 423, 592),
new RecordType(3, $r("app.string.extra_meal"), $r("app.media.ic_extra_m"), 0, 169),
new RecordType(4, $r("app.string.workout"), $r("app.media.ic_workout")),//运动无卡路里
]
export {RecordTypes, RecordTypeEnum}
import relationalStore from '@ohos.data.relationalStore'
import { ColumnInfo, ColumnType } from '../common/bean/ColumnInfo'
import RecordPO from '../common/bean/RecordPO'
import DbUtil from '../common/utils/DbUtil'
/**
* 数据库建表语句
*/
//下划线形式,增删改查时,不能直接拿着数据库对象进行处理
const CREATE_TABLE_SQL: string = `
CREATE TABLE IF NOT EXISTS record (
id INTEGER PRIMARY KEY AUTOINCREMENT,
type_id INTEGER NOT NULL,
item_id INTEGER NOT NULL,
amount DOUBLE NOT NULL,
create_time INTEGER NOT NULL
)
`
class RecordModel {//饮食记录有增删改查的功能,其还需要按日期查询--持久化保存,关系型数据库
}
let recordModel = new RecordModel()
export default recordModel as RecordModel
编写时问题:
RecordModel操作 饮食记录有增删改查的功能,其还需要按日期查询--数据库 数据库的表结构什么样,对象什么样,不是跟RecordVO一样,因为RecordVO是页面展示的,存储不合适
新建一个RecordPO模型,有id可以增删改查用到,typeId可以,需要知道类型,卡路里可以算不需保存否则增加数据库体积, 对象完整的不能存,只能存id,amount数量可以存,因为要根据数据库的日期查,需要存一下日期。
相关知识:
二、通用DB工具
主要实现功能:
关系型数据库的增删改查实现非常麻烦,业务复杂不止仅有饮食记录做增删改查操作,可能还有其他, 如何每一个都写,很麻烦,开发通用关系数据库的工具类简化其增删改查DbUtil
代码:
DbUtil .ts
//增删改查模型
import common from '@ohos.app.ability.common';
import relationalStore from '@ohos.data.relationalStore';
import { ColumnInfo, ColumnType } from '../bean/ColumnInfo';
import Logger from './Logger';
const DB_FILENAME: string = 'HeiMaHealthy.db'
class DbUtil {
rdbStore: relationalStore.RdbStore
//数据库操作初始化,该函数在项目启动时调用
initDB(context: common.UIAbilityContext): Promise<void> {
let config: relationalStore.StoreConfig = {
name: DB_FILENAME,//Db文件名称,整个应用采用一个库就可以
securityLevel: relationalStore.SecurityLevel.S1//安全级别
}
return new Promise<void>((resolve, reject) => {//rdb加载时异步的
relationalStore.getRdbStore(context, config)//获取操作对象
.then(rdbStore => {
this.rdbStore = rdbStore//保存到成员变量的位置
Logger.debug('rdbStore 初始化完成!')
resolve()
})
.catch(reason => {
Logger.debug('rdbStore 初始化异常', JSON.stringify(reason))
reject(reason)
})
})
}
//创建表(获取建表语句),在entryability中初始化
//有recordModel表创建
createTable(createSQL: string): Promise<void> {//异步
return new Promise((resolve, reject) => {
this.rdbStore.executeSql(createSQL)//把语句传进来
.then(() => {
Logger.debug('创建表成功', createSQL)
resolve()
})
.catch(err => {
Logger.error('创建表失败,' + err.message, JSON.stringify(err))
reject(err)
})
})
}
//增加
insert(tableName: string, obj: any, columns: ColumnInfo[]): Promise<number> {//新增的表名称,对象
return new Promise((resolve, reject) => {//异步返回
// 1.构建新增数据(obj转成value格式)value不能写死,因为不知道obj的具体内容(对象和数据库的名字不匹配)
//obj数据库的映射关系告诉columnInfo(数据库字段信息)
let value = this.buildValueBucket(obj, columns)
// 2.新增
this.rdbStore.insert(tableName, value, (err, id) => {//回调
if (err) {
Logger.error('新增失败!', JSON.stringify(err))
reject(err)
} else {
Logger.debug('新增成功!新增id:', id.toString())
resolve(id)
}
})
})
}
//删除
delete(predicates: relationalStore.RdbPredicates): Promise<number> {//需要传入条件
return new Promise((resolve, reject) => {
this.rdbStore.delete(predicates, (err, rows) => {
if (err) {
Logger.error('删除失败!', JSON.stringify(err))
reject(err)
} else {
Logger.debug('删除成功!删除行数:', rows.toString())
resolve(rows)
}
})
})
}
//查询
queryForList<T>(predicates: relationalStore.RdbPredicates, columns: ColumnInfo[]): Promise<T[]> {//需要字段
return new Promise((resolve, reject) => {
this.rdbStore.query(predicates, columns.map(info => info.columnName), (err, result) => {//需要的是字段数组,不是Info数组map映射
if (err) {
Logger.error('查询失败!', JSON.stringify(err))
reject(err)
} else {
Logger.debug('查询成功!查询行数:', result.rowCount.toString())//行数
resolve(this.parseResultSet(result, columns))//如果直接返回,就是一堆结果
}
})
})
}
parseResultSet<T> (result: relationalStore.ResultSet, columns: ColumnInfo[]): T[] {//需要用到字段名和字段类型
// 1.声明最终返回的结果(数组)
let arr = []
// 2.判断是否有结果
if (result.rowCount <= 0) {
return arr
}
// 3.处理结果
while (!result.isAtLastRow) {//如果最后一行就不处理,不是最后才处理
// 3.1.去下一行
result.goToNextRow()
// 3.2.解析这行数据,转为对象
let obj = {}//column中有想要的字段
columns.forEach(info => {
let val = null
switch (info.type) {
case ColumnType.LONG:
val = result.getLong(result.getColumnIndex(info.columnName))//脚标和字段名
break
case ColumnType.DOUBLE:
val = result.getDouble(result.getColumnIndex(info.columnName))
break
case ColumnType.STRING:
val = result.getString(result.getColumnIndex(info.columnName))
break
case ColumnType.BLOB:
val = result.getBlob(result.getColumnIndex(info.columnName))
break
}
obj[info.name] = val
})
// 3.3.将对象填入结果数组
arr.push(obj)
Logger.debug('查询到数据:', JSON.stringify(obj))
}
return arr
}
buildValueBucket(obj: any, columns: ColumnInfo[]): relationalStore.ValuesBucket {
let value = {}
columns.forEach(info => {//循环遍历
let val = obj[info.name]
if (typeof val !== 'undefined') {//不是空才能匹配
value[info.columnName] = val
}
})
return value
}
}
//初始化好了,导入就可以用
let dbUtil: DbUtil = new DbUtil();
export default dbUtil as DbUtil
RecordModel.ets
import relationalStore from '@ohos.data.relationalStore'
import { ColumnInfo, ColumnType } from '../common/bean/ColumnInfo'
import RecordPO from '../common/bean/RecordPO'
import DbUtil from '../common/utils/DbUtil'
/**
* 数据库建表语句
*/
//下划线形式,增删改查时,不能直接拿着数据库对象进行处理
const CREATE_TABLE_SQL: string = `
CREATE TABLE IF NOT EXISTS record (
id INTEGER PRIMARY KEY AUTOINCREMENT,
type_id INTEGER NOT NULL,
item_id INTEGER NOT NULL,
amount DOUBLE NOT NULL,
create_time INTEGER NOT NULL
)
`
//数据库列()映射关系
const COLUMNS: ColumnInfo[] = [
{name: 'id', columnName: 'id', type: ColumnType.LONG},
{name: 'typeId', columnName: 'type_id', type: ColumnType.LONG},
{name: 'itemId', columnName: 'item_id', type: ColumnType.LONG},
{name: 'amount', columnName: 'amount', type: ColumnType.DOUBLE},//可以是小数
{name: 'createTime', columnName: 'create_time', type: ColumnType.LONG}
]
const TABLE_NAME = 'record'//表名
const ID_COLUMN = 'id'
const DATE_COLUMN = 'create_time'
class RecordModel {//饮食记录有增删改查的功能,其还需要按日期查询--持久化保存,关系型数据库
//一调用Record Model就可以拿到建表语句
getCreateTableSql(): string {//返回值是字符串
return CREATE_TABLE_SQL
}
insert(record: RecordPO): Promise<number>{
return DbUtil.insert(TABLE_NAME, record, COLUMNS)
}
deleteById(id: number): Promise<number>{
// 1.删除条件
let predicates = new relationalStore.RdbPredicates(TABLE_NAME)//删除知道表名
predicates.equalTo(ID_COLUMN, id)
// 2.删除
return DbUtil.delete(predicates)
}
listByDate(date: number): Promise<RecordPO[]>{//分日期查询
// 1.查询条件
let predicates = new relationalStore.RdbPredicates(TABLE_NAME)
predicates.equalTo(DATE_COLUMN, date)
// 2.查询
return DbUtil.queryForList(predicates, COLUMNS)
}
}
let recordModel = new RecordModel()
export default recordModel as RecordModel
ColumnInfo.ts
export interface ColumnInfo{
name: string//实体名
columnName: string//映射的名
type: ColumnType//数据类型对应枚举,是否小数
}
//为什么是这四个(枚举)
export enum ColumnType{
LONG,
DOUBLE,
STRING,
BLOB
}
async onCreate(want, launchParam) {
//加载用户首选项
PreferenceUtil.loadPreference(this.context)
// 2.初始化日期
AppStorage.SetOrCreate(CommonConstants.RECORD_DATE, DateUtil.beginTimeOfDay(new Date()))
//初始化RDB
await DbUtil.initDB(this.context)
//创建表,等待完后再创建表
DbUtil.createTable(RecordModel.getCreateTableSql())
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
}
编写时问题:
rdb加载时异步的,为什么用promise
promise对象就可以将异步操作以同步的流程表达出来,避免了层层嵌套导致的
相关知识:
三、饮食记录业务层开发
主要实现功能:
饮食记录的数据库增删改查,但是数据库查的跟页面的有差异,实现把数据包括的转为页面所需要的。实现了新增饮食记录,根据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 {
//新增饮食记录
insert(typeId: number, itemId: number, amount: number): Promise<number>{//日期不用传,在全局变量中
// 1.获取时间
let createTime = (AppStorage.Get('selectedDate') || DateUtil.beginTimeOfDay(new Date())) as number//没取到就是今天(不一定number)
// 2.新增
return RecordModel.insert({typeId, itemId, amount, createTime})//给匿名对象
}
//根据id删除饮食记录
deleteById(id: number): Promise<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.查询记录项(处理1)
rv.recordItem = ItemModel.getById(rp.itemId, rp.typeId !== RecordTypeEnum.WORKOUT)//从记录项的model找,运动无热量
// 2.3.计算热量(处理2)
rv.calorie = rp.amount * rv.recordItem.calorie
return rv
})
}
//卡片
calculateStatsInfo(records: RecordVO[]): StatsInfo{
// 1.准备结果
let info = new StatsInfo()//默认0
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
}
//分组的统计
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
GroupInfo.ets
//方便调用,循环写出bar,可点
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
}
}
效果:
四、数据持久化和页面交互
主要实现功能:
利用上面功能,实现饮食记录和数据交互。
代码:
RecordIndex
@Provide records:RecordVO[]=[]
@Prop @Watch('handlePageShow') isPageShow:boolean//true才aboutToAppear
@StorageProp('selectedDate')
@Watch('aboutToAppear')
selectedDate: number = DateUtil.beginTimeOfDay(new Date())//获取这一天的毫秒值
handlePageShow(){
if (this.isPageShow) {
this.aboutToAppear()
}
}
//生命周期钩子中查询
async aboutToAppear(){//查到共卡片和列表使用
this.records=await RecordService.queryRecordByDate(this.selectedDate)//拿到选中的日期
}
StatsCard.ets
import BreakpointType from '../../common/bean/BreakpointType'
import BreakpointConstants from '../../common/constants/BreakpointConstants'
import { CommonConstants } from '../../common/constants/CommonConstants'
import DateUtil from '../../common/utils/DateUtil'
import RecordService from '../../service/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 {
//单项读取
//DAteUtil获取当前日期的毫秒值
@StorageProp('selectedDate') selectedDate: number = DateUtil.beginTimeOfDay(new Date())//获取这一天的毫秒值
@StorageProp('currentBreakpoint') currentBreakpoint: string = BreakpointConstants.BREAKPOINT_SM
@Consume @Watch('handleRecordChange') records:RecordVO[]//变量名与父亲的一直
//拿到查询的,需要处理成Stats Info来渲染
@State info:StatsInfo=new StatsInfo()
//当record变更是处理
handleRecordChange(){
this.info=RecordService.calculateStatsInfo(this.records)
}
controller: CustomDialogController = new CustomDialogController({
builder: DatePickDialog({selectedDate: new Date(this.selectedDate)})//下次打开要回显,不在初始,new date显示为日期
})
build() {
Column(){
//日期信息
Row(){
Text(DateUtil.formatDate(this.selectedDate))//格式化为日期
.fontColor($r('app.color.secondary_color'))
Image($r('app.media.ic_public_spinner'))
.width(20)
.fillColor($r('app.color.secondary_color'))
}
.padding(CommonConstants.SPACE_8)
.onClick(()=>{
this.controller.open()
})
//统计信息
Swiper(){
//热量统计
CalorieStats({intake:this.info.intake,expend:this.info.expend})
//营养素统计
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 '../../service/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
export default struct RecordList {
@Consume @Watch('handleRecordChange') records:RecordVO[]//变量名与父亲的一直
//拿到查询的,需要处理成Stats Info来渲染
@State groups:GroupInfo<RecordType,RecordVO>[]=[]
//当record变更是处理
handleRecordChange(){
this.groups=RecordService.calculateGroupInfo(this.records)
}
build() {
List({space:CommonConstants.SPACE_10}){
ForEach(this.groups,(group:GroupInfo<RecordType,RecordVO>)=>{
ListItem(){
Column(){
//分组标题
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'))
}
.onClick(()=>{
router.pushUrl({
url:'pages/ItemIndex',
params:{type:group.type}
})
})
.width('100%')
//组内记录
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)
}
}
Index.ets
import BreakpointType from '../common/bean/BreakpointType'
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//实现点击当前页面的效果
//再次进行初始化,欢迎界面之后
//因为不是静态的,要new出来
private breakpointSystem:BreakpointSystem=new BreakpointSystem()
//storageProp必须初始化
@StorageProp('currentBreakpoint') currentBreakpoint: string=BreakpointConstants.BREAKPOINT_SM
//实现RecordIndex加载
@State isPageShow: boolean = false
onPageShow(){//显示
this.isPageShow = true
}
onPageHide(){//隐藏
this.isPageShow = false
}
@Builder TabBarBuilder(title: ResourceStr, image: ResourceStr, index: number) {
Column({ space: CommonConstants.SPACE_8 }) {
Image(image)//图片必须是svg格式
.width(22)
.fillColor(this.selectColor(index))
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.gray')
}
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))
}
.onChange(index => this.currentIndex = index)
.width('100%')
.height('100%')
.vertical(new BreakpointType({
sm:false,
md:true,
lg:true
}).getValue(this.currentBreakpoint))//布局模式
}
}
效果:
编写时问题:
1.RecordIndex首页about Appear是一次查询,只有在组件构建时触发,我们是页面跳转,跳转再回来,其不会重新出发About。。,函数没有触发,没有重新查询。
页面每次加载查询,recordIndex是组件没有页面显示,首页有页面显示,每次显示时通知RIndex。
2.统计中没有渲染:
builder默认下是传的值,而不是引用,所以値变更不会重新渲染
相关知识:
@Builder声明实体,表示可以进行Builder方式初始化,@Value注解,表示只公开getter,对所有属性的setter都封闭,即private修饰,所以它不能和@Builder现起用。
总结:
一、饮食记录
定义了几个类和枚举,用于管理饮食记录的数据模型、视图对象、持久化对象以及数据库访问。
二、通用DB工具
通过RecordType
、RecordVO
和RecordPO
类定义了不同类型的数据模型,用于在内存中操作和数据库中存储饮食记录数据。使用RecordTypeEnum
枚举和RecordTypes
常量来管理饮食记录的类型,提供类型安全和易于维护的数据。通过SQL语句定义了数据库的表结构,为持久化存储做准备。RecordModel
类可能包含对数据库的增删改查操作,以及数据查询功能。
三、饮食记录业务层开发
引入了groupInfo来实现数据的分组功能。通过分组,用户能够更直观地查看和管理自己的饮食记录,如按餐次、食品类别或日期等方式进行分类。此外,我们还利用StatsInfo模块来统计和展示用户当日的卡路里摄入、运动消耗等关键信息。
四、数据持久化和页面交互
通过生命周期方法和事件处理函数响应用户的交互操作。组件的builderer
方法使用了链式调用,构建了组件的UI布局和样式。Index中onPageShow
和onPageHide
方法用于处理页面显示和隐藏的事件。RecordIndex组件即将显示(isPageShow
变为true
)时,调用handlePageShow
方法。handlePageShow
方法触发aboutToAppear
生命周期钩子。aboutToAppear
方法通过异步调用服务层的queryRecordByDate
方法,根据持久化的selectedDate
获取记录数据,并更新到records
数组中,供组件的其他部分使用。