:大致思路
参考源码:https://github.com/star7th/showdoc
editormd.vue中引入下载的editor.md的文件, 即书写配置选项的path路径
页面中引用editormd.vue
一, 了解一下editormd的属性与方法
用requirejs等工具引入所有所需依赖以后,window上就会挂载editormd方法,可直接调用
1 用于编辑的editormd
<div id="layout">
<header>
<h1>Auto height test</h1>
</header>
// 用于在外部添加功能
<div class="btns">
<button id="append-btn">Append markdown</button>
</div>
<div id="test-editormd">
<textarea style="display:none;"></textarea>
</div>
</div>
var testEditor = editormd("test-editormd", { // 配置选项对象
autoHeight: true, // 自动高度
atLink : false, // disable @link
codeFold: true, // 代码折叠
emailLink : false, // disable email address auto link
emoji : true,
flowChart : true, //流程图
height : 640,
syncScrolling: true | false | 'single' // 编辑与预览是否同步滚动 默认true
htmlDecode : "style,script,iframe",
lineNumbers : false // 隐藏编辑窗口行号,默认true
mode: 'text/html', // ["text/html", "javascript", "php", "text/xml", "text/json", "clike", "javascript", "perl", "go", "python", "clike", "css", "ruby"];
path : "../lib/", // 下载的editor.md文件夹下的lib目录地址
placeholder: 'Enjoy coding!',
readOnly: true, // 开启会隐藏toolbar
saveHTMLToTextarea : true,
searchReplace: true,
sequenceDiagram : true, // 时序图,系列图
styleActiveLine : false, // 高亮鼠标所在行,默认true
taskList : true, // - [ ] this // - [x] this 是否支持该md语法,默认false
todoList: true,
tex : true, // katex科学公式 默认false
toc: true,
tocContainer: '某<div>的id如: #id', // ''空字符串表示默认的textarea, 有id表示为textarea外部
tocDropdown : true, // 下拉菜单,自动索引###
tocStartLevel : 4 , // #### 顶格为4, 不渲染h1,h2,h3 .默认:1,
toolbar: false, // 是否显示工具栏
toolbarIcons:'"undo", "redo"',
theme: (localStorage.theme) ? localStorage.theme : "default",
value: $('#id').val(), // textarea首尾标签之间的内容
watch: false, // 初始化时显示预览? 默认true
imageUpload: false, //文件本地上传 默认false,
width : "90%",
})
}
// 实例方法,可用于外部添加btn
testEditor.appendMarkdown(md);
testEditor.config('syncScrolling', true)
testEditor.getMarkdown(); // 获取 Markdown 源码
testEditor.getHTML(); // 获取 Textarea 保存的 HTML 源码
testEditor.getPreviewedHTML(); // 获取预览窗口里的 HTML,在开启 watch 且没有开启 saveHTMLToTextarea 时使用
testEditor.getValue(),
testEditor.setValue(code);
testEditor.fullscreen();
testEditor.setTheme(theme); // var theme = editormd.themes[i];
testEditor.setCodeMirrorOption("mode", mode);
testEditor.watch();
testEditor.unwatch();
用于编辑的editor.md 使用过程注意点:
1, 不支持v-model, 有因为需要特殊的保存为.md格式
2用于显示的editor.md
总结了部分后我就停笔了,因为感觉到配置选项应该是一样的
<div id="test-editormd-view">
// textarea可缺省
<textarea style="display:none;" name="test-editormd-markdown-doc">###Hello world!</textarea>
</div>
$(function(){
var testEditormdView;
$.get('url').then(markdown=>{
testEditorView = editormd.markdownToHtml('test-editor-view',{
markdown: markdown,
// htmlDecode: true // 开启html标签解析,为了安全性,默认不开启
htmlDecode: "style,script,iframe",
taskList: true,
tex: true, // 默认false
flowChart: true, // 默认false
sequenceDiagram: true // 默认false
}))
//实例方法
testEditormdView.getMarkdown()
})
二: 简单封装vue-editormd组件
1, 定义处,
<template>
<div :id="id" class="main-editor" @keydown.ctrl.83.prevent="save">
<link href="js/editor.md/css/editormd.css" rel="stylesheet">
<textarea v-html="content" style="display:none;"></textarea>
<!-- 放大图片 -->
<!-- <BigImg v-if="showImg" @clickit="showImg = false" :imgSrc="imgSrc"></BigImg>-->
</div>
</template>
<script>
import $s from 'scriptjs'
// import BigImg from '@/components/common/BigImg'
export default {
name: 'Editormd',
props: {
width: '',
content:{
type: String,
default: ''
},
type: {
type:String,
default: 'editor'
},
id: {
type: String,
default: 'editor-md'
},
editorPath: {
type: String,
default: 'js/editor.md', // editormd文件夹所在路径
},
editorConfig: {
type: Object,
default() { // 对象或数组默认值必须从一个工厂函数获取
return {
path: '/js/editor.md/lib/',
height: 300,
taskList : true, // 任务完成图
watch: false,
placeholder: "本编辑器支持Markdown编辑,左边编写,右边预览",
tex : true, // 科学公式
flowChart : true, // 流程图
sequenceDiagram : true, // 时序图/序列图
syncScrolling: "single", // 预览,编辑窗同步滚动
toolbarIcons: ["undo", "redo", "|","bold","italic","|", "image","link","||","watch","search"],
htmlDecode: "style,script,iframe,title,onmouseover,onmouseout,style",
// imageUpload: true,
imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp", "JPG", "JPEG", "GIF", "PNG", "BMP", "WEBP"],
// imageUploadURL: DocConfig.server+"/api/page/uploadImg",
onload: () => {
console.log('onload')
console.log(this.instance);
},
};
},
},
},
// components:{
// BigImg
// },
data() {
return {
instance: null, // editormd的实例
showImg:false,
imgSrc: ''
};
},
computed: {
},
mounted() {
//加载依赖""
$s([`${this.editorPath}/../jquery.min.js`,
`${this.editorPath}/lib/raphael.min.js`,
`${this.editorPath}/lib/flowchart.min.js`,
],()=>{
$s([
// `${this.editorPath}/../xss.min.js`,
`${this.editorPath}/lib/marked.min.js`,
`${this.editorPath}/lib/prettify.min.js`,
`${this.editorPath}/lib/underscore.min.js`,
`${this.editorPath}/lib/sequence-diagram.min.js`,
`${this.editorPath}/lib/jquery.flowchart.min.js`,
], () => {
$s(`${this.editorPath}/editormd.js`, () => {
this.initEditor();
});
// $s(`${this.editorPath}/../highlight/highlight.min.js`, () => {
// hljs.initHighlightingOnLoad();
// });
});
});
},
beforeDestroy() {
//清理所有定时器
for (var i = 1; i < 999; i++){
window.clearInterval(i);
};
//window.removeEventListener('beforeunload', e => this.beforeunloadHandler(e))
},
methods: {
initEditor() {
this.$nextTick((editorMD = window.editormd) => {
if (editorMD) {
if (this.type == 'editor'){
this.instance = editorMD(this.id, this.editorConfig);
//草稿跟后端初始化会冲突
// this.draft();
//window.addEventListener('beforeunload', e => this.beforeunloadHandler(e));
} else {
this.instance = editorMD.markdownToHTML(this.id, this.editorConfig);
}
this.deal_with_content();
}
});
},
//插入数据到编辑器中。插入到光标处
insertValue(insertContent){
this.instance.insertValue(this.html_decode(insertContent));
},
getMarkdown(){
return this.instance.getMarkdown();
},
editor_unwatch(){
return this.instance.unwatch();
},
editor_watch(){
return this.instance.watch();
},
clear(){
return this.instance.clear();
},
//草稿
// draft(){
// var that = this ;
// //定时保存文本内容到localStorage
// setInterval(()=>{
// localStorage.page_content= that.getMarkdown() ;
// }, 60000);
// //检测是否有定时保存的内容
// var page_content = localStorage.page_content ;
// if (page_content && page_content.length > 0) {
// localStorage.removeItem("page_content");
// that.$confirm('检测到有本地保存的草稿,是否恢复','提示',{
// confirmButtonText: '恢复',
// cancelButtonText: '放弃',
// showClose:false
// }
// ).then(()=>{
// that.clear() ;
// that.insertValue(page_content) ;
// localStorage.removeItem("page_content");
// }).catch(()=>{
// localStorage.removeItem("page_content");
// });
// };
// },
//对内容做些定制化改造
deal_with_content(){
var that = this ;
//当表格列数过长时将自动出现滚动条
$.each($("#"+this.id+' table'), function() {
$(this).prop('outerHTML', '<div style="width: 100%;overflow-x: auto;">'+$(this).prop('outerHTML')+'</div>');
});
//超链接都在新窗口打开
$("#"+this.id+' a[href^="http"]').each(function() {
$(this).attr('target', '_blank');
});
//对表格进行一些改造
$("#"+this.id+" table tbody tr").each(function(){
var tr_this = $(this) ;
var td1 = tr_this.find("td").eq(1).html() ;
var td2 = tr_this.find("td").eq(2).html() ;
if(td1 =="object" || td1 =="array[object]" || td2 =="object" || td2 =="array[object]"){
tr_this.css({"background-color":"#F8F8F8"});
}else{
tr_this.css("background-color","#fff");
}
//设置表格hover
tr_this.hover(function(){
tr_this.css("background-color","#F8F8F8");
},function(){
if(td1 =="object" || td1 =="array[object]" || td2 =="object" || td2 =="array[object]"){
tr_this.css({"background-color":"#F8F8F8"});
}else{
tr_this.css("background-color","#fff");
}
});
});
//获取内容总长度
var contentWidth = $("#"+this.id+" p").width() ;
// contentWidth = contentWidth ? contentWidth : 722;
//表格列 的宽度
$("#"+this.id+" table").each(function(i){
var $v =$(this).get(0) ;//原生dom对象
var num = $v.rows.item(0).cells.length ; //表格的列数
var colWidth = Math.floor(contentWidth/num) -2 ;
if (num <= 5) {
$(this).find("th").css("width",colWidth.toString()+"px");
}
});
//图片点击放大
$("#"+this.id+" img").click(function(){
var img_url = $(this).attr("src");
that.showImg = true;
// 获取当前图片地址
that.imgSrc = img_url;
});
//表格头颜色
$("#"+this.id+" table thead tr").css("background-color","#409eff") ;
$("#"+this.id+" table thead tr").css("color","#fff") ;
//代码块美化
// $("#"+this.id+" .linenums").css("padding-left","5px") ;
// $("#"+this.id+" .linenums li").css("list-style-type","none") ;
// $("#"+this.id+" .linenums li").css("background-color","#fcfcfc") ;
// $("#"+this.id+" pre").css("background-color","#fcfcfc") ;
// $("#"+this.id+" pre").css("border","1px solid #e1e1e8") ;
//
// $("#"+this.id+" code").css("color","#d14");
},
//转义
html_decode(str){
var s = "";
if (str.length == 0) return "";
s = str.replace(/>/g, "&");
s = s.replace(/</g, "<");
s = s.replace(/>/g, ">");
s = s.replace(/ /g, " ");
s = s.replace(/'/g, "\'");
s = s.replace(/"/g, "\"");
//s = s.replace(/<br>/g, "\n");
return s;
}
}
};
</script>
<style scoped></style>
2, 引入处
1, 编辑:
两件事: 保存.md, 初始化渲染md
1, 保存:
import Editormd from '../../../utils/editormd/Editormd.vue'
components: {
Editormd
},
<editormd type="editor" ref="editormdRef" :content="currentEdit.remark"></editormd>
<script>
const editormdRef = this.$refs['editormdRef'][0]
let content = editormdRef.getMarkdown() // 把content传到后端即可
</script>
2, 初始化:
在vue-editormd组件中写了v-html = content即可
2, 纯渲染(vue组件中):
<editormd type="html" v-if="item.remark" :content="item.remark"></editormd>
END