Vue使用mind-map实现在线思维导图

 更新时间:2024年07月16日 11:57:04   作者:牛老师讲GIS  
Vue中的Mind-Map通常是指使用Vue.js这个前端框架构建的思维导图组件或库,它可以帮助开发者在Web应用中创建动态、交互式的思维导图,让用户可以直观地组织信息和结构化数据,本文介绍了Vue使用mind-map实现在线思维导图,需要的朋友可以参考下

概述

在本文,给大家分享一下基于mind-map实现在线的思维导图,并实现:1. 导图导出为图片;2. 打开xmind文件。

实现效果

图标选择

主题选择

结构选择

实现

1. mind-map简介

simple-mind-map(思绪思维导图)是一个简单&强大的Web思维导图库,不依赖任何特定框架。可以帮助你快速开发思维导图产品。

2. 实现

1)添加依赖

{
    "simple-mind-map": "^0.10.2-fix.1",
}

2)引入插件

import MindMap from 'simple-mind-map'

3)插件导入

import MiniMap from 'simple-mind-map/src/plugins/MiniMap.js'
import Watermark from 'simple-mind-map/src/plugins/Watermark.js'
import KeyboardNavigation from 'simple-mind-map/src/plugins/KeyboardNavigation.js'
import ExportPDF from 'simple-mind-map/src/plugins/ExportPDF.js'
import ExportXMind from 'simple-mind-map/src/plugins/ExportXMind.js'
import Export from 'simple-mind-map/src/plugins/Export.js'
import Drag from 'simple-mind-map/src/plugins/Drag.js'
import Select from 'simple-mind-map/src/plugins/Select.js'
import AssociativeLine from 'simple-mind-map/src/plugins/AssociativeLine.js'
import TouchEvent from 'simple-mind-map/src/plugins/TouchEvent.js'
import NodeImgAdjust from 'simple-mind-map/src/plugins/NodeImgAdjust.js'
import SearchPlugin from 'simple-mind-map/src/plugins/Search.js'
import Painter from 'simple-mind-map/src/plugins/Painter.js'
import Formula from 'simple-mind-map/src/plugins/Formula.js'
import RainbowLines from 'simple-mind-map/src/plugins/RainbowLines.js'
import Demonstrate from 'simple-mind-map/src/plugins/Demonstrate.js'
import OuterFrame from 'simple-mind-map/src/plugins/OuterFrame.js'

// 注册插件
MindMap.usePlugin(MiniMap)
  .usePlugin(Watermark)
  .usePlugin(Drag)
  .usePlugin(KeyboardNavigation)
  .usePlugin(ExportPDF)
  .usePlugin(ExportXMind)
  .usePlugin(Export)
  .usePlugin(Select)
  .usePlugin(AssociativeLine)
  .usePlugin(NodeImgAdjust)
  .usePlugin(TouchEvent)
  .usePlugin(SearchPlugin)
  .usePlugin(Painter)
  .usePlugin(Formula)
  .usePlugin(RainbowLines)
  .usePlugin(Demonstrate)
  .usePlugin(OuterFrame)

4)自定义主题

import cactus from './themes/cactus'
import classic5 from './themes/classic5'
import classic6 from './themes/classic6'
import classic7 from './themes/classic7'
import dark3 from './themes/dark3'
import dark4 from './themes/dark4'
import darkNightLceBlade from './themes/darkNightLceBlade'
import index from './themes/index'
import lemonBubbles from './themes/lemonBubbles'
import morandi from './themes/morandi'
import neonLamp from './themes/neonLamp'
import oreo from './themes/oreo'
import rose from './themes/rose'
import seaBlueLine from './themes/seaBlueLine'
import shallowSea from './themes/shallowSea'

MindMap.defineTheme('cactus', cactus)
MindMap.defineTheme('classic5', classic5)
MindMap.defineTheme('classic6', classic6)
MindMap.defineTheme('classic7', classic7)
MindMap.defineTheme('dark3', dark3)
MindMap.defineTheme('dark4', dark4)
MindMap.defineTheme('darkNightLceBlade', darkNightLceBlade)
MindMap.defineTheme('index', index)
MindMap.defineTheme('lemonBubbles', lemonBubbles)
MindMap.defineTheme('morandi', morandi)
MindMap.defineTheme('neonLamp', neonLamp)
MindMap.defineTheme('oreo', oreo)
MindMap.defineTheme('rose', rose)
MindMap.defineTheme('seaBlueLine', seaBlueLine)
MindMap.defineTheme('shallowSea', shallowSea)

5)引入图标

import {<!--{cke_protected}{C}%3C!%2D%2D%20%2D%2D%3E--> nodeIconList } from 'simple-mind-map/src/svg/icons'

6)导出图片

mindMapInstance.export('png', true, this.name)

7)打开xmind文件

import xmind from 'simple-mind-map/src/parse/xmind.js'

{
  methods:{
      ElMessageBox.confirm('是否直接替换当前思维导图?', '警告', {
        confirmButtonText: '确认',
        cancelButtonText: '取消',
        type: 'warning',
      })
     .then(() => {
          const { raw } = file
          xmind.parseXmindFile(raw).then(data => {
            that.setData(data)
          })
        })
        .catch(() => {})
     }
}

