基于markmap实现思维导图

起因:

由于项目的需求要把大模型返回的 markdown 数据变成思维导图,并且看到了秘塔搜索的思维导图之后markmap就这样的进过了我的生活

官方网站: markmap

依赖包下载:

      npm i markmap-common markmap-lib markmap-toolbar markmap-view

第一步:首先你需要一个svg的标签作为你的思维导图的容器

    <svg id="markMap"></svg>

第二步:你需要再页面引入markmap

import {Transformer} from "markmap-lib";
import { Toolbar } from "markmap-toolbar";
import {loadCSS, loadJS, Markmap} from "markmap-view";

第三步:如何将你的markdom渲染成思维导图?

假设你已经拿到了大模型返回的markdom数据

    \n# 中国武器大全\n## **导弹武器**\n  - 蓝箭7反坦克导弹[[1]]\n  - 巨浪-2(JL-2/CSS-N-4)[[1]]\n  - M-9/东风-15/CSS-6[[1]]\n  - 飞腾-2000(FT-2000)[[1]]\n  - 前卫-1(QW-1)[[1]]\n  - M11[[1]]\n  - 空地-88(KD-88)[[1]]\n  - 猎鹰-60(LY-60)[[1]]\n  - 陆盾2000(LD2000)[[1]]\n  - 红箭73(HJ-73)[[1]]\n  - 鹰击1(YJ-1/C-101)[[1]]\n  - 鹰击-62(YJ-62/C-602)[[1]]\n  - J-201[[1]]\n  - 霹雳-6(PL-6)[[1]]

1、首先我们需要把我们 Markdown 数据转化成 markmap 可渲染的数据,这时候就需要使用我们刚刚引用的markmap-lib,markmap-lib的作用就是把我们的 Markdown 数据转化成 markmap 可渲染的数据.

 const transformer = new Transformer();
 //this.initValue就是我们的markdom数据,此方法就是转换markdom数据,可以打印root查看数据结构
 const { root, features } = transformer.transform(this.initValue);
 const { styles, scripts } = transformer.getUsedAssets(features);

2.先在我们需要把我们获取的渲染数据root,渲染到我们svg标签上,这时候就需要用到markmap-view了

      if (styles) loadCSS(styles);
      if (scripts) loadJS(scripts, { getMarkmap: () => markmap });
      const svgEl = document.querySelector('#markMap');
      const mm= Markmap.create(svgEl, undefined, root);

其实到这里我们就可以获取一个转换后的思维导图了,但是项目还需要将思维导图转换成PNG、JPG、PDF三种格式所以应用了第二个插件 html2canvas

1.我们先要获取到思维导图svg标签的父元素

  <div ref="markMap">
    <svg id="markMap"></svg>
  </div>

2.现在我们就可以直接转换为图片格式了

          const svg = this.$refs.markMap
          html2canvas(svg,{
            scale: 3,//下载的图片或者pdf可能不太清晰,可以在这调节图片的大小
            dpi: 300,
            imageQuality: 0.9,
          }).then((canvas) => {
            // 创建一个图片元素
            let img = new Image();
            if(type=='png'){
              img.src = canvas.toDataURL('image/png');
              // 下载图片
              let link = document.createElement('a');
              link.href = img.src;
              link.download = `exported-image.${type}`;
              link.click();
            }else if(type=='jpg'){
              img.src = canvas.toDataURL('image/jpeg');
              // 下载图片
              let link = document.createElement('a');
              link.href = img.src;
              link.download = `exported-image.${type}`;
              link.click();
            }else if (type=='pdf'){
              let contentWidth = canvas.width;
              let contentHeight = canvas.height;
              let pdfWidth = 595.28; // A4 width in pixels
              let pdfHeight = (pdfWidth / contentWidth) * contentHeight;
              let pageData = canvas.toDataURL('image/jpeg',1.0);
              let PDF = new JsPDF('p', 'pt', 'a4');
              let scale = pdfWidth / contentWidth;
              PDF.addImage( pageData, 'JPEG', 0, 0, pdfWidth, pdfHeight);
              let remainingHeight = pdfHeight;

              while (remainingHeight < contentHeight) {
                PDF.addPage();
                let offsetY = -remainingHeight * scale;
                PDF.addImage(pageData, 'JPEG', 0, offsetY, pdfWidth, pdfHeight);
                remainingHeight += pdfHeight;
              }
              PDF.save('export.pdf'); //直接下载
            }


          });

