自己也搭建了个新的博客,部分文章与csdn同步.本文在我的新博客同样可以看到:
http://spzgczyj.top/blog/article.html?articleId=350

致谢

首先感谢下面三个博主提供是思路:

Vue 递归组件_任重道远-CSDN博客_vue中什么是递归组件

vue递归组件实现多级列表 - 菲H - 博客园

评论系统数据库设计及实现 - Mario_Xue - 博客园

我的项目是前后端分离的,后台给前端带有层级的json格式的文件,前端用vue渲染。这时候就出现了两个问题:

1. 前端vue渲染层级的回复需要递归,而我当时只会简单的v-for。

2. 后台数据库设计如何在3nf范式的前提下实现评论层级回复的存储,如果要查询出来,sql应该如何写,这搞搞就变成了SQL递归查询的问题,但是我们其实就不是很喜欢查询的时候用到SQL递归查询,搞搞就变得复杂了。一旦递归查询,效率就会很慢时间复杂度就和层数挂钩了。

后端实现

针对这两个问题,我先找到了后台的实现,我从评论系统数据库设计及实现 - Mario_Xue - 博客园 这位博主的文章找到了思路,

我的评论表字段是这样的:

不需要多余的表,一张表就够了。

这是我的实体类:

package com.gkd.blog.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.lang.NonNull;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import java.util.Date;
import java.util.List;

/**
 * <p>description: 评论和该评论的用户信息 </p>
 * <p>create: 2020/1/10 12:15</p>
 * @author zhaoyijie
 * @version v1.0
 */
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public class CommentAndUser {

    /**
     * 评论id
     */
    private Integer id;

    /**
     * 文章id
     */
    private Integer articleId;

    /**
     * 游客的昵称
     */
    @NotBlank
    private String visitorName;

    /**
     * 游客的邮箱
     */
    @NonNull
    @Pattern(regexp= "^[A-Za-z]{1,40}@[A-Za-z0-9]{1,40}\\\\.[A-Za-z]{2,3}$")
    private String userEmaill;

    /**
     * 评论日期
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date addTime;

    /**
     * 评论内容
     */
    @NotBlank
    private String commentContent;

    /**
     * 评论父id
     */
    private Integer parentId;

    /**
     * 子评论列表,回复评论
     */
    private List<CommentAndUser> childList;
}

针对这个表和实体类,先查询数据出来再组装数据:

List<CommentAndUser> allList = commentMapper.findAllCommentByArticleId(articleId);
        Map<Integer, CommentAndUser> map = new HashMap<>();
        List<CommentAndUser> result = new ArrayList<>();
        for (CommentAndUser c : allList) {
            if (c.getParentId() == null) {
                result.add(c);
            }
            map.put(c.getId(), c);
        }
        for (CommentAndUser c : allList) {
            if (c.getParentId() != null) {
                CommentAndUser parent = map.get(c.getParentId());
               
                if (parent == null) {
                    continue;
                }
                
                if (parent.getId().intValue() == c.getId().intValue()) {
                    continue;
                }
                if (parent.getChildList() == null) {
                    parent.setChildList(new ArrayList<>());
                }
                parent.getChildList().add(c);
            }
        }

 这里result是最后返回给前端的数据列表,当然需要分页后再写成json返回给前端,这里时间复杂度就O(n)。而不是递归那种O(n^2)了。这里需要说明的是在遍历allList的时候,我们需要判断一下中的parent是否为null,理论上这个不应该需要考虑,因为子评论有父id,那肯定是找得到父评论的,但是,如果数据被人为不小心或者故意删除了父评论,但是子评论还携带父id,这样子map取出来就是null,null操作就会报错,所以为了保险,还是得先判断从map取出来的父评论对象是否为空。接着还得判断一下父评论的id是否等于子评论的id,这是一个坑,虽然正常操作下这种情况是不可能出现的,但是一旦数据库存着这种错误的数据,子评论的父id居然是自己,那么最后写成json会递归调用,自己调用自己最后一定会栈溢出。所以必须得加个保险。

前端实现

针对前端的渲染,我选用了vue.js。主要是我前端并不是很会,然后找了很多篇文章,发现都是.vue文件的,我并不喜欢,不是import 就是 exports。拜托,我只会简单的v-for和new vue的方式,找了好久找不到我想要的,然后我找到了这两篇博主的文章:

Vue 递归组件_任重道远-CSDN博客_vue中什么是递归组件

vue递归组件实现多级列表 - 菲H - 博客园

最后整合了一下把前端代码写了出来,这里只放关键代码

<div id="app"><list :list="commentAndUserList"></list></div>
<script>
    function replyComment(id){
        //回复评论的方法,根据自己的业务需求更改
    }
    Vue.component('List', {
        name: 'list',
        props: {
            list: Array
        },
        template: `
         <div><ul class="comments-ul" v-for="(CommentAndUser,index) in list">
               <li class="usercomment">
                   <div class="fn__clear">
                       <div class="item__name" v-text="CommentAndUser.visitorName"></div>
                   </div>
                   <div class="vditor-reset">
                       <p v-text="CommentAndUser.commentContent"></p>
                   </div>
                   <div class="item__meta fn__clear">
                       <time v-text="CommentAndUser.time"></time>
                   </div>
                   <button type="button" class="layui-btn reply-btn" v-on:click="replyComment(CommentAndUser.id)">回复
                   </button>
               </li>
               <div v-if="CommentAndUser.childList" class="children-item">
                   <list :list="CommentAndUser.childList"></list>
               </div>
           </ul>
        </div> `,
    });
    new Vue({
        el: "#app",
        data: {
            commentAndUserList: []
        },
        created:
            function () {
                //这里ajax或者axios请求更新commentAndUserList即可
        }
    })
</script>

 然后大功告成,最后的结果长这样:

Logo

前往低代码交流专区

更多推荐