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

news2024/11/13 23:38:46

概述

在前面的文章Vue中实现在线画流程图实现中介绍了流程图的在线绘制,在本文,给大家分享一下基于mind-map实现在线的思维导图,并实现:1. 导图导出为图片;2. 打开xmind文件。

实现效果

图标选择

主题选择

结构选择

实现

1. mind-map简介

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

  • 在线文档:https://wanglin2.github.io/mind-map/#/index
  • 在线示例:https://wanglin2.github.io/mind-map/#/

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 { 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>

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

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

相关文章

文心一言《使用手册》,文心一言怎么用?

一、认识文心一言 &#xff08;一&#xff09;什么是文心一言 文心一言是百度研发的 人工智能大语言模型产品&#xff0c;能够通过上一句话&#xff0c;预测生成下一段话。 任何人都可以通过输入【指令】和文心一言进行对话互动、提出问题或要求&#xff0c;让文心一言高效地…

线程安全(六)AQS 的工作原理

目录 一、AQS 概述1.1 什么是 AQS?1.2 AQS 与 synchronized 区别:1.3 AQS 常见的实现类:二、AQS 的工作原理2.1 state 的用途:2.2 AQS 双向链表:2.3 ConditionObject 双向队列:2.4 总结:画图说明三、AQS 资源同步3.1 AQS 资源同步方式3.2 自定义同步器3.3 常见同步工具类…

简单记录一下在java的Mybatis-plus中用一个SQL语句查询一个嵌套的实体类(实体类中有List,List中还有List)

简单记录一下在java的Mybatis-plus中用一个SQL语句查询一个嵌套的实体类(实体类中有List,List中还有List) 也就是KpCourseInfoClientDetailVO课程详情类中含有List courseChapterList章节列表,然后章节列表中含有List lessonList课时列表,通过一个SQL查询得到结果 KpCourseIn…

对服务器进行基本了解(二)

目录 一. 云服务器数据库 1.查看MYSQL版本 2.查看mysql的运行状态 3.运行mysql 4. 进入mysql的用户 5. 更改用户密码 6. 查找mysql端口号 7. 创建一个数据库 8. 查看用户 9. 查看数据库 10. 显示数据库的表 11. 修改用户的host 12. 对用户赋权 13. 开放指定端…

2024Datawhale AI夏令营---基于术语词典干预的机器翻译挑战赛--学习笔记

#Datawhale #NLP 1.背景介绍&#xff1a; 机器翻译&#xff08;Machine Translation&#xff0c;简称MT&#xff09;是自然语言处理领域的一个重要分支&#xff0c;其目标是将一种语言的文本自动转换为另一种语言的文本。机器翻译的发展可以追溯到20世纪50年代&#xff0c;经历…

Redis的计数功能

Redis的学习专栏&#xff1a;http://t.csdnimg.cn/a8cvV 许多应用都会使用Redis作为计数的基本工具&#xff0c;可以实现快速计数、查询缓存的功能&#xff0c;同时数据也可以异步处理。例如&#xff1a;博客浏览&#xff0c;用户每查看一次&#xff0c;就会增加一次的访问量&a…

CSS-1_0 CSS和文档流

文章目录 CSS和文档流如何证明这个流的存在呢&#xff1f;流和display番外&#xff1a;inline-block 碎碎念 CSS和文档流 首先什么叫流呢&#xff1f; 通常来说&#xff0c;我们最终看到的网页是HTML文档中定义的各个元素挨个输出的结果&#xff0c;这种一个接一个输出的方式…

kettle从入门到精通 第七五课 ETL之kettle血缘,数据血缘

在了解kettle血缘之前&#xff0c;咱们先来了解下什么是数据血缘&#xff1f; 1、数据血缘定义&#xff08;来自gpt&#xff09; 数据血缘&#xff08;Data Lineage&#xff09;是指在数据管理和数据分析中追踪数据的源头、流向和处理过程的能力。具体来说&#xff0c;数据血…

MyBatis框架学习笔记(四):动态SQL语句、映射关系和缓存

1 动态 SQL 语句-更复杂的查询业务需求 1.1 动态 SQL-官方文档 &#xff08;1&#xff09;文档地址: mybatis – MyBatis 3 | 动态 SQL &#xff08;2&#xff09;为什么需要动态 SQL 动态 SQL 是 MyBatis 的强大特性之一 使用 JDBC 或其它类似的框架&#xff0c;根据不同条…

基于jeecgboot-vue3的Flowable流程同时支持bpmn流程设计器与仿钉钉流程设计器(全网首创)

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 1、新建流程的时候可以选择使用不同的流程设计器 2、选择bpmn流程设计器 3、选择仿钉钉流程设计器

百度“文心•跨模态大模型”又有新动态,支持内容分析时输出自定义标签库

大模型真正的价值在于应用。 一、基本概念 AI大模型具有强大的表征学习能力&#xff0c;能够在海量数据中提取有用的特征&#xff0c;为各种复杂任务提供解决方案。例如GPT-4o、BERT等模型的出现&#xff0c;不仅展示了大规模参数和复杂计算结构的优势&#xff0c;还在自然语…

django学习入门系列之第四点《案例 后台管理样例》

文章目录 往期回顾 前期准备&#xff1a; 导航新建&#xff0c;按钮表格 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><!-- 开发版本 --><link rel"stylesheet…

【操作系统】文件管理——文件的物理结构(个人笔记)

学习日期&#xff1a;2024.7.15 内容摘要&#xff1a;文件的物理结构&#xff0c;逻辑结构与物理结构 目录 引言 文件分配方式 连续分配 链接分配 隐式链接 显式链接 索引分配 索引块大小不够装入整个索引表怎么办&#xff1f; ①链接方案 ②多层索引 ③混合索引 …

5、 测试

这里写目录标题 1、自动化测试简介&#xff08;1&#xff09;自动化测试是什么&#xff08;2&#xff09;为什么要写测试测试节约你的时间发现错误&#xff0c;预防错误测试使得代码更有吸引力 2、基础测试策略3、开始写第一个测试&#xff08;1&#xff09;首先得有个bug&…

目标检测之单类别NMS

long time no see! 在目标检测中&#xff0c;常见的是多类别NMS&#xff0c;也就是只对相同类别的boxes来计算IOU&#xff1b;但现实场景中经常遇到同一个物体被识别成2个类别&#xff0c;也就是模型认为它既是类别1也是类别2.这时候通过多类别nms就过滤不掉这种重叠的框。所以…

Android Studio - adb.exe已停止运作的解决方案

adb.exe 是Android Debug Bridge 的缩写&#xff0c;它是Android SDK 中的一个调试工具&#xff0c;允许开发者通过命令行界面与设备进行交互&#xff0c;执行各种操作&#xff0c;如运行设备的shell、管理模拟器或设备的端口映射、在计算机和设备之间上传/下载文件、将本地APK…

元服务体验-服务发现

服务发现&#xff0c;无论线上或线下的方式都可以发现元服务。 线上&#xff1a;基于用户意图。从精准意图的搜索、用户事件触发的推荐到主动探索等场景。用户可以在设备的负一屏、全局搜索、应用市场、桌面等场景发现元服务。 线下&#xff1a;用户在 HarmonyOS Connect标签…

Flask启动5000端口后关不掉了?

事情是这样的&#xff1a; 使用python app.py启动flask应用后&#xff0c;又启动了另一个flask测试应用&#xff0c;也能启动成功&#xff0c;也没有报设么端口冲突&#xff0c;关闭黑窗口后&#xff0c;访问还是有守护进程在运行&#xff0c; 为什么我知道5000还在运行&#…

转型Web3开发第二课:Dapp开发入门基础 | 01 | 安装MetaMask

前言 完成了《转型 Web3 开发第一课》之后&#xff0c;得到了不少读者的认可&#xff0c;很多都在问什么时候开始下一课&#xff0c;近期终于抽出了时间开始搞起这第二课。 这第二课的主题为「Dapp开发入门基础」&#xff0c;即想要转型做 Dapp 开发的人员&#xff0c;不管是…

01数据结构 - 顺序表

这里是只讲干货不讲废话的炽念&#xff0c;这个系列的文章是为了我自己以后复习数据结构而写&#xff0c;所以可能会用一种我自己能够听懂的方式来描述&#xff0c;不会像书本上那么枯燥和无聊&#xff0c;且全系列的代码均是可运行的代码&#xff0c;关键地方会给出注释^_^ 全…