最近在开发一款在线知识库系统,为了方便使用,提供了两种编辑器,一种是基于markdown的,一种是基于富文本的。
基于markdown的编辑器,我们使用了MavonEditor
插件,同时也是使用该插件进行markdown内容的展示,这个插件自带了目录功能,我们可以直接使用,感兴趣的可以看一下Vue使用mavon-editor插件实现Markdown文件编辑及预览
为了保证使用体验的一致性,基于富文本编辑器的内容,我们也给他添加一个目录功能,目录结构的展示我们使用elementUI
框架的el-tree
组件。
template代码
template代码比较简单,因为是基于elementUI
框架的el-tree
组件的,所以需要先安装elementUI
。
<el-tree class="toc-tree" ref="tree" node-key="uuid" :data="tocTreeData" :props="tocDefaultProps" v-if="tocTreeData && tocTreeData.length>0" default-expand-all>
<div class="custom-tree-node" slot-scope="{ node, data }">
<div @click="toDiv(data)">{{ data.text }}</div>
</div>
</el-tree>
data代码
其实看到上面template
的代码,我们大概能猜测到data
里面需要的内容
tocTreeData: [],
tocDefaultProps: {
label: 'text',
children: 'children'
},
tocTreeData
是我们存储的目录树形的结构
tocDefaultProps
是el-tree
组件用来配置展示内容及子节点的。
js代码
getCatalog()
是入口方法,当我们在前端完成富文本内容的渲染后,就可以调用此方法,创建目录数据,进行展示。当然,提取目录的层级是根据我们的<h>
标签进行提取的,可以自己定义提取的深度,比如我这里提取到了<h6>
。
toDiv()
是点击目录时,用于页面滚动到对应位置的方法。
getCatalog() {
const h = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']
const elements = document.querySelectorAll(h)
let hElements = []
for (const key of elements) {
if (h.indexOf(key.localName) > -1) {
let text
if (key.children && key.children.length) {
text = this.getText(key.children)
} else {
text = key.innerHTML
}
hElements.push({
hLevel: parseInt(key.localName[1]),
text,
id: key.localName,
uuid: key.id,
offsetTop: key.offsetTop
})
}
}
this.tocTreeData = this.toTree(hElements)
},
getText(arr) {
let result = null
if (!arr.length) return
for (let i = 0; i < arr.length; i++) {
if (arr[i].children && arr[i].children.length) {
result = this.getText(arr[i].children)
} else {
result = arr[i].innerHTML
}
}
return result
},
toTree(flatArr) {
const tree = []
const copyArr = flatArr.map(function(item) {
return item
})
// 根据指定级别查找该级别的子孙级,并删除掉已经查找到的子孙级
const getChildrenByLevel = function(currentLevelItem, arr, level) {
if (!currentLevelItem) {
return
}
// 将level值转成负数,再进行比较
const minusCurrentLevel = -currentLevelItem.hLevel
const children = []
let i = 0, len = arr.length
for (; i < len; i++) {
const levelItem = arr[i]
if (-levelItem.hLevel < minusCurrentLevel) {
children.push(levelItem)
} else {
// 只找最近那些子孙级
break
}
}
// 从数组中删除已经找到的那些子孙级,以免影响到其他子孙级的查找
if (children.length > 0) {
arr.splice(0, children.length)
}
return children
}
const getTree = function(result, arr, level) {
// 首先将数组第一位移除掉,并添加到结果集中
let currentItem = arr.shift()
currentItem.level = level
result.push(currentItem)
while (arr.length > 0) {
if (!currentItem) {
return
}
// 根据当前级别获取它的子孙级
const children = getChildrenByLevel(currentItem, arr, level)
// 如果当前级别没有子孙级则开始下一个
if (children.length === 0) {
currentItem = arr.shift()
currentItem.level = level
if (currentItem) {
result.push(currentItem)
}
continue
}
currentItem.children = []
// 查找到的子孙级继续查找子孙级
getTree(currentItem.children, children, level + 1)
}
}
getTree(tree, copyArr, 1)
return tree
},
toDiv(data) {
document.getElementById('contentMainId').scrollTop = data.offsetTop - 20
},
评论 (0)