v-md-editor高级使用之自定义目录

​ 官方给出的目录设置参见:https://code-farmer-i.github.io/vue-markdown-editor/zh/senior/toc.html#%E7%9B%B8%E5%85%B3%E9%85%8D%E7%BD%AE

​ 在做实际使用中往往可能需要将目录结构独立出来,经过近一天的研究终于明白其实现逻辑,并将目录结构独立出来,支持独立渲染,独立处理目录点击事件,同时也兼容原来的点击事件处理逻辑。具体如下:

效果对比图

在这里插入图片描述

具体实现如下:

<!-- 注意组件的常规属性省略掉了 -->
<a-layout-sider >
  <a-tabs >
    <a-tab-pane key="1" tab="文件" >
      <!-- 省略 -->
    </a-tab-pane>
    <a-tab-pane key="2" tab = "大纲">
      <ScrollContainer>
        <!-- 1.渲染目录的代码,基本就是复制的源码,样式什么的都是用的其本身的
					关键点:a.定义目录项的点击事件handleNavClick
								 b.计算出目录项 fileTitles
				-->
        <ul class="v-md-editor__toc-nav">
          <li :style="{paddingLeft: `${indent * (item.indent)}px`}"
              @click="handleNavClick(item)"
              class="v-md-editor__toc-nav-item"
              v-for="item in fileTitles">
            <span class="v-md-editor__toc-nav-title">{{ item.title }}</span>
          </li>
        </ul>
      </ScrollContainer>
    </a-tab-pane>
  </a-tabs>
</a-layout-sider>
<a-layout-content >
  <div style="width: 100%;height:100%;"> 
    <!-- 2.mode使用纯预览模式 这个也影响点击事件的处理-->
    <v-md-editor mode="preview" ref="mdEditorRef"></v-md-editor>
  </div>
</a-layout-content>

<script lang="ts" setup >
  const fileTitles = ref<Array<any>>([]);
  const mdEditorRef = ref();
  
  // b.计算出目录项 fileTitles
  const updateToc = ()=>{
        const noteEditorObj = mdEditorRef.value;
    		// 注意:若不是纯预览模式,这个方法会出问题,没有这个没有具体研究。
    		// 计算目录项使用的是其原来的方式,所以没有兼容问题。
        let anchors = noteEditorObj.getPreviewScrollContainer().document.querySelectorAll(noteEditorObj.anchorsSelector);
        let titles = Array.from(anchors).filter(function (title) {
            return !!(title as any).innerText.trim();
        });
        if (!titles.length) {
            return;
        }

        let hTags = Array.from(new Set(titles.map(function (title) {
            return (title as any).tagName;
        }))).sort();
        titles = titles.map(function (el) {
            return {
                title: (el as any).innerText,
                lineIndex: (el as any).getAttribute("data-v-md-line"),
                indent: hTags.indexOf((el as any).tagName)
            };
        });
        fileTitles.value.splice(0);
        fileTitles.value.push(...titles); // 最终计算出目录项
    }
  
  // a.定义目录项的点击事件handleNavClick
  const handleNavClick = (currentNav)=>{
        console.log("currentNav",currentNav);
        const noteEditorObj = mdEditorRef.value;
    		// 取预览模式下滚动容器,非预览模式下获取的方式可能有差异.
        const previewScroller = noteEditorObj.$refs.previewScroller.$el.querySelector('.scrollbar__wrap');
        let target = noteEditorObj.$el.querySelector("[data-v-md-line=\"" + currentNav.lineIndex + "\"]");
    		// 调用原始的方式滚动的对应位置.
        noteEditorObj.$refs.preview.scrollToTarget({target: target,onScrollEnd:false,scrollContainer:previewScroller});
    };
</script>

总结实现步骤其实就两步:

  1. 计算出目录数据项
  2. 重新写目录项点击事件。

而以上两步其实要与其原生保持一致,所以需要找到其源码在复制过来进行响应的调整。在此基础上我们就可以在目录上做我们自己的处理了,如目录渲染的位置及样式,点击事件增加自己的业务逻辑等

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蜗牛_snail

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值