This is my first Blog !
鸿蒙开发项目一
NO.1 Store一号店
鸿蒙应用开发 - 一号店购物车
1.一号店界面展示
2.案例分析
1.1 项目结构
在ets文件下新建data文件用于存放数据,page中新建No_1_Main_Page用于显示一号店购物车页面
1.2 界面分析
TitleCom:购物车顶部搜索栏
FrightCom:判断是否可以减免运费
GoodsListItem:商品列表渲染
EmptyCom:当购物车位空时显示
3.代码分析与实现
3.1 顶部区域(购物车种类)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| // 标题(显示商品种类) @Component struct TitleCom { @Prop typeSize: number
build() { Row() { // 文字 Stack({ alignContent: Alignment.Bottom }) { Text(`购物车(${this.typeSize} )`) .height('100%') Text('') .width(25) .height(2) .linearGradient({ angle: 90, colors: [[MAIN_RED, 0], [Color.White, 1]] }) } .height('100%')
// 地址 Row() { Image($r('app.media.ic_yhd_location')) .width(15) .fillColor(DEEP_GRAY) Text('北京市昌平区') .fontSize(12) .fontColor(DEEP_GRAY) } .height(20) .padding({ left: 5, right: 5 }) .borderRadius(10) .backgroundColor(LIGHT_GRAY)
// 编辑 Text('编辑') } .padding({ left: 20, right: 20 }) .width('100%') .height(40) .justifyContent(FlexAlign.SpaceBetween) .backgroundColor(Color.White) } }
|
为了判断购物车当前商品种类,需要从父组件传递typeSize值到子组件,此时需要用到@Prop进行父子组件之间的数据传递
1 2 3 4 5 6 7 8 9
| build() { Column() { // 标题 TitleCom({ typeSize: this.list.length }) } .height('100%') .backgroundColor(LIGHT_GRAY) } }
|
在父组件中直接传递list数组的长度即为当前商品种类数量
3.2 中间内容区域
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| // 内容区域 @Component struct ContentCom { @Link list: CartGood[]
@Builder delAction(id: string) { Text(`删除`) .fontColor(Color.White) .backgroundColor(Color.Red) .height('100%') .width(70) .textAlign(TextAlign.Center) .onClick(() => { //删除的索引值 let delIndex: number = 0 //循环遍历list找到准确的index this.list.forEach((item: CartGood, index: number) => { if (item.id == id) {
//找到 delIndex = index } }) this.list.splice(delIndex, 1) //console.log(delIndex.toString()) }) }
build() { Scroll() { Column() { // 支付 FreightCom() .margin(10) // 商品列表 Column({ space: 10 }) { ListTitleCom() // 自营区域 List() { ForEach(this.list, (item: CartGood, index: number) => { ListItem() { GoodsListItem({ listItem: item }) } .swipeAction({ end: this.delAction(item.id) }) }, (item: CartGood, index: number) => { return item.id } ) } .divider({ strokeWidth: .5, startMargin: 10, endMargin: 10, color: DEEP_GRAY }) } .backgroundColor(Color.White) .margin({ left: 10, right: 10 })
// 空车 商品为空时显示 if (this.list.length == 0) { EmptyCom() } } } .align(Alignment.Top) .padding({ bottom: 10 }) .edgeEffect(EdgeEffect.Spring) .layoutWeight(1) } }
|
选择删除
利用到ListItem的一个属性
1 2 3 4 5 6 7 8 9 10 11 12
| List() { ForEach(this.list, (item: CartGood, index: number) => { ListItem() { GoodsListItem({ listItem: item }) } .swipeAction({ end: this.delAction(item.id) }) }, (item: CartGood, index: number) => { return item.id } ) }
|
3.3 底部结算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| // 支付(最下方组件) @Component struct PayCom { @Link selectedIds: string[] @Consume totalPrice: number @Prop totalCount: number
build() { Row() { Row() { CheckboxGroup({ group: 'cart' }) .selectedColor(MAIN_RED) .onChange((res: CheckboxGroupResult) => { this.selectedIds = res.name //console.log(JSON.stringify(res)) })
Text('全选') .fontSize(12) }
Row() { Text('合计:') .fontSize(14) PriceCom({ fontColor: Color.Black, price: this.totalPrice }) Button(`入会结算(${this.totalCount})`) .fontColor('#ffe3cc') .backgroundColor(Color.Black) .fontSize(14) .margin({ left: 5 }) }
} .justifyContent(FlexAlign.SpaceBetween) .padding({ left: 10, right: 10 }) .height(48) .width('100%') .backgroundColor(Color.White) } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| // 价格(根据传入的价格渲染数字) @Component struct PriceCom { @Prop price: number = 0 fontColor: ResourceColor = MAIN_RED discard: boolean = false
getSplicePrice() { return this.price.toFixed(2) .split('.') }
build() { Text() { Span('¥') .fontSize(12) Span(this.getSplicePrice()[0] .toString()) .fontSize(this.discard ? 12 : 16) .fontWeight(600) Span('.') Span(this.getSplicePrice()[1] == undefined ? '00' : this.getSplicePrice()[1]) .fontSize(12) } .fontColor(this.fontColor) .decoration({ type: this.discard ? TextDecorationType.LineThrough : TextDecorationType.None }) } }
|
在渲染底部的结算价钱和结算商品数量以及筛选出来的选中商品列表需要从父组件传入相应的值
在父组件中需要用到数组对应的方法来实现reduce(求和)filter(筛选)include
@Watch监管选中商品列表的变化 一旦商品列表发生变化就会执行对应的方法 selectedIdChange
从父组件需要传递给子组件以及孙组件所以需要使用@Provide来修饰
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| @Entry @Component export default struct Main { @State list: CartGood[] = goodList @Provide totalPrice: number = 0 //总价 @State totalCount: number = 0 @State //选中的ID @Watch('selectedIdChange') selectedIds: string[] = []
selectedIdChange() { const arr = this.list.filter((item: CartGood) => { if (this.selectedIds.includes(item.id)) { return true } else { return false } }) //console.log(JSON.stringify(arr)) const totalCount = arr.reduce((prev: number, item: CartGood) => { return prev + item.count }, 0) this.totalCount = totalCount
const totalPrice = arr.reduce((prev: number, item: CartGood) => { return prev + item.count * item.good.jdPrice }, 0) this.totalPrice = totalPrice //console.log(totalPrice.toString()) }
build() { Column() { // 标题 TitleCom({ typeSize: this.list.length }) // 内容 ContentCom({ list: this.list })
// 支付 PayCom({ selectedIds: this.selectedIds, totalCount: this.totalCount, //totalPrice: this.totalPrice }) } .height('100%') .backgroundColor(LIGHT_GRAY) } }
|
3.4运费
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
| // 运费(地址下方) @Component struct FreightCom { // 默认 69 可以由外部传入 minPrice: number = 69.00 @Consume totalPrice: number
build() { Column() { if (this.totalPrice < this.minPrice) { // 运费不够 提示 Row() { Row({ space: 5 }) { // 凑单免运费 Text() { Span('凑单') Span('免运费') .fontColor(MAIN_RED) } .fontSize(13) .fontFamily('medium')
// 分割线 Divider() .vertical(true) .height(8) .color(DEEP_GRAY) .strokeWidth(1)
// 运费信息 Row() { Text() { Span('还需凑钱 ') Span(`¥${(this.minPrice - this.totalPrice).toFixed(2)}`) .fontColor(MAIN_RED) Span('可免运费') } .fontSize(13)
Image($r('app.media.ic_yhd_order_info')) .width(15) } }
// 按钮 Button() { Row() { Text('去凑单') .fontColor(Color.White) .fontSize(12) Image($r('app.media.ic_public_arrow_right')) .height(14) .width(10) .fillColor(Color.White) } .backgroundColor(MAIN_RED) .borderRadius(20) .padding({ left: 10, top: 3, bottom: 3, right: 2 }) } } .width('100%') } else { // 运费足够 提示 Row({ space: 5 }) { Text('运费') .backgroundColor(MAIN_RED) .fontSize(12) .fontColor(Color.White) .padding(2) .borderRadius(3) Divider() .vertical(true) .height(12) .strokeWidth(2) Text('已免运费') .fontSize(12) .fontColor(Color.Gray) Image('/common/day08-10/yhd/ic_yhd_order_info.png') .width(15)
} } } .borderRadius(5) .height(30) .padding({ left: 8, right: 8 }) .linearGradient({ colors: [['#ffe8ea', 0], [Color.White, 1]] }) .width('100%') .justifyContent(FlexAlign.Center) } }
|