完整代码如下:

<template>
  <div class="mind-map-container">
    <div class="mindmap-tools" v-show="!readonly">
      <el-upload
        ref="upload"
        style="margin-right: 0.8rem"
        :limit="1"
        :on-change="handleChange"
        :show-file-list="false"
        :accept="'.xmind'"
        :auto-upload="false"
      >
        <template #trigger>
          <el-button class="my-button" size="small">
            <div class="my-button">
              <svg
                t="1720518684993"
                class="icon"
                viewBox="0 0 1024 1024"
                version="1.1"
                xmlns="http://www.w3.org/2000/svg"
                p-id="29039"
                width="32"
                height="32"
              >
                <path
                  d="M910.19 175.16L776.1 41.07A136.94 136.94 0 0 0 679.27 1H235A161.29 161.29 0 0 0 73.69 162.25v699.5A161.29 161.29 0 0 0 235 1023h554a161.29 161.29 0 0 0 161.31-161.25V272a136.89 136.89 0 0 0-40.12-96.84z m-148.8 459.72c-1.53 8-14.94 8.24-36.72 11.59s-95.51 17.6-103 17.6-12.56-11.31-13.4-14.66-9.35-33.24-20.53-46.92-49.74-54.17-112.86-51.66-68.14 49.57-69.82 64.65 2.52 30.16-10.89 32.67-103 19.27-109.33 19.69-11.31-4.19-10.89-20.11 6.28-45.24 31.84-65.35 93.83-48.17 100.95-52.38 10.47-7.54 8-13.82-46.54-21.76-65.39-30.18-54.45-26-71.21-54.87-17.59-42.73-15.08-46.92S274 378 274 378s125.67-23 131.53-21.78 16.76 39.37 20.94 47.33 30.16 61.58 93.42 58.64 52.78-41 49.42-53-8.16-22.44-1.46-25.8 108.28-20.52 115.4-19.68 19.27 4.47 18.71 31.83-6.56 46.92-27.93 60.32-65.48 19.55-68.13 26.39 5.58 12.43 25.41 21.36 72.88 36 88.8 51.67 46.59 56.43 41.28 79.6z"
                  p-id="29040"
                  fill="currentColor"
                ></path>
              </svg>
              <div>打开xmind文件</div>
            </div>
          </el-button>
        </template>
      </el-upload>
      <el-button
        :disabled="activeNodes.length === 0"
        class="my-button"
        size="small"
        @click="addNode"
      >
        <div class="my-button">
          <svg
            t="1719284042715"
            class="icon"
            viewBox="0 0 1024 1024"
            version="1.1"
            xmlns="http://www.w3.org/2000/svg"
            p-id="32998"
            width="32"
            height="32"
          >
            <path
              d="M510.833431 62.695924c-247.644193 0-448.406636 200.762443-448.406636 448.406636 0 247.65545 200.762443 448.416869 448.406636 448.416869 247.65545 0 448.416869-200.76142 448.416869-448.416869C959.2503 263.458367 758.488881 62.695924 510.833431 62.695924zM779.544429 562.112328 560.358381 562.112328l0 219.186048-102.008278 0L458.350103 562.112328 239.164055 562.112328l0-102.008278 219.186048 0L458.350103 240.918002l102.008278 0 0 219.186048 219.186048 0L779.544429 562.112328z"
              fill="currentColor"
              p-id="32999"
            ></path>
          </svg>
          <div>添加同级节点</div>
        </div>
      </el-button>
      <el-button
        :disabled="activeNodes.length === 0"
        class="my-button"
        size="small"
        @click="addChildNode"
      >
        <div class="my-button">
          <svg
            t="1719283662028"
            class="icon"
            viewBox="0 0 1024 1024"
            version="1.1"
            xmlns="http://www.w3.org/2000/svg"
            p-id="23087"
            width="32"
            height="32"
          >
            <path
              d="M739.555556 0a56.888889 56.888889 0 0 1 56.888888 56.888889v227.555555a56.888889 56.888889 0 0 1-56.888888 56.888889H227.555556v455.111111h170.666666v-56.888888a56.888889 56.888889 0 0 1 56.888889-56.888889h512a56.888889 56.888889 0 0 1 56.888889 56.888889v227.555555a56.888889 56.888889 0 0 1-56.888889 56.888889H455.111111a56.888889 56.888889 0 0 1-56.888889-56.888889v-56.888889H170.666667a56.888889 56.888889 0 0 1-56.888889-56.888889V341.333333H56.888889a56.888889 56.888889 0 0 1-56.888889-56.888889V56.888889a56.888889 56.888889 0 0 1 56.888889-56.888889h682.666667z"
              fill="currentColor"
              p-id="23088"
            ></path>
          </svg>
          <div>添加子节点</div>
        </div>
      </el-button>
      <el-button
        :disabled="activeNodes.length === 0"
        class="my-button"
        size="small"
        @click="dropNode"
      >
        <div class="my-button">
          <svg
            t="1719283698787"
            class="icon"
            viewBox="0 0 1024 1024"
            version="1.1"
            xmlns="http://www.w3.org/2000/svg"
            p-id="24126"
            width="32"
            height="32"
          >
            <path
              d="M316.652201 74.043048L339.889115 28.356912A53.16921 53.16921 0 0 1 386.756789 0h250.486058a53.16921 53.16921 0 0 1 46.867674 28.356912l23.236914 45.686136a53.16921 53.16921 0 0 0 47.261521 28.356913h177.624547A51.987672 51.987672 0 0 1 984.615021 153.599941a51.987672 51.987672 0 0 1-52.381518 51.19998H91.766134A51.987672 51.987672 0 0 1 39.384615 153.599941a51.987672 51.987672 0 0 1 52.381519-51.19998h177.624547a53.16921 53.16921 0 0 0 47.26152-28.356913z m-118.153801 181.956854h630.153604a51.593826 51.593826 0 0 1 52.381518 51.19998v614.399764a101.218423 101.218423 0 0 1-30.719988 72.467664 105.944575 105.944575 0 0 1-74.043048 29.932296H250.879919a105.944575 105.944575 0 0 1-74.043049-29.932296 101.218423 101.218423 0 0 1-30.719988-72.467664V307.199882A51.593826 51.593826 0 0 1 196.923016 255.999902z m52.381519 153.59994v460.799823a51.987672 51.987672 0 0 0 52.775364 51.199981h419.839838a51.987672 51.987672 0 0 0 52.775365-51.199981V409.599842a51.987672 51.987672 0 0 0-52.775365-51.19998H303.655283a51.987672 51.987672 0 0 0-54.350748 51.19998z m157.538401 51.199981a51.987672 51.987672 0 0 1 52.775364 51.19998v255.999902a51.987672 51.987672 0 0 1-52.775364 51.19998 51.987672 51.987672 0 0 1-52.381519-51.19998V511.999803a51.987672 51.987672 0 0 1 50.806135-51.19998z m210.313765 0a51.987672 51.987672 0 0 1 52.381518 51.19998v255.999902a51.987672 51.987672 0 0 1-52.381518 51.19998 51.987672 51.987672 0 0 1-52.775364-51.19998V511.999803a51.987672 51.987672 0 0 1 51.19998-51.19998z"
              p-id="24127"
              fill="currentColor"
            ></path>
          </svg>
          <div>删除节点</div>
        </div>
      </el-button>
      <el-button
        :disabled="activeNodes.length === 0"
        class="my-button"
        size="small"
        @click="addGeneralization"
      >
        <div class="my-button">
          <svg
            t="1719283740026"
            class="icon"
            viewBox="0 0 1024 1024"
            version="1.1"
            xmlns="http://www.w3.org/2000/svg"
            p-id="26481"
            width="32"
            height="32"
          >
            <path
              d="M128 576h416a64 64 0 0 1 64 64v192a64 64 0 0 1-64 64H128a64 64 0 0 1-64-64v-192a64 64 0 0 1 64-64z m32 64h352a32 32 0 0 1 32 32v128a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32v-128a32 32 0 0 1 32-32zM128 128h416a64 64 0 0 1 64 64v192a64 64 0 0 1-64 64H128a64 64 0 0 1-64-64V192a64 64 0 0 1 64-64z m766.56 415.968A352.16 352.16 0 0 1 704 825.632c-23.68 3.52-35.52-7.2-35.52-32.224 0-15.584 11.84-29.568 35.52-41.92A287.712 287.712 0 0 0 832 512c0-99.84-50.816-187.84-128-239.488-23.68-14.848-35.52-29.536-35.52-44.064 0-25.568 11.84-35.584 35.52-30.08a352.16 352.16 0 0 1 190.56 281.664L896 480h32a32 32 0 0 1 0 64h-32l-1.44-0.032zM160 192h352a32 32 0 0 1 32 32v128a32 32 0 0 1-32 32H160a32 32 0 0 1-32-32V224a32 32 0 0 1 32-32z"
              fill="currentColor"
              p-id="26482"
            ></path>
          </svg>
          <div>添加概要</div>
        </div>
      </el-button>
      <el-button class="my-button" size="small" @click="exportPng">
        <div class="my-button">
          <svg
            t="1719283812353"
            class="icon"
            viewBox="0 0 1024 1024"
            version="1.1"
            xmlns="http://www.w3.org/2000/svg"
            p-id="28576"
            width="32"
            height="32"
          >
            <path
              d="M896.341333 128c23.381333 0 42.325333 18.986667 42.325334 42.368v398.890667A255.573333 255.573333 0 0 0 853.333333 554.666667V213.333333H170.666667l0.042666 597.333334 396.458667-396.501334a42.624 42.624 0 0 1 56.32-3.584l3.968 3.626667 151.296 151.466667a256.128 256.128 0 0 0-166.826667 330.368L127.658667 896A42.368 42.368 0 0 1 85.333333 853.632V170.368A42.666667 42.666667 0 0 1 127.658667 128h768.682666zM341.333333 298.666667a85.333333 85.333333 0 1 1 0 170.666666 85.333333 85.333333 0 0 1 0-170.666666z m512 469.333333v-128l170.666667 170.666667-170.666667 170.666666v-128h-170.666666v-85.333333h170.666666z"
              fill="currentColor"
              p-id="28577"
            ></path>
          </svg>
          <div>导出图片</div>
        </div>
      </el-button>
      <el-button
        :disabled="activeNodes.length === 0"
        :type="activedPanel === 'icon' ? 'primary' : 'default'"
        class="my-button"
        size="small"
        @click="activePanel('icon')"
      >
        <div class="my-button">
          <svg
            t="1719283775623"
            class="icon"
            viewBox="0 0 1026 1024"
            version="1.1"
            xmlns="http://www.w3.org/2000/svg"
            p-id="27549"
            width="32"
            height="32"
          >
            <path
              d="M495.465 830.665c-187.138 0-264.948-168.222-268.193-175.401l62.966-28.307c2.486 5.49 62.207 134.667 205.194 134.667 153.067-1.795 211.302-129.213 213.685-134.633l63.103 27.996c-3.141 7.077-79.054 173.364-273.13 175.643l-3.625 0.034zM500.16 991.221c-256.974 0-466.034-209.060-466.034-466.034s209.060-466.034 466.034-466.034 466.034 209.060 466.034 466.034-209.060 466.034-466.034 466.034zM500.16 128.196c-218.897 0-396.991 178.094-396.991 396.991s178.094 396.991 396.991 396.991 396.991-178.094 396.991-396.991-178.059-396.991-396.991-396.991zM311.088 444.27c0 29.689 24.062 53.818 53.818 53.818s53.818-24.096 53.818-53.818-24.062-53.818-53.818-53.818-53.818 24.096-53.818 53.818zM580.765 444.27c0 29.689 24.062 53.818 53.818 53.818s53.818-24.096 53.818-53.818-24.062-53.818-53.818-53.818-53.818 24.096-53.818 53.818z"
              fill="currentColor"
              p-id="27550"
            ></path>
          </svg>
          <div>设置图标</div>
        </div>
      </el-button>
      <el-button
        :type="activedPanel === 'theme' ? 'primary' : 'default'"
        class="my-button"
        size="small"
        @click="activePanel('theme')"
      >
        <div class="my-button">
          <svg
            t="1719283846317"
            class="icon"
            viewBox="0 0 1024 1024"
            version="1.1"
            xmlns="http://www.w3.org/2000/svg"
            p-id="29654"
            width="32"
            height="32"
          >
            <path
              d="M772.8 96v64L936 321.6l-91.2 91.2c-12.8-11.2-27.2-16-43.2-16-36.8 0-65.6 28.8-65.6 65.6V800c0 35.2-28.8 64-64 64H352c-35.2 0-64-28.8-64-64V462.4c0-36.8-28.8-65.6-65.6-65.6-16 0-32 6.4-43.2 16L88 321.6 249.6 160h40l1.6 1.6C336 228.8 420.8 272 512 272c91.2 0 176-41.6 220.8-110.4 0-1.6 1.6-1.6 1.6-1.6h38.4V96m-481.6 0H256c-22.4 0-38.4 6.4-49.6 19.2L43.2 276.8c-25.6 25.6-25.6 65.6 0 89.6l94.4 94.4c11.2 11.2 27.2 17.6 41.6 17.6s30.4-6.4 41.6-17.6h1.6c1.6 0 1.6 0 1.6 1.6V800c0 70.4 57.6 128 128 128h320c70.4 0 128-57.6 128-128V462.4c0-1.6 0-1.6 1.6-1.6h1.6c11.2 11.2 27.2 17.6 41.6 17.6 16 0 30.4-6.4 41.6-17.6l94.4-94.4c25.6-25.6 25.6-65.6 0-89.6L819.2 115.2C806.4 102.4 790.4 96 772.8 96h-40c-22.4 0-41.6 11.2-54.4 30.4-33.6 49.6-96 81.6-168 81.6s-134.4-33.6-168-81.6C332.8 107.2 312 96 291.2 96z"
              fill="currentColor"
              p-id="29655"
            ></path>
          </svg>
          <div>选择主题</div>
        </div>
      </el-button>
      <el-button
        :type="activedPanel === 'layout' ? 'primary' : 'default'"
        class="my-button"
        size="small"
        @click="activePanel('layout')"
      >
        <div class="my-button">
          <svg
            t="1719283873990"
            class="icon"
            viewBox="0 0 1024 1024"
            version="1.1"
            xmlns="http://www.w3.org/2000/svg"
            p-id="30805"
            width="32"
            height="32"
          >
            <path
              d="M938.666667 640h-64v-42.666667c0-72.533333-55.466667-128-128-128H554.666667V384h64c46.933333 0 85.333333-38.4 85.333333-85.333333V85.333333c0-46.933333-38.4-85.333333-85.333333-85.333333h-213.333334c-46.933333 0-85.333333 38.4-85.333333 85.333333v213.333334c0 46.933333 38.4 85.333333 85.333333 85.333333H469.333333v85.333333H277.333333c-72.533333 0-128 55.466667-128 128v29.866667c0 4.266667 0 8.533333 4.266667 12.8H85.333333c-46.933333 0-85.333333 38.4-85.333333 85.333333v213.333334c0 46.933333 38.4 85.333333 85.333333 85.333333h213.333334c46.933333 0 85.333333-38.4 85.333333-85.333333v-213.333334c0-46.933333-38.4-85.333333-85.333333-85.333333H230.4c0-4.266667 4.266667-8.533333 4.266667-12.8V597.333333c0-25.6 17.066667-42.666667 42.666666-42.666666h469.333334c25.6 0 42.666667 17.066667 42.666666 42.666666v42.666667H725.333333c-46.933333 0-85.333333 38.4-85.333333 85.333333v213.333334c0 46.933333 38.4 85.333333 85.333333 85.333333h213.333334c46.933333 0 85.333333-38.4 85.333333-85.333333v-213.333334c0-46.933333-38.4-85.333333-85.333333-85.333333zM298.666667 725.333333v213.333334H85.333333v-213.333334h213.333334zM405.333333 298.666667V85.333333h213.333334v213.333334h-213.333334zM938.666667 938.666667h-213.333334v-213.333334h213.333334v213.333334z"
              fill="currentColor"
              p-id="30806"
            ></path>
          </svg>
          <div>选择结构</div>
        </div>
      </el-button>
    </div>
    <div
      id="mindMapContainer"
      @dragenter.stop.prevent
      @dragleave.stop.prevent
      @dragover.stop.prevent
      @drop.stop.prevent
    ></div>
    <div class="active-panel" v-show="!readonly && activedPanel !== ''">
      <!-- 图标 -->
      <div class="active-panel-box" v-if="activedPanel === 'icon'">
        <div class="title">
          选择图标
          <span class="close" @click="activedPanel = ''">×</span>
        </div>
        <div class="content icon">
          <div class="icon-group" v-for="group of nodeIconList" :key="group.type">
            <div class="group-title">
              {{ group.name }}
            </div>
            <div class="group-content">
              <span
                class="icon-item"
                :class="activeNodes.length === 0 ? 'disabled' : ''"
                v-for="icon of group.list"
                :key="icon.name"
                @click="setIcon(group.type, icon.name)"
                v-html="icon.icon"
              ></span>
            </div>
          </div>
        </div>
      </div>
      <!-- 主题 -->
      <div class="active-panel-box" v-else-if="activedPanel === 'theme'">
        <div class="title">
          选择主题
          <span class="close" @click="activedPanel = ''">×</span>
        </div>
        <div class="content">
          <li
            v-for="item of themes"
            :key="item.value"
            @click="changeTheme(item.value)"
            :class="theme === item.value ? 'active' : ''"
          >
            <img :src="`/pages/online-doc/icons/theme/${item.value}.jpg`" />
            <div>{{ item.label }}</div>
          </li>
        </div>
      </div>
      <!-- 结构 -->
      <div class="active-panel-box" v-else-if="activedPanel === 'layout'">
        <div class="title">
          选择结构
          <span class="close" @click="activedPanel = ''">×</span>
        </div>
        <div class="content">
          <li
            v-for="item of layouts"
            :key="item.value"
            @click="changeLayout(item.value)"
            :class="layout === item.value ? 'active' : ''"
          >
            <img :src="`/pages/online-doc/icons/layout/${item.value}.png`" />
            <div>{{ item.label }}</div>
          </li>
        </div>
      </div>
    </div>
  </div>

  <div class="mind-map-scale">
    <el-button text @click="zoomOut">
      <span style="font-size: 1.2rem; font-weight: bold">─</span>
    </el-button>
    <el-input
      style="display: inline-block; width: 50px; text-align: center"
      v-model="showScale"
      disabled
    />
    <el-button text @click="zoomIn"><span style="font-size: 1.2rem">+</span></el-button>
  </div>
