知乎案例

2024-01-17  本文已影响0人  家乡的蝈蝈

1.1、定义一个评论列表

export class ReplyItem {
  // next版本在定义属性时需要初始值
  id: number = 0
  avatar: string | Resource = "" // 联合类型,可以是类型中的一个
  author: string = ""
  content: string = ""
  time: string = ""
  area: string = ""
  likeNum: number = 0
  likeFlag?: boolean = false
}
@State commentList: ReplyItem[] = []
ForEach(this.commentList, (obj: ReplyItem) => {
      CommentItem({ item: obj })
  })
@Component
struct  CommentItem {
  // 父组件给子组件传参数,在子组件中定义一个属性即可,非响应式数据
  // item:ReplyItem = {id:0,avatar:"",....}
  // 默认public属性
  public item:Partial<ReplyItem> = {}   //Partial是个泛型工具,把类型中的所有属性都变成可选;等价于在类中的属性 id ?: number = 0
  build() {
    Row() {
      Image(this.item.avatar)
        .width(32)
        .aspectRatio(1)
        .borderRadius(16)
      Column({space:10}) {
        Text(this.item.author)
          .fontWeight(FontWeight.Bold)
        Text(this.item.content)
          .fontSize(16)
          .fontColor("#565656")
          .lineHeight(20)
        Row() {
          Text(`${this.item.time} .ip属地${this.item.area}`).fontSize(12).fontColor("#c3c4c5")
          Row() {
            Image($r("app.media.favorite_block"))
              .width(12)
              .aspectRatio(1)
              .fillColor('#c3c4c5')
              .margin({
                right:5
              })
            Text(this.item.likeNum?.toString()) // ?表示可选,如果取不到,后面的不执行
              .fontSize(12)
              .fontColor('#c3c4c5')
          }
        }.justifyContent(FlexAlign.SpaceBetween)
          .width("100%")
      }
        .alignItems(HorizontalAlign.Start)
        .layoutWeight(1)
        .margin({left:10})
    }.padding(15)
    .alignItems(VerticalAlign.Top)
  }
}
@Component
struct NavBar {
  build() {
    Row() {
      Row() {
        Image($r("app.media.ic_public_arrow_left"))
          .fillColor("#848484")
          .width(16)
          .height(16)
      }
      .justifyContent(FlexAlign.Center)
      .width(24)
      .aspectRatio(1)
      .backgroundColor("#f5f5f5")
      .borderRadius(12)
      .margin({
        left:15
      })
      Text("评论回复")
        .layoutWeight(1)
        .textAlign(TextAlign.Center)
        .fontSize(18)
        .padding({
          right:39
        })
    }
    .height(40)
    .border({
      color:"#f4f4f4",
      width:{
        bottom:0.5
      }
    })
  }
}
build() {
    Scroll() { //只能放置一个子组件
      Column() {
        NavBar()
        CommentItem({
          item: {
            id: 1,
            avatar: $r('app.media.icon'),
            author: '周杰伦',
            content: '意大利拌面应该使用42号钢筋混凝土再加上量子力学缠绕最后通过不畏浮云遮望眼',
            time: '11-30',
            area: '海南',
            likeNum: 100
          }
        })
        // 分割线
        Divider()
          .strokeWidth(6)
          .color("#f4f4f4")
        Row() {
          Text("回复7")
            .width('100%')
            .fontWeight(FontWeight.Bold)
        }.height(40).padding({left:20})
        ForEach(this.commentList,(item:ReplyItem) => {
          // CommentItem({item:item}) // 在实例化子组件时,给子组件的属性赋值
          CommentItem({item}) // 在es6中,当属性和值相同时可以简写
        })
      }
    }
  }

1.2、底部回复按钮

// 回复评论组件
@Component
struct ReplyInput {
  build() {
    Row() {
      TextInput({placeholder:'回复~'})
        .layoutWeight(1) // 占据除'发布'按钮的所有空间
        .backgroundColor('#4f5f6')
      Text('发布')
        .fontColor('#6ecff6')
        .margin({
          left:10
        })
    }
      .border({
        color:'#f4f5f6',
        width: {
          top:1
        }
      })
      .height(50)
      .backgroundColor(Color.White)
      .width('100%')
      .padding({
        left:10,
        right:10
      })
  }
}