3.现在我们就可以实现导出的功能了,但是在此项目在出现了bug

问题描述:在思维导图放大时当思维导图超出元素时,就会出现导出图片不全的情况

解决方案:参考了秘塔搜索,引入markmap自带的工具栏,其中有个按键是复原思维导图大小的操作,在导出前js代码模拟点击事件将思维导图回复至最初比例,并导出,应为项目设计图没有这三个按钮所以我们需要将他们隐藏设置opacity:0

参考图片:


    //展示XMind
    initXMind(){
      const transformer = new Transformer();
      const { root, features } = transformer.transform(this.initValue);
      const { styles, scripts } = transformer.getUsedAssets(features);
      const toolbar = new Toolbar();
      if (styles) loadCSS(styles);
      if (scripts) loadJS(scripts, { getMarkmap: () => markmap });
      let options={}


      const svgEl = document.querySelector('#markMap');
      const mm= Markmap.create(svgEl, undefined, root);
      toolbar.setBrand(false); // 隐藏 toolbar 中 markmap 的logo和url
      toolbar.attach(mm);
      toolbar.setItems(['zoomIn', 'zoomOut', 'fit']); // 重新设置默认的功能模块,添加导图导航栏   
      this.$refs.markMap.append(toolbar.render());

    },
    zoonIn(){
      //xmind缩放自适应
      let element=document.querySelector('.mm-toolbar').lastChild
      element.click()
    },
    exportPng(type) {
      this.$nextTick(async ()=>{
        await this.zoonIn() //自适应后的代码需要延时一下不然没效果
        setTimeout(()=>{
          const svg = this.$refs.markMap
          html2canvas(svg,{
            scale: 3,
            dpi: 300,
            imageQuality: 0.9,
          }).then((canvas) => {
            // 创建一个图片元素
            let img = new Image();
            if(type=='png'){
              img.src = canvas.toDataURL('image/png');
              // 下载图片
              let link = document.createElement('a');
              link.href = img.src;
              link.download = `exported-image.${type}`;
              link.click();
            }else if(type=='jpg'){
              img.src = canvas.toDataURL('image/jpeg');
              // 下载图片
              let link = document.createElement('a');
              link.href = img.src;
              link.download = `exported-image.${type}`;
              link.click();
            }else if (type=='pdf'){
              let contentWidth = canvas.width;
              let contentHeight = canvas.height;
              let pdfWidth = 595.28; // A4 width in pixels
              let pdfHeight = (pdfWidth / contentWidth) * contentHeight;
              let pageData = canvas.toDataURL('image/jpeg',1.0);
              let PDF = new JsPDF('p', 'pt', 'a4');
              let scale = pdfWidth / contentWidth;
              PDF.addImage( pageData, 'JPEG', 0, 0, pdfWidth, pdfHeight);
              let remainingHeight = pdfHeight;

              while (remainingHeight < contentHeight) {
                PDF.addPage();
                let offsetY = -remainingHeight * scale;
                PDF.addImage(pageData, 'JPEG', 0, offsetY, pdfWidth, pdfHeight);
                remainingHeight += pdfHeight;
              }
              PDF.save('export.pdf'); //直接下载
            }


          });
        },1000)
      })

    },

至此就是实现导出的全部代码

4、项目中还有在思维导图添加了注脚功能,不能放项目图片参考秘塔搜索

我们来看一下实现思路(此思路不是最好的)

我们反过来看一下大模型返回的markdom数据,我们将 [[数字]] 定义为注脚

    \n# 中国武器大全\n## **导弹武器**\n  - 蓝箭7反坦克导弹[[1]]\n  - 巨浪-2(JL-2/CSS-N-4)[[1]]\n  - M-9/东风-15/CSS-6[[1]]\n  - 飞腾-2000(FT-2000)[[1]]\n  - 前卫-1(QW-1)[[1]]\n  - M11[[1]]\n  - 空地-88(KD-88)[[1]]\n  - 猎鹰-60(LY-60)[[1]]\n  - 陆盾2000(LD2000)[[1]]\n  - 红箭73(HJ-73)[[1]]\n  - 鹰击1(YJ-1/C-101)[[1]]\n  - 鹰击-62(YJ-62/C-602)[[1]]\n  - J-201[[1]]\n  - 霹雳-6(PL-6)[[1]]

