SpringBoot+Vue+Mybatis-plus 博客(六):完成评论管理前后端交互

SpringBoot+Vue+Mybatis-plus 博客:个人博客介绍及效果展示
SpringBoot+Vue+Mybatis-plus 博客(一):完成博客后台前端登录页面、后端登录接口
SpringBoot+Vue+Mybatis-plus 博客(二):完成登录的前后端对接、完善左侧菜单栏
SpringBoot+Vue+Mybatis-plus 博客(三):完成搜索及博客列表展示功能前后端
SpringBoot+Vue+Mybatis-plus 博客(四):完成发布文章、编辑文章、删除文章及查询文章功能
SpringBoot+Vue+Mybatis-plus 博客(五):完成分类管理和标签管理前后端对接
SpringBoot+Vue+Mybatis-plus 博客(六):完成评论管理前后端交互
SpringBoot+Vue+Mybatis-plus 博客(七):完成友链管理前后端对接

文章目录

    • 效果
  • 一、评论后端接口
    • 1、TComment实体类
    • 2、TCommentMapper
    • 3、TCommentMapper.xml
    • 4、TCommentService
    • 5、TCommentServiceImpl
    • 6、TCommentController
  • 二、评论前端页面
    • CommentBlog.vue

效果

评论管理
SpringBoot+Vue+Mybatis-plus 博客(六):完成评论管理前后端交互_第1张图片
搜索
SpringBoot+Vue+Mybatis-plus 博客(六):完成评论管理前后端交互_第2张图片
回复
SpringBoot+Vue+Mybatis-plus 博客(六):完成评论管理前后端交互_第3张图片

一、评论后端接口

1、TComment实体类

@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="TComment对象", description="")
public class TComment implements Serializable {

    private static final long serialVersionUID = 1L;

    @JsonSerialize(using = ToStringSerializer.class) //系统序列化时,保留相关精度
    @ApiModelProperty(value = "评论id")
    private Long id;

    @ApiModelProperty(value = "是否为管理员评论")
    private Boolean adminComment;

    @ApiModelProperty(value = "头像")
    private String avatar;

    @ApiModelProperty(value = "评论内容")
    private String content;

    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "Asia/ShangHai")
    @ApiModelProperty(value = "创建时间")
    private LocalDateTime createTime;

    @ApiModelProperty(value = "邮箱")
    private String email;

    @ApiModelProperty(value = "ip地址")
    private String ip;

    @ApiModelProperty(value = "昵称")
    private String nickname;

    @JsonSerialize(using = ToStringSerializer.class) //系统序列化时,保留相关精度
    @ApiModelProperty(value = "博客id")
    private Long blogId;

    @JsonSerialize(using = ToStringSerializer.class) //系统序列化时,保留相关精度
    @ApiModelProperty(value = "父评论id")
    private Long parentCommentId;

    //回复评论
    @TableField(exist = false) //查询时过滤非数据库字段
    private List<TComment> replyComments = new ArrayList<>();
    @TableField(exist = false) //查询时过滤非数据库字段
    private TComment parentComment;
    @TableField(exist = false) //查询时过滤非数据库字段
    private String parentNickname;

}

2、TCommentMapper

public interface TCommentMapper extends BaseMapper<TComment> {
    //查询父级评论
    List<TComment> findByBlogIdParentIdNull(@Param("blogId") Long blogId, @Param("blogParentId") Long blogParentId);

    //查询一级回复
    List<TComment> findByBlogIdParentIdNotNull(@Param("blogId") Long blogId, @Param("id") Long id);

    //查询二级回复
    List<TComment> findByBlogIdAndReplayId(@Param("blogId") Long blogId,@Param("childId") Long childId);

    //添加一个评论
    int saveComment(TComment comment);

    //删除评论
    void deleteComment(Long id);

}

