Nav
Comment
组件Stack
组件底部输入框固定在下方Scroll
将来页面内容可以滚动1@Entry
2@Component
3struct Index {
4 build() {
5 Stack({ alignContent: Alignment.Bottom }) {
6 Column() {
7 Scroll() {
8 Column() {
9 // 导航
10 NavComp()
11 // 评论
12 CommentComp()
13 // 分割线
14 Divider()
15 .strokeWidth(8)
16 .color('#f5f5f5')
17 // 回复列表
18
19 }
20 .padding({ bottom: 50 })
21 }
22 }
23 .width('100%')
24 .height('100%')
25
26 Row({ space: 15 }){
27 TextInput({ placeholder: '回复~' })
28 .layoutWeight(1)
29 Text('发布')
30 .fontColor('#069')
31 }
32 .padding({ left: 15, right: 15 })
33 .width('100%')
34 .height(50)
35 .backgroundColor('#fff')
36 .border({ width: { top: 0.5 }, color: '#e4e4e4' })
37 }
38
39 }
40}
41
42// 导航
43@Component
44struct NavComp {
45 build() {
46 Row() {
47 Row() {
48 Image($r('app.media.ic_public_arrow_left'))
49 .width(16)
50 .aspectRatio(1)
51 // svg 图标可以使用填充颜色
52 // .fillColor('red')
53 }
54 .width(24)
55 .aspectRatio(1)
56 .backgroundColor('#f5f5f5')
57 .borderRadius(12)
58 .justifyContent(FlexAlign.Center)
59 .margin({ left: 16 })
60
61 Text('评论回复')
62 .layoutWeight(1)
63 .textAlign(TextAlign.Center)
64 .padding({ right: 40 })
65 }
66 .height(40)
67 .border({ width: { bottom: 0.5 }, color: '#e4e4e4' })
68 }
69}
70
71// 评论
72@Component
73struct CommentComp {
74 build() {
75 Row() {
76 Image($r('app.media.avatar'))
77 .width(32)
78 .aspectRatio(1)
79 .borderRadius(16)
80 Column({ space: 5 }) {
81 Text('周杰伦')
82 .width('100%')
83 .fontWeight(FontWeight.Bold)
84 .fontSize(15)
85 Text('大理石能雕刻出肌肉和皮肤的质感,那个年代的工匠好牛啊')
86 .width('100%')
87 Row() {
88 Text('10-21 · IP属地北京')
89 .fontSize(12)
90 .fontColor('#c3c4c5')
91 Row({ space: 4 }) {
92 Image($r('app.media.ic_public_heart'))
93 .width(14)
94 .aspectRatio(1)
95 .fillColor('#c3c4c5')
96 Text('100')
97 .fontSize(12)
98 .fontColor('#c3c4c5')
99 }
100 }
101 .width('100%')
102 .justifyContent(FlexAlign.SpaceBetween)
103 }
104 .layoutWeight(1)
105 .padding({ left: 10 })
106 }
107 .padding(15)
108 .alignItems(VerticalAlign.Top)
109 }
110}
ForEach
循环相同的回复容器1Column() {
2 Text('回复 100')
3 .width('100%')
4 .fontWeight(600)
5 ForEach([1, 2, 3, 4, 5, 6, 7], () => {
6 Row() {
7 Image($r('app.media.avatar'))
8 .width(32)
9 .aspectRatio(1)
10 .borderRadius(16)
11 Column({ space: 5 }) {
12 Text('周杰伦')
13 .width('100%')
14 .fontWeight(FontWeight.Bold)
15 .fontSize(15)
16 Text('大理石能雕刻出肌肉和皮肤的质感,那个年代的工匠好牛啊')
17 .width('100%')
18 Row() {
19 Text('10-21 · IP属地北京')
20 .fontSize(12)
21 .fontColor('#c3c4c5')
22 Row({ space: 4 }) {
23 Image($r('app.media.ic_public_heart'))
24 .width(14)
25 .aspectRatio(1)
26 .fillColor('#c3c4c5')
27 Text('100')
28 .fontSize(12)
29 .fontColor('#c3c4c5')
30 }
31 }
32 .width('100%')
33 .justifyContent(FlexAlign.SpaceBetween)
34 }
35 .layoutWeight(1)
36 .padding({ left: 10 })
37 }
38 .padding({ top: 15, bottom: 15 })
39 .alignItems(VerticalAlign.Top)
40 })
41
42}
43.padding(15)
class
定义好回复数据模型 ReplyItem
1export class ReplyItem {
2 id: number
3 avatar: string
4 author: string
5 content: string
6 time: string
7 area: string
8 likeNum: number
9 likeFlag?: boolean
10}
11
12export const replyList: ReplyItem[] = [
13 {
14 id: 1,
15 avatar: 'https://picx.zhimg.com/027729d02bdf060e24973c3726fea9da_l.jpg?source=06d4cd63',
16 author: '偏执狂-妄想家',
17 content: '更何况还分到一个摩洛哥[惊喜]',
18 time: '11-30',
19 area: '海南',
20 likeNum: 34
21 },
22 {
23 id: 2,
24 avatar: 'https://pic1.zhimg.com/v2-5a3f5190369ae59c12bee33abfe0c5cc_xl.jpg?source=32738c0c',
25 author: 'William',
26 content: '当年希腊可是把1:0发挥到极致了',
27 time: '11-29',
28 area: '北京',
29 likeNum: 58
30 },
31 {
32 id: 3,
33 avatar: 'https://picx.zhimg.com/v2-e6f4605c16e4378572a96dad7eaaf2b0_l.jpg?source=06d4cd63',
34 author: 'Andy Garcia',
35 content: '欧洲杯其实16队球队打正赛已经差不多,24队打正赛意味着正赛阶段在小组赛一样有弱队。',
36 time: '11-28',
37 area: '上海',
38 likeNum: 10
39 },
40 {
41 id: 4,
42 avatar: 'https://picx.zhimg.com/v2-53e7cf84228e26f419d924c2bf8d5d70_l.jpg?source=06d4cd63',
43 author: '正宗好鱼头',
44 content: '确实眼红啊,亚洲就没这种球队,让中国队刷',
45 time: '11-27',
46 area: '香港',
47 likeNum: 139
48 },
49 {
50 id: 5,
51 avatar: 'https://pic1.zhimg.com/v2-eeddfaae049df2a407ff37540894c8ce_l.jpg?source=06d4cd63',
52 author: '柱子哥',
53 content: '我是支持扩大的,亚洲杯欧洲杯扩到32队,世界杯扩到64队才是好的,世界上有超过200支队伍,欧洲区55支队伍,亚洲区47支队伍,即使如此也就六成出现率',
54 time: '11-27',
55 area: '旧金山',
56 likeNum: 29
57 },
58 {
59 id: 6,
60 avatar: 'https://picx.zhimg.com/v2-fab3da929232ae911e92bf8137d11f3a_l.jpg?source=06d4cd63',
61 author: '飞轩逸',
62 content: '禁止欧洲杯扩军之前,应该先禁止世界杯扩军,或者至少把亚洲名额一半给欧洲。',
63 time: '11-26',
64 area: '里约',
65 likeNum: 100
66 }
67]
1import { ReplyItem, replyList } from '../models'
2@Entry
3@Component
4struct Index {
5
6 @State
7 replyList: ReplyItem[] = replyList
8
9 build() {
10 Stack({ alignContent: Alignment.Bottom }) {
11 Column() {
12 Scroll() {
13 Column() {
14 // 导航
15 NavComp()
16 // 评论
17 CommentComp()
18 // 分割线
19 Divider()
20 .strokeWidth(8)
21 .color('#f5f5f5')
22 // 回复列表
23 Column() {
24 Text('回复 100')
25 .width('100%')
26 .fontWeight(600)
27 ForEach(
28 this.replyList,
29 (item: ReplyItem) => {
30 Row() {
31 Image(item.avatar)
32 .width(32)
33 .aspectRatio(1)
34 .borderRadius(16)
35 Column({ space: 5 }) {
36 Text(item.author)
37 .width('100%')
38 .fontWeight(FontWeight.Bold)
39 .fontSize(15)
40 Text(item.content)
41 .width('100%')
42 Row() {
43 Text(`${item.time} · IP属地${item.area}`)
44 .fontSize(12)
45 .fontColor('#c3c4c5')
46 Row({ space: 4 }) {
47 Image($r('app.media.ic_public_heart'))
48 .width(14)
49 .aspectRatio(1)
50 .fillColor('#c3c4c5')
51 Text(item.likeNum.toString())
52 .fontSize(12)
53 .fontColor('#c3c4c5')
54 }
55 }
56 .width('100%')
57 .justifyContent(FlexAlign.SpaceBetween)
58 }
59 .layoutWeight(1)
60 .padding({ left: 10 })
61 }
62 .padding({ top: 15, bottom: 15 })
63 .alignItems(VerticalAlign.Top)
64 },
65 // key 有默认你规则
66 // key 为了元素复用
67 // 如果没有写,会自动生成一个key,index_ + JSON.stringify(item),不建议不写
68 // (item: ReplyItem) => item.id.toString() 写一个ID做唯一标识,需要key也更新才能更新对应UI
69 // item => id + likeNum + likeFlag 把需要更新的字段合在一起当做key
70 // ({ id, likeNum, likeFlag }) => JSON.stringify({ id, likeNum, likeFlag })
71
72 // 学习了 @Observed @ObjectLink 这样也可以更新~
73 // (item: ReplyItem) => item.id.toString()
74 )
75
76 }
77 .padding(15)
78 }
79 .padding({ bottom: 50 })
80 }
81 }
82 .width('100%')
83 .height('100%')
84
85 Row({ space: 15 }){
86 TextInput({ placeholder: '回复~' })
87 .layoutWeight(1)
88 Text('发布')
89 .fontColor('#069')
90 }
91 .padding({ left: 15, right: 15 })
92 .width('100%')
93 .height(50)
94 .backgroundColor('#fff')
95 .border({ width: { top: 0.5 }, color: '#e4e4e4' })
96 }
97
98 }
99}
1onLike(item: ReplyItem) {
2 const reply = { ...item }
3 if (reply.likeFlag) {
4 reply.likeNum--
5 reply.likeFlag = false
6 promptAction.showToast({ message: '取消点赞' })
7 } else {
8 reply.likeNum++
9 reply.likeFlag = true
10 promptAction.showToast({ message: '点赞成功' })
11 }
12 const index = this.replyList.findIndex(rep => rep.id === reply.id)
13 this.replyList[index] = reply
14}
1Row({ space: 4 }) {
2 Image($r('app.media.heart'))
3 .width(14)
4 .height(14)
5 .fillColor(item.likeFlag ? '#ff6600' : '#c3c4c5')
6 .margin({ right: 4 })
7 Text(item.likeNum.toString())
8 .fontSize(14)
9 .fontColor(item.likeFlag ? '#ff6600' : '#c3c4c5')
10}
11.onClick(() => {
12 this.onLike(item)
13})
Resource
类型1export class ReplyItem {
2 id: number
3+ avatar: string | Resource
4 author: string
5 content: string
6 time: string
7 area: string
8 likeNum: number
9 likeFlag?: boolean
10}
1onReply () {
2 const reply: ReplyItem = {
3 id: Math.random(),
4 content: this.content,
5 author: 'Zhousg',
6 avatar: $r('app.media.avatar'),
7 time: '12-01',
8 likeNum: 0,
9 area: '北京'
10 }
11 this.replyList.unshift(reply)
12 this.content = ''
13 promptAction.showToast({ message: '回复成功' })
14}
1Row({ spcae: 15 }) {
2 TextInput({ placeholder: '回复~', text: this.content })
3 .placeholderColor('#c3c4c5')
4 .layoutWeight(1)
5 .onChange((value) => {
6 this.content = value
7 })
8 Text('发布')
9 .fontSize(14)
10 .fontColor('#09f')
11 .onClick(()=>{
12 this.onReply()
13 })
14}