springboot+thymeleaf+jpa博客多级评论展示案例
效果:


- 实体层
- 业务层:
- 控制层:
- 前端:
实体层
省略了get set方法
@Entity
@Table(name = "t_comment")
public class Comment {@Id@GeneratedValueprivate Long id;private String nickname;private String email;private String content;private String avatar;@Temporal(TemporalType.TIMESTAMP)private Date createTime;@ManyToOneprivate Blog blog;@OneToMany(mappedBy = "parentComment")private List<Comment> replyComments = new ArrayList<>();@ManyToOneprivate Comment parentComment;private boolean adminComment;
}
业务层:
@Service
public class CommentServiceImpl implements CommentService {@Autowiredprivate CommentRepository commentRepository;@Overridepublic List<Comment> listCommentByBlogId(Long blogId) {Sort sort = new Sort("createTime");List<Comment> comments = commentRepository.findByBlogIdAndParentCommentNull(blogId,sort);return eachComment(comments);}@Transactional@Overridepublic Comment saveComment(Comment comment) {Long parentCommentId = comment.getParentComment().getId();if (parentCommentId != -1) {comment.setParentComment(commentRepository.findOne(parentCommentId));} else {comment.setParentComment(null);}comment.setCreateTime(new Date());return commentRepository.save(comment);}/*** 循环每个顶级的评论节点* @param comments* @return*/private List<Comment> eachComment(List<Comment> comments) {List<Comment> commentsView = new ArrayList<>();for (Comment comment : comments) {Comment c = new Comment();BeanUtils.copyProperties(comment,c);commentsView.add(c);}//合并评论的各层子代到第一级子代集合中combineChildren(commentsView);return commentsView;}/**** @param comments root根节点,blog不为空的对象集合* @return*/private void combineChildren(List<Comment> comments) {for (Comment comment : comments) {List<Comment> replys1 = comment.getReplyComments();for(Comment reply1 : replys1) {//循环迭代,找出子代,存放在tempReplys中recursively(reply1);}//修改顶级节点的reply集合为迭代处理后的集合comment.setReplyComments(tempReplys);//清除临时存放区tempReplys = new ArrayList<>();}}//存放迭代找出的所有子代的集合private List<Comment> tempReplys = new ArrayList<>();/*** 递归迭代,剥洋葱* @param comment 被迭代的对象* @return*/private void recursively(Comment comment) {tempReplys.add(comment);//顶节点添加到临时存放集合if (comment.getReplyComments().size()>0) {List<Comment> replys = comment.getReplyComments();for (Comment reply : replys) {tempReplys.add(reply);if (reply.getReplyComments().size()>0) {recursively(reply);}}}}
}
控制层:
@Controller
public class CommentController {@Autowiredprivate CommentService commentService;@Autowiredprivate BlogService blogService;@Value("${comment.avatar}")//comment.avatar: /images/avatar.pngprivate String avatar;@GetMapping("/comments/{blogId}")public String comments(@PathVariable Long blogId, Model model) {model.addAttribute("comments", commentService.listCommentByBlogId(blogId));return "blog :: commentList";//}// 评论
//
//
//
//
//
//
// Matt@PostMapping("/comments")public String post(Comment comment, HttpSession session) {Long blogId = comment.getBlog().getId();comment.setBlog(blogService.getBlog(blogId));User user = (User) session.getAttribute("user");if (user != null) {comment.setAvatar(user.getAvatar());comment.setAdminComment(true);} else {comment.setAvatar(avatar);}commentService.saveComment(comment);return "redirect:/comments/" + blogId;}
}
前端:
<div id="comment-container" class="ui teal segment"><div th:fragment="commentList"><div class="ui threaded comments" style="max-width: 100%;"><h3 class="ui dividing header">评论h3><div class="comment" th:each="comment : ${comments}"><a class="avatar"><img src="https://unsplash.it/100/100?image=1005" th:src="@{${comment.avatar}}">a><div class="content"><a class="author" ><span th:text="${comment.nickname}">Mattspan><div class="ui mini basic teal left pointing label m-padded-mini" th:if="${comment.adminComment}">博主div>a><div class="metadata"><span class="date" th:text="${#dates.format(comment.createTime,'yyyy-MM-dd HH:mm')}">Today at 5:42PMspan>div><div class="text" th:text="${comment.content}">How artistic!div><div class="actions"><a class="reply" data-commentid="1" data-commentnickname="Matt" th:attr="data-commentid=${comment.id},data-commentnickname=${comment.nickname}" onclick="reply(this)">回复a>div>div><div class="comments" th:if="${#arrays.length(comment.replyComments)}>0"><div class="comment" th:each="reply : ${comment.replyComments}"><a class="avatar"><img src="https://unsplash.it/100/100?image=1005" th:src="@{${reply.avatar}}">a><div class="content"><a class="author" ><span th:text="${reply.nickname}">span><div class="ui mini basic teal left pointing label m-padded-mini" th:if="${reply.adminComment}">div> <span th:text="|@ ${reply.parentComment.nickname}|" class="m-teal">span>a><div class="metadata"><span class="date" th:text="${#dates.format(reply.createTime,'yyyy-MM-dd HH:mm')}">span>div><div class="text" th:text="${reply.content}">How artistic!div><div class="actions"><a class="reply" data-commentid="1" data-commentnickname="Matt" th:attr="data-commentid=${reply.id},data-commentnickname=${reply.nickname}" onclick="reply(this)">回复a>div>div>div>div>div><div id="comment-form" class="ui form"><input type="hidden" name="blog.id" th:value="${blog.id}"><input type="hidden" name="parentComment.id" value="-1"><div class="field"><textarea name="content" placeholder="请输入评论信息...">textarea>div><div class="fields"><div class="field m-mobile-wide m-margin-bottom-small"><div class="ui left icon input"><i class="user icon">i><input type="text" name="nickname" placeholder="姓名" th:value="${session.user}!=null ? ${session.user.nickname}">div>div><div class="field m-mobile-wide m-margin-bottom-small"><div class="ui left icon input"><i class="mail icon">i><input type="text" name="email" placeholder="邮箱" th:value="${session.user}!=null ? ${session.user.email}">div>div><div class="field m-margin-bottom-small m-mobile-wide"><button id="commentpost-btn" type="button" class="ui teal button m-mobile-wide"><i class="edit icon">i>发布button>div>div>
javascript部分
//评论表单验证$('.ui.form').form({fields: {title: {identifier: 'content',rules: [{type: 'empty',prompt: '请输入评论内容'}]},content: {identifier: 'nickname',rules: [{type: 'empty',prompt: '请输入你的大名'}]},type: {identifier: 'email',rules: [{type: 'email',prompt: '请填写正确的邮箱地址'}]}}});$(function () {$("#comment-container").load(/*[[@{/comments/{id}(id=${blog.id})}]]*/"comments/6");});$('#commentpost-btn').click(function () {var boo = $('.ui.form').form('validate form');if (boo) {console.log('校验成功');postData();} else {console.log('校验失败');}});function postData() {$("#comment-container").load(/*[[@{/comments}]]*/"",{"parentComment.id" : $("[name='parentComment.id']").val(),"blog.id" : $("[name='blog.id']").val(),"nickname": $("[name='nickname']").val(),"email" : $("[name='email']").val(),"content" : $("[name='content']").val()},function (responseTxt, statusTxt, xhr) {
// $(window).scrollTo($('#comment-container'),500);clearContent();});}function clearContent() {$("[name='content']").val('');$("[name='parentComment.id']").val(-1);$("[name='content']").attr("placeholder", "请输入评论信息...");}function reply(obj) {var commentId = $(obj).data('commentid');var commentNickname = $(obj).data('commentnickname');$("[name='content']").attr("placeholder", "@"+commentNickname).focus();$("[name='parentComment.id']").val(commentId);$(window).scrollTo($('#comment-form'),500);}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
