记一次使用vue-markdown在vue中解析markdown格式文件,并自动生成目录大纲

news2024/11/19 13:23:54

先上效果图

如图所示,在网页中,能直接解析markdown文档,并且生成目录大纲,也支持点击目录标题跳转到对应栏目中,下面就来讲讲是如何实现此功能的。

1、下载vue-markdown

yarn add vue-markdown

2、在页面中渲染markdown文件

<template>
     <div>
        <VueMarkdown
            class="api-content"
            :source="markdownContent"
            id="content"
        />
    </div>
</template>

<script>
import VueMarkdown from "vue-markdown";

export default {
    components: {
        VueMarkdown,
    },
    data() {
        return {
            markdownContent: "",
         };
    },
    mounted() {
        this.loadMarkdownFile();
    },
    methods: {
        async loadMarkdownFile() {
            try {
                // api.md文件存放在根目录下的public文件夹中
                const response = await fetch("/api.md");
                const markdownText = await response.text();
                this.markdownContent = markdownText;
 
            } catch (error) {
                console.error("Failed to load the Markdown file:", error);
            }
        }, 
    },
};
</script>

此时,打开浏览器查看,页面中已经正常渲染markdown文件了。

3、生成目录大纲

现在,我们需要有目录大纲方便我们查看文档。我的思路是,首先拿到markdown文件的html结构,然后找到所有H1-H5的标题标签,并给这些标签设置id,生成一个新数组,通过这个数组生成目录结构,说干就干。

//html部分

<div class="api-tree" id="tree">
    <el-tree
        :data="tree"
        :default-expand-all="true"
        @node-click="handleNodeClick"
    ></el-tree>
</div>


// js部分
catalogTree() {
    const content = document.getElementById("content").children;
    var arr = [];
    let currentHightestLevel;
    let parentId;
    let index = 0;
    for (let i = 0; i < content.length; i++) {
        let header = content[i].localName;
        if (/\b[h][0-9]\b/.test(header)) {
            let ele = $(content[i]);
            let name = ele.text();
            // 设置id
            ele.attr("id", i);
            let id = i;
            if (index === 0 || header <= currentHightestLevel) {
                currentHightestLevel = header;
                parentId = id;
            }
            arr.push({
                id: id,
                label: name,
                parentId: parentId == id ? "0" : parentId,
            });
            index++;
        }
    }
    const tree = [];
    arr.forEach((item) => {
        if (item.parentId === "0") {
            tree.push(this.convertArrayToTree(arr, item));
        }
    });
    this.tree = tree;
},
convertArrayToTree(arr, node) {
    for (let i = 0; i < arr.length; i++) {
        if (arr[i].parentId === node.id) {
            const res = this.convertArrayToTree(arr, arr[i]);
            if (node.children) {
                node.children.push(res);
            } else {
                node.children = [res];
            }
        }
    }
    return node;
},
handleNodeClick(data) {
    let anchorElement = document.getElementById(data.id);

    if (anchorElement) {
         anchorElement.scrollIntoView({
             behavior: "smooth",
             block: "end",
         });
     }
},

4、大功告成,最后,附上全部代码,带css样式

<template>
    <div class="page-api" id="myElement">
        <div class="api-tree" id="tree">
            <el-tree
                :data="tree"
                :default-expand-all="true"
                @node-click="handleNodeClick"
            ></el-tree>
        </div>
        <VueMarkdown
            class="api-content"
            :source="markdownContent"
            id="content"
        />
    </div>
</template>

<script>
import VueMarkdown from "vue-markdown";
import $ from "jquery";

export default {
    components: {
        VueMarkdown,
    },
    data() {
        return {
            markdownContent: "",
            tree: [],
        };
    },
    mounted() {
        this.loadMarkdownFile();
    },
    methods: {
        async loadMarkdownFile() {
            try {
                const response = await fetch("/api.md");
                const markdownText = await response.text();
                this.markdownContent = markdownText;
                this.$nextTick(() => {
                    this.catalogTree();
                });
            } catch (error) {
                console.error("Failed to load the Markdown file:", error);
            }
        },
        catalogTree() {
            const content = document.getElementById("content").children;
            var arr = [];
            let currentHightestLevel;
            let parentId;
            let index = 0;
            for (let i = 0; i < content.length; i++) {
                let header = content[i].localName;
                if (/\b[h][0-9]\b/.test(header)) {
                    let ele = $(content[i]);
                    let name = ele.text();
                    // 设置id
                    ele.attr("id", i);
                    // let id = ele.children("a").attr("id");
                    let id = i;
                    if (index === 0 || header <= currentHightestLevel) {
                        currentHightestLevel = header;
                        parentId = id;
                    }
                    arr.push({
                        id: id,
                        label: name,
                        parentId: parentId == id ? "0" : parentId,
                    });
                    index++;
                }
            }
            const tree = [];
            arr.forEach((item) => {
                if (item.parentId === "0") {
                    tree.push(this.convertArrayToTree(arr, item));
                }
            });
            this.tree = tree;
        },
        convertArrayToTree(arr, node) {
            for (let i = 0; i < arr.length; i++) {
                if (arr[i].parentId === node.id) {
                    const res = this.convertArrayToTree(arr, arr[i]);
                    if (node.children) {
                        node.children.push(res);
                    } else {
                        node.children = [res];
                    }
                }
            }
            return node;
        },
        handleNodeClick(data) {
            let anchorElement = document.getElementById(data.id);

            let scrollPosition = anchorElement.offsetTop - 20;

            let myElement = document.getElementById("myElement");
            myElement.scrollTo({
                left: 0,
                top: scrollPosition,
                behavior: "smooth",
            });

            // if (anchorElement) {
            //     anchorElement.scrollIntoView({
            //         behavior: "smooth",
            //         block: "end",
            //     });
            // }
        },
    },
};
</script>

