热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Vue+SpringBoot项目实战(十八):博客功能开发

Vue+SpringBoot项目实战(十八):博客功能开发,Go语言社区,Golang程序员人脉社

logo



重要链接:
「系列文章目录」

「项目源码(GitHub)」

本篇目录

  • 前言
  • 一、mavon-editor 编辑器
  • 二、功能设计
  • 三、功能实现
    • 1.数据库设计
    • 2.编辑器的引入与改造
    • 3.文章列表页面
    • 4.文章详情页面
    • 5.文章管理页面
  • 下一步


前言

各位朋友们你们好哇,隐约感觉我已经鸽了好几周了,所以虽然快过年了,我还是决定肝出一篇文章出来。前几篇文章发布后好多同学表示看不懂了,但我觉得都到第十八篇了,有些问题确实应该让你们自己尝试解决一下。不过放心,这篇文章是特地写给你们找自信的。

博客可以说是技术人的标配了,有个自己运营的博客网站更是可以在小白面前装一波逼。网上有很多开源的博客系统,如 WordPress、Hexo 等,功能强大页面美观运行稳定,但毕竟不是自己做的。一开始我也没想在这个项目里加这个模块,后来做着做着越来越像 CMS,这就好比你和朋友出去玩他想上厕所你陪他到了门口虽然你并没有什么库存但是来都来了放空一下也不是不可以,所以我就花了几个晚上把这个功能弄了一下。如果你们有兴趣真的可以搞个服务器尝试当博客运营一下,能学到很多东西。

这篇文章除了讲解如何搭出一个博客系统,还设计到如下知识点:


  • 如何使用开源编辑器?

  • Vue 如何在不同页面传递参数?

  • Spring Data JPA 分页功能如何使用?

一、mavon-editor 编辑器

其实项目一开始的时候我就暗搓搓地安装了这个编辑器,所以如果你们复制过我的 package.json 文件或者直接从 github 上下的源码,就不用再安装了。

目前常见的文本编辑器有两种,即富文本编辑器和 markdown 编辑器,我一直写作用的都是 markdown,基本不用动鼠标,而且在各个平台样式比较统一,方便迁移。一开始可能觉得语法麻烦,其实用的多的就那么几个,写几篇就熟悉了。

简单介绍一下,这个 mavon-editor 编辑器应该是最火的国产开源 markdown 编辑器里最火的一个,github 3.4k star。功能比较齐全,界面比较舒服,作者也很热情地解决使用者的问题,我用了一下,暂时没发现什么 BUG。仓库地址:

https://github.com/hinesboy/mavonEditor

readme 提供了 API 文档。

markdown 编辑器的本质是把你的输入源(按一定语法规则组织的文本)转换为 html 代码,以在浏览器上生动形象地展示,这个过程其实类似于「翻译」或者说「编译」。同时作为一个成熟的应用,又需要一些附加的按钮、快捷键等功能,但其实 markdown 本身就是为了简化功能的使用,类似加粗、斜体等操作都有相对应的语法,完全可以直接键入,不必要过分使用快捷键或按钮。


二、功能设计

博客功能大概可以分为三个部分,分别是文章展示文章管理编辑器,文章展示又可以划分为文章列表与文章详情两个部分。
在这里插入图片描述

虽然编辑器提供预览功能,但一般我们在前台只不需要向用户展示 markdown 原文,所以最好还是单独编写一个文章详情页面渲染出 html。有两种思路:


  • 第一种,在数据库中仅保存 markdown 语法的文本,在需要使用时解析为 html,并在前台渲染

  • 第二种,markdown、html 均保存在数据库中,需要使用时取出 html 并在前台渲染

第一种的好处就是节省传输的数据量与数据库空间,坏处就是需要自己编写解析方法,相当于又重写了一遍编辑器,而且难以保证解析出来的样式跟原编辑器一致(用一些公开的解析函数也存在这个问题)。

当然,如果编辑器提供了解析的 API 那就比较舒服了,但我暂时没找到相关的内容。作者可能并不想这么做,而是提供了一个可以传递 markdown 与 html 值的 save 方法,因此就这个编辑器而言,我觉得选择第二种方法方便一些。

下面是各个页面的初步设计与功能介绍:

文章列表:

展示文章的题目、摘要、封面等信息,提供文章详情页面入口。主要是前端设计与分页功能实现,后期可以扩展分类标签、检索、归档等功能,还可以在侧边栏加入作者简介等信息。
笔记本
文章详情:

