评论页面小demo
效果展示
1.拆解组件,分层搭建
我们将整个评论页面拆解为三个组件,分别是头部导航,评论项,回复三个部分,然后统一在index界面导入
2.头部导航界面搭建
@Preview
@Component
struct HmNavBar {
// 属性:是可以被传递值进行替换的
build() {
Stack({ alignContent: Alignment.Start }) {
Row() {
Image($r('sys.media.ohos_ic_compnent_titlebar_back'))
.width('100%')
.aspectRatio(1)
}.width(24)
.padding(4)
.borderRadius(12)
.backgroundColor('#f5f7f8')
Text('评论回复')//先设置占页面的100% 然后在居中对齐
.width('100%')
.textAlign(TextAlign.Center)
}
.width('100%')
.padding(20)
}
}
export { HmNavBar }
- 上述UI界面搭建很简单,但要注意一点就是在有了返回按钮的情况下如何让评论回复在整行居中,我们可以采用Stack布局,或者是在右边也放置一个宽度为24的容器
3.评论项搭建
@Preview
@Component
struct HmReplay {
build() {
Row({ space: 8 }) {
Image($r('app.media.Cover1'))
.width(60)
.borderRadius(30)
Column({ space: 5 }) {
Text('遇到困难睡大觉')
.fontSize(18)
.fontWeight(700)
Text('你已经是一个成熟的评论了要学会自己打破零回复!')
.fontSize(18)
Row() {
Row() {
Text('10-21.IP 属地安徽')
.fontColor('#ffcfcfcf')
}
Row({ space: 5 }) {
Image($r('app.media.love'))
.width(24)
.aspectRatio(1)
Text('100')
.fontColor('#ffcfcfcf')
}
}.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
}.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
}.width('100%')
.padding(20)
.height(120)
.alignItems(VerticalAlign.Top)
}
}
export { HmReplay }
4.评论列表搭建
4.1.1分割线+回复 部分
Row() {
Text('回复 (7)')
}.padding(12)
.border({
width: {
top: 6
},
color: 'rgb(246,246,246)'
})
.width('100%')
4.1.2 评论列表
1.导入对应的数据结构
@State commentList: ReplyItemModel[] = [
new ReplyItemModel({
id: 1,
avatar: 'https://picx.zhimg.com/027729d02bdf060e24973c3726fea9da_l.jpg?source=06d4cd63',
author: '偏执狂-妄想家',
content: '更何况还分到一个摩洛哥[惊喜]',
time: '11-30',
area: '海南',
likeNum: 34,
likeFlag: false
}),
new ReplyItemModel({
id: 2,
avatar: 'https://pic1.zhimg.com/v2-5a3f5190369ae59c12bee33abfe0c5cc_xl.jpg?source=32738c0c',
author: 'William',
content: '当年希腊可是把1:0发挥到极致了',
time: '11-29',
area: '北京',
likeNum: 58,
likeFlag: false
}),
new ReplyItemModel({
id: 3,
avatar: 'https://picx.zhimg.com/v2-e6f4605c16e4378572a96dad7eaaf2b0_l.jpg?source=06d4cd63',
author: 'Andy Garcia',
content: '欧洲杯其实16队球队打正赛已经差不多,24队打正赛意味着正赛阶段在小组赛一样有弱队。',
time: '11-28',
area: '上海',
likeNum: 10,
likeFlag: false
}),
new ReplyItemModel({
id: 4,
avatar: 'https://picx.zhimg.com/v2-53e7cf84228e26f419d924c2bf8d5d70_l.jpg?source=06d4cd63',
author: '正宗好鱼头',
content: '确实眼红啊,亚洲就没这种球队,让中国队刷',
time: '11-27',
area: '香港',
likeNum: 139,
likeFlag: false
}),
new ReplyItemModel({
id: 5,
avatar: 'https://pic1.zhimg.com/v2-eeddfaae049df2a407ff37540894c8ce_l.jpg?source=06d4cd63',
author: '柱子哥',
content: '我是支持扩大的,亚洲杯欧洲杯扩到32队,世界杯扩到64队才是好的,世界上有超过200支队伍,欧洲区55支队伍,亚洲区47支队伍,即使如此也就六成出现率',
time: '11-27',
area: '旧金山',
likeNum: 29,
likeFlag: false
}),
new ReplyItemModel({
id: 6,
avatar: 'https://picx.zhimg.com/v2-fab3da929232ae911e92bf8137d11f3a_l.jpg?source=06d4cd63',
author: '飞轩逸',
content: '禁止欧洲杯扩军之前,应该先禁止世界杯扩军,或者至少把亚洲名额一半给欧洲。',
time: '11-26',
area: '里约',
likeNum: 100,
likeFlag: false
})
]
//先定义一个接口 然后可以使用接口转换工具转换成对应的类
export interface ReplyItem {
avatar: ResourceStr // 头像
author: string // 作者
id: number // 评论的id
content: string // 评论内容
time: string // 发表时间
area: string // 地区
likeNum: number // 点赞数量
likeFlag: boolean | null // 当前用户是否点过赞
}
export class ReplyItemModel implements ReplyItem {
avatar: ResourceStr = ''
author: string = ''
id: number = 0
content: string = ''
time: string = ''
area: string = ''
likeNum: number = 0
likeFlag: boolean | null = null
constructor(model: ReplyItem) {
this.avatar = model.avatar
this.author = model.author
this.id = model.id
this.content = model.content
this.time = model.time
this.area = model.area
this.likeNum = model.likeNum
this.likeFlag = model.likeFlag
}
}
4.搭建评论列表界面
List() {
ForEach(this.commentList, (item: ReplyItemModel) => {
ListItem() {
HmCommentItem({
commentInfo: item,
//但凡传函数吧必须用箭头函数包裹!
changeLike:(id:number)=>{
this.changeLike(id)
}
})
}
})
}.layoutWeight(1)
- 这里需要注意几点
- List里面必须放置
ListItem()
- 评论列表的高度可以给一个自适应,这样可以让列列表超出屏幕的高度时实现自适应
5.点赞逻辑的实现
实现任务:当我们点击爱心或者点赞的时候,点赞数量+1,爱心变位红色,当我们再次点击,点赞由原来的点赞变为取消点赞,爱心的颜色变为灰色,点赞的数量-1
代码层面分析:由于我们显示的数据是由主界面传到子界面的,所以我们需要在父界面定义一个方法,传递到子面去,在子界面去调用这个方法,所以在子界面需要有一个接受的方法
5.1.1 子界面接收的方法
changeLike:(id:number)=>void = ()=> {
}
5.1.2 点赞业务逻辑的实现
//点赞逻辑处理
changeLike(id:number){
//遍历数组 对commentlist数组中的每一个元素item进行迭代
const index = this.commentList.findIndex(item =>item.id === id)
//分支处理主评论和回复评论点赞状态
if(index < 0){//处理主评论
if(this.comment.likeFlag){//已经点赞
this.comment.likeNum -- //点赞数量--
}else {//未点赞
//点赞数量++
this.comment.likeNum++
}
this.comment.likeFlag = !this.comment.likeFlag
}else{//处理回复评论 找到回复列表中的某一个子评论
//返回第一次匹配元素的数组索引(0~N)
if(this.commentList[index].likeFlag){
this.commentList[index].likeNum--
}else {
this.commentList[index].likeNum++
}
this.commentList[index].likeFlag = !this.commentList[index].likeFlag
//@State修饰的数据只能监听到第一层或者本身 需要new一下在使用
this.commentList[index] = new ReplyItemModel(this.commentList[index])
// this.commentList.splice(index,1,this.commentList[index])
}
5.1.3父界面进行传值
6.发布界面的搭建
6.1.1 发布的逻辑
publishComment(content:string){
this.commentList.unshift(new ReplyItemModel({
// id: Math.random()的作用是生成一个基于随机数的临时唯一标识,但需注意:
// 数值范围: 0 ≤ N < 1 (浮点数)
// 格式示例: 0.1234567890123456
// 非整型: 生成16位小数的浮点数
//id不能重复
id: Math.random(),
avatar: 'https://picx.zhimg.com/027729d02bdf060e24973c3726fea9da_l.jpg?source=06d4cd63',
author: '遇到困难睡大觉',
content: '山外青山楼外楼,不回消息我记仇',
time: '11-30',
area: '安徽',
likeNum: 0,
likeFlag: false
}))
}
6.1.2 发布界面
@Preview
@Component
struct replay {
content: string = ''
@Link
conmentsnum: number
publish: (content: string) => void = () => {
}
build() {
Row() {
TextInput({ placeholder: '请留下你的评论~', text: $$this.content })
.layoutWeight(1)
.onSubmit(() => {
this.publish(this.content)
this.content = ''
})
Button('发布')
.onClick(() => {
this.publish(this.content)
this.content = ''
this.conmentsnum++
})
}
.width('100%')
.padding(12)
}
}
export { replay }