Vue custom article directory

1. Demand

Extract the H1-H6 tags according to the article content of the string type, generate the article directory structure display, and generate corresponding anchor points, click to jump.

2. Rendering

3. Code implementation

1. Template code

<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. Method code

1) Generate a directory structure based on the content of the article

/*设置文章目录结构*/  
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) Directory style

/*文章结构标题缩进*/  
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) Get the label anchor

/*获取标签锚点*/  
getElementmaodian(item){   
    var placeholder = document.createElement('div')   
    placeholder.innerHTML = item       //返回id锚点   
    return '#' + placeholder.firstElementChild.id  
},

4) Add the anchor code

Since the content generated by using quill rich text to create an article has no anchor point, it needs to be customized:

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. Advanced - directory tree

need

The directory tree structure can be expanded and contracted.

renderings

 

Code

1. Generate the Json of the directory tree structure

menuTree_json stores structural data

 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. The directory is recursively generated according to the Json of the directory tree structure in template

Here, this part is defined as a component to facilitate recursive calls.

Component call:

<ContentMenu :menu="menuTree_json"></ContentMenu>

Component definition:

<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. Directory search

need

When there are too many directory entries, search filtering is required

Effect

 

Code

<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. Encountered problems

1. Anchor point Chinese jump console error problem

 The Chinese anchor point #anchor point 1  is escaped to #%E9%94%9A%E7%82%B91 , causing the console to report an error, but it can work and jump correctly. . .

2. A tag href jumps to the IIS after the jump is published to an error

<a :href="getElementmaodian(menuitem)"

solve:

<a href="javascript:void(0)" @click="goAnchor(getElementmaodian(menuitem))"

goAnchor(selector){ //锚点跳转    document.querySelector(selector).scrollIntoView({  behavior:'smooth'    })},

3. If the anchor point starts with a number, it will be considered illegal

Solution 1 : After the title string is processed, the anchor id is generated to ensure that the anchor is correct.

There are still problems:

1. The last two digits of the btoa-encrypted string are '==', and the anchor point will be invalid. Temporary solution: only extract the first 20 digits

2. If only the top 20 bits are extracted, the same anchor points may exist

//标题字符串处理,保证锚点正确

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)

Solution two :

String of random numbers

//标题字符串处理,保证锚点正确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,否则可能替换所有

Solution three:

Directly generate three random numbers and convert them into 36 hexadecimal, intercept the last 8 digits respectively, and then splicing

​
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

​

Guess you like

Origin blog.csdn.net/gcf10080353/article/details/130616738