SpringBoot整合Editor.md等其他功能
从官网下载Editor.md(https://pandao.github.io/editor.md/)压缩包,仿照里面的案例进行修改和使用。
数据库设计
1 CREATE TABLE `article`( 2 `id` INT(10) NOT NULL AUTO_INCREMENT COMMENT '文章的唯一ID', 3 `author` varchar(50) NOT NULL COMMENT '作者', 4 `title` varchar(100) NOT NULL COMMENT '标题', 5 `content` longtext NOT NULL COMMENT '文章的内容', 6 PRIMARY KEY (`id`) 7 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
搭建SpringBoot项目
1、pom.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4 <modelVersion>4.0.0</modelVersion> 5 <!-- 父一个项目 --> 6 <parent> 7 <groupId>org.springframework.boot</groupId> 8 <artifactId>spring-boot-starter-parent</artifactId> 9 <version>2.2.11.RELEASE</version> 10 <relativePath/> <!-- lookup parent from repository --> 11 </parent> 12 <groupId>com.hbedu</groupId> 13 <artifactId>demo</artifactId> 14 <version>0.0.1-SNAPSHOT</version> 15 <name>demo</name> 16 <description>Demo project for Spring Boot</description> 17 18 <properties> 19 <java.version>1.8</java.version> 20 </properties> 21 22 <dependencies> 23 <!-- 启动器 --> 24 <dependency> 25 <groupId>org.springframework.boot</groupId> 26 <artifactId>spring-boot-starter</artifactId> 27 </dependency> 28 29 <!-- web依赖:tomacat,dispatcherServlet,xml... --> 30 <dependency> 31 <groupId>org.springframework.boot</groupId> 32 <artifactId>spring-boot-starter-web</artifactId> 33 </dependency> 34 35 <!-- spring-boot-starter所有的springboot依赖都是使用这个开头的 --> 36 <!-- 单元测试 --> 37 <dependency> 38 <groupId>org.springframework.boot</groupId> 39 <artifactId>spring-boot-starter-test</artifactId> 40 <scope>test</scope> 41 <exclusions> 42 <exclusion> 43 <groupId>org.junit.vintage</groupId> 44 <artifactId>junit-vintage-engine</artifactId> 45 </exclusion> 46 </exclusions> 47 </dependency> 48 49 <!-- springboot集成thymeleaf --> 50 <dependency> 51 <groupId>org.springframework.boot</groupId> 52 <artifactId>spring-boot-starter-thymeleaf</artifactId> 53 </dependency> 54 55 <!-- 用于pojo类自动生成getter、setter、构造方法 --> 56 <dependency> 57 <groupId>org.projectlombok</groupId> 58 <artifactId>lombok-maven-plugin</artifactId> 59 <version>1.18.12.0</version> 60 <scope>provided</scope> 61 </dependency> 62 63 <!-- Java对象与JSON 格式互相转换工具包 --> 64 <dependency> 65 <groupId>com.alibaba</groupId> 66 <artifactId>fastjson</artifactId> 67 <version>1.2.73</version> 68 </dependency> 69 70 <!-- springboot集成mybatis --> 71 <dependency> 72 <groupId>org.mybatis.spring.boot</groupId> 73 <artifactId>mybatis-spring-boot-starter</artifactId> 74 <version>2.1.1</version> 75 </dependency> 76 77 <!-- druid数据源 --> 78 <dependency> 79 <groupId>com.alibaba</groupId> 80 <artifactId>druid</artifactId> 81 <version>1.1.22</version> 82 </dependency> 83 84 <!-- mysql驱动 --> 85 <dependency> 86 <groupId>mysql</groupId> 87 <artifactId>mysql-connector-java</artifactId> 88 <version>8.0.16</version> 89 </dependency> 90 91 <!-- 日志 --> 92 <dependency> 93 <groupId>log4j</groupId> 94 <artifactId>log4j</artifactId> 95 <version>1.2.17</version> 96 </dependency> 97 98 </dependencies> 99 100 <build> 101 <resources> 102 <resource> 103 <directory>src/main/java</directory> 104 <includes> 105 <include>**/*.xml</include> 106 </includes> 107 <filtering>true</filtering> 108 </resource> 109 </resources> 110 111 <plugins> 112 <!-- 打jar包插件 --> 113 <plugin> 114 <groupId>org.springframework.boot</groupId> 115 <artifactId>spring-boot-maven-plugin</artifactId> 116 </plugin> 117 </plugins> 118 </build> 119 120 </project>
2、实体类
1 package com.hbedu.pojo; 2 3 import lombok.AllArgsConstructor; 4 import lombok.Data; 5 import lombok.NoArgsConstructor; 6 7 import java.io.Serializable; 8 9 @Data 10 @NoArgsConstructor 11 @AllArgsConstructor 12 public class Article implements Serializable { 13 14 private int id; //文章的唯一ID 15 private String author; //作者名 16 private String title; //标题 17 private String content; //文章的内容 18 }
3.mapper接口
1 package com.hbedu.dao; 2 3 import com.hbedu.pojo.Article; 4 import org.apache.ibatis.annotations.Mapper; 5 import org.springframework.stereotype.Repository; 6 7 import java.util.List; 8 9 @Mapper 10 @Repository 11 public interface ArticleMapper { 12 13 //查询所有的文章 14 List<Article> queryArticles(); 15 16 //新增一个文章 17 int addArticle(Article article); 18 19 //根据文章id查询文章 20 Article getArticleById(int id); 21 22 //根据文章id删除文章 23 int deleteArticleById(int id); 24 25 //修改文章 26 int updateArticle(Article article); 27 }
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 3 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 4 5 <mapper namespace="com.hbedu.dao.ArticleMapper"> 6 7 <cache/> 8 9 <select id="queryArticles" resultType="Article"> 10 select * from article 11 </select> 12 13 <select id="getArticleById" resultType="Article"> 14 select * from article where id = #{id} 15 </select> 16 17 <insert id="addArticle" parameterType="Article"> 18 insert into article (author,title,content) values (#{author},#{title},#{content}); 19 </insert> 20 21 <delete id="deleteArticleById" parameterType="int"> 22 delete from article where id = #{id} 23 </delete> 24 25 <update id="updateArticle" parameterType="Article"> 26 update article set author = #{author}, 27 title = #{title}, 28 content = #{content} 29 where id = #{id}; 30 </update> 31 32 </mapper>
4、application.yml
1 spring: 2 datasource: 3 username: root 4 password: 123 5 # ?serverTimezone=UTC解决时区的报错 6 url: jdbc:mysql://127.0.0.1:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8 7 driver-class-name: com.mysql.cj.jdbc.Driver 8 type: com.alibaba.druid.pool.DruidDataSource 9 thymeleaf: 10 cache: false 11 12 mybatis: 13 type-aliases-package: com.hbedu.pojo 14 mapper-locations: classpath:mybatis/mapper/*.xml 15 configuration: 16 # 用于mybatis在控制台打印sql日志 17 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl 18 # 用于开启二级缓存 19 cache-enabled: true
5、设置上传图片回显
package com.hbedu.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class MyMvcConfig implements WebMvcConfigurer { // 文件保存在真实目录/upload/下, // 访问的时候使用虚路径/upload,比如文件名为1.png,就直接/upload/1.png就ok了。 @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/upload/**") .addResourceLocations("file:"+System.getProperty("user.dir")+"/upload/"); } }
6、controller
文章controller
package com.hbedu.controller; import com.alibaba.fastjson.JSONObject; import com.hbedu.pojo.Article; import com.hbedu.service.impl.ArticleServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import java.io.File; import java.io.IOException; import java.util.Calendar; import java.util.List; import java.util.UUID; @Controller @RequestMapping("/article") public class ArticleController { @Autowired private ArticleServiceImpl articleService; @GetMapping("/toEditor") public String toEditor() { return "editor"; } // 添加内容 @PostMapping("/addArticle") public String addArticle(Article article) { articleService.addArticle(article); return "redirect:/article/toBlogging"; } //博客图片上传问题 @RequestMapping("/file/upload") @ResponseBody public JSONObject fileUpload(@RequestParam(value = "editormd-image-file", required = true) MultipartFile file, HttpServletRequest request) throws IOException { //上传路径保存设置 // 获得SpringBoot当前项目的路径:System.getProperty("user.dir") String path = System.getProperty("user.dir") + "/upload/"; // 按照月份进行分类: Calendar instance = Calendar.getInstance(); String month = (instance.get(Calendar.MONTH) + 1) + "月"; // 文件保存地址 path = path + month; File realPath = new File(path); if (!realPath.exists()) { realPath.mkdir(); } // 上传文件地址 System.out.println("上传文件保存地址:" + realPath); // 解决文件名字问题:我们使用uuid; String filename = "ks-" + UUID.randomUUID().toString().replaceAll("-", ""); // 通过CommonsMultipartFile的方法直接写文件(注意这个时候) file.transferTo(new File(realPath + "/" + filename)); //给editormd进行回调 JSONObject res = new JSONObject(); res.put("url", "/upload/" + month + "/" + filename); res.put("success", 1); res.put("message", "upload success!"); return res; } @GetMapping("/toBlogging") public String toBlogging(Model model) { List<Article> articles = articleService.queryArticles(); model.addAttribute("articles", articles); return "blogging"; } // 文章展示 @GetMapping("/{id}") public String show(@PathVariable("id") int id, Model model) { Article article = articleService.getArticleById(id); model.addAttribute("article", article); return "article"; } // 获取文章ID @GetMapping("/modifyArticle/{id}") public String modifyArticle(@PathVariable("id") int id, HttpSession session) { session.setAttribute("id", id); return "redirect:/article/modify"; } // 文章修改页 @GetMapping("/modify") public String modify(Model model, HttpSession session) { int id = (int) session.getAttribute("id"); Article article = articleService.getArticleById(id); model.addAttribute("article", article); return "modify"; } // 文章修改 @PostMapping("/updateArticle") public String updateArticle(Article article) { try{ articleService.updateArticle(article); }catch (Exception e){ throw new NumberFormatException(); }finally { return "redirect:/article/toBlogging"; } } // 文章删除 Complete @GetMapping("/deleteArticle/{id}") public String deleteArticle(@PathVariable("id") int id) { articleService.deleteArticleById(id); return "redirect:/article/toBlogging"; } }
照片controller
package com.hbedu.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @Controller public class PhotoController { @GetMapping("/toPhoto") public String photo(){ return "photo"; } }
页面展示
1.index.html
1 <!DOCTYPE html> 2 <html lang="zh" xmlns:th="http://www.thymeleaf.org"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Index</title> 6 <script th:src="@{/editormd/lib/jquery.min.js}"></script> 7 <link th:href="@{/bootstrap/css/bootstrap.min.css}" rel="stylesheet"> 8 <link th:href="@{/bootstrap/css/main.css}" rel="stylesheet"> 9 </head> 10 <body> 11 <div class="container"> 12 <div class="row"> 13 <div class="col-sm-6 col-md-4"> 14 <div class="thumbnail"> 15 <div class="caption"> 16 <h3>博客</h3> 17 <hr> 18 <p><a th:href="@{/article/toEditor}">blog</a></p> 19 </div> 20 </div> 21 </div> 22 23 <div class="col-sm-6 col-md-4"> 24 <div class="thumbnail"> 25 <div class="caption"> 26 <h3>照片墙</h3> 27 <hr> 28 <p><a th:href="@{/toPhoto}" target="_blank">photo</a></p> 29 </div> 30 </div> 31 </div> 32 33 <div class="col-sm-6 col-md-4"> 34 <div class="thumbnail"> 35 <div class="caption"> 36 <h3>博客列表</h3> 37 <hr> 38 <p><a th:href="@{/article/toBlogging}" target="_blank">blogging</a></p> 39 </div> 40 </div> 41 </div> 42 </div> 43 </div> 44 </body> 45 </html>
2、editor.html
1 <!DOCTYPE html> 2 <html class="x-admin-sm" lang="zh" xmlns:th="http://www.thymeleaf.org"> 3 4 <head> 5 <meta charset="UTF-8"> 6 <title>editor</title> 7 <!--Editor.md--> 8 <link rel="stylesheet" th:href="@{/editormd/css/editormd.css}"/> 9 <!-- 图标 --> 10 <!--<link rel="shortcut icon" href="https://pandao.github.io/editor.md/favicon.ico" type="image/x-icon" />--> 11 <!--layui--> 12 <!--<link rel="stylesheet" th:href="@{/layui/css/layui.css}"/>--> 13 </head> 14 15 <body> 16 17 <div class="layui-fluid"> 18 <div class="layui-row layui-col-space15"> 19 <div class="layui-col-md12"> 20 <!--博客表单--> 21 <form name="mdEditorForm"> 22 <div> 23 标题:<input type="text" name="title" required> 24 </div> 25 <div> 26 作者:<input type="text" name="author" required> 27 </div> 28 <div id="article-content"> 29 <textarea name="content" id="content" style="display:none;" required></textarea> 30 </div> 31 </form> 32 </div> 33 </div> 34 </div> 35 </body> 36 37 <!--editormd--> 38 <script th:src="@{/editormd/lib/jquery.min.js}"></script> 39 <script th:src="@{/editormd/editormd.js}"></script> 40 <!--<script th:src="@{/layui/layui.js}"></script>--> 41 <script type="text/javascript"> 42 var testEditor; 43 44 //window.onload = function(){ } 45 $(function() { 46 testEditor = editormd("article-content", { 47 width : "95%", 48 height : 400, 49 syncScrolling : "single", 50 path : "../editormd/lib/", 51 saveHTMLToTextarea : true, // 保存 HTML 到 Textarea 52 emoji: true, 53 //theme: "dark", //工具栏主题 54 // previewTheme: "dark", //预览主题 55 //editorTheme: "pastel-on-dark", //编辑主题 56 tex : true, // 开启科学公式TeX语言支持,默认关闭 57 flowChart : true, // 开启流程图支持,默认关闭 58 sequenceDiagram : true, // 开启时序/序列图支持,默认关闭, 59 //图片上传 60 imageUpload : true, 61 imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"], 62 imageUploadURL : "/article/file/upload", 63 onload : function() { 64 console.log('onload', this, 'image'); 65 console.log(this.); 66 }, 67 /*指定需要显示的功能按钮*/ 68 toolbarIcons : function() { 69 return ["undo","redo","|", 70 "bold","del","italic","quote","ucwords","uppercase","lowercase","|", 71 "h1","h2","h3","h4","h5","h6","|", 72 "list-ul","list-ol","hr","|", 73 "link","reference-link","image","code","preformatted-text", 74 "code-block","table","datetime","emoji","html-entities","pagebreak","|", 75 "goto-line","watch","preview","fullscreen","clear","search","|", 76 "help","info","releaseIcon", "index"] 77 }, 78 79 /*自定义功能按钮,下面我自定义了2个,一个是发布,一个是返回首页*/ 80 toolbarIconTexts : { 81 releaseIcon : "<span bgcolor=\"gray\">发布</span>", 82 index : "<span bgcolor=\"red\">返回首页</span>", 83 }, 84 85 /*给自定义按钮指定回调函数*/ 86 toolbarHandlers:{ 87 releaseIcon : function(cm, icon, cursor, selection) { 88 //表单提交 89 mdEditorForm.method = "post"; 90 mdEditorForm.action = "/article/addArticle";//提交至服务器的路径 91 mdEditorForm.submit(); 92 }, 93 index : function(){ 94 window.location.href = '/'; 95 }, 96 } 97 }); 98 }); 99 </script> 100 101 </html>
3、photo.html
1 <!DOCTYPE html> 2 <html lang="zh" xmlns:th="http://www.thymeleaf.org"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>photo</title> 6 <link th:href="@{/photo/css/style.css}" rel="stylesheet"> 7 <script th:src="@{/photo/js/prefixfree.min.js}"></script> 8 </head> 9 <body> 10 <div class="box"> 11 <ul class="minbox"> 12 <li></li> 13 <li></li> 14 <li></li> 15 <li></li> 16 <li></li> 17 <li></li> 18 </ul> 19 <ol class="maxbox"> 20 <li></li> 21 <li></li> 22 <li></li> 23 <li></li> 24 <li></li> 25 <li></li> 26 </ol> 27 </div> 28 29 <audio autoplay="autoplay" controls="controls" loop="loop" preload="auto" th:src="@{/photo/music/云烟成雨.mp3}"> 30 </audio> 31 </body> 32 </html>
4、blogging.html
<!DOCTYPE html> <html lang="zh" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>blogging</title> <style type="text/css"> ul { list-style: none; } a { text-decoration: none; } </style> </head> <body> <h1>Blogging</h1> <ul th:each="c : ${articles}"> <a th:href="@{'/article/'+${c.getId()}}"><li th:text="${c.getTitle()}"></li></a> </ul> </body> </html>
5、article.html
<!DOCTYPE html> <html lang="zh" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title th:text="${article.title}"></title> <style type="text/css"> a { text-decoration: none; } </style> </head> <body> <div> <!--文章头部信息:标题,作者,最后更新日期,导航--> <h2 style="margin: auto 0" th:text="${article.title}"></h2> <span style="float: left" th:text="'作者:'+${article.author}"></span> <a th:href="@{'/article/modifyArticle/' + ${article.getId()} }">修改</a> <a th:href="@{'/article/deleteArticle/' + ${article.getId()} }">删除</a> <!--文章主体内容--> <div id="doc-content" style="width: auto;"> <textarea style="display:none;" placeholder="markdown" th:text="${article.content}"></textarea> </div> </div> <link rel="stylesheet" th:href="@{/editormd/css/editormd.preview.css}" /> <script th:src="@{/editormd/lib/jquery.min.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 th:src="@{/editormd/editormd.js}"></script> <script type="text/javascript"> var testEditor; $(function () { 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> </body> </html>
6、modify.html
<!DOCTYPE html> <html class="x-admin-sm" lang="zh" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>modify</title> <!--Editor.md--> <link rel="stylesheet" th:href="@{/editormd/css/editormd.css}"/> <!--图标--> <!--<link rel="shortcut icon" href="https://pandao.github.io/editor.md/favicon.ico" type="image/x-icon" />--> <!--layui--> <!--<link rel="stylesheet" th:href="@{/layui/css/layui.css}"/>--> </head> <body> <div class="layui-fluid"> <div class="layui-row layui-col-space15"> <div class="layui-col-md12"> <!--博客表单--> <form name="mdEditorForm"> <div style="display: none"> <input type="text" name="id" th:value="${article.getId()}" readonly> </div> <div> 标题:<input type="text" name="title" th:value="${article.getTitle()}"> </div> <div> 作者:<input type="text" name="author" th:value="${article.getAuthor()}"> </div> <div id="article-content"> <textarea name="content" id="content" style="display:none;" th:text="${article.getContent()}"></textarea> </div> </form> </div> </div> </div> </body> <!--editormd--> <script th:src="@{/editormd/lib/jquery.min.js}"></script> <script th:src="@{/editormd/editormd.js}"></script> <!--<script th:src="@{/layui/layui.js}"></script>--> <script type="text/javascript"> var testEditor; // window.onload = function(){ } $(function() { testEditor = editormd("article-content", { width : "95%", height : 400, syncScrolling : "single", path : "../editormd/lib/", saveHTMLToTextarea : true, // 保存 HTML 到 Textarea emoji: true, //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 : "/article/file/upload", onload : function() { console.log('onload', this); }, /*指定需要显示的功能按钮*/ 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","info","releaseIcon", "index"] }, /*自定义功能按钮,下面我自定义了2个,一个是发布,一个是返回首页*/ toolbarIconTexts : { releaseIcon : "<span bgcolor=\"gray\">修改</span>", index : "<span bgcolor=\"red\">返回博客</span>", }, /*给自定义按钮指定回调函数*/ toolbarHandlers:{ releaseIcon : function(cm, icon, cursor, selection) { //表单提交 mdEditorForm.method = "post"; mdEditorForm.action = "/article/updateArticle";//提交至服务器的路径 mdEditorForm.submit(); }, index : function(){ window.location.href = '/article/toBlogging'; }, } }); }); </script> </html>
页面设计很随意,功能还不够完善,源代码地址:https://gitee.com/Nie-quan/learning.git