3、TCommentMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xlblog.blog.mapper.TCommentMapper">


    <!--添加评论-->
    <insert id="saveComment" parameterType="com.xlblog.blog.entity.TComment">
        insert into t_comment (nickname,email,content,avatar,create_time,blog_id,parent_comment_id,admin_comment,ip)
        values (#{nickname},#{email},#{content},#{avatar},#{createTime},#{blogId},#{parentCommentId},#{adminComment},#{ip});
    </insert>

    <!--查询父级评论-->
    <select id="findByBlogIdParentIdNull" resultType="com.xlblog.blog.entity.TComment">
        select *
        from t_comment c
        where c.blog_id = #{blogId} and c.parent_comment_id = #{blogParentId}
        order by c.create_time desc
    </select>

    <!--查询一级子评论-->
    <select id="findByBlogIdParentIdNotNull" resultType="com.xlblog.blog.entity.TComment">
        select *
        from t_comment c
        where c.blog_id = #{blogId} and c.parent_comment_id = #{id}
        order by c.create_time desc
    </select>

    <!--查询二级子评论-->
    <select id="findByBlogIdAndReplayId" resultType="com.xlblog.blog.entity.TComment">
        select *
        from t_comment c
        where c.blog_id = #{blogId} and c.parent_comment_id = #{childId}
        order by c.create_time desc
    </select>

    <!--删除评论-->
    <delete id="deleteComment" >
        delete from t_comment where id = #{id}
    </delete>

</mapper>

4、TCommentService

public interface TCommentService extends IService<TComment> {
    //根据博客id查询评论信息
    List<TComment> listCommentByBlogId(Long blogId);

    //添加保存评论
    int saveComment(TComment comment);

    //删除评论
    void deleteComment(Long id);

    List<TComment> findCommentByNickname(String nickname);

    Page<TComment> getCommentByPage(Long current, Long size);
    
}

5、TCommentServiceImpl

@Service
public class TCommentServiceImpl extends ServiceImpl<TCommentMapper, TComment> implements TCommentService {

    @Autowired
    private TCommentMapper commentMapper;

    @Autowired
    private TBlogMapper blogMapper;
    
    //存放迭代找出的所有子代的集合
    private List<TComment> tempReplys = new ArrayList<>();

    /**
     * @Description: 查询评论
     * @Param: blogId:博客id
     * @Return: 评论消息
     */
    @Override
    public List<TComment> listCommentByBlogId(Long blogId) {
        //查询出父节点
        List<TComment> comments = commentMapper.findByBlogIdParentIdNull(blogId, Long.parseLong("-1"));
        for(TComment comment : comments){
            Long id = comment.getId();
            String parentNickname1 = comment.getNickname();
            List<TComment> childComments = commentMapper.findByBlogIdParentIdNotNull(blogId,id);
            //查询出子评论
            combineChildren(blogId, childComments, parentNickname1);
            comment.setReplyComments(tempReplys);
            tempReplys = new ArrayList<>();
        }
        return comments;
    }

    /**
     * @Description: 查询出子评论
     * @Auther: ONESTAR
     * @Date: 10:43 2020/6/23
     * @Param: childComments:所有子评论
     * @Param: parentNickname1:父评论姓名
     * @Return:
     */
    private void combineChildren(Long blogId, List<TComment> childComments, String parentNickname1) {
        //判断是否有一级子评论
        if(childComments.size() > 0){
            //循环找出子评论的id
            for(TComment childComment : childComments){
                String parentNickname = childComment.getNickname();
                childComment.setParentNickname(parentNickname1);
                tempReplys.add(childComment);
                Long childId = childComment.getId();
                //查询出子二级评论
                recursively(blogId, childId, parentNickname);
            }
        }
    }

    /**
     * @Description: 循环迭代找出子集回复
     * @Auther: ONESTAR
     * @Date: 10:44 2020/6/23
     * @Param: chileId:子评论id
     * @Param: parentNickname1:子评论姓名
     * @Return:
     */
    private void recursively(Long blogId, Long childId, String parentNickname1) {
        //根据子一级评论的id找到子二级评论
        List<TComment> replayComments = commentMapper.findByBlogIdAndReplayId(blogId,childId);

        if(replayComments.size() > 0){
            for(TComment replayComment : replayComments){
                String parentNickname = replayComment.getNickname();
                replayComment.setParentNickname(parentNickname1);
                Long replayId = replayComment.getId();
                tempReplys.add(replayComment);
                recursively(blogId,replayId,parentNickname);
            }
        }
    }

    //新增评论
    @Override
    public int saveComment(TComment comment) {
        comment.setCreateTime(LocalDateTime.now());
        int comments = commentMapper.saveComment(comment);
        //文章评论计数
//        blogMapper.getCommentCountById(comment.getBlogId());
        return comments;
    }

    //删除评论
    @Override
    public void deleteComment(Long id) {
        TComment comment = commentMapper.selectById(id);
        commentMapper.deleteComment(id);
        TBlog tBlog = blogMapper.selectById(comment.getBlogId());
        tBlog.setCommentCount(tBlog.getCommentCount() - 1);
        blogMapper.updateById(tBlog);
//        blogMapper.getCommentCountById(comment.getBlogId());
    }

    //根据昵称查询评论
    @Override
    public List<TComment> findCommentByNickname(String nickname) {
        QueryWrapper<TComment> queryWrapper = new QueryWrapper<TComment>();
        queryWrapper.like("nickname",nickname);
        //以创建时间排序(降序)
        queryWrapper.orderByDesc("create_time");
        List<TComment> tBlogList = commentMapper.selectList(queryWrapper);
        return tBlogList;
    }

    @Override
    public Page<TComment> getCommentByPage(Long current, Long size) {
        //创建Page对象
        Page<TComment> commentPage = new Page<>(current,size);
        //构建条件
        QueryWrapper<TComment> wrapper = new QueryWrapper<>();
        //排序
        wrapper.orderByDesc("create_time");
        //调用mybatis plus分页方法进行查询
        commentMapper.selectPage(commentPage,wrapper);
        //通过Page对象获取分页信息
        List<TComment> typeList = commentPage.getRecords(); //每页的数据 list集合
        long pagesize = commentPage.getSize(); //每页显示的条数
        long total = commentPage.getTotal(); //总记录数
        long pages = commentPage.getPages(); //总页数
        return commentPage;
    }


}

6、TCommentController

@RestController
@RequestMapping("/comment")
public class TCommentController {

    @Autowired
    private TCommentService commentService;
    @Autowired
    private TBlogService tBlogService;

    //查询评论列表
    @GetMapping("/comments/{blogId}")
    public List<TComment> comments(@PathVariable Long blogId) {
        return commentService.listCommentByBlogId(blogId);
    }

    //查询所有评论
    @GetMapping("/getCommentByPage")
    public Page<TComment> getCommentByPage(Long current, Long size){
        return commentService.getCommentByPage(current, size);
    }

    //根据评论人姓名查询评论
    @GetMapping("/comments")
    public List<TComment> findCommentByNickname(String nickname){
        return commentService.findCommentByNickname(nickname);
    }


    //新增评论
    @PostMapping("/comments")
    public List<TComment> post(@RequestBody TComment tComment,Authentication authentication,HttpServletRequest request) {
        //获取评论人的ip
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        Long blogId = tComment.getBlogId();
        if (authentication != null){
            TUser user = (TUser)authentication.getPrincipal();
            tComment.setAvatar(user.getAvatar());
            tComment.setAdminComment(true);
            tComment.setIp(ip);
        }
        else {
            //设置头像
            tComment.setAvatar("https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2070453827,1163403148&fm=26&gp=0.jpg");
            tComment.setIp(ip);
        }
        if (tComment.getParentComment() != null) {
            tComment.setParentCommentId(tComment.getParentComment().getId());
        }
        commentService.saveComment(tComment);
        TBlog tBlog = tBlogService.getById(blogId);
        tBlog.setCommentCount(tBlog.getCommentCount() + 1);
        tBlogService.updateByComment(tBlog);
        return commentService.listCommentByBlogId(blogId);
    }

    //删除评论
    @DeleteMapping("/deleteComment")
    public String delete(Long id){
        commentService.deleteComment(id);

        return "OK";
    }

}

二、评论前端页面

CommentBlog.vue

<template>
  <div>
      <div style="margin-top: 20px;">
          <!-- 搜索 -->
          <el-input size="small" v-model="input_name" placeholder="请输入评论人名称,可回车搜索..." prefix-icon="el-icon-search"
          style="width: 400px;margin-right: 10px;" @keydown.enter.native="search_name"></el-input>
          <el-button size="small" type="primary"  @click="search_name" icon="el-icon-search">搜索</el-button>
          <!-- <el-button size="small" type="success"  @click="dialog_add = true" icon="el-icon-plus">新增</el-button> -->
      </div>
      <div>
          <el-table
          :data="commentData"
          style="width: 100%">
          <el-table-column
            label="编号"
            width="70">
            <template slot-scope="scope"> 
              <span style="margin-left: 10px">{{ scope.row.id }}</span>
            </template>
          </el-table-column>
          <el-table-column
            label="昵称"
            width="150">
            <template slot-scope="scope">
              <el-tag size="medium">{{ scope.row.nickname }}</el-tag>
            </template>
          </el-table-column>
          <el-table-column
            label="身份"
            width="100">
            <template slot-scope="scope">
              <el-tag v-if="scope.row.adminComment" type="danger" size="medium">管理员</el-tag>
              <el-tag v-if="!scope.row.adminComment" size="medium">游客</el-tag>
            </template>
          </el-table-column>
          <el-table-column
            label="评论内容"
            width="300">
            <template slot-scope="scope">
              <el-tag size="medium">{{ scope.row.content }}</el-tag>
            </template>
          </el-table-column>
          <el-table-column
            label="发表时间"
            width="230">
            <template slot-scope="scope">
              <el-tag size="medium">{{ scope.row.createTime }}</el-tag>
            </template>
          </el-table-column>
          <el-table-column
            label="ip地址"
            width="180">
            <template slot-scope="scope">
              <el-tag size="medium">{{ scope.row.ip }}</el-tag>
            </template>
          </el-table-column>
          <el-table-column
            label="头像"
            width="230">
            <template slot-scope="scope">
              <el-tag size="medium">{{ scope.row.avatar }}</el-tag>
            </template>
          </el-table-column>
          <el-table-column
            label="博客id"
            width="200">
            <template slot-scope="scope">
              <el-tag size="medium">{{ scope.row.blogId }}</el-tag>
            </template>
          </el-table-column>
          <el-table-column
            label="父评论id"
            width="100">
            <template slot-scope="scope">
              <el-tag size="medium">{{ scope.row.parentCommentId }}</el-tag>
            </template>
          </el-table-column>


          <el-table-column label="操作"  fixed="right" width="180">
            <template slot-scope="scope">
              <el-button
                size="mini"
                type="primary"
                @click="handleLook(scope.$index, scope.row)">回复</el-button>
              <!-- <el-button
                size="mini"
                @click="handleEdit(scope.$index, scope.row)">编辑</el-button> -->
              <el-button
                size="mini"
                type="danger"
                @click="handleDelete(scope.$index, scope.row)">删除</el-button>
            </template>
          </el-table-column>
        </el-table>
      </div>

      <!-- 分页 -->
      <div style="margin-top: 20px;" v-if="showPage">
        <el-pagination
        background
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page.sync="currentPage"
        :page-size="pagesize"
        layout="total, prev, pager, next"
        :total="total">
      </el-pagination>
      </div>

      <!-- 对话框 回复评论 -->
      <el-dialog
        title="回复评论"
        :visible.sync="dialog_look"
        width="30%"
      >
      <el-form status-icon  label-width="80px" :model="subComment" ref="subComment">
        <el-form-item label="文章:">
          {{BackComment.blogTitle}}
        </el-form-item>
        <el-form-item label="昵称:">
          {{BackComment.nickname}}
        </el-form-item>
        <el-form-item label="内容:">
          {{BackComment.content}}
        </el-form-item>
        <el-form-item label="日期:">
          {{BackComment.time}}
        </el-form-item>
        <el-form-item label="回复:" prop="content"
         :rules=" { required: true, message: '请输入评论内容', trigger: 'blur' }">
          <el-input type="textarea" v-model="subComment.content"></el-input>
        </el-form-item>
      </el-form>
        <span slot="footer" class="dialog-footer">
          <el-button @click="dialog_look = false">取 消</el-button>
          <el-button type="primary" @click="submitBackComment('subComment')">确 定</el-button>
        </span>
      </el-dialog>


  </div>
</template>

<script>
export default {
  name: 'CommentBlog',
  data () {
   return {
      commentData:[],  //评论数据
      currentPage: 1,  //当前页
      total:0, //总记录数
      pagesize:5, //页面大小
      input_name:'', //搜索框值
      dialog_add: false, //添加评论的对话框
      dialog_edit: false, //编辑评论的对话框
      type_edit:{
        id:'',
        name:''
      },
      showPage: true, //是否显示分页
      dialog_look: false, //显示回复的对话框
      BackComment: { 
        blogTitle: '',
        nickname: '',
        content: '',
        time: '',
      }, 
      subComment:{ 
        blogId:'',
        parentCommentId:'',
        adminComment:'',
        nickname:'小L-Admin',
        content:'', //回复评论的内容
        email:'[email protected]'
      }
   }
  },
  mounted() {
    this.initComment();
  },
  methods:{
    //初始化评论数据
    initComment(){
      const _this = this
      this.getRequest('/comment/getCommentByPage?current=' + this.currentPage + '&size=' + this.pagesize).then(resp=>{
        console.log(resp)
        _this.commentData = resp.records
        _this.total = resp.total
      })
    },
    //查看评论
    handleLook(index, row){
      console.log(row)
      const _this = this
      _this.dialog_look = true
      this.getRequest('/blog/getByBlogId?id=' + row.blogId).then(resp=>{
        console.log(resp)
        _this.BackComment.blogTitle = resp.obj.title
      })
      _this.BackComment.nickname = row.nickname
      _this.BackComment.content = row.content
      _this.BackComment.time = row.createTime

      _this.subComment.blogId = row.blogId
      _this.subComment.parentCommentId = row.parentCommentId
      _this.subComment.adminComment = true
    },
    //提交回复
    submitBackComment(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          const _this = this
          this.postRequest('/comment/comments',this.subComment).then(resp=>{
            console.log(resp)
            _this.initComment()
            _this.BackComment.backComment = ''
            _this.dialog_look = false
            this.$message({ message: '回复评论成功!',type: 'success' });
          })

        } else {
          console.log('error submit!!');
          return false;
        }
      });
    },
    //编辑评论
    handleEdit(index, row) {
      const _this = this
      console.log(index, row);
      this.dialog_edit = true
      this.getRequest('/type/getTypeById?id=' + row.id).then(resp=>{
          console.log(resp)
          _this.type_edit = resp.obj
      })
    },
    //更新评论
    updateType(){
      const _this = this 
      this.putRequest('/type/updateType',this.type_edit).then(resp=>{
        if(resp){
          this.initComment()
          _this.dialog_edit = false
        }
      })
    },
    //删除评论
    handleDelete(index, row) {
      console.log(index, row);
      const _this = this
      this.$confirm('此操作将永久删除该评论, 是否继续?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          this.deleteRequest('/comment/deleteComment?id=' + row.id).then(resp=>{
            if(resp){
              this.initComment()
              this.$message({
                type: 'success',
                message: '已删除该评论'
              });     
            }
          })
        }).catch(() => {
          this.$message({
            type: 'info',
            message: '已取消删除'
          });          
        });
     
    },
    //搜索评论
    search_name(){
       const _this = this
       this.getRequest('/comment/comments?nickname=' + this.input_name).then(resp=>{
         console.log(resp)
         if(_this.input_name == ''){
           _this.initComment();
           _this.showPage = true
         }else{
          _this.commentData = resp
          _this.showPage = false
          _this.input_name = ''
         }
         

       })
    },
    handleSizeChange(val) {
      console.log(`每页 ${val}`);
    },
    handleCurrentChange(val) {
      console.log(`当前页: ${val}`);
      this.currentPage = val
      this.initComment()
    }
  }
}
</script>

<style scoped>

</style>

关注【小L星光】回复 “博客” 即可获整个项目源码 ~
SpringBoot+Vue+Mybatis-plus 博客(六):完成评论管理前后端交互_第4张图片

你可能感兴趣的:(小L星光博客)