二级嵌套多级评论系统与点击消息提示的定位消息功能实现

83 阅读3分钟

翻翻以前做的demo,有个博客项目,其中评论系统稍显复杂。今日拿来扯一扯。

效果是二级评论,就是一级评论之下,多层回复在同一楼,以@符号表示上一级。两层嵌套评论都做了分页。每一层评论下都有回复功能,不做过多嵌套,是照顾了美观与实用。

前端是用的比较老的vue2,前后端变量命名有些随意和简洁了。为了实现这种嵌套,数据库评论表中的设计了cu1和cu2两个字段,前者表示一级评论是谁,就在谁的楼层下显示评论,如果该字段为空就表示是blog下的一级评论。而cu2表示一级评论下的二级评论是谁,就是二层中显示的@谁的回复,没有cu3,因为虽是多级但没有三层,有cu2这个字段就够了。

前端的评论组件必须是嵌套组件了,cNum用于标记一级,而在嵌套组件中这个值就不同了,用于样式区分设定。

<comment  v-if="item.children" :data="item.children"  :id="item.id" :cNum="cNum + 1"/>

下面再说说定位消息功能,效果是登陆用户有未读消息提醒,单击消息上每一条都会定位到相关回复的位置并高亮显示。这在其他评论系统中根本没见过或实现过。就特别想做出功能看看。

因为评论有多页,二层评论也有多页,那必须是自动翻页到一级再翻页到二级层中高亮显示。

1.点击消息提示,向后端传递blogId,cu1Id和消息id三个参数。

2.要检测出消息所在哪一条评论,确定出所在哪一页评论十分重要,就是两层第几页的count值。

3.评论id是自增的,根据这个值就能统计出楼层的第多少个评论,从而确定是第几页。

4.根据回复条目查询一级评论队列中第几个:

        select count(id) from comment where blog_id = #{blogId} and cu1_id is null and id>= #{id}

5.根据回复条目查询二级评论队列中第几个:

        select count(id) from comment where blog_id = #{blogId} and cu1_id=#{cu1id} and id<= #{id}

6.根据这两个序列号,能确定在第几页,比如一页5条。查出两页数据显示即可。

7.在后端组装数据时,塞入该条id的tip属性,例如m.put("tip",1); 。

8.前端解析出tip设置高亮样式:

  <div class="c-c" :class="item.tip?'commentTip':''">{{ item.content }} </div>

9.滚动到消息位置:

        document.querySelector('.commentTip').scrollIntoView(true)

这个评论系统也就是前端代码稍显复杂,comment 组件:

<template>
  <div v-if="data">
    <div
      class="comments-o"
      :class="[cNum > 0 ? 'ono' : '']"
      v-for="(item, index) in contents.data"
      :key="index"
    >
      <div class="c-title">
        <el-avatar
          size="small"
          :src="
            item.tx
              ? item.tx
              : 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png'
          "
        ></el-avatar>
        <b>{{ item.userName }}</b>
        <span v-show="item.cu2Id"
          >回复:@<a>{{ item.cu2Name }}</a></span
        >
        <em>|</em>
        <time>{{ item.time | dateFormat }}</time>
      </div>
      <div class="c-c" :class="item.tip?'commentTip':''">
        {{ item.content }}
      </div>
      <div class="c-g">
        <span
          @click="
            () => {
              item.ishf = true;
            }
          "
          >回复</span
        >
        <span>删除</span>
        <span
          v-show="item.ishf"
          @click="
            () => {
              item.ishf = false;
            }
          "
          >收起回复</span
        >
      </div>
      <div class="lou">
        <el-form
          :model="ruleForm"
          :rules="rules"
          label-width="100px"
          class="demo-ruleForm"
          v-show="item.ishf"
        >
          <el-form-item label="" prop="desc">
            <el-input type="textarea" v-model="ruleForm.desc"></el-input>
          </el-form-item>
          <el-form-item class="plbotton">
            <el-button
              size="mini"
              type="primary"
              @click="reply(item.id, item.userId)"
              >回复</el-button
            >
            <el-button size="mini" @click="resetForm('ruleForm')"
              >重置</el-button
            >
          </el-form-item>
        </el-form>
        <comment
          v-if="item.children"
          :data="item.children"
          :id="item.id"
          :cNum="cNum + 1"
        />
      </div>
    </div>
    <div class="fyo">
      <el-pagination
        class="fy"
        :page-size="5"
        v-show="contents.total > 5"
        layout="prev, pager, next"
        :total="contents.total"
        v-if="cNum < 2"
        :current-page.sync="currentPage"
        @current-change="currChange"
      >
      </el-pagination>
    </div>
  </div>
</template>