仿掘金评论区组件开发(一) | 青训营笔记

291 阅读4分钟

这是我参与「第四届青训营 」笔记创作活动的第3天。

项目需求分析

  1. 在发布评论区域,发布评论的的输入框在获取焦点时,下方弹出发布评论的按钮等图标,点击评论组件的其他区域时,输入框失去焦点,图标隐藏。当输入框中存在内容时,点击评论组件的其他区域时,输入框不失去焦点,图标不隐藏。
  2. 在发布评论区域,点击发布评论的按钮后,在全部评论区域创建出一个一级评论。当输入框中的内容为空时,弹出弹窗,提示输入框不能为空。
  3. 在全部评论区域,渲染出数据中存储的所有评论,并根据发布评论区域发布的新的评论进行实时渲染到界面。
  4. 发布的评论具有回复,点赞的功能,回复的评论也具有回复,点赞的功能。将发布评论区域发布的评论定为一级评论,回复的评论定为二级评论,一级二级都具有回复功能,点击回复图标时,在该一级评论的二级评论的最下方显示评论输入框等。

项目相关数据结构

import imageone from "@/assets/images/1.jpg";
import imagetwo from "@/assets/images/2.jpg";
import imagethree from "@/assets/images/3.jpg";
import imagefour from "@/assets/images/4.jpg";

data() {
    return {
      commentData: {
        //我的评论
        btnShow: false,//是否显示发布评论按钮
        index: '0', //存储上一个回复评论的输入框的位置
        replyComment: '',//暂时存储发布评论的内容
        myName: '张三', //用户姓名
        myHeader: imageone, //用户头像
        myId: 19870621, //用户ID
        to: '', //被回复人名称
        toId: -1, //被回复人ID
        //评论区
        comments: [
          {
            //一级评论信息
            name: '张三', //用户姓名
            id: 19870621, //用户ID
            headImg: imageone, //用户头像
            comment: '1212122', //评论内容
            time: '2022年8月14日 8:43', //评论事件
            commentNum: 2, //回复数
            like: 15, //点赞数
            inputShow: false, //是否显示回复框
            reply: [
              {
                //二级评论
                from: '王二',//评论人名称
                fromId: 19891221, //评论ID
                headImg: imagetwo, //评论头像
                to: '张三',//存储被回复人名称
                toId: 19870621, //存储被回复人id
                comment: '21312313123', //回复信息
                time: '2022年8月14日 8:43', //评论时间
                commentNum: 1, //回复数
                like: 15, //点赞
                inputShow: false //是否显示回复框
              },
              {
                from: '李四',
                fromId: 1123,
                headImg: imagethree,
                to: '张三',
                toId: 19870621,
                comment: '3131312123',
                time: '2022年8月14日 8:43',
                commentNum: 0,
                like: 5,
                inputShow: false

              }
            ]
          },
          {
            name: '王二',
            id: 19891221,
            headImg: imagetwo,
            comment: '213131313',
            time: '2022年8月14日 8:43',
            commentNum: 1,
            like: 5,
            inputShow: false,
            reply: [
              {
                from: '张三',
                fromId: 19870621,
                headImg: imageone,
                to: '麻子',
                toId: 19891221,
                comment: '23123122113',
                time: '2022年8月14日 8:43',
                commentNum: 25,
                like: 5,
                inputShow: false

              }
            ]
          },
          {
            name: '麻子',
            id: 20190830,
            headImg: imagefour,
            comment: '12312312312',
            time: '2022年8月14日 8:43',
            commentNum: 0,
            like: 5,
            inputShow: false,
            reply: []
          },
        ]
      }
    }
  },

设计及实现思路

评论发布区域

设计思路

  1. 为充当输入框的div标签绑定一个 @focus ,@focus 是元素获取焦点时所触发的事件,当点击输入框时,输入框获取焦点,执行 @focus 绑定的方法,该方法执行后显示发布评论的按钮,并让输入框的边框变为蓝色,作为输入框获得焦点的提示。
  2. 为充当输入框的div标签绑定一个@input , @input适用于实时查询,每输入一个字符都会触发该事件 ,在输入框中输入内容时,执行 @input 绑定的方法,将输入框中的内容存储起来,当点击发布按钮时,根据存储的内容,进行一级评论的创建。
  3. 评论的创建实际上就是将新创建的评论的信息存储到评论信息的数组中,再渲染出来,具体内容见实现思路。

