富文本编辑器
Editormd
简介
Editor.md——功能非常丰富的编辑器,左端编辑,右端预览,非常方便,完全免费
- 官网:https://pandao.github.io/editor.md/
主要特征
- 支持“标准” Markdown / CommonMark 和 Github 风格的语法,也可变身为代码编辑器;
- 支持实时预览、图片(跨域)上传、预格式文本/代码/表格插入、代码折叠、搜索替换、只读模式、自定义样式主题和多语言语法高亮等功能;
- 支持 ToC 目录(Table of Contents)、Emoji 表情、Task lists、@链接等 Markdown 扩展语法;
- 支持 TeX 科学公式(基于 KaTeX)、流程图 Flowchart 和 时序图 Sequence Diagram;
- 支持识别和解析 HTML 标签,并且支持自定义过滤标签解析,具有可靠的安全性和几乎无限的扩展性;
- 支持 AMD / CMD 模块化加载(支持 Require.js & Sea.js),并且支持自定义扩展插件;
- 兼容主流的浏览器(IE8+)和 Zepto.js,且支持 iPad 等平板设备;
- 支持自定义主题样式;
注:这里我们使用 SpringBoot 集成 Editor.md
下载安装
我们可以在官网下载它:https://pandao.github.io/editor.md/ , 得到它的压缩包!
解压以后,在examples目录下面,可以看到他的很多案例使用!学习,其实就是看人家怎么写的,然后进行模仿就好了!
我们可以将整个解压的文件倒入我们的项目,将一些无用的测试和案例删掉即可!
- 下载好的目录解压
创建数据库
数据库设计
- article:文章表
字段 | 备注 | |
id | int | 文章的唯一ID |
author | varchar | 作者 |
title | varchar | 标题 |
content | longtext | 文章的内容 |
- 建表SQL:
CREATE TABLE `article` (
`id` int(10) NOT NULL AUTO_INCREMENT COMMENT 'int文章的唯一ID',
`author` varchar(50) NOT NULL COMMENT '作者',
`title` varchar(100) NOT NULL COMMENT '标题',
`content` longtext NOT NULL COMMENT '文章的内容',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
基础项目搭建
- 引入依赖
<!-- mysql依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- jdbc依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- druid依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.17</version>
</dependency>
<!-- lombok依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- mybatis-plus依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
- 配置 application.xml
# 配置端口
server:
port: 8001
spring:
# 配置数据源
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
username: root
password: 123456
# 配置静态资源访问
resources:
static-locations: classpath:/META-INF/resources/, classpath:/resources/, classpath:/static/, classpath:/public/, classpath:/templates/
# 配置Thymeleaf模板
thymeleaf:
cache: false
# 配置mybatisPlus
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
- 实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Article {
/**
* 文章的唯一ID
*/
private int id;
/**
* 作者名
*/
private String author;
/**
* 标题
*/
private String title;
/**
* 文章的内容
*/
private String content;
}
- mapper 接口
@Mapper
public interface ArticleMapper extends BaseMapper<Article> {
}
- service 接口
public interface ArticleService extends IService<Article> {
}
- impl 实现类
@Service
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService {
}
编写Controller测试一下是否OK?
文章编辑整合(重点)
1、 导入 editor.md 资源,删除多余文件。
2、 编辑文章页面 editor.html,需要映入jQuery;
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>文章发布</title>
</head>
<link rel="stylesheet" th:href="@{/editormd/css/editormd.css}"/>
<body>
<div>
<!-- 文章发布表单 -->
<form id="mdEditorForm">
<div>
标题:<input type="text" name="title">
</div>
<div>
作者:<input type="text" name="author">
</div>
<!-- Editor.md 富文本编辑器 -->
<div id="article-content">
<textarea name="content" id="content" style="display:none;"></textarea>
</div>
</form>
</div>
</body>
<script th:src="@{/js/jquery.min.js}"></script>
<script th:src="@{/editormd/js/editormd.js}"></script>
<script>
$(function() {
var editor = editormd("article-content", {
width: "95%", // 宽
height: "400px", // 高
path : "/editormd/lib/" // 加载编辑器lib路径
});
});
</script>
</html>
3、 编辑Controller,进行跳转;
@Controller
public class ArticleController {
/**
* 文章页面跳转
* @return editor.html
*/
@GetMapping("/toEditor")
public String toEditor(){
return "editor";
}
}
4、 访问 http://localhost:8001/toEditor,结果显示:
Editor.md 参数解析
- 常用参数
width: "95%", // 宽
height: "400px", // 高
path : "/editormd/lib/", // 加载编辑器lib路径
syncScrolling: "single", // 设置同步显示,三个选项[ true| false| "single",默认为true
saveHTMLToTextarea : true, // 保存 HTML 到 Textarea
emoji: true, // 是否开启表情功能,默认为false
tex : true, // 开启科学公式TeX语言支持,默认关闭
flowChart : true, // 开启流程图支持,默认关闭
sequenceDiagram : true, // 开启时序/序列图支持,默认关闭,
//图片上传
imageUpload : true, // 开启图片上传
imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"], // 图片上传的类型
imageUploadURL : "", // 图片上传路径
// 初始化函数
onload : function() {
// alert("初始化函数");
},
/*指定需要显示的功能按钮*/
toolbarIcons : function() {
return ["undo","redo","|",
"bold","del","italic","quote","ucwords","uppercase","lowercase","|",
"h1","h2","h3","h4","h5","h6","|",
"list-ul","list-ol","hr","|",
"link","reference-link","image","code","preformatted-text",
"code-block","table","datetime","emoji","html-entities","pagebreak","|",
"goto-line","watch","preview","fullscreen","clear","search","|",
"help","releaseIcon", "index"]
},
/*自定义功能按钮*/
toolbarIconTexts : {
},
/*给自定义按钮指定回调函数*/
toolbarHandlers:{
}
图片上传问题
- 前端 js 中添加配置
//图片上传
imageUpload : true, // 开启图片上传
imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"], // 图片上传的类型
imageUploadURL : "/fileUpload", // 图片上传路径
- 后端接口请求,接收保存图片!
@RestController
public class ArticleApi {
@PostMapping("/fileUpload")
public Map fileUpload(@RequestParam("editormd-image-file") MultipartFile file, String guid) throws IOException {
Map<String, Object> hashMap = new LinkedHashMap<>();
// 获得后缀类型
String type = file.getOriginalFilename().substring(file.getOriginalFilename().indexOf("."), file.getOriginalFilename().length());
// 获得文件上传路径
String path = System.getProperty("user.dir")+"/EditorText/src/main/resources/static/image/";
// 将路径传给 File 对象
File realPath = new File(path);
// 判断路径上的文件夹是否存在,不存在就创建
if (!realPath.exists()){
realPath.mkdir();
}
// 设置上传的文件名字
String filename = guid + type;
//通过CommonsMultipartFile的方法直接写文件
file.transferTo(new File(realPath +"/"+ filename));
// 返回上传路径
hashMap.put("url","/image/" + filename);
// 返回是否成功
hashMap.put("success", 1);
// 返回信息提示
hashMap.put("message", "upload success!");
return hashMap;
}
}
参数 @RequestParam(“editormd-image-file”) MultipartFile file 是隐藏参数,在请求头中不会显示
- 解决文件回显显示的问题,设置虚拟目录映射!
@Configuration
public class MyWebConfig extends WebMvcConfigurationSupport {
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
String property = System.getProperty("user.dir");
registry.addResourceHandler("/image/**")
.addResourceLocations("file:"+property+"/EditorText/src/main/resources/static/image/");
}
}
表情包问题
在外面设置图片虚拟路径显示后,图片显示没问题了,但表情包就出现了Bug!
- 解决
在图片虚拟路径后面接着添加
registry.addResourceHandler("/editormd/**")
.addResourceLocations("file:"+property+"/EditorText/src/main/resources/static/editormd/");
配置 editormd 的虚拟路径即可!
推荐静态资源中的文件都配置虚拟路径,缓存清理掉后,静态资源文件可能会找不到
@Configuration
public class MyWebConfig extends WebMvcConfigurationSupport {
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
String property = System.getProperty("user.dir");
registry.addResourceHandler("/image/**")
.addResourceLocations("file:"+property+"/EditorText/src/main/resources/static/image/");
registry.addResourceHandler("/editormd/**")
.addResourceLocations("file:"+property+"/EditorText/src/main/resources/static/editormd/");
registry.addResourceHandler("/js/**")
.addResourceLocations("file:"+property+"/EditorText/src/main/resources/static/js/");
registry.addResourceHandler("/layui/**")
.addResourceLocations("file:"+property+"/EditorText/src/main/resources/static/layui/");
}
}
添加文章
- 前端 js 中添加配置, 定义发布文章和返回首页按钮
/*自定义功能按钮,下面我自定义了2个,一个是发布,一个是返回首页*/
toolbarIconTexts : {
releaseIcon : "<span bgcolor=\"gray\">发布</span>",
index : "<span bgcolor=\"red\">返回首页</span>"
},
/*给自定义按钮指定回调函数*/
toolbarHandlers:{
releaseIcon : function(cm, icon, cursor, selection) {
//表单提交
$.ajax({
url: "",
type: "POST",
data: mdEditorForm.serialize(),
success: function (result) {
alert(result.magger);
}
});
},
index : function(){
window.location.href = '/';
}
}
- 后端接口请求,接收前端表单存入数据库!
@PostMapping("/addArticle")
public Map addArticle(Article article) {
Map<String, Object> hashMap = new LinkedHashMap<>();
boolean save = articleService.save(article);
if (save) {
hashMap.put("magger", "添加成功");
} else{
hashMap.put("magger", "添加失败");
}
return hashMap;
}
测试一下,看是否能添加成功!
文章展示
1、 Controller中添加方法
// 查看文章
@GetMapping("/toArticle/{id}")
public String toArticle(@PathVariable("id") Integer id, Model model){
Article article = articleService.getById(id);
model.addAttribute("article",article);
return "article";
}
2、 编写页面 article.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>文章展示</title>
</head>
<link rel="stylesheet" th:href="@{/editormd/css/editormd.css}"/>
<body>
<div>
<h2 style="margin: auto 0" th:text="${article.title}"></h2>
作者:<span style="float: left" th:text="${article.author}"></span>
<!--文章主体内容-->
<div id="doc-content">
<textarea style="display:none;" placeholder="markdown" th:text="${article.content}"></textarea>
</div>
</div>
</body>
<script th:src="@{/js/jquery.min.js}"></script>
<script th:src="@{/editormd/js/editormd.js}"></script>
<script th:src="@{/editormd/lib/marked.min.js}"></script>
<script th:src="@{/editormd/lib/prettify.min.js}"></script>
<script th:src="@{/editormd/lib/raphael.min.js}"></script>
<script th:src="@{/editormd/lib/underscore.min.js}"></script>
<script th:src="@{/editormd/lib/sequence-diagram.min.js}"></script>
<script th:src="@{/editormd/lib/flowchart.min.js}"></script>
<script th:src="@{/editormd/lib/jquery.flowchart.min.js}"></script>
<script>
$(function () {
var testEditor = editormd.markdownToHTML("doc-content", {//注意:这里是上面DIV的id
htmlDecode: "style,script,iframe",
emoji: true,
taskList: true,
tocm: true,
tex: true, // 默认不解析
flowChart: true, // 默认不解析
sequenceDiagram: true, // 默认不解析
codeFold: true
});
});
</script>
</html>
重启项目,访问进行测试!大功告成!
在Vue.js中使用
基础vue工程搭建
1、 打开控制命令符,选择创建位置
2、 创建 vue 工程
vue init webpack vuedemo5
- 创建完成显示
3、 安装、运行工程,测试是否成功
- 进入工程目录
cd vuedemo5
- 安装
i 是 install 的简写
npm install
或者
npm i
- 运行
npm run dev
显示:
打开浏览器输入网址:http://localhost:8080
初始化 vue 工程成功!
使用 idea 接管工程!
文章编辑整合(重点)
1、 导入 edtior.md 文件,以及 jquery.js
2、 打开 Terminal,安装插件
- 安装 scriptjs
npm i scriptjs
- 安装 aixos
npm install --save vue-axios
main.js中引用
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(VueAxios, axios)
3、 创建 Edtior.vue,并配置路由
<template>
<div>
<link rel="stylesheet" href="/static/editormd/css/editormd.css"/>
<!-- 文章发布表单 -->
<form id="mdEditorForm">
<div>
标题:<input type="text" name="title">
</div>
<div>
作者:<input type="text" name="author">
</div>
<!-- Editor.md 富文本编辑器 -->
<div id="article-content">
<textarea name="content" id="content" style="display:none;"></textarea>
</div>
</form>
</div>
</template>
<script>
import scriptjs from 'scriptjs'
export default {
name: "Edtior",
mounted(){
var _this = this;
(async () =>{
await _this.loadJs("/static/js/jquery.min.js");
await _this.loadJs("/static/editormd/js/editormd.js");
_this.jsLoadOver = true;
editormd("article-content", {
width: "95%", // 宽
height: "400px", // 高
path : "/static/editormd/lib/", // 加载编辑器lib路径
syncScrolling: "single", // 设置同步显示,三个选项[ true| false| "single",默认为true
saveHTMLToTextarea : true, // 保存 HTML 到 Textarea
emoji: true, // 开启表情功能,默认为false
// theme: "dark",//工具栏主题
// previewTheme: "dark",//预览主题
// editorTheme: "pastel-on-dark",//编辑主题
tex : true, // 开启科学公式TeX语言支持,默认关闭
flowChart : true, // 开启流程图支持,默认关闭
sequenceDiagram : true, // 开启时序/序列图支持,默认关闭,
//图片上传
imageUpload : true, // 开启图片上传
imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"], // 图片上传的类型
imageUploadURL : "http://localhost:8001/fileUpload", // 图片上传路径
/*指定需要显示的功能按钮*/
toolbarIcons : function() {
return ["undo","redo","|",
"bold","del","italic","quote","ucwords","uppercase","lowercase","|",
"h1","h2","h3","h4","h5","h6","|",
"list-ul","list-ol","hr","|",
"link","reference-link","image","code","preformatted-text",
"code-block","table","datetime","emoji","html-entities","pagebreak","|",
"goto-line","watch","preview","fullscreen","clear","search","|",
"help","releaseIcon", "index"]
},
/*自定义功能按钮,下面我自定义了2个,一个是发布,一个是返回首页*/
toolbarIconTexts : {
releaseIcon : "<span bgcolor=\"gray\">发布</span>",
index : "<span bgcolor=\"red\">返回首页</span>"
},
/*给自定义按钮指定回调函数*/
toolbarHandlers:{
releaseIcon : function(cm, icon, cursor, selection) {
_this.axios({
method: 'post',
url: 'http://localhost:8001/addArticle',
data: $("#mdEditorForm").serialize()
}).then(function (response) {
console.log(response)
})
},
index : function(){
window.location.href = '/';
}
}
})
})();
},
methods: {
loadJs: function (url) {
return new Promise((resolve) => {
scriptjs(url, () =>{
resolve();
})
})
}
}
}
</script>
<style scoped>
</style>
- 路由配置
import Vue from 'vue'
import Router from 'vue-router'
import editor from '../components/Edtior'
Vue.use(Router)
export default new Router({
mode: 'history',
routes: [
{path:"/", name: editor, component:editor}
]
})
4、 启动项目
npm run dev
- 结果显示
参考文章:https://www.kuangstudy.com/bbs/1366420418474299393
- 解决
找到 static/editormd/plugins/image-dialog/image-dialog.js 文件
查找 submitHandler 函数
将函数里面的内容修改为
var form = dialog.find("[enctype=\"multipart/form-data\"]")[0];
var formData = new FormData(form);
$.ajax({
type: 'post',
// url: "http://localhost:8001/fileUpload", // 你的服务器端的图片上传接口。如果你设置了 imageUploadURL,那么可以使用下面的方式
url: settings.imageUploadURL + (settings.imageUploadURL.indexOf("?") >= 0 ? "&" : "?") + "guid=" + guid,
data: formData,
cache: false,
processData: false,
contentType: false,
success: function(data, textStatus, jqXHR) {
// console.log(data);
// console.log(textStatus);
// console.log(jqXHR);
if (data.success === 1) { // 上传成功
dialog.find("[data-url]").val(data.url); // 设置图片地址
}
else {
alert(data.message); // 上传失败,弹出警告信息
}
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
// console.log(XMLHttpRequest);
// console.log(textStatus);
// console.log(errorThrown);
}
});
loading(false);
return false;
函数后面的
dialog.find("[type=\"submit\"]").bind("click", submitHandler).trigger("click");
修改为
dialog.bind("click", submitHandler).trigger("click");
重启测试~问题解决!
文章展示
1、 编写页面 article.vue
<template>
<div>
<h2 style="margin: auto 0">{{articles.title}}</h2>
作者:<span style="float: left">{{articles.author}}</span>
<!--文章主体内容-->
<div id="doc-content">
<textarea style="display:none;" placeholder="markdown" >{{articles.content}}</textarea>
</div>
</div>
</template>
<script>
import scriptjs from 'scriptjs'
export default {
props: ['id'],
name: "Article",
data(){
return{
articles: {
title: "",
author: "",
content: ""
}
}
},
mounted(){
var _this = this;
(async () => {
await _this.loadJs("/static/js/jquery.min.js");
await _this.loadJs("/static/editormd/js/editormd.js");
await _this.loadJs("/static/editormd/lib/marked.min.js");
await _this.loadJs("/static/editormd/lib/prettify.min.js");
await _this.loadJs("/static/editormd/lib/raphael.min.js");
await _this.loadJs("/static/editormd/lib/underscore.min.js");
await _this.loadJs("/static/editormd/lib/sequence-diagram.min.js");
await _this.loadJs("/static/editormd/lib/flowchart.min.js");
await _this.loadJs("/static/editormd/lib/jquery.flowchart.min.js");
_this.jsLoadOver = true;
var testEditor = editormd.markdownToHTML("doc-content", {//注意:这里是上面DIV的id
htmlDecode: "style,script,iframe",
emoji: true,
taskList: true,
tocm: true,
tex: true, // 默认不解析
flowChart: true, // 默认不解析
sequenceDiagram: true, // 默认不解析
codeFold: true
});
})()
_this.axios({
method: 'get',
url: 'http://localhost:8001/getArticle/'+ _this.id
}).then(function (response) {
_this.articles = response.data.article;
})
},
methods:{
loadJs: function (url) {
return new Promise((resolve) => {
scriptjs(url, () =>{
resolve();
})
})
}
}
}
</script>
<style scoped>
</style>
2、 配置路由
{path:"/article/:id", name: article, component:article, props: true}
3、 网站输入http://localhost:8004/article/2
测试成功~富文本编辑器搞定! emoji: true, taskList: true, tocm: true, tex: true, // 默认不解析 flowChart: true, // 默认不解析 sequenceDiagram: true, // 默认不解析 codeFold: true }); })()
_this.axios({
method: 'get',
url: 'http://localhost:8001/getArticle/'+ _this.id
}).then(function (response) {
_this.articles = response.data.article;
})
},
methods:{
loadJs: function (url) {
return new Promise((resolve) => {
scriptjs(url, () =>{
resolve();
})
})
}
}
}
2、 配置路由
```java
{path:"/article/:id", name: article, component:article, props: true}
3、 网站输入http://localhost:8004/article/2
测试成功~富文本编辑器搞定!