vue实现思维导图组件_思维导图构造大全

大家好,我是知秋君,一个会写博客吟诗的知秋码农。今天说一说vue实现思维导图组件_思维导图构造大全,希望能够帮助大家进步!!! 在项目中遇到组织架构或思维导图的需求,选的技术是 jsmind 官方文档给的示例,有需要的可以参考:示例 先来看看效果图: 查看完整代码 如想查看完整代码 :请访问 jsmind_demo 尝试了两种类型:第一张截图是 普通菜单 类型,第二张截图可以进行 右键菜单 操作

大家好,我是知秋君,一个会写博客吟诗的知秋码农。今天说一说vue实现思维导图组件_思维导图构造大全,希望能够帮助大家进步!!!

在项目中遇到组织架构或思维导图的需求,选的技术是 jsmind
官方文档给的示例,有需要的可以参考:示例
先来看看效果图:
在这里插入图片描述
在这里插入图片描述

查看完整代码

如想查看完整代码:请访问 jsmind_demo

尝试了两种类型:第一张截图是普通菜单类型,第二张截图可以进行右键菜单操作。最终选用了普通菜单类型。
jsmind 可以导入 jm 文件并渲染出来,也可以对编辑后的文件进行保存,也可以下载编辑好的思维导图,可以展开指定层级节点,修改主题,以及对节点进行增删改查等操作。
演示:

在这里插入图片描述

接下来如何使用:

安装

npm i jsmind // 或者 yarn add jsmind 
只听到从知秋君办公室传来知秋君的声音:

此中有真意,欲辨已忘言。有谁来对上联或下联?

普通菜单代码

此代码由一叶知秋网-知秋君整理
<template> <!-- 普通菜单 --> <div class="jsmind_layout"> <div class="jsmind_toolbar" v-if="showBar"> <el-upload class="pad" :multiple="false" ref="upload" action="action" :before-upload="beforeUpload" :http-request="upload"> <el-button type="primary" size="medium">导入</el-button> </el-upload> <el-button @click="save_nodearray_file" size="medium">保存</el-button> <el-button @click="screen_shot" size="medium">下载导图</el-button> <el-button @click="get_nodearray_data" size="medium">获取数据</el-button> <el-button @click="addNode" size="medium">新增节点</el-button> <el-button @click="addBrotherNode" size="medium">新增兄弟节点</el-button> <el-button @click="editNode" size="medium">编辑节点</el-button> <el-button @click="removeNode" size="medium">删除节点</el-button> <el-button @click="zoomIn" size="medium" :disabled="isZoomIn">放大</el-button> <el-button @click="zoomOut" size="medium" :disabled="isZoomOut" class="pad">缩小</el-button> <span>展开:</span> <el-select v-model="level" placeholder="展开节点" @change="expand_to_level" class="pad pad-left" size="medium"> <el-option v-for="item in nodeOptions" :key="item.value" :label="item.label" :value="item.value"> </el-option> </el-select> <span>主题:</span> <el-select v-model="localTheme" placeholder="选择主题" @change="set_theme" size="medium"> <el-option v-for="item in themeOptions" :key="item.value" :label="item.label" :value="item.value"> </el-option> </el-select> </div> <div id="jsmind_container" ref="container"> </div> <el-drawer title="编辑节点" :visible.sync="dialogVisible" size="500px"> <el-form label-width="80px" class="form-con"> <el-form-item label="字体大小"> <el-input-number controls-position="right" v-model.number="nodeOption.fontSize" class="ele-width" :min="1" :max="30" maxLength="2"></el-input-number> </el-form-item> <el-form-item label="字体粗细"> <el-select v-model="nodeOption.fontWeight" class="ele-width"> <el-option value="normal" label="常规"></el-option> <el-option value="bold" label="粗体"></el-option> <el-option value="bolder" label="更粗"></el-option> </el-select> </el-form-item> <el-form-item label="字体样式"> <el-select v-model="nodeOption.fontStyle" class="ele-width"> <el-option value="normal" label="标准"></el-option> <el-option value="italic" label="斜体"></el-option> <el-option value="oblique" label="倾斜"></el-option> </el-select> </el-form-item> <el-row> <el-col :span="12"> <el-form-item label="背景颜色"> <el-color-picker v-model="nodeOption.bgColor" show-alpha size="mini"></el-color-picker> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="字体颜色"> <el-color-picker v-model="nodeOption.fontColor" show-alpha size="mini"></el-color-picker> </el-form-item> </el-col> </el-row> <el-form-item label="节点内容"> <el-input type="textarea" :rows="2" v-model="nodeOption.content" class="ele-width" maxLength="64"></el-input> </el-form-item> </el-form> <template v-slot:footer> <div class="right mr-10"> <el-button type="primary" class="common-btn" @click="sureEditNode" size="medium">确 定</el-button> </div> </template> </el-drawer> </div> </template>

