1.安装mavon-editor
引入markdwon编辑器
<el-scrollbar style="height:100%">
<mavon-editor v-model="mdContent" ref="md" :toolbars="toolbars" @change="change"
@save="submit" @imgAdd="$imgAdd" @imgDel="$imgDel" class="mavonClass" />
<div class="buttonClass">
<el-button @click="submit" type="primary">保存</el-button>
</div>
</el-scrollbar>
import { mavonEditor } from 'mavon-editor';
import 'mavon-editor/dist/css/index.css';
import toolbars from 'markDownToolbars';
var rendererMD = new marked.Renderer();
marked.setOptions({
renderer: rendererMD,
gfm: true,
tables: true,
breaks: false,
pedantic: false,
sanitize: false,
smartLists: true,
smartypants: false
});
export default {
data () {
return {
mdContent: '', // markdown语法的内容, 如果没有设置为'',不能设置为null,为null会报错
toolbars: toolbars, // markdown提示栏
img_file: {},
htmlCont: '' // 转化成html存储的内容
}
},
components: {
mavonEditor
},
methods: {
change (value, render) {
this.$nextTick(() => {
this.mdContent = value;
this.htmlCont = render;
})
},
// 上传图片
$imgAdd (pos, $file) {
// 第一步.将图片上传到服务器.
var formdata = new FormData();
formdata.append('file', $file);
this.img_file[pos] = $file;
axios.post(`${this.productAppBase}/fileUpload/upload`, formdata)
.then(res => {
let _res = res.data;
// 第二步将返回的url替换到文本原位置![...](0) -> ![...](url)
this.$refs.md.$img2Url(pos, this.productAppBase + _res.url);
console.log('res=>', res);
})
},
// 删除图片
$imgDel (pos) {
delete this.img_file[pos];
},
}
}
markDownToolbars.js
let toolbars = {
bold: true, // 粗体
italic: true, // 斜体
header: true, // 标题
underline: true, // 下划线
strikethrough: true, // 中划线
mark: true, // 标记
superscript: true, // 上角标
subscript: true, // 下角标
quote: true, // 引用
ol: true, // 有序列表
ul: true, // 无序列表
link: true, // 链接
imagelink: true, // 图片链接
code: true, // code
table: true, // 表格
fullscreen: true, // 全屏编辑
readmodel: true, // 沉浸式阅读
htmlcode: true, // 展示html源码
help: true, // 帮助
/* 1.3.5 */
undo: true, // 上一步
redo: true, // 下一步
trash: true, // 清空
// save: true, // 保存(触发events中的save事件)
/* 1.4.2 */
navigation: true, // 导航目录
/* 2.1.8 */
alignleft: true, // 左对齐
aligncenter: true, // 居中
alignright: true, // 右对齐
/* 2.2.1 */
// subfield: true, // 单双栏模式
preview: true // 预览
}
export default toolbars;
2.安装marked(解析markdwon语法)
<el-container>
<el-aside width="300px" class="leftNav">
<el-scrollbar style="height:100%">
<bc-menu v-if="navList.length > 0" ref="bcMenu" :menuData="navList"></bc-menu>
</el-scrollbar>
</el-aside>
<el-main class="rightCont">
<el-scrollbar style="height:100%" ref="helpDocs" @scroll="handleScroll">
<div class="main-cont">
<div class="markdownBox" v-html="compiledMarkdown"></div>
</div>
</el-scrollbar>
</el-main>
</el-container>
import marked from 'marked';
import bcMenu from 'bcMenu'; // elementui 菜单
export default {
data () {
return {
navList: [],
activeIndex: 0,
docsFirstLevels: [],
}
},
components: {
bcMenu
},
mounted () {
if (this.mdContent) {
this.navList = this.handleNavTree();
this.getDocsFirstLevels(0);
}
},
methods: {
change (value, render) {
this.$nextTick(() => {
this.mdContent = value;
this.htmlCont = render;
})
// render 为 markdown 解析后的结果[html]
}
// markdown方法结束
getDocsFirstLevels (times) {
// 解决图片加载会影响高度问题
setTimeout(() => {
let firstLevels = [];
Array.from(document.querySelectorAll('h2'), element => {
firstLevels.push(element.offsetTop - 60)
})
this.docsFirstLevels = firstLevels;
if (times < 8) {
this.getDocsFirstLevels(times + 1);
}
}, 500);
},
//
handleScroll () {
// 根据滚动右侧内容定位到左侧菜单
if (this.$refs['helpDocs']) {
let scrollTop = this.$refs['helpDocs'].wrap.scrollTop;
let _article = document.querySelectorAll('.step-jump')
_article.forEach((item, index) => {
if (scrollTop >= item.offsetTop - 70) {
this.$refs.bcMenu.getCurrent(`index-${index}`);
}
})
}
},
getTitle (content) {
let nav = [];
let tempArr = [];
content.replace(/(#+)[^#][^\n]*?(?:\n)/g, function (match, m1, m2) {
let title = match.replace('\n', '');
if (title.indexOf('</font>') > -1) {
return false;
}
let level = m1.length;
tempArr.push({
name: title.replace(/^#+/, '').replace(/\([^)]*?\)/, ''),
level: level,
children: [],
icon: 'icon-dian'
});
});
// 处理菜单,以及添加与id对应的index值
nav = tempArr.filter(item => item.level <= 4 && item.level > 1) || [];
// 设置大标题
let nameFind = tempArr.find(item => item.level == 1) || {};
this.name = nameFind.name
let index = 0;
// eslint-disable-next-line no-return-assign
return nav = nav.map(item => {
item.index = index++;
item.code = item.index;
item.anchor = `index-${item.index}`;
return item;
});
},
// 将标题数据处理成树结构
handleNavTree () {
let navs = this.getTitle(this.content);
// 设置了4级导航
let navLevel = [1, 2, 3, 4];
let retNavs = [];
let toAppendNavList, parentNavList = [];
navLevel.forEach(level => {
// 遍历标题,将同一级的标题组成新数组
toAppendNavList = this.find(navs, {
level: level
});
parentNavList = this.find(navs, {
level: level - 1
});
if (retNavs.length === 0) {
// 处理一级标题
retNavs = retNavs.concat(toAppendNavList);
} else {
// 处理其他标题,并将其他标题添加到对应的父级标题的children中
toAppendNavList.forEach(item => {
item = Object.assign(item);
let parentNavIndex = this.getParentIndex(navs, item.index);
return this.appendToParentNav(parentNavList, parentNavIndex, item);
});
}
});
return retNavs;
},
find (arr, condition) {
return arr.filter(item => {
for (let key in condition) {
if (condition.hasOwnProperty(key) && condition[key] !== item[key]) {
return false;
}
}
return true;
});
},
getParentIndex (nav, endIndex) {
for (var i = endIndex - 1; i >= 0; i--) {
if (nav[endIndex].level > nav[i].level) {
return nav[i].index;
}
}
},
appendToParentNav (nav, parentIndex, newNav) {
let index = this.findIndex(nav, {
index: parentIndex
});
nav[index].children = nav[index].children.concat(newNav);
},
findIndex (arr, condition) {
let ret = -1;
arr.forEach((item, index) => {
for (var key in condition) {
if (condition.hasOwnProperty(key) && condition[key] !== item[key]) {
return false;
}
}
ret = index;
});
return ret;
}
},
computed: {
content () {
return this.mdContent
},
compiledMarkdown: function () {
let index = 0, that = this;
rendererMD.heading = function (text, level) {
// 导航
if (level <= 4 && level != 1) {
return `<h${level} id="index-${index++}" class="step-jump">${text}</h${level}>`;
} else {
return `<h${level}>${text}</h${level}>`;
}
};
rendererMD.code = function (code, language) {
code = code.replace(/\r\n/g, '<br>')
code = code.replace(/\n/g, '<br>');
return `<div class="text">${code}</div>`;
};
return marked(this.content);
}
}
}