对一个论坛网站来说,回复功能也是很重要的功能,在完成之前所有功能后,今天的任务就是完成问题的回复功能,主要的效果图如下:
# (一)搭建Comment数据库 {#一-搭建comment数据库}
一个回复需要包含主键id;所回复问题或者回复评论的id;一个判断是回复问题还是回复别人评论的type,当type=1时,回复问题,当type=2时,回复别人的评论;回复人commentor;回复时间createtime;点赞数like_count;回复内容content;回复数:commentcount
CREATE TABLE `comment` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`parent_id` int(11) NOT NULL,
`type` int(11) NOT NULL,
`commentor` int(11) DEFAULT NULL,
`createtime` bigint(20) DEFAULT NULL,
`like_count` int(11) DEFAULT '0',
`content` varchar(200) NOT NULL,
`commentcount` int(11) DEFAULT '0',
PRIMARY KEY (`id`)
)
建完数据库之后创建实体类:
public class Comment {
private int id;
private int parent_id;
private int type;
private int commentor;
private int like_count;
private long createtime;
private int commentcount;
private String content;
//省略getter and setter 方法
}
# (二)Comment的数据传输结构 {#二-comment的数据传输结构}
当用户输入回复,点击提交之后,我希望从前端传过来的数据包含parent_id(所回复对象的id),content(回复的内容)和type(判断回复的是问题还是评论),因为user的信息和创建时间等都可以直接在后端获取,不需要从前端传过来。于是建立commentcreateDTO,用来封装传递过来的数据:
public class CommentCreateDto {
private int parent_id;
private int type;
private String content;
//省略getter and setter 方法
}
前端需要展示回复的信息,因此需要从后端传递回复数据给前端,传给前端的数据不仅要包含comment的所有属性,还要包含user的所有信息,因为我们需要展示出是回复人的姓名和头像,于是创建CommentDTO用于封装传给前端的数据:
public class CommentDto {
private int id;
private int parent_id;
private int type;
private int commentor;
private int like_count;
private long createtime;
private int commentcount;
private String content;
private User user;
//省略getter and setter 方法
}
同时,如果comment成功插入到数据库了,我希望前端能知道成功了,因此再创建一个ResultDto,里面包含是否成功的信息以及CommentDTO的数据:
public class ResultDto<T> {
private int code;
private String message;
private T data;
public ResultDto success(){
ResultDto resultDto=new ResultDto();
resultDto.setCode(200);
resultDto.setMessage("成功");
return resultDto;
}
public <T> ResultDto success(T data){
ResultDto resultDto=new ResultDto();
resultDto.setCode(200);
resultDto.setMessage("成功");
resultDto.setData(data);
return resultDto;
}
//省略getter and setter 方法
}
# (三)完成回复问题功能 {#三-完成回复问题功能}
问题回复功能有两个模块,一是展示回复内容,二是回复框的处理。展示回复内容很简单,在questionController中增加两行代码:
List<CommentDto> comments=commentService.getByid(id);
model.addAttribute("comments",comments);
通过问题的id查找到回复信息,放进一个List中,并通过model传递给前端,前端只需要通过each的方式逐条获取数据展示就行。前端的代码请看源码的question.html 前端回复框包含一个textarea和一个button,给button设置onclick属性,通过jquery来处理点击提交后的回复的逻辑,写一个隐藏起来的input,这个input不会展示在前端页面上,但是可以用来传递问题的id,让后台知道是在哪个问题下评论。
<input type="hidden" id="question_id" th:value="${questionDto.id}">
<textarea class="form-control" rows="6" style="margin-top:10px; margin-bottom: 10px;"
id="content"></textarea>
<button type="button" class="btn btn-success" style="float: right;margin: 10px;"
onclick="post()">提交
新建community.js,把数据封装成一个json格式给到后端
function post() {
//获取问题id
var questionid = $("#question_id").val();
//获取回复内容
var content = $("#content").val();
if(content==''){
alert("回复内容不能为空")
}else {
$.ajax({
type:"POST",
url:"/comment",
contentType:'application/json',
data: JSON.stringify({
"parent_id":questionid,
"type":1,
"content":content
}),
//获取后端返回过来的数据,也就是ResultDTO
success: function (data) {
if (data.code==200){
window.location.reload();
} else{
alert("出现了错误");
}
},
dataType:"json"
});
}
}
新建CommentController,将数据写进数据库,具体操作和之前基本一致
@Controller
public class CommentController {
@Resource
private UserMapper userMapper;
@Resource
private CommentMapper commentMapper;
@Resource
private QuestionMapper questionMapper;
@ResponseBody
@RequestMapping(value = "/comment",method = RequestMethod.POST)
public Object post(@RequestBody CommentCreateDto commentCreateDto,
HttpServletRequest request){
//获取user
Cookie[] cookies = request.getCookies();
if (cookies == null) {
return "login";
}
User user = null;
for (Cookie cookie : cookies) {
if (cookie.getName().equals("token")) {
String token = cookie.getValue();
user = userMapper.findBytoken(token);
if (user != null) {
request.getSession().setAttribute("user", user);
}
break;
}
}
//将前端传递过来的信息上传数据库
Comment comment=new Comment();
comment.setParent_id(commentCreateDto.getParent_id());
comment.setContent(commentCreateDto.getContent());
comment.setType(commentCreateDto.getType());
comment.setCreatetime(System.currentTimeMillis());
comment.setCommentor(user.getId());
commentMapper.insert(comment);
if (commentCreateDto.getType()==2){
commentMapper.updatecommentcount(commentCreateDto.getParent_id());
}else {
questionMapper.updatecomment(commentCreateDto.getParent_id());
}
ResultDto resultDto=new ResultDto();
//返回结果
return resultDto.success();
}
}
这样一个回复功能就完成了。
# (四)完成二级回复问题功能 {#四-完成二级回复问题功能}
二级回复功能的处理比一级回复要更麻烦一些,二级回复也分成回复展示和回复框两块,只是二级回复的回复展示我打算用js传递给前端,所以先写二级评论的输入框功能,同样包含一个input和button,在button上有onclick属性
<div class="col-lg-12 col-md-12 col-sm-12 col-ss-12" style="margin-top: 10px;">
<input type="text" class="form-control" placeholder="评论一下....." th:id="${'input-'+comment.id}">
<button type="button" class="btn btn-success" style="float: right;margin: 10px;"
onclick="replypost(this)" th:data-id="${comment.id}">
提交
</button>
</div>
在community.js中新增方法,也一样是将回复内容上传给后端,只是这里将type改成了2,因为回复的是别人的评论,parentid也变成了评论的id
function replypost(e) {
var commentid = e.getAttribute("data-id");
var content = $("#input-"+commentid).val();
if(content==''){
alert("回复内容不能为空")
}else {
$.ajax({
type:"POST",
url:"/comment",
contentType:'application/json',
data: JSON.stringify({
"parent_id":commentid,
"type":2,
"content":content
}),
success: function (data) {
if (data.code==200){
window.location.reload();
} else{
alert("出现了错误");
}
},
dataType:"json"
});
}
}
在一级评论的回复按钮上增加onclick,用于点击后展开回复内容
<!--回复按钮-->
<span class="glyphicon glyphicon-comment icon" aria-hidden="true"
th:data-id="${comment.id}" th:data-check="1" onclick="secondcomment(this)" th:text="${comment.commentcount}"></span>
在CommentController新增一个方法,把回复数据传递给前端
@ResponseBody
@RequestMapping(value = "/comment/{id}",method = RequestMethod.GET)
public ResultDto<List<CommentDto>> comments(@PathVariable(name = "id") int id,
HttpServletRequest request){
//查找type=2,即是回复评论的评论
List<Comment> comments = commentMapper.getCommentByid(id,2);
List<CommentDto> commentDto=new ArrayList<>();
//找到User
Cookie[] cookies = request.getCookies();
User user = null;
for (Cookie cookie : cookies) {
if (cookie.getName().equals("token")) {
String token = cookie.getValue();
user = userMapper.findBytoken(token);
break;
}
}
//把二级评论和对应的User写进每个CommentDto集合中
for (Comment comment:comments){
CommentDto dto=new CommentDto();
BeanUtils.copyProperties(comment,dto);
dto.setUser(user);
commentDto.add(dto);
}
ResultDto resultDto=new ResultDto();
return resultDto.success(commentDto);
}
前端js中的secondcomment方法通过$.getJSON获取到后端发过来的数据,然后通过 $.each循环List中的每一条数据,回复展示的内容我使用jquery将html代码插入到html页面中id为comment-id的div块中,js中所插入的html代码相当于这一段:
<div class="col-lg-12 col-md-12 col-sm-12 col-ss-12" th:each="comment:${comments}">
<div class="media">
<div class="media-left">
<img class="media-object img-rounded picset"
src="https://profile.csdnimg.cn/F/C/F/1_qq_41973594?v=1574041386">
</div>
<div class="media-body">
<h4 class="media-heading">
<span th:text="${comment.user.name}"/>
<div style="font-size: 15px; margin-top:5px;"
th:text="${comment.content}">
</div>
<div class="question-menu">
<span th:text="${#dates.format(comment.createtime,'yyyy-MM-dd')}"
style="float: right"></span>
</div>
</h4>
</div>
</div>
</div>
js代码
function secondcomment(e) {
var id = e.getAttribute("data-id");
var check = e.getAttribute("data-check");
var comment = $("#comment-"+id);
//如果check为1则展开二级评论,否则收缩
if (check=="1"){
$.getJSON("/comment/"+id , function (data) {
var subCommentContainer=$("#comment-"+id) ;
//如果子元素的长度为1,即第一次添加,则调用下面的方法
if (subCommentContainer.children().length ==1){
$.each(data.data.reverse(),function (index,comment) {
//对应<span th:text="${comment.user.name}"/>
var usernameElement=$("<span/>",{
html:comment.user.name
});
//对应<div style="font-size: 15px; margin-top:5px;"
// th:text="${comment.content}">
// </div>
var contentElement=$("<div/>",{
"style":"font-size: 15px; margin-top:5px;",
html:comment.content
});
var timeElement=$("<span/>",{
"style":"float: right",
html:moment(comment.createtime).format('YYYY-MM-DD')
});
var questionmenuElement=$("<div/>",{
"class":"question-menu"
}).append(timeElement);
var imgElement = $("<img/>",{
"class":"media-object img-rounded picset",
"src":"https://profile.csdnimg.cn/F/C/F/1_qq_41973594?v=1574041386"
});
var medialeftElement=$("<div/>",{
"class":"media-left"
}).append(imgElement);
var mediaheadingElement=$("<h4/>",{
"class":"media-heading",
}).append(usernameElement)
.append(contentElement)
.append(questionmenuElement);
var mediabodyElement=$("<div/>",{
"class":"media-body",
}).append(mediaheadingElement);
var mediaElement=$("<div/>",{
"class":"media"
}).append(medialeftElement)
.append(mediabodyElement);
var commentElement=$("<div/>",{
"class":"col-lg-12 col-md-12 col-sm-12 col-ss-12 comments",
}).append(mediaElement);
subCommentContainer.prepend(commentElement);
})
}
});
comment.addClass("in");
e.setAttribute("data-check","0");
e.classList.add("active");
}else {
comment.removeClass("in");
e.setAttribute("data-check","1")
e.classList.remove("active");
}
}
这样回复和二级回复功能就完成了,完成这些后,一个论坛所需要的基本功能就全部完成了,接下来就是对论坛界面和其他一些小功能的小修小补,首页的热门问题还未写,搜索功能目前也没有,用户的头像目前用的是统一的图片,个人中心中我的消息功能还未做,还想优化一下标签功能。