<template> ???<div class="wrapper"> ???????<div class="container"> ???????????<div class="menu"> ???????????????<ul class="menu-list"> ???????????????????<li v-for="(nav, index) in navList" :key="index" :class="{on: activeIndex === index}" @click="currentClick(index)"> ???????????????????????<a href="javascript:;" :key="nav.index" @click="pageJump(nav.index)">{{nav.title}} ???????????????????????</a> ???????????????????????<div v-if="nav.children.length > 0 && activeIndex === index" class="menu-children-list"> ???????????????????????????<ul class="nav-list"> ???????????????????????????????<li v-for="(item, index) in nav.children" :key="index"> ???????????????????????????????????<a ?href="javascript:;" :key="item.index" @click.stop="pageJump(item.index)">{{item.title}} ???????????????????????????????????</a> ???????????????????????????????</li> ???????????????????????????</ul> ???????????????????????</div> ???????????????????</li> ???????????????</ul> ???????????</div> ???????????<div class="content" v-html="compiledMarkdown" ref="helpDocs" @scroll="docsScroll"></div> ???????</div> ??????</div></template>
<script>import marked from ‘marked‘;let rendererMD = new marked.Renderer();marked.setOptions({ ???renderer: rendererMD, ???gfm: true, ???tables: true, ???breaks: false, ???pedantic: false, ???sanitize: false, ???smartLists: true, ???smartypants: false});export default { ???props: [‘mdContent‘], ???data() { ???????return { ???????????navList: [], ???????????activeIndex: 0, ???????????docsFirstLevels: [] ???????} ???}, ???mounted() { ???????this.navList = this.handleNavTree(); ???????this.getDocsFirstLevels(0); ???}, ???methods: { ???????getDocsFirstLevels(times) { ???????????// 解决图片加载会影响高度问题 ???????????setTimeout(() => { ???????????????let firstLevels = []; ???????????????Array.from(document.querySelectorAll(‘h1‘), element => { ???????????????????firstLevels.push(element.offsetTop - 60) ???????????????}) ???????????????this.docsFirstLevels = firstLevels; ???????????????if (times < 8) { ???????????????????this.getDocsFirstLevels(times + 1); ???????????????} ???????????}, 500); ???????}, ???????docsScroll() { ???????????if (this.titleClickScroll) { ???????????????return; ???????????} ???????????let scrollTop = this.$refs.helpDocs.scrollTop ???????????let currentIdx = null; ???????????let nowActive = this.docsFirstLevels.some((currentValue, index) => { ???????????????if(currentValue >= scrollTop) { ???????????????????currentIdx = index ???????????????????return true ???????????????} ???????????}) ???????????currentIdx = currentIdx - 1 ???????????????????????if (nowActive && currentIdx === -1) { ???????????????currentIdx = 0 ???????????} else if (!nowActive && currentIdx === -1) { ???????????????currentIdx = this.docsFirstLevels.length - 1 ???????????} ???????????this.currentClick(currentIdx) ???????}, ???????pageJump(id) { ???????????this.titleClickScroll = true; ???????????this.$refs.helpDocs.scrollTop = this.$el.querySelector(`#data-${id}`).offsetTop - 40; ???????????setTimeout(() => this.titleClickScroll = false, 100); ???????}, ???????currentClick(index) { ??????????this.activeIndex = index ???????}, ???????getTitle(content) { ???????????let nav = []; ???????????let tempArr = []; ???????????content.replace(/(#+)[^#][^\n]*?(?:\n)/g, function(match, m1, m2) { ???????????????????let title = match.replace(‘\n‘, ‘‘); ???????????????????let level = m1.length; ???????????????????tempArr.push({ ???????????????????????title: title.replace(/^#+/, ‘‘).replace(/\([^)]*?\)/, ‘‘), ???????????????????????level: level, ???????????????????????children: [], ???????????????????}); ???????????????}); ???????????// 只处理一级二级标题,以及添加与id对应的index值 ???????????nav = tempArr.filter(item => item.level <= 2); ???????????let index = 0; ???????????return nav = nav.map(item => { ???????????????item.index = index++; ???????????????return item; ???????????}); ???????}, ???????// 将一级二级标题数据处理成树结构 ???????handleNavTree() { ???????????let navs = this.getTitle(this.content) ???????????let navLevel = [1, 2]; ???????????let retNavs = []; ???????????let toAppendNavList; ???????????navLevel.forEach(level => { ???????????????// 遍历一级二级标题,将同一级的标题组成新数组 ???????????????toAppendNavList = this.find(navs, { ???????????????????level: level ???????????????}); ???????????????????????????????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(retNavs, 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; ???????????rendererMD.heading = function(text, level) { ???????????????if (level <= 2) { ???????????????????return `<h${level} id="data-${index++}">${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); ???????} ???}}</script>
参考链接:
https://github.com/markedjs/marked
https://www.jianshu.com/p/d182ea991609
https://hk.saowen.com/a/bf975e4296e33a14e2d0ad50aa7cbf24fbfb4a9fb851de171b4c71da54eb95e5
vue使用marked.js实现markdown转html并提取标题生成目录
原文地址:https://www.cnblogs.com/yhquan/p/10276811.html