1.2.1、使用Stack包裹整个的主页组件,并设置alignContent为 Alignment.Bottom

image.png

  使用Stack包裹整个的主页组件,并设置alignContent为 Alignment.Bottom,Scroll被盖住的地方 需要加个padding

1.3、实现点赞

使用函数传递的方式

 changeLike(obj:ReplyItem) {
    if (obj.likeFlag) {
      obj.likeFlag = false
      obj.likeNum--
    } else {
      obj.likeFlag = true
      obj.likeNum++
    }
    // State数据只能监听到第一层的变化
    // 怎么让数据具备驱动型
    const index = this.commentList.findIndex(item => item.id === obj.id)
    // this.commentList[index] = {...obj} // 禁用延展运算符,next不支持
    this.commentList.splice(index,1,obj) // splice表示从哪个位置删除,删除几个,使用obj来替换
  } // 由于不一代延展运算符只能用于数组,对于对象不再支持,所以采用对数组采用替换更新的方式来进行响应式更新
  ForEach(this.commentList,(item:ReplyItem) => {
            // CommentItem({item:item}) // 在实例化子组件时,给子组件的属性赋值
            // CommentItem({item}) // 在es6中,当属性和值相同时可以简写
            // CommentItem({item,changLike:this.changeLike.bind(this)}) // 写法错误,
            CommentItem({item, changeLike:(obj:ReplyItem) => {
              this.changeLike(obj)
            }})
          })
struct  CommentItem {
  changeLike:(params:ReplyItem) => void = () => {}
  build() {
    Row() {
            Text(this.item.likeNum?.toString()) // ?表示可选,如果取不到,后面的不执行
              .fontSize(12)
              .fontColor('#c3c4c5')
          }
            .onClick(() => {
              // 点赞
              // this.item属性都是可选的
              this.changeLike(this.item as ReplyItem); // as 为类型断言
            })
        }.justifyContent(FlexAlign.SpaceBetween)
          .width("100%")
      }
}

1.4、实现回复

// 回复评论组件
@Component
struct ReplyInput {
  @State commentStr:string = ""
  // 在回复组件中声明了一个变量add,它的类型是函数,给了一个初始值是函数
  add: (item:ReplyItem) => void = () => {}
 TextInput({placeholder:'回复~', text:this.commentStr})
        .layoutWeight(1) // 占据除'发布'按钮的所有空间
        .backgroundColor('#4f5f6')
        .onChange((value) => {
          this.commentStr = value
        })
 addComment(item:ReplyItem) {
    this.commentList.unshift(item) // 在数组的头部添加元素
  }
 ReplyInput({add: (item:ReplyItem) => {
        this.addComment(item)
      }})
 Text('发布')
        .fontColor('#6ecff6')
        .margin({
          left:10
        })
        .onClick(() => {
          // AlertDialog.show({message:this.commentStr}) // alert弹窗
          // 告诉父组件,我拿到值,你给我个方法我来调一下
          // 要传入一个新的评论对象
          if (this.commentStr !== "") {
            let obj:ReplyItem = {
              id: Date.now(),
              content: this.commentStr,
              avatar: $r("app.media.icon"),
              likeNum:0,
              likeFlag:false,
              author:"高大的绿地",
              time:`${ new Date().getMonth() + 1}-${ new Date().getDate()}`,
              area:'上海'
            }
            this.add(obj)
            this.commentStr = ""
          }
        })

1.5、foreach中key的疑问

大家发现没有,我们没有给key,我们的id是唯一的,给个id试试
(item: ReplyItem) => item.id.toString()

● 因为ForEach中更新UI时,key也必须连带更新,否则它会认为你没有发生变化,不去触发UI更新, 怎么办
● 直接把id-点赞-点赞数一起作为key就可以了

(item: ReplyItem) => JSON.stringify({ id: item.id, flag: item.likeFlag, num: item.likeNum })
(item: ReplyItem) => JSON.stringify(item)
上一篇下一篇

猜你喜欢

热点阅读