续 开发评论功能模块
续 开发添加评论的功能
表单绑定
现在页面上的添加评论的按钮会引发所有回答的添加评论的输入框展开
这是不合理的
需要绑定id分别展开控制
detail.html文件中"采纳答案"附近代码修改为:
<p class="text-left text-dark">
<a class="btn btn-primary mx-2"
href="#">采纳答案</a>
<a class="btn btn-outline-primary"
data-toggle="collapse" href="#collapseExample1"
v-bind:href="'#addComment'+answer.id"
role="button" aria-expanded="false" aria-controls="collapseExample">
<i class="fa fa-edit"></i>添加评论
</a>
</p>
<div class="collapse" id="collapseExample1"
v-bind:id="'addComment'+answer.id">
<div class="card card-body border-light">
<form action="#" method="post"
v-on:submit.prevent="postComment(answer.id)"
class="needs-validation" novalidate>
<div class="form-group">
<textarea class="form-control" name="content" rows="3" required></textarea>
<div class="invalid-feedback">
评论内容不能为空!
</div>
</div>
<button type="submit" class="btn btn-primary my-1 float-right">提交评论</button>
</form>
</div>
</div>
开发js代码
评论的新增无需自己单独编写一个vue模块
直接借助已经编写好的answersApp模块即可
answersApp模块中添加一个方法postComment即可
代码如下
postComment:function(answerId){
//现在我们需要获得回答id和评论内容,以新增评论
let content=$("#addComment"+answerId+" textarea").val()
if(!content){
console.log("评论内容不能为空");
return;
}
let data={
answerId:answerId,
content:content
}
$.ajax({
url:"/v1/comments",
method:"post",
data: data,
success:function(r){
console.log(r);
if(r.code==CREATED){
alert(r.message);
}else{
alert(r.message);
}
}
})
}
编写业务逻辑层
先编写ICommentService
添加方法如下
public interface ICommentService extends IService<Comment> {
Comment saveComment(CommentVo commentVo,String username);
}
准备一个服务器忙的异常方法方便调用
ServiceException类中添加方法
//返回服务器忙的异常
public static ServiceException busy(){
return new ServiceException("数据库忙",R.INTERNAL_SERVER_ERROR);
}
再编写CommentServiceImpl
@Service
@Slf4j
public class CommentServiceImpl extends ServiceImpl<CommentMapper, Comment> implements ICommentService {
@Resource
private UserMapper userMapper;
@Resource
private CommentMapper commentMapper;
@Override
public Comment saveComment(CommentVo commentVo, String username) {
//获得当前登录用户信息
User user=userMapper.findUserByUsername(username);
//构建要新增的评论对象
Comment comment=new Comment()
.setUserId(user.getId())
.setUserNickName(user.getNickname())
.setAnswerId(commentVo.getAnswerId())
.setContent(commentVo.getContent())
.setCreatetime(LocalDateTime.now());
int num=commentMapper.insert(comment);
if(num!=1){
throw ServiceException.busy();
}
return comment;
}
}
这里可以测试一下新增效果
重构控制层代码
@PostMapping
public R<Comment> postComment(
@Validated CommentVo commentVo,
BindingResult result,
@AuthenticationPrincipal UserDetails userDetails){
if(result.hasErrors()){
String message=result.getFieldError().getDefaultMessage();
return R.unproecsableEntity(message);
}
log.debug("收到评论信息{}:",commentVo);
//这里调用业务逻辑层方法执行评论的新增即可
Comment comment=commentService.saveComment(
commentVo,userDetails.getUsername());
return R.created(comment);
}
新增评论成功后,立即显示在页面上
只需要修改js代码postComment方法中ajax操作成功的代码即可
$.ajax({
url:"/v1/comments",
method:"post",
data: data,
success:function(r){
console.log(r);
if(r.code==CREATED){
//清空textarea的内容
$("#addComment"+answerId+" textarea").val("");
//获得新增的评论
let comment=r.data;
//获得当前所有的回答
let answers=answersApp.answers;
//遍历所有回答
for(let i=0;i<answers.length;i++){
//判断本次添加的评论是不是属于当前回答的
if(answers[i].id == answerId){
//把新增的评论保存到当前回答中评论的集合里
answers[i].comments.push(comment);
break;
}
}
}else{
alert(r.message);
}
}
开发删除评论的功能
评论的内容如果想撤销当然可以删除
但是删除评论还是有条件的
1.老师可以删除任何人的评论
2.评论的发布者可以删除自己的评论
删除的方式:
为了防止用户误删除,我们将删除的操作设计为
先点击删除链接,再删除链接右侧出现一个红叉,再点击红叉实现删除效果
所以,我们先要把右侧的红叉编写出来
步骤1:
编写确认删除的红叉
<!--老师角色或者属于本用户的评论可以删除该评论-->
<a class="ml-2" style="font-size: small"
data-toggle="collapse" role="button"
onclick="$(this).next().toggle(200)"
aria-expanded="false" aria-controls="collapseExample">
<i class="fa fa-close"></i><small>删除</small>
</a>
<a class="text-white badge badge-pill badge-danger"
style="display: none; cursor: pointer">
<i class="fa fa-close"></i>
</a>
步骤2:
在红叉上绑定点击事件
触发ajax方法
<a class="text-white badge badge-pill badge-danger"
style="display: none; cursor: pointer"
@click="removeComment(comment.id)"
>
<i class="fa fa-close"></i>
</a>
@click是v-on:click的缩写形式
步骤3:编写删除评论的方法
removeComment:function(commentId){
if(!commentId){
return;
}
$.ajax({
// 匹配/v1/comments/{id}/delete
url:"/v1/comments/"+commentId+"/delete",
method:"get",
success:function(r){
if(r.code==GONE){
alert(r.message);
}else{
alert(r.message);
}
}
})
},
步骤4:
控制层的开发
CommentController类中添加新的方法,用于删除评论
代码如下
@GetMapping("/{id}/delete")
public R removeComment(@PathVariable Integer id){
log.debug("开始执行删除评论,id为:{}",id);
//这里调用业务逻辑层删除的调用
return R.gone("删除成功");
}
步骤5:
开发业务逻辑层接口和实现
ICommentService接口
// 删除评论
boolean removeComment(Integer commentId,String username);
CommentServiceImpl类实现
@Override
public boolean removeComment(Integer commentId, String username) {
User user=userMapper.findUserByUsername(username);
//判断身份
if(user.getType()==1){
//如果是老师,可以删除
int num=commentMapper.deleteById(commentId);
return num == 1;
}
//不是老师要删除评论,要判断这个评论是不是当前登录用户发布的
//那么就获得这个评论的对象
Comment comment=commentMapper.selectById(commentId);
//判断要删除的评论的发布者的id是不是当前登录用户的id
if(comment.getUserId()==user.getId()){
//是同一用户,可以删除
int num=commentMapper.deleteById(commentId);
return num == 1;
}
throw ServiceException.invalidRequest("权限不足");
}
步骤6:
重构控制器代码
@GetMapping("/{id}/delete")
public R removeComment(
@PathVariable Integer id,
@AuthenticationPrincipal User user){
log.debug("开始执行删除评论,id为:{}",id);
//这里调用业务逻辑层删除的调用
boolean isdelete=commentService.removeComment(id,user.getUsername());
if(isdelete) {
return R.gone("删除成功");
}else{
return R.notFound("没有找到对应记录");
}
}
将删除结果同步更新到页面
我们删除了评论,但是没删除页面上已经显示出来的那份
所以要编写删除已经显示出来的评论内容的代码
步骤1:
更新detail.html的代码
循环li标签的v-for修改如下
<li class="media my-2" v-for="(comment,index) in answer.comments">
v-for="(comment,index) in answer.comments"中
(comment,index)中的comment仍然代表集合中的每个元素
而index代表当前循环的索引从0开始
在确认删除的红叉链接上,修改调用的删除代码,添加几个参数
代码如下
<a class="text-white badge badge-pill badge-danger"
style="display: none; cursor: pointer"
@click="removeComment(comment.id,index,answer.comments)"
>
<i class="fa fa-close"></i>
</a>
步骤2
页面中的调用变化了,那么我们的方法的实现也要随之变化
question_detail.js代码如下
removeComment:function(commentId,index,comments){
if(!commentId){
return;
}
$.ajax({
// 匹配/v1/comments/{id}/delete
url:"/v1/comments/"+commentId+"/delete",
method:"get",
success:function(r){
if(r.code==GONE){
//splice方法是从指定数组中,从index的位置开始删除,删除几个元素
//这里写1就表示只删除index位置的一个元素
comments.splice(index,1);
}else{
alert(r.message);
}
}
})
}
开发编辑评论的功能
步骤1:
准备页面上的绑定和提交以及相关的功能
detail.html页面
<div class="collapse" id="editCommemt1"
v-bind:id="'editComment'+comment.id">
<div class="card card-body border-light">
<form action="" method="post"
v-on:submit.prevent=
"updateComment(comment.id,answer.id,index,answer.comments)"
class="needs-validation" novalidate>
<div class="form-group">
<textarea class="form-control"
id="textareaComment1" name="content" rows="4"
v-text="comment.content"
required></textarea>
<div class="invalid-feedback">
内容不能为空!
</div>
</div>
<button type="submit" class="btn btn-primary my-1 float-right">提交修改</button>
</form>
</div>
</div>
步骤2:
编写js文件
继续在question_detail.js文件中的answersApp模块中添加方法
updateComment方法
代码如下
updateComment:function(commentId,answerId,index,comments){
let textarea=$("#editComment"+commentId+" textarea");
let content=textarea.val();
if(!content){
return;
}
let data={
answerId:answerId,
content:content
};
$.ajax({
url:"/v1/comments/"+commentId+"/update",
method:"post",
data:data,
success:function(r){
console.log(r)
if(r.code==OK){
//如果是对数组内部的属性值的修改
//不会触发Vue的绑定更新
//Vue提供了手动绑定更新的方法,能够修改数组中的值
// 而且还能触发绑定的更新
Vue.set(comments,index,r.data)
//将当前显示编辑输入框的div隐藏
$("#editComment"+commentId).collapse("hide");
}else{
alert(r.message);
}
}
})
}
步骤3:编写控制代码
CommentController类中编写代码如下
@PostMapping("/{id}/update")
public R<Comment> update(
@PathVariable Integer id,
@Validated CommentVo commentVo,BindingResult result,
@AuthenticationPrincipal User user){
if(result.hasErrors()){
String message=result.getFieldError().getDefaultMessage();
return R.unproecsableEntity(message);
}
Comment comment=
commentService.updateComment(id,commentVo,user.getUsername());
return R.ok(comment);
}
上面代码因为调用了还不存在的方法
所以报错是正常的
步骤4:
编写业务逻辑层接口
控制层已经调用了接口的方法,按照控制层的调用编写
ICommentService中的方法即可
// 修改评论
Comment updateComment(Integer commentId,CommentVo commentVo,String username);
步骤5:
编写业务逻辑层的实现
CommentServiceImpl的updateComment方法
@Override
public Comment updateComment(Integer commentId,
CommentVo commentVo, String username) {
//获得登录用户信息
User user=userMapper.findUserByUsername(username);
//获得要修改的评论信息
Comment comment=commentMapper.selectById(commentId);
//判断修改权限
if((user.getType()!=null&&user.getType()==1)
|| comment.getUserId()==user.getId()){
//权限允许,开始修改,修改只能改内容
comment.setContent(commentVo.getContent());
int num=commentMapper.updateById(comment);
if(num != 1){
throw ServiceException.busy();
}
return comment;
}
throw ServiceException.invalidRequest("权限不足");
}
同学们需要自己完成对answer表进行删除和编辑的操作!
有能力的同学尝试实现,如果套路或思路还不熟练,那么可以多做几次评论的增删改!
做answer的删除时需要注意
删除一个回答时,要先删除这个回答的所有评论,再删除这个回答,别忘了事务!
开发采纳答案的功能
学生问题详情页
首先我们要将学生访问的问题详情页和老师访问的问题详情页分离
就像我们开发老师首页和学生首页一样
现在我们detail.html页面是老师访问的标准
步骤1
赋值当前的detail.html页面,名为detail_teacher.html
步骤2:
在detail.html页面代码中删除学生不能执行的操作
如果找不到删除的标签,见参考代码,参考代码中采用了注释的方式编写
步骤3:
将老师和学生业务逻辑不同的js文件分离
只需要将question.detail.js文件中,postAnswer模块的代码分离出来
复制到一个新建的js文件:post_answer.js中
老师的detail_teacher.html中引入question.detail.js和post_answer.js
而学生的detail.html只引入question.detail.js