HarmonyOS NEXT星河版之实战知乎App评论功能

一、目标完成页面

在这里插入图片描述

二、实战

2.1 定义数据

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 {
  id: number = 0
  avatar: string | Resource = ''
  author: string = ''
  content: string = ''
  time: string = ''
  area: string = ''
  likeNum: number = 0
  likeFlag: boolean | null = null

  constructor(model: ReplyItem) {
    this.id = model.id
    this.avatar = model.avatar
    this.author = model.author
    this.content = model.content
    this.time = model.time
    this.area = model.area
    this.likeNum = model.likeNum
    this.likeFlag = model.likeFlag
  }
}
export enum CommentType {
  MAIN,// 顶部
  NORMAL// 普通
}

2.2 mock数据

export const mockReplyList: 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
  })
]

2.3 封装顶部标题栏

在这里插入图片描述
代码:

@Component
struct ZhiHuNavBar {
  title: string = '标题'

  build() {
    Stack({ alignContent: Alignment.Start }) {
      Row() {
        Image($r('app.media.ic_left_arrow'))
          .width(14)
          .margin({ left: 3 })
      }
      .height(30)
      .width(30)
      .borderRadius(15)
      .justifyContent(FlexAlign.Center)
      .zIndex(999)
      .backgroundColor(Color.Gray)

      Text(this.title)
        .width('100%')
        .textAlign(TextAlign.Center)
    }
    .width('100%')
    .height(50)
    .padding({ left: 20, right: 20 })
    .backgroundColor(Color.White)
    .border({
      color: '#e5e5e5',
      width: {
        bottom: 1
      }
    })
  }
}

export { ZhiHuNavBar }

2.4 封装评论Item

在这里插入图片描述
代码:

import { ReplyItem, ReplyItemModel } from '../models'

@Preview
@Component
struct ZhiHuComponentItem {
  @Prop item: ReplyItemModel = new ReplyItemModel({} as ReplyItem)
  changeLike: () => void = () => {
  }

  build() {
    Row({ space: 10 }) {
      Image(this.item.avatar)
        .width(28)
        .borderRadius(14)
      Column() {
        Text(this.item.author)
          .fontSize(16)
          .fontWeight(FontWeight.Bold)
        Text(this.item.content)
          .fontSize(13)
          .maxLines(3)
          .lineHeight(18)
          .textOverflow({ overflow: TextOverflow.Ellipsis })
          .margin({ top: 8 })
          .padding({ bottom: 12 })
        Row({ space: 5 }) {
          Text(`${this.item.time} IP归属地${this.item.area}`)
            .fontSize(12)
            .fontColor('#999999')
          Row({ space: 3 }) {
            Image($r('app.media.ic_like'))
              .width(12)
              .fillColor(this.item.likeFlag ? Color.Red : Color.Black)
            Text(this.item.likeNum.toString())
              .fontSize(12)
              .fontColor('#999999')
          }
          .onClick(() => {
            this.changeLike()
          })
        }
        .width('100%')
        .justifyContent(FlexAlign.SpaceBetween)
      }
      .layoutWeight(1)
      .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .alignItems(VerticalAlign.Top)
    .padding(20)
  }
}

export { ZhiHuComponentItem }

2.5 定义回复组件

在这里插入图片描述
代码:

@Component
struct ZhiHuReply {
  @State content: string = ''
  onSubmitContent: (content: string) => void = () => {
  }

  submitContent() {
    if (this.content) {
      this.onSubmitContent(this.content)
      this.content = ''
    }
  }

  build() {
    Row({ space: 15 }) {
      TextInput({ placeholder: '请输入', text: $$this.content })
        .layoutWeight(1)
        .height(40)
        .onSubmit(() => {
          this.submitContent()
        })
      Button('发布')
        .onClick(() => {
          this.submitContent()
        })
    }
    .height(60)
    .width('100%')
    .padding({
      left: 20,
      right: 20
    })
    .border({
      color: '#e5e5e5',
      width: {
        top: 1
      }
    })
  }
}

export { ZhiHuReply }

2.6 主页面

import { ZhiHuComponentItem, ZhiHuNavBar, ZhiHuReply } from './components'
import { mockReplyList, ReplyItemModel, ReplyItem, CommentType } from './models'

@Entry
@Component
struct ZhiHuDemoPage {
  @State commentList: ReplyItem[] = mockReplyList
  @State mainComment: ReplyItemModel = new ReplyItemModel({
    id: 999,
    author: '周杰伦',
    avatar: $r("app.media.zfb_pro_pic3"),
    likeNum: 10,
    likeFlag: false,
    time: '03-02',
    area: '北京',
    content: '人到了一定的年龄新陈代谢就慢了,吃了胖不吃瘦了皱纹就多,要靠锻炼 '
  })
  private scroller: Scroller = new Scroller()

  /**
   * 点赞or取消点赞
   * @param item
   * @param type
   */
  doChangeLike(item: ReplyItemModel, type?: CommentType) {
    if (item.likeFlag) {
      item.likeNum--
    } else {
      item.likeNum++
    }
    item.likeFlag = !item.likeFlag
    if (type === CommentType.MAIN) {
      this.mainComment = item
    } else {
      const index = this.commentList.findIndex(obj => obj.id === item.id)
      // this.commentList[index] = new ReplyItemModel(item)
      this.commentList.splice(index, 1, item)
    }
  }

  /**
   * 提交评论
   * @param content
   */
  onSubmitContent(content: string) {
    const replyItem = new ReplyItemModel({
      id: Math.random(),
      author: '李佳琦',
      avatar: $r("app.media.zfb_pro_pic3"),
      likeNum: 0,
      likeFlag: false,
      time: `${(new Date().getMonth() + 1).toString().padStart(2, '0')}-${new Date().getDate()}`,
      area: '上海',
      content
    })
    this.commentList.unshift(replyItem)
    this.scroller.scrollEdge(Edge.Top)
  }

  build() {
    Column() {
      // 标题栏
      ZhiHuNavBar({ title: '评论' })
      // 主评论
      ZhiHuComponentItem({
        item: this.mainComment,
        changeLike: () => {
          this.doChangeLike(this.mainComment, CommentType.MAIN)
        }
      })
      // 分割线
      Divider().strokeWidth(6)
      // 评论数
      Row() {
        Text(`评论数${this.commentList.length}`)
      }
      .width('100%')
      .height(50)
      .padding({ left: 20 })
      .border({
        color: '#f3f4f5',
        width: {
          bottom: 1
        }
      })
      // 普通评论列表
      List({ scroller: this.scroller }) {
        ForEach(this.commentList, (item: ReplyItemModel) => {
          ListItem() {
            ZhiHuComponentItem({
              item,
              changeLike: () => {
                this.doChangeLike(item)
              }
            })
          }
        })
      }
      .layoutWeight(1)

      // 回复模块
      ZhiHuReply({
        onSubmitContent: (content: string) => {
          this.onSubmitContent(content)
        }
      })

    }
    .height('100%')
    .width('100%')
    .backgroundColor(Color.White)
  }
}

三、小结

  • 组件拆分及布局
  • 父子组件数据传递
  • 父子组件事件传递
  • 数据更新及回调
  • 37
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值