这个页面用于展示文章的具体内容,也就是渲染从数据库中取出的 html。打码的部分说明了我是一个遵守平台规则的老实人。
文章详情
文章管理:

后台的管理页面,提供查看、发布、修改文章的入口以及删除功能,需要内容管理权限。
文章管理
编辑器:

核心页面,在开源编辑器的基础上,添加了标题、摘要及封面设置功能。
编辑器


三、功能实现

1.数据库设计

为了保存文章相关的信息,设计 jotter_article 表如下:
文章表

目前包含的字段是 id、标题、文章 html、md 原文、文章摘要、文章标题和发文日期。


2.编辑器的引入与改造

如果之前没有安装过编辑器,可以先在项目 wj-vue 根目录下执行如下命令:

$ npm install mavon-editor --save

再在 main.js 里全局注册一下:

import mavonEditor from 'mavon-editor'
...
...
Vue.use(mavonEditor)

即可在组件中使用。考虑到编辑功能应该向具有内容管理权限的用户使用,我们在 components/admin/content 文件夹下新建一个组件,命名为 ArticleEditor.vue。该组件的主体就是 mavon-editor 编辑器,最初的状态如下:

<template>
<mavon-editor
v-model="article.articleContentMd"
style
="height: 100%;"

ref=md
@save="saveArticles"
fontSize="16px">

mavon-editor>
template>
<script>
export default {
name: 'Editor',
data () {
return {
article: {}
}
}
script>

接下来我们需要对它做一些邪恶的事情,把它改造成我们想要的样子。改造工序是:


  • 第一步,添加标题输入栏

  • 第二步,插入自定义工具,提供摘要与封面录入功能

  • 第三步,编写 save 方法,与后端交互

实现标题的输入只需要添加一个

<el-input
v-model="article.articleTitle"
style
="margin: 10px 0px;font-size: 18px;"

placeholder="请输入标题">
el-input>

插入自定义工具,文档中并没有相关内容,但是我寻思肯定有人会问,就搜了一下 issues,果然
插槽

作者在 2018 年 8 月份就这个问题提交了一次更新。当然其实也可以直接看源码,里面设置了 4 个插槽,对应不同的插入位置。
插槽

为了保证我们插入的图标跟原来的图标样式一致,需要再瞅一眼 tool-bar 的源码。里面的按钮大概是这样写的

<button type="button"
v-if="toolbars.save"
@click="$clicks('save')"
class="op-icon fa fa-mavon-floppy-o"
aria-hidden="true"
:title="`${d_words.tl_save} (ctrl+s)`">
button>

我们照葫芦画瓢设置一下 type、class 和 title 属性,弄一个添加摘要和封面的按钮:

<button type="button"
class="op-icon el-icon-document"
:title="'摘要/封面'"
slot="left-toolbar-after"
@click="dialogVisible = true">
button>

其实样式主要是 class 里的 op-icon 控制的。

添加摘要和封面的表单被我做在弹出框里了。上传文章封面复用之前上传图书封面的组件。

<el-dialog
:visible.sync="dialogVisible"
width="30%">

<el-divider content-position="left">摘要el-divider>
<el-input
type="textarea"
v-model="article.articleAbstract"
rows="6"
maxlength="255"
show-word-limit>
el-input>
<el-divider content-position="left">封面el-divider>
<div style="margin-top: 20px">
<el-input v-model="article.articleCover" autocomplete="off" placeholder="图片 URL">el-input>
<img-upload @onUpload="uploadImg" ref="imgUpload" style="margin-top: 5px">img-upload>
div>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消el-button>
<el-button type="primary" @click="dialogVisible = false">确 定el-button>
span>
el-dialog>

界面是这个样子:
封面、摘要

然后我惊奇地发现图片上传不了,好像之前也有同学反映过这个问题,我没当回事。后来折腾半天,发现是图片上传这个组件比较狗,需要单独设置属性才能带上 COOKIE,不带 COOKIE 后端就拿不到 sessionId,就会被 shiro 拦截。修改后的组件模板部分如下:

<template>
<el-upload
class="img-upload"
ref="upload"
action="http://localhost:8443/api/admin/content/books/covers"
with-credentials
:on-preview="handlePreview"
:on-remove="handleRemove"
:before-remove="beforeRemove"
:on-success="handleSuccess"
multiple
:limit="1"
:on-exceed="handleExceed"
:file-list="fileList">

<el-button size="small" type="primary">点击上传el-button>
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kbdiv>
el-upload>
template>

相比之前添加了 with-credentials

最后一步编写保存方法,常规操作,向后端发送数据即可。

组件完整的代码如下:

<template>
<div>
<el-row style="margin: 18px 0px 0px 18px ">
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/admin/dashboard'}">管理中心el-breadcrumb-item>
<el-breadcrumb-item :to="{ path: '/admin/content/book'}">内容管理el-breadcrumb-item>
<el-breadcrumb-item :to="{ path: '/admin/content/article'}">文章管理el-breadcrumb-item>
<el-breadcrumb-item>编辑器el-breadcrumb-item>
el-breadcrumb>
el-row>
<el-row>
<el-input
v-model="article.articleTitle"
style
="margin: 10px 0px;font-size: 18px;"

placeholder="请输入标题">
el-input>
el-row>
<el-row style="height: calc(100vh - 140px);">
<mavon-editor
v-model="article.articleContentMd"
style
="height: 100%;"

ref=md
@save="saveArticles"
fontSize="16px">

<button type="button" class="op-icon el-icon-document" :title="'摘要/封面'" slot="left-toolbar-after"
@click="dialogVisible = true">
button>
mavon-editor>
<el-dialog
:visible.sync="dialogVisible"
width="30%">

<el-divider content-position="left">摘要el-divider>
<el-input
type="textarea"
v-model="article.articleAbstract"
rows="6"
maxlength="255"
show-word-limit>
el-input>
<el-divider content-position="left">封面el-divider>
<div style="margin-top: 20px">
<el-input v-model="article.articleCover" autocomplete="off" placeholder="图片 URL">el-input>
<img-upload @onUpload="uploadImg" ref="imgUpload" style="margin-top: 5px">img-upload>
div>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消el-button>
<el-button type="primary" @click="dialogVisible = false">确 定el-button>
span>
el-dialog>
el-row>
div>
template>
<script>
import ImgUpload from './ImgUpload'
export default {
name: 'Editor',
components: {ImgUpload},
data () {
return {
article: {},
dialogVisible: false
}
},
mounted () {
if (this.$route.params.article) {
this.article = this.$route.params.article
}
},
methods: {
saveArticles (value, render) {
// value 是 md,render 是 html
this.$confirm('是否保存并发布文章?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$axios
.post('/admin/content/article', {
id: this.article.id,
articleTitle: this.article.articleTitle,
articleContentMd: value,
articleContentHtml: render,
articleAbstract: this.article.articleAbstract,
articleCover: this.article.articleCover,
articleDate: this.article.articleDate
}).then(resp => {
if (resp && resp.status === 200) {
this.$message({
type: 'info',
message: '已保存成功'
})
}
})
}
).catch(() => {
this.$message({
type: 'info',
message: '已取消发布'
})
})
},
uploadImg () {
this.article.articleCover = this.$refs.imgUpload.url
}
}
}
script>