</template>

<script>
import MindMap from 'simple-mind-map'
import MiniMap from 'simple-mind-map/src/plugins/MiniMap.js'
import Watermark from 'simple-mind-map/src/plugins/Watermark.js'
import KeyboardNavigation from 'simple-mind-map/src/plugins/KeyboardNavigation.js'
import ExportPDF from 'simple-mind-map/src/plugins/ExportPDF.js'
import ExportXMind from 'simple-mind-map/src/plugins/ExportXMind.js'
import Export from 'simple-mind-map/src/plugins/Export.js'
import Drag from 'simple-mind-map/src/plugins/Drag.js'
import Select from 'simple-mind-map/src/plugins/Select.js'
import AssociativeLine from 'simple-mind-map/src/plugins/AssociativeLine.js'
import TouchEvent from 'simple-mind-map/src/plugins/TouchEvent.js'
import NodeImgAdjust from 'simple-mind-map/src/plugins/NodeImgAdjust.js'
import SearchPlugin from 'simple-mind-map/src/plugins/Search.js'
import Painter from 'simple-mind-map/src/plugins/Painter.js'
import Formula from 'simple-mind-map/src/plugins/Formula.js'
import RainbowLines from 'simple-mind-map/src/plugins/RainbowLines.js'
import Demonstrate from 'simple-mind-map/src/plugins/Demonstrate.js'
import OuterFrame from 'simple-mind-map/src/plugins/OuterFrame.js'
import { nodeIconList } from 'simple-mind-map/src/svg/icons'
import { ElMessageBox } from 'element-plus'
import xmind from 'simple-mind-map/src/parse/xmind.js'

