SpringBoot个人博客开发(十五)----功能实现4.博客管理

功能实现4.博客管理

终于到了博客管理了,这里呢还是按老步骤上效果图
https://i0.hdslb.com/bfs/album/09aa8b7f697e45648eee461e73ccc8b6557a4d04.gif
效果图实现步骤也是一样的
先把加上each什么的新的管理页和发布页列出来
blogs.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org/">
<head th:replace="admin/_fragments :: head(~{::title})">
    <title>博客管理</title>
</head>
<body>

    <!--头部导航-->
    <nav th:replace="admin/_fragments :: menu(1)"></nav>
    <!--二级导航-->
    <div class="ui inverted attached pointing menu">
        <div class="ui container">
            <div class="right menu">
                <a href="#" th:href="@{/admin/blogs/post}" class="item">发布</a>
                <a href="#" th:href="@{/admin/blogs}" class="active teal item">列表</a>
            </div>
        </div>
    </div>
    <!--页面主体-->
    <div class="m-container-small m-padded-tb-big">
        <div class="ui container">
            <!--查询功能的表单-->
            <div method="post" class="ui segment form">
                <input type="hidden" name="page">
                <!-- 标题 -->
                <div class=" inline fields">
                    <div class="field">
                        <input type="text" name="title" placeholder="标题">
                    </div>
                    <!-- 分类下拉框 -->
                    <div class="field">
                        <div class="ui labeled action input">
                            <div class="ui type selection dropdown">
                                <input type="hidden" name="typeId">
                                <i class="dropdown icon"></i>
                                <div class="default text">分类</div>
                                <div class="menu">
                                    <div th:each="type : ${types}" class="item" data-value="1" th:data-value="${type.id}" th:text="${type.name}">错误日志</div>
                                    <!--                                <div class="item" data-value="2">开发者手册</div>-->
                                </div>
                            </div>
                            <button id="clear-btn" class="ui compact button">Clear</button>
                        </div>
                    </div>
                    <!-- 推荐复选框 -->
                    <div class="field">
                        <div class="ui checkbox">
                            <input type="checkbox" id="recommend" name="recommend">
                            <label for="recommend">推荐</label>
                        </div>
                    </div>
                    <!-- 搜索按钮 -->
                    <div class="field">
                        <button type="button" id="search-btn" class="ui teal mini basic button"><i class="search icon"></i>搜索</button>
                    </div>
                </div>
            </div>
            <!-- table 列表-->
            <div id="table-container">
                <table th:fragment="blogList" class="ui celled table">
                    <thead>
                    <tr >
                        <th >#</th>
                        <th >标题</th>
                        <th >类型</th>
                        <th >推荐</th>
                        <th >状态</th>
                        <th>更新时间</th>
                        <th>操作</th>
                    </tr>
                    </thead>
                    <tbody>
                    <tr th:each="blog,iterStat : ${page.content}">
                        <td th:text="${iterStat.count}">1</td>
                        <td th:text="${blog.title}">叽里呱啦</td>
                        <td th:text="${blog.type.name}">叽里呱啦</td>
                        <td th:text="${blog.recommend} ? '':''"></td>
                        <td th:text="${blog.published} ? '发布':'草稿'">草稿</td>
                        <td th:text="${blog.updateTime}">年-月-日 小时:分钟</td>
                        <td>
                            <a href="#" th:href="@{/admin/blogs/{id}/post(id=${blog.id})}" class="ui mini teal basic button">编辑</a>
                            <a href="#" th:href="@{/admin/blogs/{id}/delete(id=${blog.id})}" class="ui mini red basic button">删除</a>
                        </td>
                    </tr>
                    </tbody>
                    <tfoot>
                    <tr>
                        <th colspan="7">
                            <div class="ui teal pagination menu" th:if="${page.totalPages}>1">
                                <a onclick="page(this)" th:attr="data-page=${page.number}-1" class="item" th:unless="${page.first}">上一页</a>
                                <a onclick="page(this)" th:attr="data-page=${page.number}+1" class="item" th:unless="${page.last}">下一页</a>
                            </div>
                            <a th:href="@{/admin/blogs/post}" class="ui right floated primary basic button">新增</a>
                        </th>
                    </tr>
                    </tfoot>
                </table>
                <div class="ui success message" th:unless="${#strings.isEmpty(message)}">
                    <i class="close icon"></i>
                    <div class="header">提示:</div>
                    <p th:text="${message}">操作成功</p>
                </div>
            </div>

        </div>
    </div>
    <th:block th:replace="admin/_fragments :: script"> </th:block>
    <!--按钮点击事件-->
    <script>

        $('.menu.toggle').click(function () {
            $('.m-item').toggleClass('m-mobile-hide');
        });

        $('.ui.dropdown').dropdown({
            on : 'hover'
        });

        function page(obj){
            $("[name='page']").val($(obj).data("page"));
            loaddata();
        }

        $("#search-btn").click(function (){
            $("[name='page']").val(0);
            loaddata();
        });

        function loaddata(){
            $("#table-container").load(/*[[@{/admin/blogs/search}]]*/"/admin/blogs/search",{
                title : $("[name='title']").val(),
                typeId : $("[name='typeId']").val(),
                recommend : $("[name='recommend']").prop('checked'),
                page : $("[name='page']").val()
            });
        }

        //消息提示关闭初始化
        $('.message .close')
            .on('click', function () {
                $(this)
                    .closest('.message')
                    .transition('fade');
            });
        //消息提示关闭初始化
        $('#clear-btn')
          .on('click', function () {
            $('.ui.type.dropdown')
              .dropdown('clear')
          });

    </script>
