先看效果
知识点
组件封装
// 使用@Component修饰某一个组件,在其中实现`build`方法即可
@Component
struct BannerView {
@State content: string = "" // 定义变量,可以是初始化的时候传入对应的值
@State item: Partial<ReplyItem> = {} // 也可以传入对象
onClicked: (item: ReplyItem) => void = () => { // 定义一个函数来接受传入的函数,在自定义类中调用的时候可以回传事件给父类
}
build() {
}
}
初始化封装的组件
headerReply: ReplyItem = {}
onClicked(item: ReplyItem) {
// 当子类调用onClicked的时候会父类会执行此代码块
}
// 初始化的时候可以传入对应的值
BannerView({content:"文字内容",item:this.headerReply, onClicked: (item: ReplyItem) => {
this.onClicked(item)
} })
!!!特别说明
BannerView({content:"文字内容",item:this.headerReply, onClicked: this.onClicked(item))
这是错误的写法,此时this
指向的是子类,而非父类。
循环渲染
ForEach
事件传递
子组件和父组件同时定义对应的函数,在父组件初始化子组件的时候,在对应的函数参数的闭包中调用父组件的函数。
@Component
struct Test {
onClicked(str: string) {
console.log(str)
}
build() {
SubTest({ onClicked: (str: string) => {
this.onClicked(str)
} })
}
}
@Component
struct SubTest {
onClicked: (str: string) => void = () => {
}
build() {
Text("Hello Word").onClick(() => {
this.onClicked("Hello Word")
})
}
}
完整代码
// @ts-nocheck
@Entry
@Component
struct ZhihuComment {
@State commentList: ReplyItem[] = [
{
id: 1,
avatar: $r("app.media.avatar_1"),
author: "路人甲",
content: "你是我的小苹果,怎么爱你都不嫌多,啦啦啦啦啦~",
time: "10-11",
area: "天津",
likeNum: 12,
},
{
"id": 2,
"avatar": $r("app.media.avatar_2"),
"author": "随风而逝",
"content": "窗外的风景如诗如画,心随风飘荡在无边的蓝天之下。",
"time": "09-25",
"area": "广州",
"likeNum": 8
},
{
"id": 3,
"avatar": $r("app.media.avatar_3"),
"author": "梦中人",
"content": "在星光的照耀下,我仿佛能听见远方的微笑声。",
"time": "08-14",
"area": "成都",
"likeNum": 15
},
{
"id": 4,
"avatar": $r("app.media.avatar_4"),
"author": "雨巷行人",
"content": "雨丝轻柔地落下,街角的小店散发着咖啡的香气。",
"time": "07-02",
"area": "上海",
"likeNum": 10
},
{
"id": 5,
"avatar": $r("app.media.avatar_5"),
"author": "星辰之梦",
"content": "璀璨的星空映照着心灵深处的梦想,让人感叹生命的辽阔。",
"time": "06-19",
"area": "北京",
"likeNum": 20
},
{
"id": 6,
"avatar": $r("app.media.avatar_6"),
"author": "山水之恋",
"content": "山谷间传来悠扬的笛声,溪水潺潺流过,宛如仙境般的美妙。",
"time": "05-08",
"area": "南京",
"likeNum": 18
},
{
"id": 7,
"avatar": $r("app.media.avatar_7"),
"author": "烟雨江南",
"content": "江南的烟雨中,一缕忧伤被吹散,留下的是宁静和安详。",
"time": "04-17",
"area": "杭州",
"likeNum": 13
},
{
"id": 8,
"avatar": $r("app.media.avatar_8"),
"author": "岁月流转",
"content": "时光荏苒,岁月如梭,每个瞬间都是生命的珍贵记忆。",
"time": "03-29",
"area": "重庆",
"likeNum": 25
},
{
"id": 9,
"avatar": $r("app.media.avatar_9"),
"author": "橙黄橘绿",
"content": "阳光洒在稻田上,金黄的颜色让人感受到大自然的丰饶。",
"time": "02-11",
"area": "西安",
"likeNum": 16
},
{
"id": 10,
"avatar": $r("app.media.avatar_10"),
"author": "梧桐树下",
"content": "悠扬的风铃声在梧桐树下回荡,时光仿佛静止。",
"time": "01-05",
"area": "深圳",
"likeNum": 22
},
{
"id": 11,
"avatar": $r("app.media.avatar_11"),
"author": "静谧夜空",
"content": "夜幕降临,星光点点,宁静的夜空中弥漫着宝石般的梦幻。",
"time": "12-23",
"area": "武汉",
"likeNum": 11
},
{
"id": 12,
"avatar": $r("app.media.avatar_12"),
"author": "流年如梦",
"content": "沿着小巷漫步,古老的石板路记录着岁月的故事。",
"time": "11-08",
"area": "南昌",
"likeNum": 17
},
{
"id": 13,
"avatar": $r("app.media.avatar_13"),
"author": "琴音轻扬",
"content": "琴音轻扬,悠扬的旋律在空气中飘荡,引人陶醉。",
"time": "10-30",
"area": "福州",
"likeNum": 14
},
{
"id": 14,
"avatar": $r("app.media.avatar_14"),
"author": "画中人生",
"content": "画笔在画布上舞动,勾勒出生命中丰富的色彩。",
"time": "09-15",
"area": "济南",
"likeNum": 19
},
{
"id": 15,
"avatar": $r("app.media.avatar_15"),
"author": "茶香袅袅",
"content": "茶香袅袅,温暖的气息在宁静的午后悠然散开。",
"time": "08-03",
"area": "青岛",
"likeNum": 9
},
{
"id": 16,
"avatar": $r("app.media.avatar_16"),
"author": "晨曦微光",
"content": "晨曦微光洒在大地上,新的一天开始了。",
"time": "07-21",
"area": "沈阳",
"likeNum": 21
},
{
"id": 17,
"avatar": $r("app.media.avatar_17"),
"author": "翩翩起舞",
"content": "花瓣飘落,舞动的姿态如梦如幻,轻盈而美丽。",
"time": "06-06",
"area": "长沙",
"likeNum": 23
},
{
"id": 18,
"avatar": $r("app.media.avatar_18"),
"author": "海浪之梦",
"content": "海浪拍打着沙滩,梦幻般的音乐在耳畔回荡。",
"time": "05-27",
"area": "厦门",
"likeNum": 16
},
{
"id": 19,
"avatar": $r("app.media.avatar_19"),
"author": "空山新雨",
"content": "空山新雨后,清新的空气中弥漫着花香和泥土的味道。",
"time": "04-02",
"area": "南宁",
"likeNum": 12
},
{
"id": 20,
"avatar": $r("app.media.avatar_20"),
"author": "古城回忆",
"content": "古城的记忆在石板路上延续,岁月的痕迹如诗如画。",
"time": "03-14",
"area": "哈尔滨",
"likeNum": 14
}
]
headerReply: ReplyItem = {
id: 10086,
avatar: $r("app.media.header_icon"),
author: "小苹果",
content: "你是我的小苹果,怎么爱你都不嫌多,啦啦啦啦啦~",
time: "10-11",
area: "天津",
likeNum: 120,
}
onClickLike(item: ReplyItem) {
console.log(`点了 id=${item.id}`)
if (item.id === 10086) {
this.headerReply = { ...item }
} else {
const clickIndex = this.commentList.findIndex(i => i.id === item.id)
this.commentList.splice(clickIndex, 1, item)
}
}
onReply(item: ReplyItem) {
this.commentList.unshift(item)
}
build() {
Stack({ alignContent: Alignment.Bottom }) {
Scroll() {
Column({ space: 20 }) {
BannerView()
CommentView({ item: this.headerReply, onClickLike: (cellItem: ReplyItem) => {
this.onClickLike(cellItem)
} })
Divider()
.strokeWidth(6)
.color("#f4f4f4")
Row() {
Text(`回复${this.commentList.length}`)
.width("100%")
.fontWeight(FontWeight.Bold)
.margin({ left: 8 })
}
ForEach(this.commentList, (item: ReplyItem, index: number) => {
CommentView({ item: item, onClickLike: (cellItem: ReplyItem) => {
this.onClickLike(cellItem)
} })
})
}
}
.padding({ bottom: 54 })
CommentInputView({ onReply: (item: ReplyItem) => {
this.onReply(item)
} })
.width("100%")
.height(54)
.backgroundColor(Color.White)
.border({
color: "#f4f5f6",
width: { top: 1 }
})
}
}
}
@Component
struct BannerView {
build() {
Row() {
Image($r("app.media.back_icon"))
.height(24)
.aspectRatio(1)
.backgroundColor("#F5F5F5")
.borderRadius(12)
.margin({ left: 8 })
Text("评论回复")
.layoutWeight(1)
.textAlign(TextAlign.Center)
.margin({ right: 39 })
}
.height(50)
.border({
color: "#f4f4f4",
width: {
bottom: 0.5
}
})
}
}
@Component
struct CommentView {
@State item: Partial<ReplyItem> = {}
onClickLike: (item: ReplyItem) => void = () => {
}
build() {
Row() {
Image(this.item.avatar)
.width(44)
.aspectRatio(1)
.borderRadius(22)
.margin({ left: 8 })
Column({ space: 10 }) {
Text(this.item.author)
.fontSize(20)
.fontStyle(FontWeight.Bold)
Text(this.item.content)
Row() {
Text(`${this.item.time} .IP属地${this.item.area}`)
.fontColor("#c3c4c5")
Row({ space: 8 }) {
Image($r("app.media.hart_icon"))
.width(20)
.fillColor(this.item.likeFlag ? "red" : "#c3c4c5")
Text(this.item.likeNum.toString())
.fontColor("#c3c4c5")
}.onClick(() => {
if (this.item.likeFlag) {
this.item.likeNum--
} else {
this.item.likeNum++
}
this.item.likeFlag = !this.item.likeFlag
this.onClickLike(this.item as ReplyItem)
})
}
.width("100%")
.justifyContent(FlexAlign.SpaceBetween)
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
.margin({ left: 8, right: 8 })
}
.alignItems(HorizontalAlign.Start)
}
}
@Component
struct CommentInputView {
@State content: string = ""
onReply: (item: ReplyItem) => void = () => {
}
build() {
Row() {
TextInput({ placeholder: "请输入回复~", text: this.content })
.layoutWeight(1)
.backgroundColor("#f4f5f6")
.onChange((value) => {
this.content = value
})
Button("回复")
.margin({ left: 10, right: 10 })
.height(44)
.onClick(() => {
if (this.content === "") {
AlertDialog.show({ message: "不能回复为空" })
} else {
const item: ReplyItem = {
id: Date.now(),
avatar: $r("app.media.header_icon"),
author: "哲大爷",
content: this.content,
time: `${new Date().getMonth() + 1}-${new Date().getDate()}`,
area: "北京",
likeNum: 0,
likeFlag: false
}
this.onReply(item)
this.content = ""
}
})
}
.height(50)
.width("100%")
.margin({ left: 8, right: 8 })
}
}
export class ReplyItem {
id: number = 0
avatar: string | Resource = ""
author: string = ""
content: string = ""
time: string = ""
area: string = ""
likeNum: number = 0
likeFlag?: boolean = false
}