当前位置:   article > 正文

Vue自定义文章目录_vue提取一段富文本的目录

vue提取一段富文本的目录

一. 需求

根据string类型的字符串文章内容,提取H1-H6标签,生成文章目录结构展示,并且对应生成锚点,点击可跳转。

二. 效果图

三. 代码实现

1. Template代码

  1. <a-card style="margin:10px 10px 0 20px;position: absolute;top:30%;width:15%;">
  2. <b>文章目录</b>   
  3. <p></p>   
  4. <p v-for="menuitem in menuTree" :key="menuitem"><a :href="getElementmaodian(menuitem)" :style="menustyle(menuitem)">{{getElementcontent(menuitem)}}</a></p></a-card>

2. Method代码

1)根据文章内容生成目录结构

  1. /*设置文章目录结构*/  
  2. getCenceptQuerySelect(){   
  3. const str_content = this.selectConcept.contents   
  4. const regex = /<h[1-6](.*?)>(.*?)<\/h[1-6]>/g;//正则表达式匹配提取所有H1-H6标签         
  5. var match    
  6. while((match = regex.exec(str_content)) !== null{     
  7. this.menuTree.push(match[0])    
  8. }  
  9. },

2)目录样式

  1. /*文章结构标题缩进*/  
  2. menustyle(menuiten){
  3. var type = menuiten.slice(1,3)   
  4. var menutyle = ''   
  5. switch(type){
  6. case 'h1': menutyle = 'margin-left:10px;';break    
  7. case 'h2': menutyle = 'margin-left:20px;';break    
  8. case 'h3': menutyle = 'margin-left:30px;';break    
  9. case 'h4': menutyle = 'margin-left:40px;';break    
  10. case 'h5': menutyle = 'margin-left:50px;';break    
  11. case 'h6': menutyle = 'margin-left:60px;';break    
  12. default: break   
  13. }       
  14. return menutyle  
  15. },

3)获取标签锚点

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

4)文章创建时添加锚点代码

由于使用quill富文本创建文章是生成的内容没有锚点,所以需要自定义:

  1. const str_content = this.addConcept.contents    
  2. const regex = /<h[1-6]>(.*?)<\/h[1-6]>/g;         
  3. var match    var menuTree = []    
  4. while((match = regex.exec(str_content)) !== null){        
  5. menuTree.push(match[0])    
  6. }         
  7. menuTree.forEach(item=>{     
  8. var placeholder = document.createElement('div')     
  9. placeholder.innerHTML = item
  10. //生成有锚点的元素,锚点value为显示的内容     
  11. var rep = item.replaceAll('>'+placeholder.firstElementChild.innerHTML+'<', ' id=\'' + placeholder.firstElementChild.innerHTML + '\'>'+placeholder.firstElementChild.innerHTML+'<')      
  12. this.addConcept.contents = this.addConcept.contents.replaceAll(item, rep) //替换有锚点的元素
  13. })

四. 进阶 - 目录树

需求

目录树结构,可展开收缩。

效果图

 

代码实现

1. 生成目录树结构的Json

menuTree_json存放结构数据

  1.  generateDirectoryTree_Json(content){
  2.    const regex = /<h[1-6](.*?)>(.*?)<\/h[1-6]>/g;
  3.    var match
  4.    while ((match = regex.exec(content)) !== null) {
  5.     var item = {
  6.      level: this.menuItemLevel(match[0]),   //层级:H1-1/H2-2/H3-3/H4-4/H5-5/H6-6
  7.      anchor: this.getElementmaodian(match[0]), //锚点
  8.      title: this.getElementcontent(match[0]), //内容
  9.      child: [],                //子节点
  10.      isExpand:true               //默认该树节点展开
  11.     }
  12.       
  13.     this.addMenuitem(this.menuTree_json,item) //从树根节点开始添加子节点
  14.    }
  15.   },
  16.    
  17.   /*递归插入*/
  18.   addMenuitem(menu, item){
  19.    if (menu.length === 0) {
  20.     menu.push(item)
  21.    }
  22.    else {
  23.     if (item.level > menu[menu.length - 1].level) {
  24.      //递归添加子节点
  25.      this.addMenuitem(menu[menu.length - 1].child, item)
  26.     }
  27.     else if (menu.length - 1 === 0) {
  28.      menu.push(item)
  29.     }
  30.     else {
  31.      menu.push(item)
  32.     }
  33.    }
  34.   },

2.templace中根据目录树结构的Json递归生成目录