import cactus from './themes/cactus'
import classic5 from './themes/classic5'
import classic6 from './themes/classic6'
import classic7 from './themes/classic7'
import dark3 from './themes/dark3'
import dark4 from './themes/dark4'
import darkNightLceBlade from './themes/darkNightLceBlade'
import index from './themes/index'
import lemonBubbles from './themes/lemonBubbles'
import morandi from './themes/morandi'
import neonLamp from './themes/neonLamp'
import oreo from './themes/oreo'
import rose from './themes/rose'
import seaBlueLine from './themes/seaBlueLine'
import shallowSea from './themes/shallowSea'

// 注册插件
MindMap.usePlugin(MiniMap)
  .usePlugin(Watermark)
  .usePlugin(Drag)
  .usePlugin(KeyboardNavigation)
  .usePlugin(ExportPDF)
  .usePlugin(ExportXMind)
  .usePlugin(Export)
  .usePlugin(Select)
  .usePlugin(AssociativeLine)
  .usePlugin(NodeImgAdjust)
  .usePlugin(TouchEvent)
  .usePlugin(SearchPlugin)
  .usePlugin(Painter)
  .usePlugin(Formula)
  .usePlugin(RainbowLines)
  .usePlugin(Demonstrate)
  .usePlugin(OuterFrame)

