实现思路:
数据库设计:评论表需要定义出当前博客id以便做关联,因为评论需要有回复功能,则需要定义当前评论有无上一级评论,需要定义出上级评论id;博主回复评论需要带有标签,所以需要定义Boolean类型判断是否为博主。
代码方面:点击评论需要获取当前博客id与自己评论数据进行插入,点击回复按钮需要获取上一条评论的id以及用户姓名作为回复,回复成功后,刷新页面,后台则是在数据库中查找出所有parentCommentId为-1的进行遍历,因为上级id为-1则证明当前评论无父节点。在通过对父节点id的遍历查询出所有对应评论的子节点。
页面展示
博客实体类
private Long id;
private String nickname;
private String email;
private String content;
//头像
private String avatar;
private Date createTime;
private Long blogId; //关联博客id
private Long parentCommentId; //上级评论id
private String parentNickname;
//回复评论
private List<Comment> replyComments = new ArrayList<>();
private Comment parentComment;
private boolean adminComment; //是不是博主
private DetailedBlog blog;
评论的博客表单html
<div id="comment-form" class="ui form">
<!--获取当前博客id进行评论 使用隐藏域-->
<input type="hidden" name="blogId" 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>
点击发布按钮触发
$('#commentpost-btn').click(function () {
var boo = $('.ui.form').form('validate form');
if (boo) {
console.log('校验成功');
postData();
} else {
console.log('校验失败');
}
});
//评论表单验证
$('.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 postData() {
$("#comment-container").load(/*[[@{/comments}]]*/"",{
"parentComment.id" : $("[name='parentComment.id']").val(),
"blogId" : $("[name='blogId']").val(),
"nickname": $("[name='nickname']").val(),
"email" : $("[name='email']").val(),
"content" : $("[name='content']").val()
},function (responseTxt, statusTxt, xhr) {
$(window).scrollTo($('#goto'),500); //提交成功之后滚动到评论位置
clearContent();
});
}
function clearContent() {
$("[name='nickname']").val('');
$("[name='email']").val('');
$("[name='content']").val('');
$("[name='parentComment.id']").val(-1);
$("[name='content']").attr("placeholder", "请输入评论信息...");
}
点击回复按钮,在评论区显示回复给哪个用户
<a class="reply" data-messageid="1" data-messagenickname="Matt" th:attr="data-messageid=${reply.id},data-messagenickname=${reply.nickname}" onclick="reply(this)">回复</a>
对应函数
function reply(obj) {
var messageId = $(obj).data('messageid');
var messageNickname = $(obj).data('messagenickname');
$("[name='content']").attr("placeholder", "@"+messageNickname).focus();//在回复时显示 @对应人
$("[name='parentMessage.id']").val(messageId);//为隐藏域赋值
$(window).scrollTo(0,500); //滚动
}
对应后端代码
@Controller
public class CommentController {
@Autowired
private CommentService commentService;
@Autowired
private BlogService blogService;
@Value("${comment.avatar}")
private String avatar;
// 查询评论列表
@GetMapping("/comments/{blogId}")
public String comments(@PathVariable Long blogId, Model model) {
List<Comment> comments = commentService.listCommentByBlogId(blogId);
model.addAttribute("comments", comments);
return "blog :: commentList"; //返回到blog下面的commentList片段
}
// 新增评论
@PostMapping("/comments")
public String post(Comment comment, HttpSession session,Model model) {
Long blogId = comment.getBlogId();
User user = (User) session.getAttribute("user");
if (user != null) {
comment.setAvatar(user.getAvatar());
comment.setAdminComment(true);
} else {
//设置头像
comment.setAvatar(avatar);
}
//页面中定义为-1 不会出现空指针
if (comment.getParentComment().getId() != null) {
//获取父对象设置对应关系
comment.setParentCommentId(comment.getParentComment().getId());
}
commentService.saveComment(comment);
List<Comment> comments = commentService.listCommentByBlogId(blogId);
model.addAttribute("comments", comments);
return "blog :: commentList";
}
列表设置,前端遍历显示层级关系
<div class="ui bottom attached segment" th:if="${blog.commentabled}">
<!--评论区域列表-->
<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}">Matt</span>
<--判断是不是博主-->
<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:42PM</span>
</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>
<a class="delete" href="#" th:href="@{/comment/{param1}/{param2}/delete(param1=${comment.blogId},param2=${comment.id})}" onclick="return confirm('确定要删除该评论吗?')" th:if="${session.user}">删除</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.parentNickname}|" class="m-teal">@ 小白</span>
</a>
<div class="metadata">
<span class="date" th:text="${#dates.format(reply.createTime,'yyyy-MM-dd HH:mm')}">Today at 5:42PM</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>
<a class="delete" href="#" th:href="@{/comment/{param1}/{param2}/delete(param1=${reply.blogId},param2=${reply.id})}" onclick="return confirm('确定要删除该评论吗!')" th:if="${session.user}">删除</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
后端实现逻辑:先获取顶级的数据,在一层一层往下找、放入集合
@Service
public class CommentServiceImpl implements CommentService {
@Autowired
private CommentDao commentDao;
@Autowired
private BlogDao blogDao;
//存放迭代找出的所有子代的集合
private List<Comment> tempReplys = new ArrayList<>();
@Override
public List<Comment> listCommentByBlogId(Long blogId) {
//查询出父节点
List<Comment> comments = commentDao.findByBlogIdParentIdNull(blogId, Long.parseLong("-1"));
for(Comment comment : comments){
Long id = comment.getId();
String parentNickname1 = comment.getNickname();
List<Comment> childComments = commentDao.findByBlogIdParentIdNotNull(blogId,id);
// 查询出子评论
combineChildren(blogId, childComments, parentNickname1);
comment.setReplyComments(tempReplys);
tempReplys = new ArrayList<>();
}
return comments;
}
private void combineChildren(Long blogId, List<Comment> childComments, String parentNickname1) {
// 判断是否有一级子评论
if(childComments.size() > 0){
// 循环找出子评论的id
for(Comment childComment : childComments){
String parentNickname = childComment.getNickname();
childComment.setParentNickname(parentNickname1);
tempReplys.add(childComment);
Long childId = childComment.getId();
// 查询出子二级评论
recursively(blogId, childId, parentNickname);
}
}
}
private void recursively(Long blogId, Long childId, String parentNickname1) {
// 根据子一级评论的id找到子二级评论
List<Comment> replayComments = commentDao.findByBlogIdAndReplayId(blogId,childId);
if(replayComments.size() > 0){
for(Comment replayComment : replayComments){
String parentNickname = replayComment.getNickname();
replayComment.setParentNickname(parentNickname1);
Long replayId = replayComment.getId();
tempReplys.add(replayComment);
recursively(blogId,replayId,parentNickname);
}
}
}
// 新增评论
@Override
public int saveComment(Comment comment) {
comment.setCreateTime(new Date());
int comments = commentDao.saveComment(comment);
// 文章评论计数
blogDao.getCommentCountById(comment.getBlogId());
return comments;
}
}
总结:本章主要是简单介绍博客评论功能的实现,功能并不全面。实现逻辑:因为是博客普通用户不需要登录即可浏览,所以没有做普通用户登录功能,评论时候需要输入自己的姓名和邮箱进行评论,若为博主评论则是通过实体类定义的字段在前端做判断,若为博主则增加标签。
版权归原作者 远走与梦游 所有, 如有侵权,请联系我们删除。