当前位置: 代码迷 >> 综合 >> vue中使用markdown并根据markdown标题标签生成侧导航
  详细解决方案

vue中使用markdown并根据markdown标题标签生成侧导航

热度:42   发布时间:2023-09-20 15:45:38.0

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, // codetable: 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.namelet index = 0;// eslint-disable-next-line no-return-assignreturn 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);}}
}