这里将这部分定义为一个组件,方便递归调用。

组件调用:

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

组件定义:

  1. <template>
  2.  <div>
  3. <div v-for="menuitem in menu" :key="menuitem" style="overflow: auto;margin-top:10px;">
  4.    <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>
  5.    <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>
  6.    <a href="javascript:void(0)" @click="goAnchor(menuitem.anchor)" style="margin-left:5px;margin-top:3px;">
  7.     <span v-html="menuitem.title"></span>
  8. </a>
  9.    <div v-if="menuitem.isExpand" style="margin-left:30px;margin-top:10px;">
  10.     <!--递归生成目录树,递归自己-->
  11.     <contentMenu :menu="menuitem.child"></contentMenu>
  12.    </div>
  13.   </div>
  14.  </div>
  15. </template>
  16. <script>
  17. import { mixins_menutree } from '../../mixin/menutree'
  18. export default {
  19.  name:'contentMenu', //定义name才能组件自己递归调用自己
  20.  mixins:[mixins_menutree],
  21.  props:{
  22. menu:[]
  23.  }
  24. }
  25. </script>

五. 目录搜索

需求

当目录条目过多时,需要进行搜索过滤

效果

 

代码实现

  1. <a-input-search    
  2. size="small"    
  3. placeholder="文章目录"    
  4. v-model = "searchMenu"   
  5. @search="onSearchMenu"
  6. ></a-input-search>

  1. /*onSearchMenu 
  2.   搜索目录,重新生成目录
  3.   */
  4.   onSearchMenu() {
  5.    this.menuTree_json = []
  6.     
  7.    this.menu_init.forEach(item => {
  8.     if (item.title.toLowerCase().indexOf(this.searchMenu.toLowerCase()) != -1) {
  9. item.child = [] //孩子节点直接去掉
  10.      this.addMenuitem(this.menuTree_json, item)
  11.     }
  12.    })
  13.   },

六. 遇到问题

1. 锚点中文跳转控制台报错问题

 中文锚点 #锚点1 被转义为 #%E9%94%9A%E7%82%B91 导致控制台报错,但是能work,正确跳转。。。

2. a标签href跳转发布到IIS后跳转出错

<a :href="getElementmaodian(menuitem)"

解决:

  1. <a href="javascript:void(0)" @click="goAnchor(getElementmaodian(menuitem))"
  2. goAnchor(selector){ //锚点跳转    document.querySelector(selector).scrollIntoView({  behavior:'smooth'    })},

3. 锚点是数字开头会认为不合法

解决方法一:标题字符串处理后再生成锚点id,保证锚点正确。

还存在问题:

1、btoa加密后的字符串后两位是‘==’,也会包锚点不合法错误,临时解决:只提取前20位

2、只提取前20位的话有可能存在相同锚点

  1. //标题字符串处理,保证锚点正确
  2. var str64 = window.btoa(window.encodeURIComponent(placeholder.firstElementChild.innerHTML)).slice(0,20)
  3. var rep = item.replaceAll('>'+placeholder.firstElementChild.innerHTML+'<', ' id=\'' + str64 + '\'>'+placeholder.firstElementChild.innerHTML+'<') this.addConcept.contents = this.addConcept.contents.replaceAll(item, rep)

解决方法二

随机数拼接字符串

  1. //标题字符串处理,保证锚点正确var str64 = window.btoa(window.encodeURIComponent(placeholder.firstElementChild.innerHTML))
  2. //移除特殊字符
  3. str64 = str64.replaceAll('=', '')
  4. //生成随机数转成36进制,再截取后8位
  5. var str_random = Math.random().toString(36).slice(-8)
  6. str64 = str64 + str_random
  7. var rep = item.replaceAll('>'+placeholder.firstElementChild.innerHTML+'<', ' id=\'' + str64 + '\'>'+placeholder.firstElementChild.innerHTML+'<')
  8. this.addConcept.contents = this.addConcept.contents.replace(item, rep)//使用replace而不是replaceAll,否则可能替换所有

解决方法三:

直接生成三个随机数转成36进制,分别截取后8位,再拼接

  1. var str_random1 = Math.random().toString(36).slice(-8)
  2. var str_random2 = Math.random().toString(36).slice(-8)
  3. var str_random3 = Math.random().toString(36).slice(-8)var str_random = str_random1 + str_random2 + str_random3

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/231528
推荐阅读
相关标签
  

闽ICP备14008679号