MindMap.defineTheme('cactus', cactus)
MindMap.defineTheme('classic5', classic5)
MindMap.defineTheme('classic6', classic6)
MindMap.defineTheme('classic7', classic7)
MindMap.defineTheme('dark3', dark3)
MindMap.defineTheme('dark4', dark4)
MindMap.defineTheme('darkNightLceBlade', darkNightLceBlade)
MindMap.defineTheme('index', index)
MindMap.defineTheme('lemonBubbles', lemonBubbles)
MindMap.defineTheme('morandi', morandi)
MindMap.defineTheme('neonLamp', neonLamp)
MindMap.defineTheme('oreo', oreo)
MindMap.defineTheme('rose', rose)
MindMap.defineTheme('seaBlueLine', seaBlueLine)
MindMap.defineTheme('shallowSea', shallowSea)

let mindMapInstance = null

window.mindMapInstance = mindMapInstance

export const defaultData = {
  data: {
    text: '根节点',
    expand: true,
    isActive: true,
  },
  children: [],
}

export default {
  props: {
    readonly: {
      type: Boolean,
      default: false,
    },
    name: {
      type: String,
      default: '思维导图',
    },
    mindData: {
      type: Object,
      default() {
        return defaultData
      },
    },
  },
  data() {
    return {
      nodeIconList,
      scale: 1,
      theme: 'classic4',
      //  default(默认)、classic(脑图经典)、minions(小黄人)、
      // pinkGrape(粉红葡萄)、mint(薄荷)、gold(金色vip)、vitalityOrange(活力橙)、
      // greenLeaf(绿叶)、dark2(暗色2)、skyGreen(天清绿)、classic2(脑图经典2)、
      // classic3(脑图经典3)、classic4(脑图经典4,v0.2.0+)、classicGreen(经典绿)、
      // classicBlue(经典蓝)、blueSky(天空蓝)、brainImpairedPink(脑残粉)、
      // dark(暗色)、earthYellow(泥土黄)、freshGreen(清新绿)、freshRed(清新红)、
      // romanticPurple(浪漫紫)、simpleBlack(v0.5.4+简约黑)、courseGreen(v0.5.4+课程绿)、
      // coffee(v0.5.4+咖啡)、redSpirit(v0.5.4+红色精神)、blackHumour(v0.5.4+黑色幽默)、
      // lateNightOffice(v0.5.4+深夜办公室)、blackGold(v0.5.4+黑金)、avocado(v.5.10-fix.2+牛油果)、
      // autumn(v.5.10-fix.2+秋天)、orangeJuice(v.5.10-fix.2+橙汁)
      themes: [
        { value: 'classic4', label: '脑图经典4' },
        { value: 'classic5', label: '脑图经典5' },
        { value: 'classic6', label: '脑图经典6' },
        { value: 'classic7', label: '脑图经典7' },
        { value: 'autumn', label: '秋天' },
        { value: 'cactus', label: '仙人掌' },
        { value: 'lemonBubbles', label: '柠檬气泡' },
        { value: 'coffee', label: '咖啡' },
        { value: 'courseGreen', label: '课程绿' },
        { value: 'gold', label: '金色vip' },
        { value: 'greenLeaf', label: '绿叶' },
        { value: 'minions', label: '小黄人' },
        { value: 'morandi', label: '莫兰迪' },
        { value: 'redSpirit', label: '红色精神' },
        { value: 'simpleBlack', label: '简约黑' },
        { value: 'vitalityOrange', label: '活力橙' },
        { value: 'oreo', label: '奥利奥' },
        { value: 'rose', label: '玫瑰' },
        { value: 'seaBlueLine', label: '海蓝线' },
        { value: 'shallowSea', label: '浅海' },
      ],
      layout: 'mindMap',
      layouts: [
        { value: 'mindMap', label: '思维导图' },
        { value: 'logicalStructure', label: '逻辑结构图' },
        { value: 'organizationStructure', label: '组织结构图' },
        { value: 'catalogOrganization', label: '目录组织图' },
        { value: 'timeline', label: '时间轴' },
        { value: 'timeline2', label: '时间轴2' },
        { value: 'fishbone', label: '鱼骨图' },
        { value: 'verticalTimeline', label: '垂直时间轴' },
      ],
      activeNodes: [],
      currentIconList: [],
      activedPanel: '',
    }
  },
  computed: {
    showScale() {
      return (this.scale * 100).toFixed(0)
    },
  },
  mounted() {
    this.init()
  },
  unmounted() {
    mindMapInstance.destroy()
  },
  watch: {
    mindData() {
      mindMapInstance.destroy()
      this.init()
    },
  },
  methods: {
    handleChange(file) {
      const that = this
      ElMessageBox.confirm('是否直接替换当前思维导图?', '警告', {
        confirmButtonText: '确认',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          const { raw } = file
          xmind.parseXmindFile(raw).then(data => {
            that.setData(data)
          })
        })
        .catch(() => {})
    },
    activePanel(type) {
      this.activedPanel = type === this.activedPanel ? '' : type
    },
    setIcon(type, name) {
      if (this.activeNodes.length === 0) return
      let key = type + '_' + name
      // 检查当前节点是否存在该图标
      let index = this.currentIconList.findIndex(item => {
        return item === key
      })
      // 存在则删除icon
      if (index !== -1) {
        this.currentIconList.splice(index, 1)
      } else {
        // 否则判断当前图标是否和要插入的图标是一个组的
        let typeIndex = this.currentIconList.findIndex(item => {
          return item.split('_')[0] === type
        })
        // 是一个组的则进行替换
        if (typeIndex !== -1) {
          this.currentIconList.splice(typeIndex, 1, key)
        } else {
          // 否则添加icon
          this.currentIconList.push(key)
        }
      }
      this.activeNodes.forEach(node => {
        node.setIcon([...this.currentIconList])
      })
    },
    addRelationLine() {
      mindMapInstance.createLineFromActiveNode()
    },
    addGeneralization() {
      mindMapInstance.execCommand('ADD_GENERALIZATION')
    },
    dropNode() {
      mindMapInstance.execCommand('REMOVE_NODE')
    },
    addNode() {
      mindMapInstance.execCommand('INSERT_NODE')
    },
    addChildNode() {
      mindMapInstance.execCommand('INSERT_CHILD_NODE')
    },
    changeTheme(theme) {
      this.theme = theme
      mindMapInstance.setTheme(theme)
    },
    changeLayout(layout) {
      this.layout = layout
      mindMapInstance.setLayout(layout)
    },
    zoomMap(e) {
      mindMapInstance.view.setScale(e)
    },
    zoomIn() {
      let scale = mindMapInstance.view.scale
      scale += 0.2
      if (scale > 2) scale = 2
      mindMapInstance.view.setScale(scale)
    },
    zoomOut() {
      let scale = mindMapInstance.view.scale
      scale -= 0.2
      if (scale < 0.1) scale = 0.1
      mindMapInstance.view.setScale(scale)
    },
    getData() {
      return mindMapInstance.getData(true)
    },
    setData(data) {
      mindMapInstance.setData(data)
    },
    exportPng() {
      mindMapInstance.export('png', true, this.name)
    },
    init() {
      const data = this.mindData || defaultData
      let mindData = data
      if (data.root) {
        this.layout = data.layout
        this.theme = data.theme.template
        mindData = data.root
      }
      mindMapInstance = new MindMap({
        enableFreeDrag: false,
        readonly: this.readonly,
        layout: this.layout, // 'logicalStructure',
        theme: this.theme,
        el: document.getElementById('mindMapContainer'),
        mousewheelAction: 'zoom', // zoom(放大缩小)、move(上下移动)
        data: mindData,
        fit: true,
        nodeTextEditZIndex: 1000,
        nodeNoteTooltipZIndex: 1000,
        initRootNodePosition: ['center', 'center'],
      })
      mindMapInstance.on('scale', e => {
        this.scale = Number(e.toFixed(2))
      })
      mindMapInstance.on('node_active', (...args) => {
        this.activeNodes = args[1]
        if (this.activeNodes.length > 0) {
          let firstNode = this.activeNodes[0]
          this.currentIconList = firstNode.getData('icon') || []
        } else {
          this.currentIconList = []
        }
      })
    },
  },
}
</script>

