加载中...

HarmonyOS一号店


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)
}
}

文章作者: 太阳神小赖
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 太阳神小赖 !
  目录