所以我在获取markdom数据时将[[ 和 ]]替换成a标签

    //展示XMind
    initXMind(){
      const transformer = new Transformer();
      const { root, features } = transformer.transform(this.initValue);
      const { styles, scripts } = transformer.getUsedAssets(features);
      const toolbar = new Toolbar();
      if (styles) loadCSS(styles);
      if (scripts) loadJS(scripts, { getMarkmap: () => markmap });
      let options={}


      const svgEl = document.querySelector('#markMap');
      const mm= Markmap.create(svgEl, undefined, root);
      toolbar.setBrand(false); // 隐藏 toolbar 中 markmap 的logo和url
      toolbar.attach(mm);
      toolbar.setItems(['zoomIn', 'zoomOut', 'fit']); // 重新设置默认的功能模块
      this.$refs.markMap.append(toolbar.render());
      document.querySelectorAll('a').forEach(ele=>{
        ele.setAttribute('class','aClass')
        ele.addEventListener('mouseover',()=>{
          ele.style.background='#0456B4'
        })
        ele.addEventListener('mouseout',()=>{
          ele.style.background='#d0d5dd'
        })
      })
    },
    //思维导图接口
    async mindMapping(id){

      const res =await mindMapping({result_id:id})
      if(res.code==200){
        let regex = '[[';
        let regexRight = ']]';
        let aStar=`<a href="javascript:;"  style="  display: inline-block;
                      width: 15px;
                      height: 15px;
                      border-radius: 50%;
                      font-size: 12px;
                      text-align: center;
                      background-color: #d0d5dd;
                      color: #fff !important;
                      text-align: center;
                      font-size: 9px;
                      text-decoration: none !important;
                      vertical-align: middle;
                      margin: 0 2px 3px;
                      cursor: pointer;
                      line-height: 16px;">`
        let aEnd=`</a >`
        let pattern =  (/\[\[\d+\]\]/g);
        let msg=res.data.split(pattern)

        let str=res.data.replaceAll(regex,aStar)

        this.initValue=str.replaceAll(regexRight,aEnd)

        this.$nextTick(async ()=>{
         await this.initXMind()
          let domList=document.querySelectorAll('.aClass')
          domList.forEach(item=>{
          //在此添加注脚点击事件
            item.addEventListener('click',(e)=>{
              e.preventDefault();
              this.internetAnswer.list[Number(item.innerText)-1] && this.preview(this.internetAnswer.list[Number(item.innerText)-1])
            })
          })

        })
      }
    },

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue 是一种流行的 JavaScript 前端框架,它可以通过组件化的方式快速构建应用程序。要实现思维导图,可以按照以下思路进行: 1. 数据结构设计:思维导图可以看作是一个树状结构,每个节点表示一个思维节点,包含内容、子节点等信息。可以使用一个树型数据结构来表示思维导图,每个节点可以是一个对象,包含相应的属性。 2. 组件设计:根据数据结构设计好的树型数据,可以创建一个树形组件来展示思维导图。可以使用 Vue 的组件化特性,将思维导图拆分成不同的组件,方便管理和复用。 3. 数据绑定:通过 Vue 的响应式数据绑定机制,将思维导图的数据与组件进行关联。可以通过在组件中使用 Vue 的 data 属性来保存思维导图的数据,并在模板中根据数据动态渲染组件。 4. 组件交互:为了实现思维导图的交互功能,可以通过 Vue 的事件机制来实现。例如,可以为节点添加点击事件,实现节点的展开和收起功能。可以为节点添加拖拽事件,支持节点的移动和调整。 5. 样式设计:思维导图通常需要一定的样式设计来美化界面。可以使用 Vue 的样式绑定功能,通过绑定类名或行内样式的方式来实现样式设计。可以为组件添加相应的样式属性,或者使用 CSS 预处理器来提高样式管理的可维护性。 总结,通过设计好的树型数据结构,使用 Vue 的组件化特性、数据绑定、事件机制和样式绑定等功能,可以较好地实现思维导图。这样结构清晰、交互丰富、样式美观的思维导图可以有效帮助用户整理和展示思维。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值