<style lang="scss" scoped>
.mind-map-container,
#mindMapContainer {
  width: 100%;
  height: 100%;
  overflow: hidden;
  position: relative;
}
.my-button {
  svg {
    width: 1.5rem;
    height: 1.5rem;
  }
  width: 6rem;
  height: 3.8rem;
  text-align: center;
  line-height: 1.5rem;
  margin-top: 0.8rem;
}
.mindmap-tools {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 1000;
  display: flex;
  flex-direction: row;
  justify-content: center;
  width: 100%;
  align-items: center;
}

$iconSize: 1.4rem;

.active-panel {
  position: absolute;
  top: 10rem;
  right: 5px;
  bottom: 10rem;
  width: 16rem;
  background: white;
  user-select: none;
  .active-panel-box {
    height: 100%;
    .title {
      height: 3rem;
      line-height: 3rem;
      padding: 0 0.8rem;
      font-weight: bold;
      font-size: 1.1rem;
      border-bottom: 1px solid #ccc;
      .close {
        float: right;
        cursor: pointer;
        font-size: 1.3rem;
      }
    }
    .content {
      height: calc(100% - 3rem);
      overflow-y: auto;
      overflow-x: hidden;
      padding: 1rem;
      &.icon {
        padding-right: 0.5rem;
      }

      .icon-group {
        margin-top: 1rem;
        line-height: 2;
        &:first-child {
          margin-top: 0;
        }
      }
      .group-title {
        font-weight: bold;
      }
      .group-content {
        display: flex;
        flex-direction: row;
        flex-wrap: wrap;
        .icon-item {
          width: $iconSize;
          height: $iconSize;
          margin-right: 0.4rem;
          margin-top: 0.4rem;
          cursor: pointer;
          &.disabled {
            cursor: not-allowed;
            opacity: 0.5;
          }
          svg {
            width: $iconSize;
            height: $iconSize;
          }
        }
      }

      li {
        list-style: none;
        text-align: center;
        margin-top: 1rem;
        border: 1px solid #ccc;
        border-radius: 4px;
        &:first-child {
          margin-top: 0;
        }
        &:hover {
          cursor: pointer;
          box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.2);
        }
        &.active {
          border: 1px solid #67c23a;
          box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.2);
        }
        img {
          width: 100%;
          height: 8rem;
        }
      }
    }
  }
}
.mind-map-scale {
  position: absolute;
  bottom: -1rem;
  right: 1rem;
  z-index: 99;
  background: #efefef;
  border-radius: 5px;
  opacity: 0.8;
}
:deep .mind-map-scale {
  .el-input__wrapper .el-input__inner {
    text-align: center !important;
  }
}
</style>