</body>
</html>

blogs_post.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org/">
<head th:replace="admin/_fragments :: head(~{::title})">
    <title>博客发布</title>
</head>

<body>
<!--导航-->
<nav th:replace="admin/_fragments :: menu(1)" ></nav>
<!--二级导航-->
<div class="ui inverted attached pointing menu">
    <div class="ui container">
        <div class="right menu">
            <a href="#" th:href="@{/admin/blogs/post}" class=" active teal item">发布</a>
            <a href="#" th:href="@{/admin/blogs}" class="item">列表</a>
        </div>
    </div>
</div>



<!--页面主体-->
<div class="m-container-small m-padded-tb-big">
    <div class="ui container">
        <form id="blog-form" action="#" th:object="${blog}" th:action="@{/admin/blogs}" method="POST" class="ui form">
            <input type="hidden" name="published" >
            <input type="hidden" name="id" th:value="*{id}">
            <!--标题-->
            <div class="required field">
                <div class="ui left labeled input">
                    <!--下拉菜单-->
                    <div class="ui selection compact dropdown label">
                        <input type="hidden" value="原创" name="flag" th:value="*{flag}">
                        <i class="dropdown icon"></i>
                        <div class="text">原创</div>
                        <div class="menu">
                            <div class="item" data-value="原创">原创</div>
                            <div class="item" data-value="转载">转载</div>
                            <div class="item" data-value="翻译">翻译</div>
                        </div>
                    </div>
                    <!--标题栏-->
                    <input type="text" name="title" placeholder="标题" th:value="*{title}">
                </div>
            </div>

            <!--博客内容-->
            <div class="required field">
                <div id="md-content" style="z-index: 1 !important;">
                        <textarea name="content" cols="30" rows="10" placeholder="博客内容" th:text="*{content}">
                        </textarea>
                </div>
            </div>
            <!-- 分类和标签 -->
            <div class="two fields">
                <div class="required field">
                    <div class="ui left labeled action input">
                        <label class="ui compact teal basic label">分类</label>
                        <div class="ui fluid selection dropdown">
                            <input type="hidden" name="type.id" th:value="*{type} != null ? *{type.id}">
                            <i class="dropdown icon"></i>
                            <div class="default text">分类</div>
                            <div class="menu">
                                <div th:each="type : ${types}" class="item" data-value="1" th:data-value="${type.id}" th:text="${type.name}">错误日志</div>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="field">
                    <div class="ui left labeled action input">
                        <label class="ui compact teal basic label">标签</label>
                        <div class="ui fluid multiple selection search dropdown">
                            <input type="hidden" name="tagIds" th:value="*{tagIds}">
                            <i class="dropdown icon"></i>
                            <div class="default text">标签</div>
                            <div class="menu">
                                <div th:each="tag : ${tags}" class="item" data-value="1" th:data-value="${tag.id}" th:text="${tag.name}">Python</div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>

            <!--首页图片-->
            <div class="required field">
                <div class="ui  left labeled input">
                    <label class="ui basic label">首页图片</label>
                    <input type="text" name="firstPicture" th:value="*{firstPicture}" placeholder="首页图片引用地址">
                </div>
            </div>
            <div class="required field">
                <div id="md-description" style="z-index: 1 !important;">
                <textarea name="description" th:value="*{description}" th:text="*{description}" placeholder="博客描述" maxlength="200"></textarea>
                </div>
            </div>
            <!-- 自定义复选框 -->
            <div class="inline fields">
                <div class="field">
                    <div class="ui checkbox">
                        <input type="checkbox" id="recommend" name="recommend" checked th:checked="*{recommend}" class="hidden">
                        <label for="recommend" style="color: beige">是否推荐</label>
                    </div>
                </div>
                <div class="field">
                    <div class="ui checkbox">
                        <input type="checkbox" id="shareStatement" name="shareStatement" th:checked="*{shareStatement}" class="hidden" checked>
                        <label for="shareStatement" style="color: beige">转载声明</label>
                    </div>
                </div>
                <div class="field">
                    <div class="ui checkbox">
                        <input type="checkbox" id="appreciation" name="appreciation" th:checked="*{appreciation}" class="hidden">
                        <label for="appreciation" style="color: beige">赞赏功能</label>
                    </div>
                </div>
                <div class="field">
                    <div class="ui checkbox">
                        <input type="checkbox" id="commentabled" name="commentabled" th:checked="*{commentabled}" class="hidden" checked>
                        <label for="commentabled" style="color: beige">允许评论</label>
                    </div>
                </div>
            </div>

            <!--错误提示栏-->
            <div class="ui error message"></div>

            <!--博客是否提交-->
            <div class="ui right aligned container">
                <button type="button" class="ui basic button" onclick="window.history.go(-1)">返回</button>
                <button type="button" id="save-btn" class="ui  button">保存</button>
                <button type="button" id="publish-btn" class="ui teal button">发布</button>
            </div>
        </form>
    </div>