<script> import 'jsmind/style/jsmind.css' import jsMind from 'jsmind/js/jsmind.js' window.jsMind = jsMind require('jsmind/js/jsmind.draggable.js') require('jsmind/js/jsmind.screenshot.js') export default { 

props: {

showBar: {

// 是否显示工具栏,显示启用编辑 type: Boolean, default: true }, theme: {

// 主题 type: String, default: 'info' }, lineColor: {

// 线条颜色 type: String, default: 'skyblue' } }, data() {

return {

mind: {

}, jm: null, isZoomIn: false, isZoomOut: false, level: 0, nodeOptions: [ {

value: 1, label: '展开到一级节点' }, {

value: 2, label: '展开到二级节点' }, {

value: 3, label: '展开到三级节点' }, {

value: 0, label: '展开全部节点' }, {

value: -1, label: '隐藏全部节点' } ], themeOptions: [ {

value: 'default', label: 'default' }, {

value: 'primary', label: 'primary' }, {

value: 'warning', label: 'warning' }, {

value: 'danger', label: 'danger' }, {

value: 'success', label: 'success' }, {

value: 'info', label: 'info' }, {

value: 'greensea', label: 'greensea' }, {

value: 'nephrite', label: 'nephrite' }, {

value: 'belizehole', label: 'belizehole' }, {

value: 'wisteria', label: 'wisteria' }, {

value: 'asphalt', label: 'asphalt' }, {

value: 'orange', label: 'orange' }, {

value: 'pumpkin', label: 'pumpkin' }, {

value: 'pomegranate', label: 'pomegranate' }, {

value: 'clouds', label: 'clouds' }, {

value: 'asbestos', label: 'asbestos' } ], localTheme: this.theme, dialogVisible: false, nodeOption: {

content: '', bgColor: '', fontColor: '', fontSize: '', fontWeight: '', fontStyle: '' } } }, created() {

}, mounted() {

this.getData() this.mouseWheel() }, methods: {

beforeUpload (file) {

// 上传文件之前钩子 if (file) {

jsMind.util.file.read(file, (jsmindData) => {

const mind = jsMind.util.json.string2json(jsmindData) if (mind) {

this.jm.show(mind) this.$message({

type: 'success', message: '打开成功' }) } else {

this.prompt_info('不能打开mindmap文件') } }) } else {

this.prompt_info('请先选择文件') return false } }, upload() {

}, getData() {

this.$API({

name: 'getMind' }).then(res => {

this.mind = res.data this.open_empty() }).catch(error => {

this.$message.error(error) }) }, open_empty() {

const options = {

container: 'jsmind_container', // 必选,容器ID editable: this.showBar, // 可选,是否启用编辑 theme: this.localTheme, // 可选,主题 view: {

line_width: 2, // 思维导图线条的粗细 line_color: this.lineColor // 思维导图线条的颜色 }, shortcut: {

enable: true // 禁用快捷键 }, layout: {

hspace: 50, // 节点之间的水平间距 vspace: 20, // 节点之间的垂直间距 pspace: 13 // 节点与连接线之间的水平间距(用于容纳节点收缩/展开控制器) }, mode: 'side' // 显示模式,子节点只分布在根节点右侧 } this.jm = jsMind.show(options, this.mind) // 改变窗口大小重置画布 window.onresize = () => {

this.jm.resize() } }, save_nodearray_file() {

const mindData = this.jm.get_data('node_array') const mindName = mindData.meta.name const mindStr = jsMind.util.json.json2string(mindData) jsMind.util.file.save(mindStr, 'text/jsmind', mindName + '.jm') }, screen_shot() {

this.jm.screenshot.shootDownload() }, expand_all() {

this.jm.expand_all() }, collapse_all() {

this.jm.collapse_all() }, expand_to_level(num) {

switch (num) {

case -1: this.collapse_all() break case 0: this.expand_all() break default: this.jm.expand_to_depth(num) break } }, zoomIn() {

if (this.jm.view.zoomIn()) {

this.isZoomOut = false } else {

this.isZoomIn = true } }, zoomOut() {

if (this.jm.view.zoomOut()) {

this.isZoomIn = false } else {

this.isZoomOut = true } }, prompt_info(msg) {

this.$message({

type: 'warning', message: msg}) }, get_nodearray_data() {

const mindData = this.jm.get_data('node_array') const mindString = jsMind.util.json.json2string(mindData) this.$message({

type: 'info', message: mindString}) }, set_theme(themeName) {

this.jm.set_theme(themeName) }, scrollFunc(e) {

e = e || window.event if (e.wheelDelta) {

if (e.wheelDelta > 0) {

this.zoomIn() } else {

this.zoomOut() } } else if (e.detail) {

if (e.detail > 0) {

this.zoomIn() } else {

this.zoomOut() } } this.jm.resize() }, // 鼠标滚轮放大缩小 mouseWheel() {

if (document.addEventListener) {

document.addEventListener('domMouseScroll', this.scrollFunc, false) } this.$refs.container.onmousewheel = this.scrollFunc }, // 新增节点 addNode() {

let selectedNode = this.jm.get_selected_node() if (!selectedNode) {

this.$message({

type: 'warning', message: '请先选择一个节点!'}) return } let nodeid = jsMind.util.uuid.newid() let topic = 'new Node' let newNode = this.jm.add_node(selectedNode, nodeid, topic) if (newNode) {

this.jm.select_node(nodeid) this.jm.begin_edit(nodeid) } }, // 新增兄弟节点 addBrotherNode() {