以上就是Vue使用mind-map实现在线思维导图的详细内容,更多关于Vue mind-map思维导图的资料请关注脚本之家其它相关文章!

相关文章

  • 详解Element 指令clickoutside源码分析

    详解Element 指令clickoutside源码分析

    这篇文章主要介绍了详解Element 指令clickoutside源码分析,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-02-02
  • Vue.js实现微信过渡动画左右切换效果

    Vue.js实现微信过渡动画左右切换效果

    这篇文章主要给大家介绍了利用Vue.js仿微信过渡动画左右切换效果的相关资料,需要用到的技术栈是Vue+Vuex。文中通过示例代码介绍的非常详细,对大家具一定的参考学习价值,需要的朋友们下面来一起看看吧。
    2017-06-06
  • 在Vue中解决跨域问题的常用方式

    在Vue中解决跨域问题的常用方式

    跨域问题是由浏览器引起的安全限制,而不是Vue框架本身导致的,Vue本身并不限制跨域访问,它只是一个前端框架,负责构建用户界面和处理数据逻辑,本文给大家介绍了在Vue中解决跨域问题的常用方式,需要的朋友可以参考下
    2023-10-10
  • vue实现给当前元素添加样式,其他元素无样式问题

    vue实现给当前元素添加样式,其他元素无样式问题

    这篇文章主要介绍了vue实现给当前元素添加样式,其他元素无样式问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-05-05
  • vue中v-show和v-if的异同及v-show用法

    vue中v-show和v-if的异同及v-show用法

    这篇文章主要介绍了vue中v-show和v-if的异同 ,通过代码详解v-show用法,本文给大家介绍的非常详细具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-06-06
  • element-table如何实现自定义表格排序

    element-table如何实现自定义表格排序

    这篇文章主要介绍了element-table如何实现自定义表格排序,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • vue3.0实践之写tsx语法实例

    vue3.0实践之写tsx语法实例

    很久不写博客了,最近在使用ts和tsx开发vue类项目,网上资料比较少,顺便记录一下方便同样开发的人互相学习共同进步,下面这篇文章主要给大家介绍了关于vue3.0实践之写tsx语法的相关资料,需要的朋友可以参考下
    2022-07-07
  • Vue.js实现文件上传压缩优化处理技巧

    Vue.js实现文件上传压缩优化处理技巧

    这篇文章主要介绍了Vue.js实现文件上传压缩优化处理,本文给大家介绍两种方法一种是借助canvas的封装的文件压缩上传,二是使用compressorjs第三方插件实现,本文给大家介绍的非常详细需要的朋友可以参考下
    2022-11-11
  • vue 如何使用vue-cropper裁剪图片你知道吗

    vue 如何使用vue-cropper裁剪图片你知道吗

    这篇文章主要为大家介绍了vue 使用vue-cropper裁剪图片,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2021-11-11
  • vue.js使用watch监听路由变化的方法

    vue.js使用watch监听路由变化的方法

    这篇文章主要介绍了vue.js使用watch监听路由变化的方法,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-07-07

最新评论