美文网首页
知乎案例

知乎案例

作者: 家乡的蝈蝈 | 来源:发表于2024-01-17 15:27 被阅读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
    }
    
    • 定义一个评论列表数据- 在Entry组件中
    @State commentList: ReplyItem[] = []
    
    • 在主页中渲染
    ForEach(this.commentList, (obj: ReplyItem) => {
          CommentItem({ item: obj })
      })
    
    • CommentItem组件接收传入数据
    @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)
      }
    }
    
    • 自定义NavBar组件
    @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
          }
        })
      }
    }
    
    • 由于数据过多,我们可以使用Scroll 组件来包裹整个的Column,注意Column的高度100%要去掉
    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、底部回复按钮

    • 底部按钮固定定位的模式可以采用Stack栈布局,特点是后面的元素级别高于前面元素,会贴在上一个元素上,并且使用alignContent属性可以直接让 后加的元素去到想去的位置
    // 回复评论组件
    @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, 传递给CommentItem
     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、实现回复

    • 定义State状态,双向绑定input
    // 回复评论组件
    @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)
    

    相关文章

      网友评论

          本文标题:知乎案例

          本文链接:https://www.haomeiwen.com/subject/bhszndtx.html