一、食物列表页
点击食物后面的加号时,会跳出一个食物面板,这个面板上会跳出具体的食物信息。
例如,现在我们就要完成这个界面。整个列表页是一个列式布局
代码为:
大纲:
import router from '@ohos.router'
import { CommonConstants } from '../common/constants/CommonConstants'
import { RecordTypeEnum } 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 = RecordType[0]
@State isFood:boolean = true
//控制弹窗展示
onPanelShow(item:RecordItem){
this.amount = 1
this.value = ''
this.showPanel = true
this.item = item
}
//生命周期钩子
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按钮
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.light_gray'))
.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})
}
.mode(PanelMode.Full)
.dragBar(false)
.backgroundMask($r('app.color.light_gray'))
.backgroundColor(Color.White)
}
.width('100%')
.height('100%')
}
@Builder Header(){
Row(){
Image($r('app.media.ic_public_back'))
.width(25)
.onClick(()=>router.back())
Blank()
Text(this.type.name)
.fontSize(18)
.fontWeight(CommonConstants.FONT_WEIGHT_700)
}
.width(CommonConstants.THOUSANDTH_940)
.height(32)
}
}
列表信息代码部分:
import { CommonConstants } from '../../common/constants/CommonConstants'
import ItemModel from '../../model/ItemModel'
import GroupInfo from '../../viewmodel/GroupInfo'
import ItemCategory from '../../viewmodel/ItemCategory'
import RecordItem from '../../viewmodel/RecordItem'
@Component
export default struct itemList {
showPanel:(item:RecordItem)=>void
@Prop isFood:boolean
build() {
Tabs(){
TabContent(){
this.TabContentBuilder(ItemModel.list(this.isFood))
}
.tabBar('全部')
ForEach(ItemModel.listItemGroupByCategory(this.isFood), (group:GroupInfo<ItemCategory,RecordItem>)=>{
TabContent(){
this.TabContentBuilder(group.items)
}
.tabBar(group.type.name)
})
}
.width(CommonConstants.THOUSANDTH_940)
.height('100%')
.barMode(BarMode.Scrollable)
}
@Builder TabContentBuilder(items:RecordItem[]){
List({space:CommonConstants.SPACE_10}){
ForEach(items,(item:RecordItem)=>{
ListItem(){
Row({space:CommonConstants.SPACE_6}){
Image(item.image).width(50)
Column({space:CommonConstants.SPACE_4}){
Text(item.name).fontWeight(CommonConstants.FONT_WEIGHT_500)
Text(`${item.calorie}千卡/片${item.unit}`).fontSize(14).fontColor($r('app.color.light_gray'))
}
Blank()
Image($r('app.media.ic_public_add_norm_filled'))
.width(18)
.fillColor($r('app.color.primary_color'))
}
.width('100%')
.padding(CommonConstants.SPACE_6)
}
.onClick(()=>this.showPanel(item))
})
}
.width('100%')
.height('100%')
}
}
运行结果
二、 弹窗设计
分析得知,弹窗中为一行一行的组件。
代码为
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按钮
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.light_gray'))
.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})
}
.mode(PanelMode.Full)
.dragBar(false)
.backgroundMask($r('app.color.light_gray'))
.backgroundColor(Color.White)
运行结果
三、数字键盘设计
数字键盘用到了一个新的组件Grid组件,是一个网格容器,由“行”和“列”分割的单元格所组成,通过指定“项目”所在的单元格做出各种各样的布局。
代码
//数字键盘
import { CommonConstants } from '../../common/constants/CommonConstants'
@Component
export default struct NumberKeyboard {
numbers:string[] = ['1','2','3','4','5','6','7','8','9','0','.']
@Link amount:number
@Link value:string
@Styles keyBoxStyle(){
.backgroundColor(Color.White)
.borderRadius(8)
.height(60)
}
build() {
Grid(){
ForEach(this.numbers,num =>{
GridItem(){
Text(num).fontSize(20).fontWeight(CommonConstants.FONT_WEIGHT_900)
}
.keyBoxStyle()
.onClick(()=>this.clickNumber(num))
})
GridItem(){
Text('删除').fontSize(20).fontWeight(CommonConstants.FONT_WEIGHT_900)
}
.keyBoxStyle()
.onClick(()=>this.clickDelete())
}
.width('100%')
.height(280)
.backgroundColor($r('app.color.index_page_background'))
.columnsTemplate('1fr 1fr 1fr')
.columnsGap(8)
.rowsGap(8)
.padding(8)
.margin({top:10})
}
clickNumber(num:string){
//1.拼接用户输入的内容
let val = this.value + num
//2.校验输入格式是否正确
let firstIndex = val.indexOf('.')
let lastIndex = val.lastIndexOf('.')
if (firstIndex !== lastIndex || (lastIndex !=-1 && lastIndex < val.length - 2)) {
//非法输入
return
}
//3.将字符串转为数值
let amount = this.parseFloat(val)
//4.保存
if (amount>=999.9) {
this.amount = 999
this.value = '999'
}else {
this.amount = amount
this.value = val
}
}
clickDelete(){
if (this.value.length <= 0) {
return
}
this.value = this.value.substring(0,this.value.length - 1)
this.amount = this.parseFloat(this.value)
}
//检验是否出现2.这种情况
parseFloat(str:string) {
if (!str){
return 0
}
if (str.endsWith('.')) {
str = str.substring(0, str.length - 1)
}
return parseFloat(str)
}
}
运行结果