原创 标题:  一款好用的Markdown编辑器及使用过程中的坑

http://blog.choupangxia.com/

Markdown在技术圈越来越受欢迎,今天为大家带来一款国内开源的比较好用的Markdown编辑器——editor.md。同时也聊聊在使用过程中遇到的坑。

editor.md简介

需要预览效果可直接访问url:https://pandao.github.io/editor.md/index.html ,这里是官方网站的一个demo。同时提供了全套的下载安装教程。github开源地址:https://github.com/pandao/editor.md/blob/master/examples/image-upload.html

无论发布含有代码的文章、画流程图、展示数学公式等都有完备的支持。而且此编辑器与后端代码无关,纯前端js实现。

editor.md目录介绍

本文介绍的版本为v1.5.0,在首页下载完成,解压editor.md-master.zip文件,可以看到如下图的目录结构:

图中红色框内是我们要引用到项目的文件和目录。

  • css目录中可选择editormd.min.css放在对应的项目css目录中;
  • js可选择editormd.min.js放置在对应项目的js目录中,需要注意的是同时需要引入jQuery,这里使用jquery.min.js;
  • examples文件夹中是一部分核心功能的demo,在使用的过程中用到对应的组件或功能可打开参考;
  • fonts是需要用到字体,可一并引入项目;
  • images是一些加载类的图片;
  • lib是editor.md依赖的第三方js资源,比如流程图的js资源;
  • plugins主要是编辑器上面的操作功能插件,比如图片上传等,可选择使用的进行加载;

editor.md整合使用

在上面提到对应的文件根据项目的需要拷贝至项目中,需要注意的是最好文件夹的名字在项目中一一对照。特别是plugins这个文件夹的名称,在使用插件时editormd.min.js会默认找对应plugins路径下的插件。

引入css和js

在使用到editor.md的页面引入css和js:

<link rel="stylesheet" href="/css/editormd.css"/>
<#--js可在jsp或其他类型选择页面的底部引入-->
<script src="/js/jquery.min.js"></script>
<script src="/js/editormd.min.js"></script>

页面中添加div

在需要添加editor.md编辑器的地方输入以下div:

<div id="content-editormd" class="form-group">
    <textarea style="display:none;" class="form-control" id="content-editormd-markdown-doc" name="content-editormd-markdown-doc"></textarea>
</div>

div的id为content-editormd,后面的js代码中需要用到。textarea就是我们编写markdown文件的地方,editor.md会将此textarea渲染成在第一种图中看到的效果。默认的textarea的内容为markdown文档的内容,通过form表单提交时后台可通过content-editormd-markdown-doc获取到对应的markdown文档内容。比如Java中可通过request.getParameter(“content-editormd-markdown-doc”)获得。

注意:此处需要注意的是,无论需要html格式的内容还是markdown格式的内容,都只需要写一个textarea。此处有一个很大的坑。不少其他教程中说需要两个textarea,那么会导致后一个textarea后台获得的数据是一个数组,而不是单纯的HTML内容。

页面添加js代码

js代码用来将上面的textarea进行渲染:

<script type="text/javascript">
    $(function() {
        editormd("content-editormd", {
            width   : "100%",
            height  : 1000,
            syncScrolling : "single",
            path    : "/lib/",
            saveHTMLToTextarea : true, // 保存HTML到Textarea

        });
    });
</script>

此段js代码中的content-editormd就是上面div的id。其中path路径需要指定到项目中对应的lib的路径。saveHTMLToTextarea设置为true表示,转化为html格式的内容也同样提交到后台。

此时如果用浏览器查看页面元素会发现,不仅有name为content-editormd-markdown-doc的textarea,editor.md还会生成一个name为content-editormd-html-code的textarea。也就是说当saveHTMLToTextarea设置为true时,编辑器会自动生成这么一个textarea,后台只需要根据name获取即可,不需要在前台再次自定义一个textarea。

后台接收请求本文采用Springmvc,代码如下:

@RequestMapping("/publish")
public String publish(HttpSession session,@RequestParam("content-editormd-html-code") String contentHtml,@RequestParam("content-editormd-markdown-doc") String content) {
    return null;
}

其中content-editormd-html-code对应的html格式的内容,content-editormd-markdown-doc对应的是markdown格式的内容。如果发现无法接收到参数,可采用上面的方法查看一下页面元素。

上传图片

editor.md的上传图片功能实现起来比较简单,只需要在上段代码中再添加一些配置即可。

<script type="text/javascript">
    $(function() {
        editormd("content-editormd", {
            width   : "100%",
            height  : 1000,
            syncScrolling : "single",
            path    : "/lib/",
            saveHTMLToTextarea : true, // 保存 HTML 到 Textarea
            // 图片上传
            imageUpload : true,
            imageFormats: ["jpg","jpeg","gif","png","bmp","webp"],
            imageUploadURL: "/file/upload",
        });
    });
</script>
  • imageUpload设置为true表示支持上传,此时需要plugins中的image-dialog.js插件(默认调用,放在指定路径即可)。
  • imageFormats为支持上传的图片类型。
  • imageUploadURL要上传图片的后台服务器路径。

注意:此处需要注意的是服务器后台返回结果的格式必须json格式,且内容如下:

{
    success : 0 | 1, //0表示上传失败;1表示上传成功
    message : "提示的信息",
    url     : "图片地址" //上传成功时才返回
}

此处有一个大坑,返回的参数success的值不是字符串“0”和“1”,而是数字0和1,后台返回的时候一定要注意,否则会出现图片上传成功之后,返回结果中的图片地址无法填充。