实现思路

  1. 编写前端页面

    <div class="my-reply" >
      <div class="header">
    
        <span class="header-title">评论</span>
      </div>
    
      <div class="content">
    
        <div class="avatar-box">
          <img :src="commentThis.myHeader" alt="" class="avatar">
        </div>
        <div class="form-box">
    
          <div class="auth-card">
            <div class="input-box">
              <div
                  class="rich-input empty"
                  contenteditable="true"
                  placeholder="请输入内容"
                  spellcheck="false"
                  disabled="disabled"
                  @focus="showReplyBtn"
                  @input="onDivInput($event)"
              ></div>
            </div>
          </div>
    
          <div class="action-box" v-show="commentThis.btnShow">
            <div class="emoji-container emoji-btn">
              <div class="emoji-box">
                <i class="iconfont icon-shoucang icon"><span>表情</span></i>
              </div>
            </div>
            <div class="image-btn">
              <i class="iconfont icon-duihuaxinxitianchong icon"><span>图片</span></i>
            </div>
            <div class="submit">
              <span>Ctrl + Enter</span>
              <!--             disabled 赋予该属性时该元素将变得不可交互-->
              <button class="submit-btn"  @click="sendComment">发布评论</button>
            </div>
          </div>
        </div>
      </div>
    </div>
    

评论区域的前端页面主要分为三部分,发布人头像,输入框以及发布评论的按钮。

  1. 发布人头像的图片资源直接通过 data 中的数据 myHeader 传入,
  2. 作为输入框的div 的各种属性的作用如下:

                                         spellcheck 属性规定是否对元素内容进行拼写检查。
placeholder 实现类似输入框默认显示的效果
@focus是元素获取焦点时所触发的事件
@blur是元素失去焦点时所触发的事件
@input适用于实时查询,每输入一个字符都会触发该事件
contenteditable 是一个枚举属性,表示元素是否可被用户编辑。                                                          带 有contenteditable属性的div 可以作为输入框

    3. 发布评论按钮的显隐根据 data 中的数据 btnShow ,通过 v-if 判断是否显示。

2. 实现评论区域功能相关的 methods

 //  1.评论区域输入框获取焦点后,显示发布按钮等
    showReplyBtn(){
      this.commentThis.btnShow = true
      var richInput = document.getElementsByClassName('input-box')[0];
      richInput.style.border = "2px solid blue"
    },

@focus 是元素获取焦点时所触发的事件,@focus 绑定的方法即为该方法,当点击输入框时,输入框获得焦点,触发 showReplyBtn()方法,将判断是否显示发布评论按钮的数据 btnShow 改为 true,并获取到输入框,将输入框的边框颜色改为蓝色。

 onDivInput: function (e) {
      //e.target就是指发生事件的对象,而this.commentThis则是谁调用它则指向谁。
      //e.target.innerText 获取触发onDivInput事件的对象中的内容
      this.commentThis.replyComment = e.target.innerText;
    },

@input适用于实时查询,每输入一个字符都会触发该事件, @input 绑定的方法即为该方法,当输入框输入内容时,触发 onDivInput: function (e) 方法,根据传入的数据,获取到输入框中的内容,将获取到的数据存入data 中的数据 replyComment 中,等到创建一级评论的时候调用。

sendComment(){
      if (!this.commentThis.replyComment) {
        this.commentThis.$message({
          showClose: true,
          type: 'warning',
          message: '评论不能为空'
        })
      }
      else {
        let a = {}
        let input = document.getElementsByClassName('rich-input')[0]
        let timeNow = new Date().getTime();
        let time = this.dateStr(timeNow);
        a.name = this.commentThis.myName
        a.comment = this.commentThis.replyComment
        a.headImg = this.commentThis.myHeader
        a.time = time
        a.commentNum = 0
        a.like = 0
        //添加,解决新发布的评论,无法回复
        a.reply = []
        this.commentThis.comments.push(a)
        this.commentThis.replyComment = ''
        input.innerHTML = '';
      }
    },

为发布评论按钮绑定一个点击事件@click,点击发布评论的按钮后,触发sendComment()方法,首先判断数据 replyComment 中是否有内容,若没有,证明没有输入评论内容,弹窗提示输入框不能为空,若有,则证明需要创建一级评论,创建一个对象,将用户的各种信息复制到对象中,再将对象存入到data中专门存储评论信息的数组 comments 中。