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、在AllBlogs添加标签页
先看效果:
开始实现:
https://element.eleme.cn/#/zh-CN/component/tabs
<template>
<div>
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="全部(120)" name="first">全部(120)</el-tab-pane>
<el-tab-pane label="原创(20)" name="second">原创(20)</el-tab-pane>
<el-tab-pane label="转载(20)" name="third">转载(20)</el-tab-pane>
<el-tab-pane label="草稿(20)" name="fourth">草稿(20)</el-tab-pane>
<el-tab-pane label="公开(50)" name="fifth">公开(50)</el-tab-pane>
<el-tab-pane label="私密(10)" name="sixth">私密(10)</el-tab-pane>
<el-tab-pane label="回收站(20)" name="serven">回收站(20)</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
export default {
name: 'AllBlogs',
data () {
return {
activeName: 'second'
}
},
methods:{
handleClick(tab, event) {
console.log(tab.index);
}
}
}
</script>
<style scoped>
</style>
2、【全部】文章展示页面
AllBlogs.vue
<template>
<div>
<el-tabs v-model="activeName" @tab-click="handleClick" style="margin-top: 10px;">
<el-tab-pane label="全部(120)" name="first">
<!-- 搜索 -->
<el-input size="small" v-model="input_title" placeholder="请输入标题,可回车搜索..." prefix-icon="el-icon-search"
style="width: 400px;margin-right: 10px;"></el-input>
<el-button size="small" type="primary" icon="el-icon-search">搜索</el-button>
<el-button size="small" type="primary">高级搜索</el-button>
<!-- 博客文章 -->
<el-table
:data="blogsData"
stripe
style="width: 100%">
<el-table-column label="文章列表">
<template slot-scope="scope">
<el-card class="box-card">
<div style="font-size: 18px;">
<!-- v-if="!scope.row.shareStatement" 这里根据shareStatement属性判断文章是否为草稿 -->
<el-button size="mini" v-if="!scope.row.shareStatement" type="info" icon="el-icon-edit" circle></el-button>
{{scope.row.title}}
</div>
<div style="margin-top: 10px;">
<el-tag size="small" style="margin-right: 10px;" v-if="!scope.row.shareStatement" type="warning">草稿</el-tag>
<el-tag style="margin-right: 10px;" v-if="scope.row.shareStatement" size="small">{{scope.row.flag}}</el-tag>
<el-tag style="margin-right: 10px;" v-if="scope.row.shareStatement" size="small" type="info">{{scope.row.published}}</el-tag>
<el-tag style="margin-right: 20px;" v-if="scope.row.shareStatement" size="small" type="success">{{scope.row.typeId}}</el-tag>
<i style="margin-right: 20px;" class="el-icon-view"> {{scope.row.views}} </i>
<i style="margin-right: 20px;" class="el-icon-chat-square"> {{scope.row.commentCount}} </i>
<i style="margin-right: 20px;" class="el-icon-date"> {{scope.row.createTime}}</i>
<el-button style="float: right;" type="danger" size="mini">删除</el-button>
<el-button style="float: right; margin-right: 10px;" type="primary" size="mini">编辑</el-button>
</div>
</el-card>
</template>
</el-table-column>
</el-table>
<div style="margin-top: 20px;">
<!-- 分页 -->
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="pagesizes"
:page-size="pagesize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</div>
</el-tab-pane>
<el-tab-pane label="原创(20)" name="second">原创(20)</el-tab-pane>
<el-tab-pane label="转载(20)" name="third">转载(20)</el-tab-pane>
<el-tab-pane label="草稿(20)" name="fourth">草稿(20)</el-tab-pane>
<el-tab-pane label="公开(50)" name="fifth">公开(50)</el-tab-pane>
<el-tab-pane label="私密(10)" name="sixth">私密(10)</el-tab-pane>
<el-tab-pane label="回收站(20)" name="serven">回收站(20)</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
export default {
name: 'AllBlogs',
data () {
return {
activeName: 'first',
input_title: '', //输入框的值
blogsData: [{ //文章数据
title: '用于展示多条结构类似的数据,可对数据进行排序、筛选、对比或其他自定义操作',
flag: '原创',
published:'公开',
createTime:'2020年12月07日 12:00:00',
views:'999',
commentCount:'99',
typeId:'springboot学习',
shareStatement:false,
}, {
title: '用于展示多条结构类似的数据,可对数据进行排序、筛选、对比或其他自定义操作',
flag: '原创',
published:'公开',
createTime:'2020年12月07日 12:00:00',
views:'999',
commentCount:'99',
typeId:'springboot学习',
shareStatement:true,
}],
currentPage: 1, //当前页
total:100, //总记录数
pagesize:10, //页面大小
pagesizes:[10,20,30], //页面数组
}
},
methods:{
handleClick(tab, event) {
console.log(tab.index);
},
// 分页的当前页
handleCurrentChange(val) {
console.log(`当前页: ${val}`);
},
//每页多少条
handleSizeChange(val) {
console.log(`每页 ${val} 条`);
},
}
}
</script>
<style scoped>
</style>
二、博客展示后端开发
1、修改RespBean
public static RespBean build(){
return new RespBean();
}
2、分页查询 修改TBlogService
修改TBlogService
/分页查询
RespBean pageBlogs(Long current, Long limit, Boolean published, String flag, Boolean share_statement, Boolean is_delete);
3、修改TBlogServiceImpl
@Service
public class TBlogServiceImpl extends ServiceImpl<TBlogMapper, TBlog> implements TBlogService {
@Autowired
TBlogMapper tBlogMapper;
RespBean respBean = RespBean.build();
@Override
public RespBean pageBlogs(Long current, Long limit, Boolean published, String flag,
Boolean share_statement, Boolean is_delete) {
RespBean respBean = RespBean.build();
//创建Page对象
Page<TBlog> tBlogPage = new Page<>(current,limit);
//构建条件
QueryWrapper<TBlog> wrapper = new QueryWrapper<>();
//获取传入讲师的条件是否为空
//多条件组合查询
//判断条件值是否为空,如果不为空拼接条件
if (published != null){
//构建条件
wrapper.eq("published",published);
}
if (flag != null){
//构建条件
wrapper.eq("flag",flag);
}
if (share_statement != null){
//构建条件
wrapper.eq("share_statement",share_statement);
}
if (is_delete != null){
//构建条件
wrapper.eq("is_delete",is_delete);
}
//以更新时间排序(降序)
wrapper.orderByDesc("update_time");
//调用mybatis plus分页方法进行查询
tBlogMapper.selectPage(tBlogPage,wrapper);
//通过Page对象获取分页信息
List<TBlog> tBlogList = tBlogPage.getRecords(); //每页的数据 list集合
long size = tBlogPage.getSize(); //每页显示的条数
long total = tBlogPage.getTotal(); //总记录数
long pages = tBlogPage.getPages(); //总页数
respBean.setStatus(200);
respBean.setObj(tBlogPage);
return respBean;
}
}
4、修改TBlogController
@RestController
@RequestMapping("/blog")
public class TBlogController {
@Autowired
TBlogService tBlogService;
RespBean respBean = RespBean.build();
/**
* 分页查询
* @param current
* @param size
* @param published
* @param flag
* @param share_statement
* @param is_delete
* @return
*/
@GetMapping("/getByPage")
@ApiOperation("博客分页查询")
@ApiImplicitParams({
@ApiImplicitParam(name = "current",value = "当前页") ,
@ApiImplicitParam(name = "size",value = "每页的数量"),
@ApiImplicitParam(name = "published",value = "是否公开"),
@ApiImplicitParam(name = "flag",value = "原创或转载"),
@ApiImplicitParam(name = "share_statement",value = "草稿"),
@ApiImplicitParam(name = "is_delete",value = "是否已删除"),
})
public RespBean getByPage(Long current, Long size, Boolean published,
String flag, Boolean share_statement, Boolean is_delete){
return tBlogService.pageBlogs(current, size,published,flag,share_statement,is_delete);
}
}
5、测试
1)分页查询【全部】
对应
2)分页查询【原创】
对应:
3)分页查询【转载】
对应:
4)其他类似
6、根据标题查询
在TBlogService 添加:
//根据博客标题查询
RespBean getByTitle(String title);
在TBlogServiceImpl 添加:
/**
* 根据标题查询
* @param title
* @return
*/
@Override
public RespBean getByTitle(String title) {
RespBean respBean = RespBean.build();
QueryWrapper<TBlog> queryWrapper = new QueryWrapper<TBlog>();
queryWrapper.like("title",title);
List<TBlog> tBlogList = tBlogMapper.selectList(queryWrapper);
respBean.setStatus(200);
respBean.setObj(tBlogList);
return respBean;
}
在 TBlogController中添加:
/**
* 根据博客标题查询
* @param title
* @return
*/
@GetMapping("/getByTitle")
@ApiOperation("通过文章标题查询")
@ApiImplicitParam(name = "title",value = "文章的标题")
public RespBean findByTitle(String title){
return tBlogService.getByTitle(title);
}
7、测试
8、日期格式化
在TBlogs加入注解:
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "Asia/ShangHai")
三、博客展示前端对接
1、新建搜索专用子组件
SearchBlogsCom.vue
<template>
<div>
<!-- 博客文章 -->
<el-table
:data="blogsData"
stripe
style="width: 100%">
<el-table-column label="文章列表">
<template slot-scope="scope">
<el-card class="box-card">
<div style="font-size: 18px;">
<!-- v-if="!scope.row.shareStatement" 这里根据shareStatement属性判断文章是否为草稿 -->
<el-button size="mini" v-if="!scope.row.shareStatement" type="info" icon="el-icon-edit" circle></el-button>
{{scope.row.title}}
</div>
<div style="margin-top: 10px;">
<el-tag size="small" style="margin-right: 10px;" v-if="scope.row.isDelete" type="danger">已删除</el-tag>
<el-tag size="small" style="margin-right: 10px;" v-if="!scope.row.shareStatement" type="warning">草稿</el-tag>
<el-tag style="margin-right: 10px;" v-if="scope.row.shareStatement" size="small">{{scope.row.flag}}</el-tag>
<el-tag style="margin-right: 20px;" v-if="scope.row.shareStatement" size="small" type="info">{{scope.row.published == '0' ? '私密' : '公开'}}</el-tag>
<i style="margin-right: 20px;" class="el-icon-view"> {{scope.row.views}} </i>
<i style="margin-right: 20px;" class="el-icon-chat-square"> {{scope.row.commentCount}} </i>
<i style="margin-right: 20px;" class="el-icon-date"> {{scope.row.createTime}}</i>
<el-button style="float: right;" type="danger" size="mini">删除</el-button>
<el-button style="float: right; margin-right: 10px;" type="primary" size="mini">编辑</el-button>
</div>
</el-card>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
name: 'SearchBlogsCom',
data () {
return {
blogsData: [], //文章数据
}
},
props:["blogsData"], //接收父组件传过来的值
methods:{
}
}
</script>
<style scoped>
</style>
2、新建通用博客列表子组件
BlogListCom.vue
<template>
<div>
<!-- 博客文章 -->
<el-table
:data="blogsData"
stripe
style="width: 100%">
<el-table-column label="文章列表">
<template slot-scope="scope">
<el-card class="box-card">
<div style="font-size: 18px;">
<!-- v-if="!scope.row.shareStatement" 这里根据shareStatement属性判断文章是否为草稿 -->
<el-button size="mini" v-if="!scope.row.shareStatement" type="info" icon="el-icon-edit" circle></el-button>
{{scope.row.title}}
</div>
<div style="margin-top: 10px;">
<el-tag size="small" style="margin-right: 10px;" v-if="scope.row.isDelete" type="danger">已删除</el-tag>
<el-tag size="small" style="margin-right: 10px;" v-if="!scope.row.shareStatement" type="warning">草稿</el-tag>
<el-tag style="margin-right: 10px;" v-if="scope.row.shareStatement" size="small">{{scope.row.flag}}</el-tag>
<el-tag style="margin-right: 20px;" v-if="scope.row.shareStatement" size="small" type="info">{{scope.row.published == '0' ? '私密' : '公开'}}</el-tag>
<!-- <el-tag style="margin-right: 20px;" v-if="scope.row.shareStatement" size="small" type="success">{{scope.row.typeId}}</el-tag> -->
<i style="margin-right: 20px;" class="el-icon-view"> {{scope.row.views}} </i>
<i style="margin-right: 20px;" class="el-icon-chat-square"> {{scope.row.commentCount}} </i>
<i style="margin-right: 20px;" class="el-icon-date"> {{scope.row.createTime}}</i>
<el-button style="float: right;" type="danger" size="mini">删除</el-button>
<el-button style="float: right; margin-right: 10px;" type="primary" size="mini">编辑</el-button>
</div>
</el-card>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div v-if="!hidden_page" style="margin-top: 20px;">
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="pagesizes"
:page-size="pagesize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</div>
</div>
</template>
<script>
export default {
name: 'BlogListCom',
data () {
return {
blogsData: [], //文章数据
currentPage: 1, //当前页
total:0, //总记录数
pagesize:10, //页面大小
pagesizes:[10,20,30], //页面数组
t2index:0, //选项卡index
}
},
props:["tindex"],
mounted() {
this.initIndex();
this.initBlogs(); // 调用初始化博客数据
},
methods:{
initIndex(){
this.t2index = this.tindex //保存父组件传过来的值
},
// 初始化【全部】博客的数据
initBlogs(){
const _this = this
// 通用路由
var baseurl = '/blog/getByPage?current=' + this.currentPage + '&size=' + this.pagesize
//通过条件拼接路由
if(this.t2index == "0"){ //全部
baseurl +=' &is_delete=0'
}
if(this.t2index == "1"){ //原创
baseurl +=' &flag=原创 &share_statement=1 &is_delete=0'
}
if(this.t2index == "2"){ //转载
baseurl +=' &flag=转载 &share_statement=1 &is_delete=0'
}
if(this.t2index == "3"){ //草稿
baseurl +=' &share_statement=0 &is_delete=0'
}
if(this.t2index == "4"){ //公开
baseurl +=' &published=1 &share_statement=1 &is_delete=0'
}
if(this.t2index == "5"){ //私密
baseurl +=' &published=0 &share_statement=1 &is_delete=0'
}
if(this.t2index == "6"){ //回收站
baseurl +=' &is_delete=1 &share_statement=1 &is_delete=0'
}
// console.log(baseurl)
this.getRequest(baseurl).then(resp=>{
if(resp){
console.log(resp)
_this.blogsData = resp.obj.records //将获取到的后端的值赋值给blogsData
_this.total = resp.obj.total // 保存一下总记录数,用于前端展示
}
})
},
// 分页的当前页
handleCurrentChange(val) {
console.log(`当前页: ${val}`);
this.currentPage = val
this.initBlogs()
},
//每页多少条
handleSizeChange(val) {
console.log(`每页 ${val} 条`);
this.pagesize = val
this.initBlogs()
},
}
}
</script>
<style scoped>
</style>
3、修改博客管理
AllBlogs.vue (后面给出完整代码)
导入刚才创建的两个子组件:
完整代码:
<template>
<div>
<el-tabs v-model="activeName" @tab-click="handleClick" style="margin-top: 10px;">
<el-tab-pane label="全部(120)" name="first">
<!-- 搜索 -->
<el-input size="small" v-model="input_title" placeholder="请输入标题,可回车搜索..." prefix-icon="el-icon-search"
style="width: 400px;margin-right: 10px;" @keydown.enter.native="search_title"></el-input>
<el-button size="small" type="primary" @click="search_title" icon="el-icon-search">搜索</el-button>
<el-button size="small" type="primary">高级搜索</el-button>
<!-- 通用博客列表组件 -->
<BlogListCom :tindex="tabindex" v-if="tabindex == 0 && hidden_searchcom == false"></BlogListCom>
<!-- 搜索专用组件 -->
<SearchBlogsCom v-if="tabindex == 0 && hidden_searchcom == true" :blogsData="blogsData"></SearchBlogsCom>
</el-tab-pane>
<el-tab-pane label="原创(20)" name="second">
<BlogListCom :tindex="tabindex" v-if="tabindex == 1"></BlogListCom>
</el-tab-pane>
<el-tab-pane label="转载(20)" name="third">
<BlogListCom :tindex="tabindex" v-if="tabindex == 2"></BlogListCom>
</el-tab-pane>
<el-tab-pane label="草稿(20)" name="fourth">
<BlogListCom :tindex="tabindex" v-if="tabindex == 3"></BlogListCom>
</el-tab-pane>
<el-tab-pane label="公开(50)" name="fifth">
<BlogListCom :tindex="tabindex" v-if="tabindex == 4"></BlogListCom>
</el-tab-pane>
<el-tab-pane label="私密(10)" name="sixth">
<BlogListCom :tindex="tabindex" v-if="tabindex == 5"></BlogListCom>
</el-tab-pane>
<el-tab-pane label="回收站(20)" name="serven">
<BlogListCom :tindex="tabindex" v-if="tabindex == 6"></BlogListCom>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
//导入子组件
import BlogListCom from '@/components/blogs/BlogListCom'
import SearchBlogsCom from '@/components/blogs/SearchBlogsCom'
export default {
name: 'AllBlogs',
components:{ BlogListCom,SearchBlogsCom }, //注册子组件
data () {
return {
activeName: 'first',
input_title: '', //输入框的值
blogsData: [], //文章数据
tabindex:0, //选项卡index
hidden_searchcom: false , //是否隐藏搜索子组件
}
},
mounted() {
},
methods:{
// 获取标签栏的index
handleClick(tab, event) {
this.tabindex = tab.index
console.log("tabindex = " + this.tabindex);
},
//根据标题搜索
search_title(){
//判断使用的是通用博客子组件还是搜索专用子组件
if(this.input_title == ''){
//如果搜索值为空,就隐藏搜索专用子组件,显示通用子组件
this.hidden_searchcom = false
}else{
//如果搜索值不为空,就显示搜索专用子组件
this.hidden_searchcom = true
}
const _this = this
//发起根据标题搜索请求
this.getRequest('/blog/getByTitle?title=' + this.input_title).then(resp=>{
if(resp){
_this.blogsData = resp.obj
}
})
}
}
}
</script>
<style scoped>
</style>
4、说明一下父组件如何传值给子组件
父组件
子组件:
5、效果展示
【全部】
【搜索】
【原创】
【转载】
【草稿】
【公开】
【私密】
【回收站】
关注【小L星光】回复 “博客” 即可获整个项目源码 ~