<style lang="scss">
.page-api {
    display: flex;
    height: 100%;
    overflow-y: scroll;
    .api-tree {
        position: fixed;
        left: 20px;
        top: 120px;
        width: 200px;
        height: calc(100% - 160px);
        overflow-y: scroll;
        z-index: 99;
        .el-tree {
            background: none;
            color: #fff;
            .el-tree-node:focus > .el-tree-node__content,
            .el-tree-node__content:hover {
                background: none;
                color: rgb(24, 144, 255);
            }
        }
    }
    .api-content {
        flex: 1;
        margin-left: 220px;
        padding: 0 30px;
        color: rgba(255, 255, 255, 0.75);

        h3 {
            margin-left: 25px;
        }
        code {
            border-radius: 2px;
            color: #e96900;
            margin: 0 2px;
            padding: 3px 5px;
            white-space: pre-wrap;
        }
        table {
            border-collapse: collapse;
            border-spacing: 0;
            th,
            td {
                border: 1px solid #ddd;
                padding: 6px 13px;
                margin: 0;
            }
        }
        pre {
            background: rgba(0, 0, 0, 0.7);
            padding: 20px 30px;
        }
    }
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1081722.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL数据生成工具mysql_random_data_load

在看MySQL文章的时候偶然发现生成数据的工具&#xff0c;此处直接将软件作者的文档贴了过来&#xff0c;说明了使用方式及下载地址 Random data generator for MySQL Many times in my job I need to generate random data for a specific table in order to reproduce an is…

2023.10 秋爽版 java 软件授权激活 架构 java代码混淆 按日期授权 不联网

什么是代码混淆&#xff1f; 代码混淆是一种技术&#xff0c;用于在不改变代码功能的情况下&#xff0c;通过改变代码的结构和逻辑&#xff0c;使之变得更难理解和分析&#xff0c;从而增加反向工程的难度。 为什么要进行代码混淆&#xff1f; 在Java应用程序中&#xff0c;…

MyBatisPlus(十七)通用枚举

说明 MyBatisPlus 优雅地使用枚举类型。 声明通用枚举属性 使用 EnumValue 注解枚举属性 package com.example.web.enumeration;import com.baomidou.mybatisplus.annotation.EnumValue; import com.fasterxml.jackson.annotation.JsonValue; import lombok.AllArgsConstru…

安装Android SDK点击SDK Manager.exe一闪而退完美解决方案

如上图&#xff0c;我们点击 “SDK Manager.exe” 总是一闪而退。 1.查看提示说Detect whether Java SE Development Kit is installed&#xff0c;检查你的JDK是否安装。 2.在cmd里看了&#xff0c;java -version 和javac -version都是有显示版本的。说明安装以及环境配置成…

ES6介绍

1&#xff1a;ES6声明变量 1.变量var声明变量的问题 ES5 可以重复声明变量可以先使用再声明造成全局变量污染 2.let声明变量特点 ES6 不能先使用再说明不能重复定义一个变量具有块级作用域 3.const声明变量特点 ES6 不能先使用再说明一旦声明必须赋值赋值之后不能修改具有块级…

0144 文件管理

目录 4.文件管理 4.1文件系统基础 4.2目录 4.3文件系统 部分习题 4.文件管理 4.1文件系统基础 4.2目录 4.3文件系统 部分习题 1.UNIX操作系统忠&#xff0c;输入/输出设备视为&#xff08;&#xff09; A.普通文件 B.目录文件 C.索引文件 D.特殊文…

cesium 地图蒙版遮罩效果

示例代码 <!DOCTYPE html> <html lang"en"><head><!-- Use correct character set. --><meta charset"utf-8" /><!-- Tell IE to use the latest, best version. --><meta http-equiv"X-UA-Compatible"…

快速排序 ← PPT

【算法代码】https://blog.csdn.net/hnjzsyjyj/article/details/127825125

JavaScript (下)

1.面向对象 在 Java 中我们学习过面向对象&#xff0c;核心思想是万物皆对象。在 JavaScript 中同样也有面向对象。思想类似。 把相关的数据和方法组织为一个整体来看待&#xff0c;从更高的层次来进行系统建模&#xff0c;更贴近事物的自然运行模式 1.类的定义和使用 格式…

Java二叉树超详解(常用方法介绍)(2)

二叉树中的常用方法 静态二叉树的手动创建 这里我们先给出二叉树结点的信息(这里是内部类)&#xff1a; static class TreeNode {public char val;public TreeNode left;//左孩子的引用public TreeNode right;//右孩子的引用public TreeNode(char val) {this.val val;}} 手动…

嵌入式系统开发【深入浅出】 UART 与 USART

目录 UART: 通用串行异步收发器 串行通信的时序 8N1&#xff1a;8位数据位 N没有校验位 1停止位1位 中断控制 编程重点 引言&#xff1a; 串口通讯(Serial Communication)是一种设备间非常常用的串行通讯方式&#xff0c;并且大部分电子设备都支持该通讯方式&#xff0c;也…

TensorFlow入门(二十一、softmax算法与损失函数)

在实际使用softmax计算loss时,有一些关键地方与具体用法需要注意: 交叉熵是十分常用的,且在TensorFlow中被封装成了多个版本。多版本中,有的公式里直接带了交叉熵,有的需要自己单独手写公式求出。如果区分不清楚,在构建模型时,一旦出现问题将很难分析是模型的问题还是交叉熵的使…

【 数据结构:堆(Heap)】大根堆、小根堆、堆的向上调整算法、向下调整算法 及 堆的功能实现!

前言 本系列文章【数据结构】默认会使用 C/C 进行设计实现&#xff01;其他语言的实现方式请参照分析设计思路自行实现&#xff01; 注[1]&#xff1a;文章属于学习总结&#xff0c;相对于课本教材而言&#xff0c;不具有相应顺序性&#xff01;&#xff08;可在合集中自行查看…

C++: 继承

学习目标 1.继承的概念及定义 2.基类和派生类对象赋值转换(切片) 3.继承中的作用域(隐藏/重定义) 4.派生类的默认成员函数 5.继承与友元 6.继承与静态成员 7.菱形继承与菱形虚拟继承 8.总结 1.继承的概念及定义 1.1概念 继承: 它允许你创建一个新的类&#xff08;称为子类或派…

小程序uView2.X框架upload组件上传方法总结+避坑

呈现效果: 1.1单图片上传 1.2多图片上传 前言:相信很多人写小程序会用到uView框架,总体感觉还算OK吧,只能这么说,肯定也会遇到图片视频上传,如果用到这个upload组件相信你,肯定遇到各种各样的问题,这是我个人总结的单图片和多图片上传方法. uView2.X框架:uView 2.0 - 全面兼容…

JavaSE学习值之--String类

&#x1f495;"不要同情自己&#xff0c;同情自己是卑劣懦夫的勾当&#xff01;"&#x1f495; 作者&#xff1a;Mylvzi 文章主要内容&#xff1a;JavaSE学习值之--String类 目录 前言&#xff1a; 一.String类 1.String类的属性 2.字符串的构造 注意&#xf…

基于YOLOv8模型的塑料瓶目标检测系统(PyTorch+Pyside6+YOLOv8模型)

摘要&#xff1a;基于YOLOv8模型的塑料瓶目标检测系统可用于日常生活中检测与定位塑料瓶目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的目标检测&#xff0c;另外本系统还支持图片、视频等格式的结果可视化与结果导出。本系统采用YOLOv8目标检测算法训练数…

翻译docker官方文档(残缺版)

Build with docker(使用 Docker 技术构建应用程序或系统镜像) Overview (概述) 介绍&#xff08;instruction&#xff09; 层次结构&#xff08;Layers&#xff09; The order of Dockerfile instructions matters. A Docker build consists of a series of ordered build ins…

“高级Vue状态管理 - Vuex的魅力与应用“

目录 引言1. Vuex的简介1.1 什么是Vuex&#xff1f;1.2 Vuex的核心概念 2. Vuex的值获取与改变(综合案例)3. Vuex的异步请求总结 引言 在现代Web开发中&#xff0c;前端应用变得越来越复杂。随着应用规模的扩大和数据流的复杂性增加&#xff0c;有效地管理应用的状态成为了一项…

Android---Synchronized 和 ReentrantLock

Synchronized 基本使用 1. 修饰实例方法 public class SynchronizedMethods{private int sum 0;public synchronized void calculate(){sum sum 1;} } 这种情况下的锁对象是当前实例对象&#xff0c;因此只有同一个实例对象调用此方法才会产生互斥效果&#xff1b;不同的…