1. 效果預覽
1.1進入首頁
進入首頁後點擊一篇文章進入:
1.2 測試評論功能
進入文章詳細列表後,上方爲文章內容區域,下方爲文章評論區域
對此評論框做了數據校驗,如果評論內容爲空,給出提示並返回,否則評論成功,我們輸入一條評論信息,然後點擊回覆,如下:
測試二級評論功能:
我們點擊一級評論下的評論圖標,如果有二級評論則顯示二級評論和二級評論框,如果沒有隻顯示二級評論框
測試二級評論:
2. 功能實現
2.1 文章詳情頁佈局(articleDetail.html)
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<!-- 踩過的坑,springboot引入靜態資源路徑不要加/static/,否則會報404-->
<title th:text="${ArticleDetail.title}"></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" href="/bootstrap-3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/community.css">
<link rel="stylesheet" href="//at.alicdn.com/t/font_1643567_yxz5icboc4.css">
<link rel="stylesheet" href="/bootstrap-3.3.7/css/bootstrap-theme.min.css">
<script src="/jquery-1.12.4/jquery-1.12.4.min.js"></script>
<script src="/bootstrap-3.3.7/js/bootstrap.min.js"></script>
<script src="/js/community.js"></script>
</head>
<body>
<div th:replace="navigation :: nav"></div>
<div class="row main">
<div class="col-lg-9 col-md-12 col-sm-12 col-xs-12 col-left">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class=""> <h4 th:text="${ArticleDetail.title}"></h4></div>
<hr>
<div class="detail-msg">
作者: <span th:text="${ArticleDetail.user.name}"></span> | 發佈時間:<span th:text="${#dates.format(ArticleDetail.createTime,'yyyy-MM-dd')}"></span> | 閱讀數: <span th:text="${ArticleDetail.readCount}"></span>
<div class="link-items" th:if="${session.user !=null && ArticleDetail.user.id==session.user.id}" >
<a th:href="@{/article/edit(id=${ArticleDetail.getId()})}"><i class="iconfont icon-fabu2"></i> 編輯</a>
</div>
</div>
<div>
<span th:text="${ArticleDetail.description}"></span>
</div>
</div>
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="comment">
<h4><span th:text="${ArticleDetail.getAnswerCount()}"></span> 個回覆</h4>
</div>
<div class="media media_list" th:each="comment :${comments}">
<div class="comment-head">
<div class="media-left">
<a href="#">
<img class="img-rounded img-comment" th:src="${comment.getUser().getAvatarUrl()}" >
</a>
</div>
<div class="media-body">
<span class="media-heading" th:text="${comment.getUser().getName()}"></span>
<span class="float-right" th:text="${#dates.format(comment.createTime,'yyyy-MM-dd')}"></span>
</div>
</div>
<div class="comment-style comment-body" th:text="${comment.getContent()}">
</div>
<div class="comment-style comment-footer">
<div>
<span class="operate like">
<i class="iconfont icon-z-like" th:like-id="${comment.getId()}" onclick="like(this)"></i>
<span id="likeCount" th:text="${comment.getLikeCount()==0?'':comment.getLikeCount()}"></span>
</span>
<span class="operate answer" th:data-id="${comment.getId()}" onclick="showChildComment(this)">
<i class="iconfont icon-pinglun"></i>
<span th:id="${'commentCount-'+comment.getId()}" th:text="${comment.commentCount==0?'':comment.commentCount}"></span>
</span>
</div>
<div class="collapse childCol col-lg-12 col-md-12 col-sm-12 col-xs-12" th:id="${'comment-'+comment.getId()}">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12 comment-comment" th:if="${session.user!=null}">
<input type="text" class="form-control" th:id="${'childComment-'+comment.getId()}" name="childComment" placeholder="說說你的看法...">
<button type="button" class="btn btn-success btn-comment" th:data-parentId="${comment.getId()}" onclick="subComment(this)">評論</button>
</div>
</div>
</div>
</div>
</div>
<!-- 評論 -->
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12 ">
<div class="comment" th:if="${session.user!=null}">
<input type="hidden" id="parentId" th:value="${ArticleDetail.getId()}">
<div class="d-flex">
<div class="media">
<div class="media-comment">
<a href="#">
<img class="img-rounded img-comment" th:src="${ArticleDetail.user.getAvatarUrl()}" >
</a>
</div>
</div>
<textarea id="commentArea" name="description" class="comment-content open" maxlength="1000"></textarea>
</div>
<button type="button" class="btn btn-success btn-comment" onclick="postComment();">回覆</button>
</div>
<div class="notLogin" th:if="${session.user==null}">
要回復問題請先<a onclick="toLogin()">登錄</a>
</div>
</div>
</div>
<div class="col-lg-3 col-md-12 col-sm-12 col-xs-12">
<div>
<p>發起人</p>
<div class="media">
<div class="media-left">
<a href="#">
<img class="media-object img-rounded" th:src="${ArticleDetail.user.getAvatarUrl()}" >
</a>
</div>
<div class="media-body">
<span class="media-heading" th:text="${ArticleDetail.user.name}"></span>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
2.2 一級/二級評論回覆功能
點擊回覆按鈕事件
function postComment() {
var parentId = $("#parentId").val();
var content = $("#commentArea").val();
var data = {"parentId":parentId,"content":content,"type":1};
if(content==undefined||content==""){
alert("評論內容不能爲空");
return;
}
$.ajax({
type:"POST",
url:"/comment",
data:JSON.stringify(data),
dataType:"json",
contentType:"application/json",
success:function (result) {
if(result.code==200){
//$("#commentArea").val("");
window.location.reload();
alert(result.message);
}else {
alert(result.message)
}
}
});
}
通過ajax請求到後端“/comment”路徑(CommentController.java)
//控制層處理前端提交過來的回覆評論請求
@PostMapping("/comment")
@ResponseBody
public RespDTO doComment(@RequestBody Comment comment,
HttpServletRequest request){
User user = (User) request.getSession().getAttribute("user");
if(user==null){
return RespDTO.error(CustomizeErrorCode.NOT_LOGIN.getMessage());
}
comment.setCreateTime(System.currentTimeMillis());
comment.setModifiedTime(System.currentTimeMillis());
comment.setCommentor(user.getId());
comment.setLikeCount(0);
comment.setCommentCount(0);
//做提交操作
commentService.insert(comment);
return RespDTO.ok(CustomizeErrorCode.COMMENT_SUCCESS.getMessage(),user);
}
Service層進行提交邏輯處理(CommentService.java)
/**
通用的評論回覆處理,可以回覆一級評論和二級評論
*/
//使用事務
@Transactional
public void insert(Comment comment){
Long parentId = null;
Article article1 = articleMapper.selectByPrimaryKey(comment.getParentId());
if(comment.getType()==null || !CommentType.isExist(comment.getType())){
throw new CustomizeException(CustomizeErrorCode.COMMENT_TYPE_WRONG);
}
//類型爲ARTICLE,對文章進行評論
if(comment.getType()==CommentType.ARTICLE.getType()){
if(comment.getParentId()==null || articleMapper.selectByPrimaryKey(comment.getParentId())==null){
throw new CustomizeException(CustomizeErrorCode.COMMENT_ARTICLE_ID_NOT_FOUND);
}
parentId = comment.getParentId();
}else {
//否則處理二級評論
Comment parentComment = commentMapper.selectByPrimaryKey(comment.getParentId());
if(parentComment!=null){
parentId = parentComment.getParentId();
}else {
throw new CustomizeException(CustomizeErrorCode.COMMENT_ARTICLE_ID_NOT_FOUND);
}
Comment updateComment = new Comment();
updateComment.setCommentCount(1);
updateComment.setId(comment.getParentId());
commentExtMapper.incSubCommentCount(updateComment);
if(comment.getParentId()==null || commentMapper.selectByPrimaryKey(comment.getParentId())==null){
throw new CustomizeException(CustomizeErrorCode.COMMENT_NOT_FOUND);
}
}
//添加評論內容
commentMapper.insert(comment);
Article article = new Article();
//文章總評論數遞增
article.setId(parentId);
article.setAnswerCount(1);
articleExtMapper.incComment(article);
}
2.3 二級評論顯示功能
點擊查看二級評論,通過ajax向後臺請求數據:
function showChildComment(ele) {
var parentId =$(ele).attr("data-id");
$(ele).toggleClass("operateBgColor");
var childComment = $("#comment-"+parentId);
//控制二級評論div顯示還是隱藏
childComment.toggleClass("in ");
var data = {childId:parentId};
//如果二級評論框顯示,開始請求數據
if(childComment.hasClass("in")){
$.ajax({
type:"get",
url:"/childComment",
data:data,
dataType:"json",
contentType:"application/json",
success:function (result) {
var obj = result.obj;
$.each(obj,function (i, o) {
console.log(o.createTime);
var time = new Date(o.createTime).format("yyyy-MM-dd");
pushCommentHtml(parentId,o.user.avatarUrl,o.user.name,o.content,time);
});
}
});
}else{
//防止評論框關閉,再次點擊內容重複疊加,每次都清空二級評論
$("#comment-"+parentId +" .comment-detail").remove()
}
}
二級評論內容拼接到html:
function pushCommentHtml(id,imgurl,uname,content,time) {
var html = "<div class='comment-detail col-lg-12 col-md-12 col-sm-12 col-xs-12'>" +
" <div class='comment-head'>" +
" <div class='media-left'>" +
" <a href='#'>" +
" <img class='img-rounded img-comment' src='" + imgurl + "'>" +
" </a>" +
" </div>" +
" <div class='media-body'>" +
" <span class='media-heading'>" + uname + "</span>" +
" <span class='float-right'>" + time + "</span>"+
" </div>" +
" </div>" +
" <div class='comment-style comment-body'>" + content + "</div>" +
" </div>"
var ele = $("#comment-" + id);
ele.prepend(html);
}
前端請求“/childComment”路徑開始獲取二級評論內容:
@GetMapping("/childComment")
@ResponseBody
public RespDTO getChildComments(@Param("childId") Long childId, HttpServletRequest request){
User user = (User) request.getSession().getAttribute("user");
if(user==null){
return RespDTO.error(CustomizeErrorCode.NOT_LOGIN.getMessage());
}
if(StringUtils.isEmpty(childId)){
return RespDTO.error(CustomizeErrorCode.COMMENT_NOT_FOUND.getMessage());
}
List<CommentDTO> commentDTOS = commentService.selectCommentsById(childId, CommentType.COMMENT.getType());
return RespDTO.ok("獲取二級評論成功",commentDTOS);
}
Service層處理獲取評論功能(包括一級評論和二級評論),一級評論是在進入詳情頁就開始獲取,二級評論則是用戶操作獲取:
public List<CommentDTO> selectCommentsById(long id,int type) {
List<CommentDTO> commentDTOS = new ArrayList<>();
/** 查詢出當前文章的所有一級評論 **/
CommentExample commentExample = new CommentExample();
commentExample.createCriteria().andParentIdEqualTo(id).andTypeEqualTo(type);
if(type==CommentType.ARTICLE.getType()){
commentExample.setOrderByClause("create_time desc");
}else if(type == CommentType.COMMENT.getType()){
commentExample.setOrderByClause("create_time asc");
}
List<Comment> comments = commentMapper.selectByExample(commentExample);
if(comments.size()<=0){
return new ArrayList<>();
}
/** 使用lambda表達式獲取用戶id **/
Set<Long> ids = comments.stream().map(c -> c.getCommentor()).collect(Collectors.toSet());
UserExample user = new UserExample();
user.createCriteria().andIdIn(new ArrayList<>(ids));
List<User> users = userMapper.selectByExample(user);
//將獲取到的用戶List轉爲map
Map<Long, User> userMap = users.stream().collect(Collectors.toMap(User::getId, u -> u, (k1, k2) -> k1));
//遍歷並拷貝賦值
comments.stream().forEach(comment -> {
CommentDTO commentDTO = new CommentDTO();
BeanUtils.copyProperties(comment,commentDTO);
commentDTO.setUser(userMap.get(comment.getCommentor()));
commentDTOS.add(commentDTO);
});
return commentDTOS;
}