</div>



    <th:block th:replace="admin/_fragments :: script"> </th:block>
<!--按钮点击事件-->
<script>
    //菜单按钮
    $('.menu.toggle').click(function () {
        $('.m-item').toggleClass('m-mobile-hide');
    });
    //头像下拉菜单事件
    $('.ui.dropdown').dropdown();
    //初始化md编辑器
    var mdEditor;
    $(function() {
        mdEditor = editormd("md-content", {
            width   : "100%",
            height  : 640,
            syncScrolling : "single",
            //path    : "../static/lib/editormd/lib/"
            path    : "/lib/editormd/lib/"
        });
    });
    //非空检测
    $('.ui.form').form({
        fields:{
            title:{
                identifier:'title',rules:[{
                    type:'empty',
                    prompt:'标题:请输入博客标题'
                }]
            },
            content : {
                identifier: 'content',
                rules: [{
                    type : 'empty',
                    prompt: '标题:请输入博客内容'
                }]
            },
            typeId : {
                identifier: 'type.id',
                rules: [{
                    type : 'empty',
                    prompt: '标题:请输入博客分类'
                }]
            },
            firstPicture : {
                identifier: 'firstPicture',
                rules: [{
                    type : 'empty',
                    prompt: '标题:请输入博客首图'
                }]
            },description : {
                identifier: 'description',
                rules: [{
                    type : 'empty',
                    prompt: '标题:请输入博客描述'
                }]
            }
        }
    });
    $('#save-btn').click(function () {     //点击保存,修改发布状态为false
        $('[name="published"]').val(false);
        $('#blog-form').submit();
    });
    $('#publish-btn').click(function () {   //点击发布,修改发布状态为true
        $('[name="published"]').val(true);
        $('#blog-form').submit();
    });
    // $('.ui.tag.dropdown')//自定义标签
    // .dropdown({
    //     allowAdditions: true
    // });
</script>
</body>

</html>

老一套了,创建一个接口,一个接口实现,一个继承,一个控制器。
BlogService接口

package net.yq.springbootblog.service;

import net.yq.springbootblog.PersisentObject.Blog;
import net.yq.springbootblog.vo.BlogQuery;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.util.List;
import java.util.Map;

public interface BlogService {
	Blog getBlog(Long id);//获取博客
	Page<Blog> listBlog(Pageable pageable)//分页获取博客
	Page<Blog> listBlog(Pageable pageable, BlogQuery blog);//博客管理搜索时分页获取博客
	Blog saveBlog(Blog blog);//保存博客
	Blog updateBlog(Long id,Blog blog);//更新博客
	void deleteBlog(Long id);//删除博客


}

实现类
BlogServiceImpl
加上@Service注解
实现之前定义好的BlogService
在这里插入图片描述
BlogRepository继承JPA