let selectedNode = this.jm.get_selected_node() if (!selectedNode) {

this.$message({

type: 'warning', message: '请先选择一个节点!'}) return } else if (selectedNode.isroot) {

this.$message({

type: 'warning', message: '不能在根节点添加,请重新选择节点!'}) return } let nodeid = jsMind.util.uuid.newid() let topic = 'new Node' let newNode = this.jm.insert_node_after(selectedNode, nodeid, topic) if (newNode) {

this.jm.select_node(nodeid) this.jm.begin_edit(nodeid) } }, // 获取选中标签的 ID get_selected_nodeid () {

let selectedNode = this.jm.get_selected_node() if (selectedNode) {

return selectedNode.id } else {

return null } }, // 删除节点 removeNode() {

let selectedId = this.get_selected_nodeid() if (!selectedId) {

this.$message({

type: 'warning', message: '请先选择一个节点!' }) return } this.jm.remove_node(selectedId) }, // 编辑节点 editNode () {

let selectedId = this.get_selected_nodeid() if (!selectedId) {

this.$message({

type: 'warning', message: '请先选择一个节点!'}) return } let nodeObj = this.jm.get_node(selectedId) this.nodeOption.content = nodeObj.topic this.nodeOption.bgColor = nodeObj.data['background-color'] this.nodeOption.fontColor = nodeObj.data['foreground-color'] this.nodeOption.fontSize = nodeObj.data['font-size'] this.nodeOption.fontWeight = nodeObj.data['font-weight'] this.nodeOption.fontStyle = nodeObj.data['font-style'] this.dialogVisible = true }, sureEditNode () {

let selectedId = this.get_selected_nodeid() this.jm.update_node(selectedId, this.nodeOption.content) this.jm.set_node_font_style(selectedId, this.nodeOption.fontSize, this.nodeOption.fontWeight, this.nodeOption.fontStyle) this.jm.set_node_color(selectedId, this.nodeOption.bgColor, this.nodeOption.fontColor) this.nodeOption = {

content: '', bgColor: '', fontColor: '', fontSize: '', fontWeight: '', fontStyle: '' } this.dialogVisible = false } }, beforeDestroy() {

document.removeEventListener('domMouseScroll', this.scrollFunc, false) } } </script>

此代码由一叶知秋网-知秋君整理
<style lang="less" scoped> .jsmind_layout {

display: flex; flex-direction: column; width: 100%; height: calc(100% - 40px); overflow: hidden; .jsmind_toolbar {

width: 100%; padding: 0 10px 10px 10px; height: auto; flex-shrink: 0; display: flex; align-items: center; flex-wrap: wrap; background-color: #f8f9fa; box-shadow: 0 0 4px #b8b8b8; } /deep/ .el-button--medium, /deep/ .el-input--medium {

margin-top: 10px; } #jsmind_container {

flex: 1 1 auto; } /deep/.jsmind-inner {

overflow: hidden auto !important; } /deep/.el-upload-list {

display: none !important; } /* 隐藏滚动条 */ .jsmind-inner::-webkit-scrollbar {

display: none; } .pad {

margin-right: 10px; } .pad-left {

margin-left: 10px; } /deep/ jmnode.selected {

background-color: #b9b9b9; color: #fff; box-shadow: 2px 2px 8px #777; } /deep/ jmnode:hover {

box-shadow: 2px 2px 8px #777; } .form-con {

padding-top:20px; } .ele-width {

width: 96%; } } </style>