后端部分首先是 pojo、DAO、service 一套,目前没什么可说的,不想自己写可以参考源码:

https://github.com/Antabot/White-Jotter/tree/master/wj/src/main/java/com/gm/wj

controller 中保存对应的方法如下:

@PostMapping("api/admin/content/article")
public Result saveArticle(@RequestBody JotterArticle article) {
jotterArticleService.addOrUpdate(article);
return ResultFactory.buildSuccessResult("保存成功");
}

前端路由写法参考:

{
path: '/admin/content/editor',
name: 'Editor',
component: Editor,
meta: {
requireAuth: true
}
}

由于编辑器不在后台管理目录中,所以不用设置动态加载,虽然前端非要访问也能访问,但是反正没有写的权限,所以无所谓了。


3.文章列表页面

这个页面主要涉及到分页的问题。之前我们图书馆页面的分页是纯靠前端进行的,这里我们用后端来实现一下。

Spring Data 提供了 org.springframework.data.domain.Page 类,该类包含了页码、页面尺寸等信息,可以很方便地实现分页。我们要做的,就是编写一个传入页码与页面尺寸参数的方法,这个方法可以写在 service 层。

public Page list(int page, int size) {
Sort sort = new Sort(Sort.Direction.DESC, "id");
return jotterArticleDAO.findAll(PageRequest.of(page, size, sort));
}