public interface BlogRepository extends JpaRepository<Blog,Long>,JpaSpecificationExecutor<Blog>{

}

继承弄好了后回到实现类
看注释吧

package net.yq.springbootblog.service;

import net.yq.springbootblog.NotFoundException;
import net.yq.springbootblog.PersisentObject.Blog;
import net.yq.springbootblog.PersisentObject.Type;
import net.yq.springbootblog.repository.BlogRepository;
import net.yq.springbootblog.util.MarkdownUtils;
import net.yq.springbootblog.util.MyBeanUtils;
import net.yq.springbootblog.vo.BlogQuery;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.annotation.Transient;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.*;
import java.util.*;


@Service
public class BlogServiceImpl implements BlogService{

    @Autowired
    private BlogRepository blogRepository;

    @Override
    public Blog getBlog(Long id) {
        return blogRepository.getById(id);//根据ID获取博客
    }

    @Override
    public Page<Blog> listBlog(Pageable pageable, final BlogQuery blog) {
        return blogRepository.findAll(new Specification<Blog>() {
            @Override
            public Predicate toPredicate(Root<Blog> root,
                                         CriteriaQuery<?> cq,
                                         CriteriaBuilder cb) {
                List<Predicate> predicates = new ArrayList<>();
                if (!"".equals(blog.getTitle()) && blog.getTitle() != null) {
                    predicates.add(cb.like(root.<String>get("title"), "%" + blog.getTitle() + "%"));
                }
                if (blog.getTypeId() != null) {
                    predicates.add(cb.equal(root.<Type>get("type").get("id"), blog.getTypeId()));
                }
                if (blog.isRecommend()) {
                    predicates.add(cb.equal(root.<Boolean>get("recommend"), blog.isRecommend()));
                }
                cq.where(predicates.toArray(new Predicate[predicates.size()]));
                return null;
            }
        },pageable);//博客管理搜索时分页获取博客
    }

    @Override
    public Page<Blog> listBlog(Pageable pageable) {
        return blogRepository.findAll(pageable);//分页获取博客
    }
    

    @Transactional
    @Override
    public Blog saveBlog(Blog blog) {
        if (blog.getId() == null){//根据ID判断是否存在再保存博客
            blog.setCreateTime(new Date());//保存的时间
            blog.setUpdateTime(new Date());//更新的时间
            blog.setViews(0);//浏览量
        }else {//如果存在就调用更新方法去更新时间再保存
            blog.setUpdateTime(new Date());
        }
        return blogRepository.save(blog);
    }

    @Transactional
    @Override
    public Blog updateBlog(Long id, Blog blog) {//新增博客
        Blog b = blogRepository.getById(id);//先根据ID获取
        if (b == null){//获取不到
            throw new NotFoundException("博客不存在!");//抛出错误
        }
        BeanUtils.copyProperties(blog,b, MyBeanUtils.getNullPropertNames(blog));//获取得到就不该ID去改内容
        b.setUpdateTime(new Date());//设置更新时间
        return blogRepository.save(b);//保存
    }
    @Transactional
    @Override
    public void deleteBlog(Long id) {
        blogRepository.deleteById(id);//根据ID删除
    }
}

其中有用到一个自定义的Query和MyBeanUtils
这两分别是
BlogQuery
在这里插入图片描述

package net.yq.springbootblog.vo;

public class BlogQuery {
    private String title;
    private Long typeId;
    private boolean recommend;

    public BlogQuery() {
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public Long getTypeId() {
        return typeId;
    }

    public void setTypeId(Long typeId) {
        this.typeId = typeId;
    }

    public boolean isRecommend() {
        return recommend;
    }

    public void setRecommend(boolean recommend) {
        this.recommend = recommend;
    }

}

MyBeanUtils
在这里插入图片描述

package net.yq.springbootblog.util;

import org.springframework.beans.*;

import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.List;


public class MyBeanUtils {
    public static String[] getNullPropertNames(Object source){
        BeanWrapper beanWrapper = new BeanWrapperImpl(source);
        PropertyDescriptor[] pds = beanWrapper.getPropertyDescriptors();
        List<String> nullPropertyNames = new ArrayList<>();
        for (PropertyDescriptor pd : pds){
            String propertyName = pd.getName();
            if (beanWrapper.getPropertyValue(propertyName) == null){
                nullPropertyNames.add(propertyName);
            }
        }
        return nullPropertyNames.toArray(new String[nullPropertyNames.size()]);
    }
}

至此就弄完了发布及管理了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值