AdminEAP框架—web集成Markdown组件
来源:互联网 发布:win764位系统优化 编辑:程序博客网 时间:2024/04/28 15:52
AdminEAP框架
1、Markdown介绍
Markdown 是一种轻量级的「标记语言」,它的优点很多,目前也被越来越多的写作爱好者,撰稿者广泛使用。看到这里请不要被「标记」、「语言」所迷惑,Markdown 的语法十分简单。常用的标记符号也不超过十个,这种相对于更为复杂的HTML 标记语言来说,Markdown 可谓是十分轻量的,学习成本也不需要太多,且一旦熟悉这种语法规则,会有一劳永逸的效果。
尤其作为程序员而言,需要经常编写技术文档,使用Markdown编辑器效率很高,你们现在看到的这篇博客就是CSDN的Markdown编辑器编写的。有关Markdown的介绍可以查阅Marddown的相关资料。
一直想找一款可以免费使用的markdown编辑器,网上也有很多推荐,目前比较流行的是StackEdit(CSDN的markdown插件是基于StackEdit修改的)和cmd Markdown,有些插件需要注册和收费。同时,需要经常写个人博客和技术文档,决定在开源项目AdminEAP中集成Markdown。
本文讲述的是在AdminEAP框架(一个在AdminLTE上实现的系统管理框架,已在Github上开源)下基于Editor.md实现的Markdown在线编辑组件。
以下是实现效果
2、Markdown组件比较
经过一番调研,目前在Github上人气比较旺的Markdown组件相关项目有stackedit,markdown-js,markdown-it,strapdown,editor.md,以下做个简单的比较。
- stackedit: 功能比较齐全,包含了服务端,而我只想要前端的功能,所以舍弃了
- markdown-js:不支持正常的代码解析,弃用
- markdown-it:nodejs编写,有详细的技术文档,但缺少demo,弃用
- strapdown: 解析功能不够强大,不支持时序图、流程图、数学公式等比较复杂的解析,但使用比较简单,结果样式比较多,比较美观,可以考虑用于比较简单markdown文件的解析显示
- editor.md 解析功能强大,demo比较齐全,对于web开发者来说易于理解,所以选择editor.md作为AdminEAP项目的markdown组件开发基础。
3、实现
AdminEAP中markdown组件使用浏览器缓存离线存储,只要不清除缓存,文件不会丢失。
关键字使用bootstrap-tagsinput组件
关键代码 markdown_add.html
<link rel="stylesheet" href="${basePath}/resources/common/libs/markdown/editor.md/css/editormd.min.css"><link rel="stylesheet" href="${basePath}/resources/common/libs/tagsinput/bootstrap-tagsinput.css"><link rel="stylesheet" href="${basePath}/resources/common/libs/tagsinput/app.css"><section class="content-header"> <h1> <span>MarkDown编辑</span> <small>新增</small> </h1> <ol class="breadcrumb"> <li><a href="#"><i class="fa fa-dashboard"></i> 首页</a></li> <li><a href="#">系统管理</a></li> <li class="active">Markdown编辑器</li> </ol></section><section class="content"> <div class="row form-horizontal"> <div class="col-sm-12"> <div class="box box-primary"> <div class="box-header bg-info" style="padding-bottom: 0"> <div class="row" style="padding-bottom:5px;"> <div class="col-sm-7"> <div clas="form-group"> <input class="form-control" type="text" placeholder="请输入文章标题……"> </div> </div> <div class="col-sm-5"> <div clas="form-group"> <input type="text" id="keywords" data-role="tagsinput" placeholder="关键字" > </div> </div> </div> <div class="row"> <div class="form-group col-sm-7"> <div class="col-sm-4"> <select id="editormd-theme-select" class="form-control"> <option selected="selected" value="">工具栏样式</option> </select> </div> <div class="col-sm-4"> <select id="editor-area-theme-select" class="form-control"> <option selected="selected" value="">编辑区样式</option> </select> </div> <div class="col-sm-4"> <select id="preview-area-theme-select" class="form-control"> <option selected="selected" value="">预览区样式</option> </select> </div> </div> <div class="form-group col-sm-5 pull-right"> <div class="col-sm-6"> <label class="control-label"> <input type="checkbox" name="autoHeight" data-flag="icheck" class="flat-red" id="autoHeight"> 自适应高度 </label> </div> <div class="col-sm-6 text-right"> <div class="btn-group "> <button class="btn btn-default"><i class="fa fa-file-pdf-o"> 导出PDF</i> </button> <button class="btn btn-primary"><i class="fa fa-save"> 提交</i></button> </div> </div> </div> </div> </div> <div clas="box-body"> <div id="editormd"> <textarea style="display:none;"></textarea> </div> </div> </div> </div> </div> </div></section><script type="text/javascript" src="${basePath}/resources/common/libs/markdown/editor.md/js/editormd.js"></script><script type="text/javascript" src="${basePath}/resources/common/libs/tagsinput/bootstrap-tagsinput.min.js"></script><script> var editor; function themeSelect(id, themes, lsKey, callback) { var select = $("#" + id); for (var i = 0, len = themes.length; i < len; i++) { var theme = themes[i]; var selected = (localStorage[lsKey] == theme) ? " selected=\"selected\"" : ""; select.append("<option value=\"" + theme + "\"" + selected + ">" + theme + "</option>"); } select.bind("change", function () { var theme = $(this).val(); if (theme === "") { return false; } localStorage[lsKey] = theme; callback(select, theme); }); return select; } $(function () { //高度自定义框 $("#autoHeight").iCheck({ checkboxClass: 'icheckbox_flat-green', radioClass: 'iradio_flat-green' }); var isAutoHeight = localStorage.isAutoHeight ? localStorage.isAutoHeight : false; if (isAutoHeight == "false" || !isAutoHeight) { $("#autoHeight").iCheck("uncheck"); } else { $("#autoHeight").iCheck("check"); } $("#autoHeight").on("ifChanged", function (event) { isAutoHeight = $("#autoHeight").prop("checked"); localStorage.isAutoHeight = isAutoHeight; }); //markdown 默认内容 var markdownContent = null; $.ajax({ type: "get", url: basePath + "/resources/common/libs/markdown/readme.md", async: false, success: function (md) { markdownContent = localStorage.markdownContent ? localStorage.markdownContent : md; } }); //keywords 关键字 $("#keywords").on("change",function(event){ var $element=$(event.target); var val = $element.val(); //修复placeholder问题 if(val){ $element.prev(".bootstrap-tagsinput").find("input:eq(0)").attr("placeholder",null); }else{ $element.prev(".bootstrap-tagsinput").find("input:eq(0)").attr("placeholder","关键字"); } }); //高度不自定义时高度 var clientHeight = (document.body.clientHeight < document.documentElement.clientHeight) ? document.body.clientHeight : document.documentElement.clientHeight; editor = editormd("editormd", { width: "100%", height: clientHeight, theme: (localStorage.theme) ? localStorage.theme : "default", previewTheme: (localStorage.previewTheme) ? localStorage.previewTheme : "default", editorTheme: (localStorage.editorTheme) ? localStorage.editorTheme : "default", path: basePath + '/resources/common/libs/markdown/editor.md/lib/', autoHeight: isAutoHeight == "true" ? true : false, htmlDecode: "style,script,iframe", tex: true, emoji: true, taskList: true, flowChart: true, sequenceDiagram: true, markdown: markdownContent, //图片上传 imageUpload: true, imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp"], imageUploadURL: "${basePath}/file/markdownUpload", onchange: function () { localStorage.markdownContent = editor.getMarkdown(); } /* 上传的后台只需要返回一个 JSON 数据,结构如下: { success : 0 | 1, // 0 表示上传失败,1 表示上传成功 message : "提示的信息,上传成功或上传失败及错误信息等。", url : "图片地址" // 上传成功时才返回 } */ }); //editor.clear(); /* $.get(basePath + "/resources/common/libs/markdown/readme.md", function (md) { setTimeout(function () { editor.appendMarkdown(md) }, 500); });*/ themeSelect("editormd-theme-select", editormd.themes, "theme", function ($this, theme) { editor.setTheme(theme); }); themeSelect("editor-area-theme-select", editormd.editorThemes, "editorTheme", function ($this, theme) { editor.setCodeMirrorTheme(theme); }); themeSelect("preview-area-theme-select", editormd.previewThemes, "previewTheme", function ($this, theme) { editor.setPreviewTheme(theme); }); });</script>
4、图片上传
imageUpload: true, imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp"], imageUploadURL: "${basePath}/file/markdownUpload",
后台实现代码
/** * markdown组件上传图片 */ @RequestMapping(value = "/markdownUpload", method = RequestMethod.POST) @ResponseBody public MarkDownResult markdownUpload(HttpServletRequest request, HttpServletResponse response, @RequestParam(value = "editormd-image-file", required = false) MultipartFile attach) { try { String relPath = PropertiesUtil.getValue("markdownPath"); String dirPath = request.getRealPath("/"); //不存在目录 则创建 File filePath = new File(dirPath + relPath); if (!filePath.exists()) { filePath.mkdirs(); } File realFile = new File(dirPath + relPath + File.separator + attach.getOriginalFilename()); //上传的文件已存在 则对新上传的文件重命名 if (realFile.exists()) { String fileName = DateUtil.format(new Date(), "yyyyMMddHHmmss") + "_" + attach.getOriginalFilename(); realFile = new File(dirPath+relPath+File.separator+fileName); } boolean iscreate = FileUtil.copyInputStreamToFile(attach.getInputStream(), realFile); if (iscreate) { String url=relPath + File.separator + realFile.getName(); url=request.getAttribute("basePath").toString()+url.replaceAll("\\\\","/"); return new MarkDownResult(1, "上传成功", url); } else { return new MarkDownResult(0, "上传失败", null); } } catch (IOException ex) { return new MarkDownResult(0, "上传失败:原因" + ex.getMessage().toString(), null); } }
5、文章预览
实现效果
关键代码
<link rel="stylesheet" href="${basePath}/resources/common/libs/markdown/editor.md/css/editormd.min.css"><section class="content-header"> <h1> <span>我的文章</span> <small id="title_sm">预览</small> </h1> <ol class="breadcrumb"> <li><a href="#"><i class="fa fa-dashboard"></i> 首页</a></li> <li><a href="#">系统管理</a></li> <li class="active">我的文章</li> </ol></section><section class="content"> <div class="row form-horizontal"> <div class="col-md-12"> <div class="box box-primary"> <div class="box-header text-center"> <div clas="form-group"> <label id="title" style="font-size:21px;font-weight:normal"></label> </div> <div clas="form-group"> <label style="font-weight: normal">关键字:</label> <label id="keywords"></label> </div> </div> <div clas="box-body"> <div id="editormd"> <textarea id="append-test" style="display:none;"></textarea> </div> </div> </div> </div> </div> <div class="navbar-fixed-middle"> <div class="box box-primary box-solid"> <div class="box-header with-border"> <h4 class="box-title" style="padding-right:20px;"><i class="fa fa-hand-o-left"> 文章目录</i></h4> <div class="box-tools pull-right"> <button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i> </button> </div> <!-- /.box-tools --> </div> <!-- /.box-header --> <div class="box-body" style="padding-left:0"> <div id="custom-toc-container"></div> </div> <!-- /.box-body --> </div> </div> </div> </div></section><script type="text/javascript" src="${basePath}/resources/common/libs/markdown/editor.md/lib/marked.min.js"></script><script type="text/javascript" src="${basePath}/resources/common/libs/markdown/editor.md/lib/prettify.min.js"></script><script type="text/javascript" src="${basePath}/resources/common/libs/markdown/editor.md/lib/raphael.min.js"></script><script type="text/javascript" src="${basePath}/resources/common/libs/markdown/editor.md/lib/underscore.min.js"></script><script type="text/javascript" src="${basePath}/resources/common/libs/markdown/editor.md/lib/sequence-diagram.min.js"></script><script type="text/javascript" src="${basePath}/resources/common/libs/markdown/editor.md/lib/flowchart.min.js"></script><script type="text/javascript" src="${basePath}/resources/common/libs/markdown/editor.md/lib/jquery.flowchart.min.js"></script><script type="text/javascript" src="${basePath}/resources/common/libs/markdown/editor.md/js/editormd.min.js"></script><script> var editor; /* var height=$(document).height(); $(".navbar-fixed-middle").css({"top":height/2+"px"});*/ $(function () { var mid = "${id?default('')}"; ajaxPost(basePath + "/markdown/get/" + mid, null, function (result) { $("#title").text(result.title); $("#keywords").text(result.keywords); editor = editormd.markdownToHTML("editormd", { markdown: result.content, htmlDecode: "style,script,iframe", // you can filter tags decode emoji: true, tocm:true , // Using [TOCM] tocContainer: "#custom-toc-container", // 自定义 ToC 容器层 tocDropdown:true, taskList: true, tex: true, // 默认不解析 flowChart: true, // 默认不解析 sequenceDiagram: true // 默认不解析 }); }); }); function returnToList() { loadPage(basePath + "/markdown/list"); }</script>
关键样式
.navbar-fixed-middle { position: fixed; z-index: 1031; bottom:30px; right: 15px; margin-bottom: 0; border-width: 1px 0 0;}
6、附录
最终代码以Github上的代码为准。
AdminEAP代码:https://github.com/bill1012/AdminEAP
AdminEAP Markdown:http://www.admineap.com
欢迎大家留言至我的邮箱 admin@admineap.com 或 jrn1012@petrochina.com.cn
- AdminEAP框架—web集成Markdown组件
- AdminEAP框架-集成Shiro安全认证
- AdminEAP框架简介
- AdminEAP框架-SpringMVC+spring集成通用第三方登录(以github为例)
- adminEAP
- 基于AdminLTE的开发框架-AdminEAP
- AdminEAP框架-头像上传功能实现
- AdminEAP框架-基于AdminLTE的权限管理
- AdminEAP框架:基于AdminLTE的代码生成器
- 基于AdminLTE的开发框架-AdminEAP
- 构建web组件框架
- Spring集成其他Web框架
- AdminEAP框架数据列表render的五种方式
- AppFuse——集成Web框架开发总结(J2EE)
- 在web项目中使用MarkDown组件
- 【第十章】集成其它Web框架 之 10.4 集成JSF ——跟我学spring3
- 【第十章】集成其它Web框架 之 10.4 集成JSF ——跟我学spring3
- 【第十章】集成其它Web框架 之 10.4 集成JSF ——跟我学spring3
- Python之HelloWorld
- windows10 下 使用docker php-fpm 镜像 xdebug调试
- Android API Guide for Animation and Graphics(二)—— 动画与图形(属性动画)
- 关于CocoaPod的遇到的一些问题解决方法
- Android中使用Notification实现进度通知栏
- AdminEAP框架—web集成Markdown组件
- jQuery之ajax错误调试分析
- 进制转换
- 利用UDP来debug PHP
- 进程间通信
- APKTOOL的使用心得
- Spring boot学习笔记 001——初识Spring boot
- 欢迎使用CSDN-markdown编辑器
- ACM篇:POJ 1753----Flip Game