1. Demanda
Extraiga las etiquetas H1-H6 de acuerdo con el contenido del artículo del tipo de cadena, genere la visualización de la estructura del directorio del artículo y genere los puntos de anclaje correspondientes, haga clic para saltar.
2. Representación
3. Implementación del código
1. Código de plantilla
<a-card style="margin:10px 10px 0 20px;position: absolute;top:30%;width:15%;">
<b>文章目录</b>
<p></p>
<p v-for="menuitem in menuTree" :key="menuitem"><a :href="getElementmaodian(menuitem)" :style="menustyle(menuitem)">{
{getElementcontent(menuitem)}}</a></p></a-card>
2. Código del método
1) Generar una estructura de directorio basada en el contenido del artículo.
/*设置文章目录结构*/
getCenceptQuerySelect(){
const str_content = this.selectConcept.contents
const regex = /<h[1-6](.*?)>(.*?)<\/h[1-6]>/g;//正则表达式匹配提取所有H1-H6标签
var match
while((match = regex.exec(str_content)) !== null{
this.menuTree.push(match[0])
}
},
2) estilo de directorio
/*文章结构标题缩进*/
menustyle(menuiten){
var type = menuiten.slice(1,3)
var menutyle = ''
switch(type){
case 'h1': menutyle = 'margin-left:10px;';break
case 'h2': menutyle = 'margin-left:20px;';break
case 'h3': menutyle = 'margin-left:30px;';break
case 'h4': menutyle = 'margin-left:40px;';break
case 'h5': menutyle = 'margin-left:50px;';break
case 'h6': menutyle = 'margin-left:60px;';break
default: break
}
return menutyle
},
3) Obtener el ancla de la etiqueta
/*获取标签锚点*/
getElementmaodian(item){
var placeholder = document.createElement('div')
placeholder.innerHTML = item //返回id锚点
return '#' + placeholder.firstElementChild.id
},
4) Agregue el código
Dado que el contenido generado mediante el uso de texto enriquecido para crear un artículo no tiene un punto de anclaje, es necesario personalizarlo:
const str_content = this.addConcept.contents
const regex = /<h[1-6]>(.*?)<\/h[1-6]>/g;
var match var menuTree = []
while((match = regex.exec(str_content)) !== null){
menuTree.push(match[0])
}
menuTree.forEach(item=>{
var placeholder = document.createElement('div')
placeholder.innerHTML = item
//生成有锚点的元素,锚点value为显示的内容
var rep = item.replaceAll('>'+placeholder.firstElementChild.innerHTML+'<', ' id=\'' + placeholder.firstElementChild.innerHTML + '\'>'+placeholder.firstElementChild.innerHTML+'<')
this.addConcept.contents = this.addConcept.contents.replaceAll(item, rep) //替换有锚点的元素
})
4. Avanzado - árbol de directorios
necesidad
La estructura de árbol del directorio se puede ampliar y contraer.
representaciones
Código
1. Generar el Json de la estructura de árbol de directorios
menuTree_json almacena datos estructurales
generateDirectoryTree_Json(content){
const regex = /<h[1-6](.*?)>(.*?)<\/h[1-6]>/g;
var match
while ((match = regex.exec(content)) !== null) {
var item = {
level: this.menuItemLevel(match[0]), //层级:H1-1/H2-2/H3-3/H4-4/H5-5/H6-6
anchor: this.getElementmaodian(match[0]), //锚点
title: this.getElementcontent(match[0]), //内容
child: [], //子节点
isExpand:true //默认该树节点展开
}
this.addMenuitem(this.menuTree_json,item) //从树根节点开始添加子节点
}
},
/*递归插入*/
addMenuitem(menu, item){
if (menu.length === 0) {
menu.push(item)
}
else {
if (item.level > menu[menu.length - 1].level) {
//递归添加子节点
this.addMenuitem(menu[menu.length - 1].child, item)
}
else if (menu.length - 1 === 0) {
menu.push(item)
}
else {
menu.push(item)
}
}
},
2. El directorio se genera recursivamente según el Json de la estructura de árbol de directorios en la plantilla.
Aquí, esta parte se define como un componente para facilitar las llamadas recursivas.
Llamada de componente:
<ContentMenu :menu="menuTree_json"></ContentMenu>
Definición de componente:
<template>
<div>
<div v-for="menuitem in menu" :key="menuitem" style="overflow: auto;margin-top:10px;">
<svg @click="menuitem.isExpand=!menuitem.isExpand" v-if="menuitem.child.length != 0 && !menuitem.isExpand" t="1682300013635" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3229" width="16" height="16"><path d="M946.33106 697.353498 541.30749 284.093337c-15.690354-16.009625-41.469484-16.009625-57.160861 0l-405.024593 413.260162c-24.819269 25.323758-6.877641 68.028373 28.579919 68.028373l810.048163 0C953.209724 765.381871 971.150328 722.677257 946.33106 697.353498z" fill="#1296db" p-id="3230"></path></svg>
<svg @click="menuitem.isExpand=!menuitem.isExpand" v-if="menuitem.child.length != 0 && menuitem.isExpand" t="1682300082130" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3505" width="16" height="16"><path d="M79.123059 327.850933l405.024593 413.260162c15.690354 16.009625 41.469484 16.009625 57.160861 0l405.02357-413.260162c24.819269-25.323758 6.877641-68.028373-28.579919-68.028373L107.704001 259.82256C72.245418 259.82256 54.30379 302.527175 79.123059 327.850933z" fill="#1296db" p-id="3506"></path></svg>
<a href="javascript:void(0)" @click="goAnchor(menuitem.anchor)" style="margin-left:5px;margin-top:3px;">
<span v-html="menuitem.title"></span>
</a>
<div v-if="menuitem.isExpand" style="margin-left:30px;margin-top:10px;">
<!--递归生成目录树,递归自己-->
<contentMenu :menu="menuitem.child"></contentMenu>
</div>
</div>
</div>
</template>
<script>
import { mixins_menutree } from '../../mixin/menutree'
export default {
name:'contentMenu', //定义name才能组件自己递归调用自己
mixins:[mixins_menutree],
props:{
menu:[]
}
}
</script>
5. Búsqueda de directorio
necesidad
Cuando hay demasiadas entradas de directorio, se requiere filtrado de búsqueda
Efecto
Código
<a-input-search
size="small"
placeholder="文章目录"
v-model = "searchMenu"
@search="onSearchMenu"
></a-input-search>
/*onSearchMenu
搜索目录,重新生成目录
*/
onSearchMenu() {
this.menuTree_json = []
this.menu_init.forEach(item => {
if (item.title.toLowerCase().indexOf(this.searchMenu.toLowerCase()) != -1) {
item.child = [] //孩子节点直接去掉
this.addMenuitem(this.menuTree_json, item)
}
})
},
6. Problemas encontrados
1. Problema de error de la consola de salto china del punto de anclaje
El punto de anclaje chino #punto de anclaje 1 se escapa a #%E9%94%9A%E7%82%B91 , lo que hace que la consola informe un error, pero puede funcionar y saltar correctamente. . .
2. Una etiqueta href salta al IIS después de que el salto se publica debido a un error.
<a :href="getElementmaodian(menuitem)"
resolver:
<a href="javascript:void(0)" @click="goAnchor(getElementmaodian(menuitem))"
goAnchor(selector){ //锚点跳转 document.querySelector(selector).scrollIntoView({ behavior:'smooth' })},
3. Si el punto de anclaje comienza con un número, se considerará ilegal
Solución 1 : después de procesar la cadena de título, se genera la identificación del ancla para garantizar que el ancla sea correcta.
Todavía hay problemas:
1. Los dos últimos dígitos de la cadena cifrada con btoa son '==' y el punto de anclaje no será válido. Solución temporal: extraiga solo los primeros 20 dígitos
2. Si solo se extraen los 20 bits superiores, pueden existir los mismos puntos de anclaje
//标题字符串处理,保证锚点正确
var str64 = window.btoa(window.encodeURIComponent(placeholder.firstElementChild.innerHTML)).slice(0,20)
var rep = item.replaceAll('>'+placeholder.firstElementChild.innerHTML+'<', ' id=\'' + str64 + '\'>'+placeholder.firstElementChild.innerHTML+'<') this.addConcept.contents = this.addConcept.contents.replaceAll(item, rep)
Solución dos :
Cadena de números aleatorios
//标题字符串处理,保证锚点正确var str64 = window.btoa(window.encodeURIComponent(placeholder.firstElementChild.innerHTML))
//移除特殊字符
str64 = str64.replaceAll('=', '')
//生成随机数转成36进制,再截取后8位
var str_random = Math.random().toString(36).slice(-8)
str64 = str64 + str_random
var rep = item.replaceAll('>'+placeholder.firstElementChild.innerHTML+'<', ' id=\'' + str64 + '\'>'+placeholder.firstElementChild.innerHTML+'<')
this.addConcept.contents = this.addConcept.contents.replace(item, rep)//使用replace而不是replaceAll,否则可能替换所有
Solución tres:
Genere directamente tres números aleatorios y conviértalos a 36 hexadecimales, intercepte los últimos 8 dígitos respectivamente y luego empalme
var str_random1 = Math.random().toString(36).slice(-8)
var str_random2 = Math.random().toString(36).slice(-8)
var str_random3 = Math.random().toString(36).slice(-8)var str_random = str_random1 + str_random2 + str_random3