开发个人博客的那些事 下

开发个人博客的那些事 下

  1. 编辑博客内容使用Markdown编辑风格

    • 编辑时使用Markdown编辑风格的方法

      • 首先需要引入使用Markdown编辑风格所需要的资源。

        <!--css-->
        <link rel="stylesheet" href="../../static/editormd/css/editormd.min.css" th:href="@{/editormd/css/editormd.min.css}">
        
        <!--js-->
        <script src="../../static/editormd/editormd.min.js" th:src="@{/editormd/editormd.min.js}"></script>
        
      • 在script标签域初始化Markdown编辑器

        <script>
        	//初始化Markdown编辑器
            var contentEditor;
            $(function() {
                contentEditor = editormd("md-content", {
                    width: "100%",
                    height: 640,
                    syncScrolling: "single",
                    //path: "../../static/editormd/lib/",
                    //注意文件夹要放到resources资源包下的static包下
                    path: "/editormd/lib/"     
                });
            });
        </script>
        
      • 在需要的使用的地方加入下面代码即可

        <body>
            <div class="required field">
                <div id="md-content" style="z-index: 1!important;">
                    <textarea placeholder="博客内容" name="content" th:text="*{content}">
                    </textarea>
                </div>
            </div>
        </body>
        
    • 详情页显示时使用Markdown编辑风格的方法

      • 需要使用到Markdown工具类

        package com.fmxu.util;
        
        import org.commonmark.Extension;
        import org.commonmark.ext.gfm.tables.TableBlock;
        import org.commonmark.ext.gfm.tables.TablesExtension;
        import org.commonmark.ext.heading.anchor.HeadingAnchorExtension;
        import org.commonmark.node.Link;
        import org.commonmark.node.Node;
        import org.commonmark.parser.Parser;
        import org.commonmark.renderer.html.AttributeProvider;
        import org.commonmark.renderer.html.AttributeProviderContext;
        import org.commonmark.renderer.html.AttributeProviderFactory;
        import org.commonmark.renderer.html.HtmlRenderer;
        
        import java.util.*;
        
        /**
         * @Auther: Phoenix
         * @Date: 2021/03/17 20:07
         * @Description: markdown编辑器工具类
         */
        public class MarkdownUtils {
            /**
             * markdown格式转换成HTML格式
             * @param markdown
             * @return
             */
            public static String markdownToHtml(String markdown) {
                Parser parser = Parser.builder().build();
                Node document = parser.parse(markdown);
                HtmlRenderer renderer = HtmlRenderer.builder().build();
                return renderer.render(document);
            }
        
            /**
             * 增加扩展[标题锚点,表格生成]
             * Markdown转换成HTML
             * @param markdown
             * @return
             */
            public static String markdownToHtmlExtensions(String markdown) {
                //h标题生成id
                Set<Extension> headingAnchorExtensions = Collections.singleton(HeadingAnchorExtension.create());
                //转换table的HTML
                List<Extension> tableExtension = Arrays.asList(TablesExtension.create());
                Parser parser = Parser.builder()
                    .extensions(tableExtension)
                    .build();
                Node document = parser.parse(markdown);
                HtmlRenderer renderer = HtmlRenderer.builder()
                    .extensions(headingAnchorExtensions)
                    .extensions(tableExtension)
                    .attributeProviderFactory(new AttributeProviderFactory() {
                        public AttributeProvider create(AttributeProviderContext context) {
                            return new CustomAttributeProvider();
                        }
                    })
                    .build();
                return renderer.render(document);
            }
        
            /**
             * 处理标签的属性
             */
            static class CustomAttributeProvider implements AttributeProvider {
                @Override
                public void setAttributes(Node node, String tagName, Map<String, String> attributes) {
                    //改变a标签的target属性为_blank
                    if (node instanceof Link) {
                        attributes.put("target", "_blank");
                    }
                    if (node instanceof TableBlock) {
                        attributes.put("class", "ui celled table");
                    }
                }
            }
        
        }
        
      • 在获取到博客信息时,调用工具类对于博客正文内容渲染

        //定义的sevice层实现方法
        @Override
        public Blog getAndConvert(Long id) {
        
            Blog blog=blogRepository.getOne(id);
            blog.setViews(blog.getViews()+1);
            blogRepository.save(blog);
            if(blog==null){
                throw  new NotFoundException("该博客不存在");
            }
            Blog blog1=new Blog();
            BeanUtils.copyProperties(blog,blog1);
            String content=blog.getContent();
            //此处调用Markdown工具类方法渲染博客正文
            blog1.setContent(MarkdownUtils.markdownToHtmlExtensions(content));
            return blog1;
        }
        
  2. 评论功能

    • 评论实体类的设计

      @Entity
      @Table(name = "t_comment")
      public class Comment {
          @Id
          @GeneratedValue
          private Long id;
      
          //昵称
          private String nickname;
      
          //邮箱
          private String email;
      
          //内容
          private String content;
      
          //头像
          private String avatar;
      
      
          //创立时间
          @Temporal(TemporalType.TIMESTAMP)
          private Date createTime;
      
          @ManyToOne
          private Blog blog;
      	//需要自关联
          //一个父节点评论可能有多个子节点评论
          @OneToMany(mappedBy = "parentComment")
          private List<Comment> replyComments=new ArrayList<>();
      
          //多个子节点评论对应于一个父节点评论
          @ManyToOne
          private Comment parentComment;
      
          //判断是否为博主的评论
          private Boolean adminComment;
          
          ...
      }
      
    • service接口

      package com.fmxu.service;
      
      import com.fmxu.po.Comment;
      
      import java.util.List;
      
      /**
       * @author Dell
       * @projectName blog
       * @description: 评论接口
       * @date 2021/3/19/10:02
       */
      public interface CommentService {
      
          //根据博客id查询博客评论信息
          List<Comment> listCommentByBlogId(Long blogId);
      
          //保存评论信息
          Comment saveComment(Comment comment);
      }
      
      
    • service实现类中方法的实现

      package com.fmxu.service;
      
      import com.fmxu.dao.CommentRepository;
      import com.fmxu.po.Comment;
      import org.springframework.beans.BeanUtils;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.data.domain.Sort;
      import org.springframework.stereotype.Service;
      
      import java.util.ArrayList;
      import java.util.Date;
      import java.util.List;
      
      /**
       * @author Dell
       * @projectName blog
       * @description: 评论接口实现类
       * @date 2021/3/19/10:02
       */
      @Service
      public class CommentServiceImpl implements CommentService {
      
      
          @Autowired
          private CommentRepository commentRepository;
      
          //根据
          @Override
          public List<Comment> listCommentByBlogId(Long blogId) {
              //根据评论的创立时间逆序排序
              Sort sort=Sort.by(Sort.Direction.DESC,"createTime");
              //找到评论信息中parentComment属性值为null的,说明它是顶级评论,即是评论的根节点
              List<Comment> comments=commentRepository.findByBlogIdAndParentCommentNull(blogId,sort);
              //调用eachComment方法去查询父评论的所有子评论,并按序排列放入列表
              return eachComment(comments);
          }
      
          @Override
          public Comment saveComment(Comment comment) {
              Long parentCommentId=comment.getParentComment().getId();
              //判断是否有父评论,即判断是新添加评论还是回复评论
              if(parentCommentId!=-1){
                  comment.setParentComment(commentRepository.getOne(parentCommentId));
              }else {
                  comment.setParentComment(null);
              }
              comment.setCreateTime(new Date());
              return commentRepository.save(comment);
          }
      
      
          /**
           * 循环每个顶级的评论节点
           * @param comments
           * @return
           */
          private List<Comment> eachComment(List<Comment> comments) {
              List<Comment> commentsView = new ArrayList<>();
              for (Comment comment : comments) {
                  Comment c = new Comment();
                  BeanUtils.copyProperties(comment,c);
                  commentsView.add(c);
              }
              //合并评论的各层子代到第一级子代集合中
              combineChildren(commentsView);
              return commentsView;
          }
      
          /**
           *
           * @param comments root根节点,blog不为空的对象集合
           * @return
           */
          private void combineChildren(List<Comment> comments) {
      
              for (Comment comment : comments) {
                  List<Comment> replys1 = comment.getReplyComments();
                  for(Comment reply1 : replys1) {
                      //循环迭代,找出子代,存放在tempReplys中
                      recursively(reply1);
                  }
                  //修改顶级节点的reply集合为迭代处理后的集合
                  comment.setReplyComments(tempReplys);
                  //清除临时存放区
                  tempReplys = new ArrayList<>();
              }
          }
      
          //存放迭代找出的所有子代的集合
          private List<Comment> tempReplys = new ArrayList<>();
          /**
           * 递归迭代,剥洋葱
           * @param comment 被迭代的对象
           * @return
           */
          private void recursively(Comment comment) {
              tempReplys.add(comment);//顶节点添加到临时存放集合
              if (comment.getReplyComments().size()>0) {
                  List<Comment> replys = comment.getReplyComments();
                  for (Comment reply : replys) {
                      if (reply.getReplyComments().size()>0) {
                          recursively(reply);
                      }else {
                          tempReplys.add(reply);
                      }
                  }
              }
          }
      }
      
    • Controller类的设计

      package com.fmxu.web;
      
      import com.fmxu.po.Blog;
      import com.fmxu.po.Comment;
      import com.fmxu.po.User;
      import com.fmxu.service.BlogService;
      import com.fmxu.service.CommentService;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.beans.factory.annotation.Value;
      import org.springframework.boot.SpringApplication;
      import org.springframework.stereotype.Controller;
      import org.springframework.ui.Model;
      import org.springframework.validation.annotation.Validated;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.PathVariable;
      import org.springframework.web.bind.annotation.PostMapping;
      import sun.dc.pr.PRError;
      
      import javax.servlet.http.HttpSession;
      
      /**
       * @author Dell
       * @projectName blog
       * @description:
       * @date 2021/3/19/10:39
       */
      @Controller
      public class CommentController {
      
          @Autowired
          private CommentService commentService;
      
          @Autowired
          private BlogService blogService;
      
          @Value("${comment.avatar}")
          private String avatar;
      
      
          //根据博客id查询对应的评论信息
          @GetMapping("/comments/{blogId}")
          public String comments(@PathVariable Long blogId, Model model){
      		
              model.addAttribute("comments",commentService.listCommentByBlogId(blogId));
              //返回局部区域,只刷新当前区域
              return "blog :: commentList";
          }
      
      
      	//保存评论信息
          @PostMapping("/comments")
          public String post(Comment comment, HttpSession session){
              Long blogId=comment.getBlog().getId();
              comment.setBlog(blogService.getBlog(blogId));
              //判断session域中是否有user,即判断是否为博主
              User user= (User) session.getAttribute("user");
              if(user!=null){
                  comment.setAvatar(user.getAvatar());
                  comment.setEmail(user.getEmail());
                  comment.setNickname(user.getNickname());
                  comment.setAdminComment(true);
              }else {
                  comment.setAvatar(avatar);
              }
              commentService.saveComment(comment);
              return "redirect:/comments/"+blogId;
          }
      }
      
    • 前端页面

      <!--评论区域-->
      <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/800/450?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>
                              </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>
                                          &nbsp;<span th:text="|@ ${reply.parentComment.nickname}|" style="color: #1abc9c">@ 小白</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>
                                      </div>
                                  </div>
                              </div>
                          </div>
                      </div>
                  </div>
              </div>
          </div>
          <div id="comment-form" class="ui form">
              <input type="hidden" name="blog.id" 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-mobile-wide m-margin-bottom-small">
                      <button id="commentpost-btn" type="button"  class="ui teal button"><i class="ui edit icon"></i>发布</button>
                  </div>
              </div>
          </div>
      </div>
      
    • JS代码

      <script th:inline="javascript">
          //表单校验
      $('.ui.form').form({
          fields:{
              content:{
                  identifier:'content',
                  rules:[{
                      type:'empty',
                      prompt:'内容:请输入您的评论内容',
                  }]
              },
              nickname:{
                  identifier:'nickname',
                  rules:[{
                      type:'empty',
                      prompt:'内容:请输入您的昵称',
                  }]
              },
              email:{
                  identifier:'email',
                  rules:[{
                      type: 'email',
                      prompt:'内容:请输入您的邮箱',
                  }]
              },
          }
      });
      $('#commentpost-btn').click(function () {
          var boo= $('.ui.form').form('validate form');
          if(boo){
              console.log("校验成功")
              postData();
          }else {
              console.log("校验失败")
          }
      });
      //向后端发送请求,传递数据
      function postData() {
          $("#comment-container").load(/*[[@{/comments}]]*/"",{
              "parentComment.id" :$("[name='parentComment.id']").val(),
              "blog.id" :$("[name='blog.id']").val(),
              "nickname" :$("[name='nickname']").val(),
              "email" :$("[name='email']").val(),
              "content" :$("[name='content']").val(),
          },function (responseTxt,statusTxt,xhr) {
              $(window).scrollTo($('#comment-container'),500);
              clearContent();
          });
      }
      //清空表单
      function clearContent() {
          $("[name='content']").val('');
          $("[name='parentComment.id']").val(-1);
          $("[name='content']").attr("placeholder", "请输入评论信息...");
      };
      
      //评论回复
      function reply(obj) {
          //获取到回复按钮传递过来的值,即被回复者的id和昵称
          var commentId = $(obj).data('commentid');
          var commentNickname = $(obj).data('commentnickname');
          $("[name='content']").attr("placeholder", "@"+commentNickname).focus();
          $("[name='parentComment.id']").val(commentId);
          $(window).scrollTo($('#comment-form'),500);
      }
      </script>
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值