右键菜单代码

<template> <!-- 右键菜单 --> <div class="jsmind_layout"> <div class="jsmind_toolbar" v-if="showBar"> <el-upload class="pad" :multiple="false" ref="upload" action="action" :before-upload="beforeUpload" :http-request="upload"> <el-button type="primary" size="medium">导入</el-button> </el-upload> <el-button @click="save_nodearray_file" size="medium">保存</el-button> <el-button @click="screen_shot" size="medium">下载导图</el-button> <el-button @click="get_nodearray_data" size="medium">获取数据</el-button> <span class="pad-left">展开:</span> <el-select v-model="level" placeholder="展开节点" @change="expand_to_level" class="pad pad-left"> <el-option v-for="item in nodeOptions" :key="item.value" :label="item.label" :value="item.value"> </el-option> </el-select> <span>主题:</span> <el-select v-model="theme" placeholder="选择主题" @change="set_theme"> <el-option v-for="item in themeOptions" :key="item.value" :label="item.label" :value="item.value"> </el-option> </el-select> </div> <div id="jsmind_container" ref="container"> <div class="zoom_in_out"> <span><i class="zoom-icon el-icon-plus" @click="zoomIn" :class="isZoomIn === true ? 'disabled' : ''"></i></span> <span><i class="zoom-icon el-icon-minus" @click="zoomOut" :class="isZoomOut === true ? 'disabled' : ''"></i></span> </div> </div> </div> </template> 

<script> import 'jsmind/style/jsmind.css' import jsMind from 'jsmind/js/jsmind.js' window.jsMind = jsMind const { 

init, reBuild } = require('@/assets/js/jsmind.menu.js') require('jsmind/js/jsmind.draggable.js') require('jsmind/js/jsmind.screenshot.js') init(jsMind) export default {

props: {

showBar: {

// 是否显示工具栏 type: Boolean, default: true }, isEdit: {

// 是否启用编辑,启用编辑可以显示右键功能 type: Boolean, default: true } }, data() {

return {

mind: {

}, jm: null, isZoomIn: false, isZoomOut: false, level: 0, nodeOptions: [ {

value: 1, label: '展开到一级节点' }, {

value: 2, label: '展开到二级节点' }, {

value: 3, label: '展开到三级节点' }, {

value: 4, label: '展开到四级节点' }, {

value: 0, label: '展开全部节点' }, {

value: -1, label: '隐藏全部节点' } ], themeOptions: [ {

value: 'default', label: 'default' }, {

value: 'primary', label: 'primary' }, {

value: 'warning', label: 'warning' }, {

value: 'danger', label: 'danger' }, {

value: 'success', label: 'success' }, {

value: 'info', label: 'info' }, {

value: 'greensea', label: 'greensea' }, {

value: 'nephrite', label: 'nephrite' }, {

value: 'belizehole', label: 'belizehole' }, {

value: 'wisteria', label: 'wisteria' }, {

value: 'asphalt', label: 'asphalt' }, {

value: 'orange', label: 'orange' }, {

value: 'pumpkin', label: 'pumpkin' }, {

value: 'pomegranate', label: 'pomegranate' }, {

value: 'clouds', label: 'clouds' }, {

value: 'asbestos', label: 'asbestos' } ], theme: 'success' } }, created() {

}, mounted() {

this.getData() this.mouseWheel() }, methods: {

beforeUpload (file) {

// 上传文件之前钩子 if (file) {

jsMind.util.file.read(file,(jsmind_data) => {

const mind = jsMind.util.json.string2json(jsmind_data) if(mind){

this.jm.show(mind) reBuild() }else{

this.prompt_info('can not open this file as mindmap') } }) } else {

this.prompt_info('please choose a file first') return false } }, upload() {

}, getData() {

this.$API({

name: 'getMind' }).then(res => {

this.mind = res.data this.open_empty() }).catch(error => {

this.$message.error(error) }) }, open_empty() {

const options = {

container: 'jsmind_container', // 必选,容器ID editable: this.isEdit, // 可选,是否启用编辑 theme: this.theme, // 可选,主题 view: {

line_width: 2, // 思维导图线条的粗细 line_color: 'skyblue' // 思维导图线条的颜色 }, shortcut: {

enable: false // 禁用快捷键 }, mode: 'side', // 显示模式,子节点只分布在根节点右侧 menuOpts:{

// 这里加入一个专门配置menu的对象 showMenu: this.isEdit, //showMenu 为 true 则打开右键功能 ,反之关闭 injectionList: [ {

target: 'edit', text: '编辑节点' }, {

target: 'delete', text: '删除节点' }, {

target: 'addChild', text: '添加子节点' }, {

target: 'addBrother', text: '添加兄弟节点' } ], style: {

menuItem:{

'line-height': '28px' } } } } this.jm = jsMind.show(options, this.mind) // 改变窗口大小重置画布 window.onresize = () => {