其中message为上传成功或失败之后alert出来提示用户的信息。url为上传成功之后服务器返回的图片地址。

后台接收上传代码如下:

@Controller
@RequestMapping("/file")
public class FileUploadController {

    private static final Logger LOGGER = LoggerFactory.getLogger(FileUploadController.class);

    private static final String SUCCESS = "success";

    @ResponseBody
    @RequestMapping("/upload")
    public String upload(HttpSession session, @RequestParam(value = "editormd-image-file") MultipartFile file) {
        JSONObject res = new JSONObject();

        try {
            Map<String, String> uploadMap = QiniuUtils.upload(file.getInputStream());
            String code = uploadMap.get("code");
            if (SUCCESS.equals(code)) {
                res.put("success", 1);
                res.put("message", "上传成功");
                res.put("url", uploadMap.get("url"));
            } else {
                res.put("success", 0);
                res.put("message", "上传失败" + uploadMap.get("error"));
            }

        } catch (IOException e) {
            LOGGER.error("上传图片异常", e);
            res.put("success", 0);
            res.put("message", "上传异常");
        }

        LOGGER.info("上传图片返回结果:{}", res);
        return res.toString();
    }
}

本代码中采用了云存储,大家在使用的过程中可针对自己的情况改写为服务器路径或其他云存储路径。

注意:此处有一坑:服务接收file文件时,对应的参数为editormd-image-file,如果接收不到可采用上面的方式查看页面元素。

上传效果如下图:

markdown页面展示

通过上面的步骤,已经将html格式和markdown格式的内容都上传到服务器了。如果需要编辑则加载markdown的内容,此时只需将服务器存储的内容赋值给上面的textarea即可。

展示内容有两种方式,一种方式是直接展示html格式的内容,第二种方式是页面加载markdown格式内容,然后通过editor.md再次渲染成html格式。

首先看一下直接展示html页面需要做什么。

<link rel="stylesheet" href="/css/editormd.min.css"/>

<div id="content-editormd">
    ${contentHtml}
</div>


<script src="/js/jquery.min.js"></script>
<script src="/js/editormd.min.js"></script>
<script src="/lib/marked.min.js"></script>
<script src="/lib/prettify.min.js"></script>

<script type="text/javascript">
    $(function () {
        editormd.markdownToHTML("content-editormd");
    })
</script>

其中${contentHtml}为获取后台的html格式内容。

如果使用markdown内容前端渲染,则前端代码如下:

<link rel="stylesheet" href="/css/editormd.min.css"/>
<div id="content-editormd">
    <textarea style="display:none;">${content}</textarea>
</div>

<script src="/js/jquery.min.js"></script>
<script src="/js/editormd.min.js"></script>
<script src="/lib/marked.min.js"></script>
<script src="/lib/prettify.min.js"></script>
<script src="/lib/raphael.min.js"></script>
<script src="/lib/underscore.min.js"></script>
<script src="/lib/sequence-diagram.min.js"></script>
<script src="/lib/flowchart.min.js"></script>
<script src="/lib/jquery.flowchart.min.js"></script>


<script type="text/javascript">
    $(function () {     
        editormd.markdownToHTML("content-editormd", {
            htmlDecode: "style,script,iframe", //可以过滤标签解码
            emoji: true,
            taskList: true,
            tex: true,               // 默认不解析
            flowChart: true,         // 默认不解析
            sequenceDiagram: true, // 默认不解析
            codeFold: true
        });
    })
</script>

editor.md的更多配置项

可参看example中的full.html。

$.get('test.md', function(md){
    testEditor = editormd("test-editormd", {
        width: "90%",
        height: 740,
        path : '../lib/',
        theme : "dark",
        previewTheme : "dark",
        editorTheme : "pastel-on-dark",
        markdown : md,
        codeFold : true,
        //syncScrolling : false,
        saveHTMLToTextarea : true,    // 保存 HTML 到 Textarea
        searchReplace : true,
        //watch : false,                // 关闭实时预览
        htmlDecode : "style,script,iframe|on*",            // 开启 HTML 标签解析,为了安全性,默认不开启    
        //toolbar  : false,             //关闭工具栏
        //previewCodeHighlight : false, // 关闭预览 HTML 的代码块高亮,默认开启
        emoji : true,
        taskList : true,
        tocm            : true,         // Using [TOCM]
        tex : true,                   // 开启科学公式TeX语言支持,默认关闭
        flowChart : true,             // 开启流程图支持,默认关闭
        sequenceDiagram : true,       // 开启时序/序列图支持,默认关闭,
        //dialogLockScreen : false,   // 设置弹出层对话框不锁屏,全局通用,默认为true
        //dialogShowMask : false,     // 设置弹出层对话框显示透明遮罩层,全局通用,默认为true
        //dialogDraggable : false,    // 设置弹出层对话框不可拖动,全局通用,默认为true
        //dialogMaskOpacity : 0.4,    // 设置透明遮罩层的透明度,全局通用,默认值为0.1
        //dialogMaskBgColor : "#000", // 设置透明遮罩层的背景颜色,全局通用,默认为#fff
        imageUpload : true,
        imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
        imageUploadURL : "./php/upload.php",
        onload : function() {
            console.log('onload', this);
            //this.fullscreen();
            //this.unwatch();
            //this.watch().fullscreen();

            //this.setMarkdown("#PHP");
            //this.width("100%");
            //this.height(480);
            //this.resize("100%", 640);
        }
    });
});

更多内容请关注微信公众号:程序新视界
程序新视界