这里我们构造了一个 PageRequest 类来配合查询,sort 参数是可选的,如果报错了可能是版本问题,较新的版本里取消了公共构造方法,而是用静态工厂方法代替。将语句替换为

Sort sort = Sort.by(Sort.Direction.DESC, "id")

即可。接下来编写 controller 对应方法:

@GetMapping("/api/article/{size}/{page}")
public Page listArticles(@PathVariable("size") int size, @PathVariable("page") int page) {
return jotterArticleService.list(page - 1, size);
}

这里 page 是 1 的话其实会查询到第二页的内容,而前端组件传入的参数就是当页页码,所以需要 - 1。随便输个参数测试一下,可以看到后端查询出来的数据结构如下:

{"content":[{"id":1,"articleTitle":"凉风有兴","articleContentHtml":

推荐阅读
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 使用在线工具jsonschema2pojo根据json生成java对象
    本文介绍了使用在线工具jsonschema2pojo根据json生成java对象的方法。通过该工具,用户只需将json字符串复制到输入框中,即可自动将其转换成java对象。该工具还能解析列表式的json数据,并将嵌套在内层的对象也解析出来。本文以请求github的api为例,展示了使用该工具的步骤和效果。 ... [详细]
  • Week04面向对象设计与继承学习总结及作业要求
    本文总结了Week04面向对象设计与继承的重要知识点,包括对象、类、封装性、静态属性、静态方法、重载、继承和多态等。同时,还介绍了私有构造函数在类外部无法被调用、static不能访问非静态属性以及该类实例可以共享类里的static属性等内容。此外,还提到了作业要求,包括讲述一个在网上商城购物或在班级博客进行学习的故事,并使用Markdown的加粗标记和语句块标记标注关键名词和动词。最后,还提到了参考资料中关于UML类图如何绘制的范例。 ... [详细]
  • 在package.json中有如下两个对象:husky:{hooks:{pre-commit:lint-staged}},lint-staged:{src** ... [详细]
  • 本文比较了eBPF和WebAssembly作为云原生VM的特点和应用领域。eBPF作为运行在Linux内核中的轻量级代码执行沙箱,适用于网络或安全相关的任务;而WebAssembly作为图灵完备的语言,在商业应用中具有优势。同时,介绍了WebAssembly在Linux内核中运行的尝试以及基于LLVM的云原生WebAssembly编译器WasmEdge Runtime的案例,展示了WebAssembly作为原生应用程序的潜力。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • 本文介绍了Linux系统中正则表达式的基础知识,包括正则表达式的简介、字符分类、普通字符和元字符的区别,以及在学习过程中需要注意的事项。同时提醒读者要注意正则表达式与通配符的区别,并给出了使用正则表达式时的一些建议。本文适合初学者了解Linux系统中的正则表达式,并提供了学习的参考资料。 ... [详细]
  • 本文介绍了在Windows环境下如何配置php+apache环境,包括下载php7和apache2.4、安装vc2015运行时环境、启动php7和apache2.4等步骤。希望对需要搭建php7环境的读者有一定的参考价值。摘要长度为169字。 ... [详细]
  • SpringBoot整合SpringSecurity+JWT实现单点登录
    SpringBoot整合SpringSecurity+JWT实现单点登录,Go语言社区,Golang程序员人脉社 ... [详细]
  • Gitlab接入公司内部单点登录的安装和配置教程
    本文介绍了如何将公司内部的Gitlab系统接入单点登录服务,并提供了安装和配置的详细教程。通过使用oauth2协议,将原有的各子系统的独立登录统一迁移至单点登录。文章包括Gitlab的安装环境、版本号、编辑配置文件的步骤,并解决了在迁移过程中可能遇到的问题。 ... [详细]
  • 本文介绍了自学Vue的第01天的内容,包括学习目标、学习资料的收集和学习方法的选择。作者解释了为什么要学习Vue以及选择Vue的原因,包括完善的中文文档、较低的学习曲线、使用人数众多等。作者还列举了自己选择的学习资料,包括全新vue2.5核心技术全方位讲解+实战精讲教程、全新vue2.5项目实战全家桶单页面仿京东电商等。最后,作者提出了学习方法,包括简单的入门课程和实战课程。 ... [详细]
  • 一、路由首先需要配置路由,就是点击good组件进入goodDetail组件配置路由如下{path:goodDetail,component:goodDetail}同时在good组件中写入如下点击事件,路由中加入 ... [详细]
author-avatar
捕鱼达人2502868831
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有