this.jm.resize() } }, save_nodearray_file(){

const mind_data = this.jm.get_data('node_array') const mind_name = mind_data.meta.name const mind_str = jsMind.util.json.json2string(mind_data) jsMind.util.file.save(mind_str, 'text/jsmind', mind_name + '.jm') }, screen_shot(){

this.jm.screenshot.shootDownload() }, expand_all(){

this.jm.expand_all() }, collapse_all(){

this.jm.collapse_all() }, expand_to_level(num){

switch(num) {

case -1: this.collapse_all() break case 0: this.expand_all() break default: this.jm.expand_to_depth(num) break } }, zoomIn() {

if (this.jm.view.zoomIn()) {

this.isZoomOut = false } else {

this.isZoomIn = true } }, zoomOut() {

if (this.jm.view.zoomOut()) {

this.isZoomIn = false } else {

this.isZoomOut = true } }, prompt_info(msg){

alert(msg) }, get_nodearray_data() {

const mind_data = this.jm.get_data('node_array') const mind_string = jsMind.util.json.json2string(mind_data) this.prompt_info(mind_string) }, set_theme(theme_name){

this.jm.set_theme(theme_name) }, scrollFunc(e) {

e = e || window.event if (e.wheelDelta) {

if (e.wheelDelta > 0) {

this.zoomIn() } else {

this.zoomOut() } } else if (e.detail) {

if (e.detail > 0) {

this.zoomIn() } else {

this.zoomOut() } } this.jm.resize() }, // 鼠标滚轮放大缩小 mouseWheel() {

if (document.addEventListener) {

document.addEventListener('domMouseScroll', this.scrollFunc, false) } this.$refs.container.onmousewheel = this.scrollFunc } }, beforeDestroy() {

document.removeEventListener('domMouseScroll', this.scrollFunc, false) } } </script>

<style lang="less"> .jsmind_layout { 

position: relative; display: flex; flex-direction: column; width: 100%; height: calc(100% - 40px); overflow: hidden; /deep/ .el-button--medium, /deep/ .el-input--medium {

margin-top: 10px; } } jmnode.selected {

background-color: #b9b9b9 !important; box-shadow: 2px 2px 8px #777 !important; } jmnode:hover {

box-shadow: 2px 2px 8px #777 !important; } .jsmind_toolbar {

width: 100%; padding: 10px; height: auto; flex-shrink: 0; display: flex; align-items: center; flex-wrap: wrap; background-color: #f8f9fa; box-shadow: 0 0 4px #b8b8b8; } #jsmind_container {

flex: 1 1 auto; position: relative; } .jsmind-inner {

overflow: hidden auto !important; } .el-upload-list {

display: none !important; } .zoom_in_out {

position: absolute; bottom: 20px; right: 20px; height: auto; display: flex; align-items: center; flex-direction: column; padding: 5px; background-color: #fff; border-radius: 4px; box-shadow: 2px 2px 4px #e0e0e0; border: 1px solid #f5f5f5; z-index: 999; } .zoom-icon {

cursor: pointer; font-size: 20px; padding: 6px 5px; } .disabled {

color: #bdbcbc; cursor: not-allowed; } .el-icon-plus {

border-bottom: 1px solid #9f9f9f; } /* 隐藏滚动条 */ .jsmind-inner::-webkit-scrollbar {

display: none; } .pad {

margin-right: 10px; } .pad-left {

margin-left: 10px; } </style>
知秋君
上一篇 2024-07-03 15:31
下一篇 2024-07-03 15:32

相关推荐