diff --git a/.gitignore b/.gitignore
index 05223c9f..25c8fdba 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,2 @@
node_modules
-oss.js
+package-lock.json
\ No newline at end of file
diff --git a/README.md b/README.md
index f5c09125..a201eff4 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,36 @@
-开发中...
\ No newline at end of file
+# web思维导图的简单实现
+
+开发中...
+
+## 目录介绍
+
+1.simple-mind-map
+
+思维导图工具包。
+
+2.web
+
+使用`simple-mind-map`工具包搭建的在线思维导图。
+
+## 开发
+
+本地开发
+
+```bash
+git clone https://github.com/wanglin2/mind-map.git
+cd simple-mind-map
+npm i
+npm link
+cd ..
+cd web
+npm i
+npm link simple-mind-map
+npm run serve
+```
+
+打包
+
+```bash
+cd web
+npm run build
+```
\ No newline at end of file
diff --git a/docs/assets/swdt.jpg b/docs/assets/swdt.jpg
new file mode 100644
index 00000000..1b97cafe
Binary files /dev/null and b/docs/assets/swdt.jpg differ
diff --git a/docs/web思维导图实现的技术点分析.md b/docs/web思维导图实现的技术点分析.md
new file mode 100644
index 00000000..63890a62
--- /dev/null
+++ b/docs/web思维导图实现的技术点分析.md
@@ -0,0 +1,93 @@
+
+
+# 简介
+
+思维导图是一种常见的表达发散性思维的有效工具,市面上有非常多的工具可以用来画思维导图,百度一下,整页都是广告可供选择,此外也有一些可以用来帮助快速实现的`JavaScript`类库,如:[jsMind](https://github.com/hizzgdev/jsmind)、[KityMinder](https://github.com/fex-team/kityminder)。
+
+本文会介绍如何从头实现一个简易的思维导图。
+
+
+
+# 技术选型
+
+这种图形类的绘制一般有两种选择,`svg`与`canvas`,因为思维导图主要是节点与线的连接,使用与`html`比较接近的`svg`比较好操作,`svg`类库也有挺多,在试用了[svgjs](https://svgjs.dev/docs/3.0/)和[snap](http://snapsvg.io/)后,有些需求在`snap`里没有找到对应的方法,所以最终选择了`svgjs`,视图库使用的是`vue2.x`全家桶。
+
+
+
+# 数据结构
+
+这里主要指每个节点的数据结构,大概需要包含是否是根节点、节点层级、节点内容(包括文本、图片、图标等固定格式)、节点展开状态、子节点、父节点等等,此外还包括该节点的特定样式,用来覆盖主题的默认样式:
+
+```js
+
+```
+
+每次操作都会修改这份配置数据,然后整体刷新,有点数据驱动的意思,好处很明显,只用维护数据就行了,不用陷入对视图的操作。
+
+
+
+# 逻辑结构图
+
+思维导图常见的有几种变种,我们先看最基础的【逻辑结构图】如何布局,其他的可以在末尾小节查看。
+
+
+
+## 节点定位
+
+
+
+## 节点连线
+
+
+
+# 支持图片、图标
+
+
+
+# 展开收缩
+
+
+
+# 文字编辑
+
+# 拖动、放大缩小
+
+# 主题
+
+# 节点样式编辑
+
+# 快捷键
+
+快捷键就是监听了到特定的按键来执行特定的操作,包含单个按键和组合键,我们可以使用一个对象来保存快捷键和对应的命令,`key`代表按键,`value`代表要执行的命令,比如:
+
+```js
+const shortcutKeys = {
+ 'enter': 'addSiblingNode',
+ 'ctrl+b': 'bold'
+}
+```
+
+包含两种类型,单个按键、以`+`拼接的组合键,接下来只要监听`keydown`事件来检查即可,首先要说明的是组合键一般指的是`ctrl`、`alt`、`shift`
+
+
+
+# 实现过渡效果
+
+# 回退
+
+# 导入导出、其他格式
+
+https://github.com/canvg/canvg
+
+https://github.com/fex-team/kityminder/tree/dev/src/protocol
+
+https://github.com/fex-team/kityminder/tree/dev/native-support
+
+json、freemind、xmind
+
+png、svg
+
+# 其他几种变种结构
+
+逻辑结构图、鱼骨图、思维导图、组织结构图、目录组织图
+
diff --git a/src/package/mind-map/.DS_Store b/simple-mind-map/.DS_Store
similarity index 100%
rename from src/package/mind-map/.DS_Store
rename to simple-mind-map/.DS_Store
diff --git a/src/package/mind-map/example/exampleData.js b/simple-mind-map/example/exampleData.js
similarity index 100%
rename from src/package/mind-map/example/exampleData.js
rename to simple-mind-map/example/exampleData.js
diff --git a/src/package/mind-map/index.js b/simple-mind-map/index.js
similarity index 90%
rename from src/package/mind-map/index.js
rename to simple-mind-map/index.js
index 90fc234c..c774c823 100644
--- a/src/package/mind-map/index.js
+++ b/simple-mind-map/index.js
@@ -1,174 +1,185 @@
-import View from './src/View'
-import Event from './src/Event'
-import Render from './src/Render'
-import merge from 'deepmerge'
-import theme from './src/themes'
-import Style from './src/Style'
-import KeyCommand from './src/KeyCommand'
-import Command from './src/Command';
-import { SVG } from '@svgdotjs/svg.js'
-
-const defaultOpt = {
- // 布局
- layout: 'logicalStructure',
- // 放大缩小的增量比例,即step = scaleRatio * width|height
- scaleRatio: 0.1,
- // 主题
- theme: 'default',// 内置主题:default(默认主题)
- // 主题配置,会和所选择的主题进行合并
- themeConfig: {}
-}
-
-/**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-06 11:18:47
- * @Desc: 思维导图
- */
-class MindMap {
- /**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-06 11:19:01
- * @Desc: 构造函数
- */
- constructor(opt = {}) {
- this.opt = merge(defaultOpt, opt)
- // 容器元素
- this.el = this.opt.el
- let {
- width,
- height
- } = this.el.getBoundingClientRect()
- // 画布宽高
- this.width = width
- this.height = height
- // 画笔
- this.draw = SVG().addTo(this.el).size(width, height)
- // 节点id
- this.uid = 0
-
- // 主题
- this.initTheme()
-
- // 事件类
- this.event = new Event({
- mindMap: this
- })
-
- // 按键类
- this.keyCommand = new KeyCommand({
- mindMap: this
- })
-
- // 命令类
- this.command = new Command({
- mindMap: this
- })
-
- // 渲染类
- this.renderer = new Render({
- mindMap: this
- })
-
- // 视图操作类
- this.view = new View({
- mindMap: this,
- draw: this.draw
- })
-
- this.render()
- setTimeout(() => {
- this.command.addHistory()
- }, 0);
- }
-
- /**
- * @Author: 王林
- * @Date: 2021-04-24 13:25:50
- * @Desc: 监听事件
- */
- on(event, fn) {
- this.event.on(event, fn)
- }
-
- /**
- * @Author: 王林
- * @Date: 2021-04-24 13:51:35
- * @Desc: 触发事件
- */
- emit(event, ...args) {
- this.event.emit(event, ...args)
- }
-
- /**
- * @Author: 王林
- * @Date: 2021-04-24 13:53:54
- * @Desc: 解绑事件
- */
- off(event, fn) {
- this.event.off(event, fn)
- }
-
- /**
- * @Author: 王林
- * @Date: 2021-05-05 13:32:43
- * @Desc: 设置主题
- */
- initTheme() {
- this.themeConfig = merge(this.opt.theme && theme[this.opt.theme] ? theme[this.opt.theme] : theme.default, this.opt.themeConfig)
- Style.setBackgroundStyle(this.el, this.themeConfig)
- }
-
- /**
- * @Author: 王林
- * @Date: 2021-05-05 13:52:08
- * @Desc: 设置主题
- */
- setTheme(theme) {
- this.opt.theme = theme
- this.render()
- }
-
- /**
- * @Author: 王林
- * @Date: 2021-05-05 13:50:17
- * @Desc: 设置主题配置
- */
- setThemeConfig(config) {
- this.opt.themeConfig = config
- this.render()
- }
-
- /**
- * @Author: 王林
- * @Date: 2021-05-05 14:01:29
- * @Desc: 获取某个主题配置值
- */
- getThemeConfig(prop) {
- return prop === undefined ? this.themeConfig : this.themeConfig[prop]
- }
-
- /**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-06 18:47:29
- * @Desc: 渲染节点
- */
- render() {
- this.draw.clear()
- this.initTheme()
- this.renderer.render()
- }
-
- /**
- * @Author: 王林
- * @Date: 2021-05-04 13:01:00
- * @Desc: 执行命令
- */
- execCommand(...args) {
- this.command.exec(...args)
- }
-}
-
+import View from './src/View'
+import Event from './src/Event'
+import Render from './src/Render'
+import merge from 'deepmerge'
+import theme from './src/themes'
+import Style from './src/Style'
+import KeyCommand from './src/KeyCommand'
+import Command from './src/Command';
+import {
+ SVG
+} from '@svgdotjs/svg.js'
+
+// 默认选项
+const defaultOpt = {
+ // 布局
+ layout: 'logicalStructure',
+ // 主题
+ theme: 'default', // 内置主题:default(默认主题)
+ // 主题配置,会和所选择的主题进行合并
+ themeConfig: {},
+ // 放大缩小的增量比例,即step = scaleRatio * width|height
+ scaleRatio: 0.1
+}
+
+/**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-06 11:18:47
+ * @Desc: 思维导图
+ */
+class MindMap {
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-06 11:19:01
+ * @Desc: 构造函数
+ */
+ constructor(opt = {}) {
+ // 合并选项
+ this.opt = merge(defaultOpt, opt)
+
+ // 容器元素
+ this.el = this.opt.el
+ let {
+ width,
+ height
+ } = this.el.getBoundingClientRect()
+
+ // 画布宽高
+ this.width = width
+ this.height = height
+
+ // 画笔
+ this.draw = SVG().addTo(this.el).size(width, height)
+
+ // 节点id
+ this.uid = 0
+
+ // 初始化主题
+ this.initTheme()
+
+ // 事件类
+ this.event = new Event({
+ mindMap: this
+ })
+
+ // 按键类
+ this.keyCommand = new KeyCommand({
+ mindMap: this
+ })
+
+ // 命令类
+ this.command = new Command({
+ mindMap: this
+ })
+
+ // 渲染类
+ this.renderer = new Render({
+ mindMap: this
+ })
+
+ // 视图操作类
+ this.view = new View({
+ mindMap: this,
+ draw: this.draw
+ })
+
+ // 初始渲染
+ this.renderer.render()
+ setTimeout(() => {
+ this.command.addHistory()
+ }, 0);
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-06 18:47:29
+ * @Desc: 渲染
+ */
+ render() {
+ this.draw.clear()
+ this.initTheme()
+ this.renderer.render()
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-24 13:25:50
+ * @Desc: 监听事件
+ */
+ on(event, fn) {
+ this.event.on(event, fn)
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-24 13:51:35
+ * @Desc: 触发事件
+ */
+ emit(event, ...args) {
+ this.event.emit(event, ...args)
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-24 13:53:54
+ * @Desc: 解绑事件
+ */
+ off(event, fn) {
+ this.event.off(event, fn)
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-05 13:32:43
+ * @Desc: 设置主题
+ */
+ initTheme() {
+ // 合并主题配置
+ this.themeConfig = merge(this.opt.theme && theme[this.opt.theme] ? theme[this.opt.theme] : theme.default, this.opt.themeConfig)
+ // 设置背景样式
+ Style.setBackgroundStyle(this.el, this.themeConfig)
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-05 13:52:08
+ * @Desc: 设置主题
+ */
+ setTheme(theme) {
+ this.opt.theme = theme
+ this.render()
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-05 13:50:17
+ * @Desc: 设置主题配置
+ */
+ setThemeConfig(config) {
+ this.opt.themeConfig = config
+ this.render()
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-05 14:01:29
+ * @Desc: 获取某个主题配置值
+ */
+ getThemeConfig(prop) {
+ return prop === undefined ? this.themeConfig : this.themeConfig[prop]
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-04 13:01:00
+ * @Desc: 执行命令
+ */
+ execCommand(...args) {
+ this.command.exec(...args)
+ }
+}
+
export default MindMap
\ No newline at end of file
diff --git a/simple-mind-map/package.json b/simple-mind-map/package.json
new file mode 100644
index 00000000..f645920c
--- /dev/null
+++ b/simple-mind-map/package.json
@@ -0,0 +1,11 @@
+{
+ "name": "simple-mind-map",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {},
+ "dependencies": {
+ "@svgdotjs/svg.js": "^3.0.16",
+ "deepmerge": "^1.5.2",
+ "eventemitter3": "^4.0.7"
+ }
+}
diff --git a/src/package/mind-map/src/Command.js b/simple-mind-map/src/Command.js
similarity index 96%
rename from src/package/mind-map/src/Command.js
rename to simple-mind-map/src/Command.js
index aa023b0c..bec96595 100644
--- a/src/package/mind-map/src/Command.js
+++ b/simple-mind-map/src/Command.js
@@ -1,4 +1,4 @@
-import { copyRenderTree, simpleDeepClone } from './Utils';
+import { copyRenderTree, simpleDeepClone } from './utils';
/**
* @Author: 王林
diff --git a/src/package/mind-map/src/Event.js b/simple-mind-map/src/Event.js
similarity index 96%
rename from src/package/mind-map/src/Event.js
rename to simple-mind-map/src/Event.js
index dab2c563..3f3ae06b 100644
--- a/src/package/mind-map/src/Event.js
+++ b/simple-mind-map/src/Event.js
@@ -1,149 +1,149 @@
-import EventEmitter from 'eventemitter3'
-
-/**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-07 14:53:09
- * @Desc: 事件类
- */
-class Event extends EventEmitter {
- /**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-07 14:53:25
- * @Desc: 构造函数
- */
- constructor(opt = {}) {
- super()
- this.opt = opt
- this.mindMap = opt.mindMap
- this.isMousedown = false
- this.mousedownPos = {
- x: 0,
- y: 0
- }
- this.mousemovePos = {
- x: 0,
- y: 0
- }
- this.mousemoveOffset = {
- x: 0,
- y: 0
- }
- this.bindFn()
- this.bind()
- }
-
- /**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-07 15:52:24
- * @Desc: 绑定函数上下文
- */
- bindFn() {
- this.onDrawClick = this.onDrawClick.bind(this)
- this.onMousedown = this.onMousedown.bind(this)
- this.onMousemove = this.onMousemove.bind(this)
- this.onMouseup = this.onMouseup.bind(this)
- this.onMousewheel = this.onMousewheel.bind(this)
- }
-
- /**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-07 14:53:43
- * @Desc: 绑定事件
- */
- bind() {
- this.mindMap.draw.on('click', this.onDrawClick)
- this.mindMap.el.addEventListener('mousedown', this.onMousedown)
- window.addEventListener('mousemove', this.onMousemove)
- window.addEventListener('mouseup', this.onMouseup)
- this.mindMap.el.addEventListener('mousewheel', this.onMousewheel)
- }
-
- /**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-07 15:40:51
- * @Desc: 解绑事件
- */
- unbind() {
- this.mindMap.el.removeEventListener('mousedown', this.onMousedown)
- window.removeEventListener('mousemove', this.onMousemove)
- window.removeEventListener('mouseup', this.onMouseup)
- this.mindMap.el.removeEventListener('mousewheel', this.onMousewheel)
- }
-
- /**
- * @Author: 王林
- * @Date: 2021-04-24 13:19:39
- * @Desc: 画布的单击事件
- */
- onDrawClick(e) {
- this.emit('draw_click', e)
- }
-
- /**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-07 15:17:35
- * @Desc: 鼠标按下事件
- */
- onMousedown(e) {
- e.preventDefault()
- this.isMousedown = true
- this.mousedownPos.x = e.clientX
- this.mousedownPos.y = e.clientY
- this.emit('mousedown', e, this)
- }
-
- /**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-07 15:18:32
- * @Desc: 鼠标移动事件
- */
- onMousemove(e) {
- e.preventDefault()
- this.mousemovePos.x = e.clientX
- this.mousemovePos.y = e.clientY
- this.mousemoveOffset.x = e.clientX - this.mousedownPos.x
- this.mousemoveOffset.y = e.clientY - this.mousedownPos.y
- this.emit('mousemove', e, this)
- if (this.isMousedown) {
- this.emit('drag', e, this)
- }
- }
-
- /**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-07 15:18:57
- * @Desc: 鼠标松开事件
- */
- onMouseup(e) {
- this.isMousedown = false
- this.emit('mouseup', e, this)
- }
-
- /**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-07 15:46:27
- * @Desc: 鼠标滚动
- */
- onMousewheel(e) {
- e.stopPropagation()
- e.preventDefault()
- let dir
- if (e.wheelDeltaY > 0) {
- dir = 'up'
- } else {
- dir = 'down'
- }
- this.emit('mousewheel', e, dir, this)
- }
-}
-
+import EventEmitter from 'eventemitter3'
+
+/**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 14:53:09
+ * @Desc: 事件类
+ */
+class Event extends EventEmitter {
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 14:53:25
+ * @Desc: 构造函数
+ */
+ constructor(opt = {}) {
+ super()
+ this.opt = opt
+ this.mindMap = opt.mindMap
+ this.isMousedown = false
+ this.mousedownPos = {
+ x: 0,
+ y: 0
+ }
+ this.mousemovePos = {
+ x: 0,
+ y: 0
+ }
+ this.mousemoveOffset = {
+ x: 0,
+ y: 0
+ }
+ this.bindFn()
+ this.bind()
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 15:52:24
+ * @Desc: 绑定函数上下文
+ */
+ bindFn() {
+ this.onDrawClick = this.onDrawClick.bind(this)
+ this.onMousedown = this.onMousedown.bind(this)
+ this.onMousemove = this.onMousemove.bind(this)
+ this.onMouseup = this.onMouseup.bind(this)
+ this.onMousewheel = this.onMousewheel.bind(this)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 14:53:43
+ * @Desc: 绑定事件
+ */
+ bind() {
+ this.mindMap.draw.on('click', this.onDrawClick)
+ this.mindMap.el.addEventListener('mousedown', this.onMousedown)
+ window.addEventListener('mousemove', this.onMousemove)
+ window.addEventListener('mouseup', this.onMouseup)
+ this.mindMap.el.addEventListener('mousewheel', this.onMousewheel)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 15:40:51
+ * @Desc: 解绑事件
+ */
+ unbind() {
+ this.mindMap.el.removeEventListener('mousedown', this.onMousedown)
+ window.removeEventListener('mousemove', this.onMousemove)
+ window.removeEventListener('mouseup', this.onMouseup)
+ this.mindMap.el.removeEventListener('mousewheel', this.onMousewheel)
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-24 13:19:39
+ * @Desc: 画布的单击事件
+ */
+ onDrawClick(e) {
+ this.emit('draw_click', e)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 15:17:35
+ * @Desc: 鼠标按下事件
+ */
+ onMousedown(e) {
+ e.preventDefault()
+ this.isMousedown = true
+ this.mousedownPos.x = e.clientX
+ this.mousedownPos.y = e.clientY
+ this.emit('mousedown', e, this)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 15:18:32
+ * @Desc: 鼠标移动事件
+ */
+ onMousemove(e) {
+ e.preventDefault()
+ this.mousemovePos.x = e.clientX
+ this.mousemovePos.y = e.clientY
+ this.mousemoveOffset.x = e.clientX - this.mousedownPos.x
+ this.mousemoveOffset.y = e.clientY - this.mousedownPos.y
+ this.emit('mousemove', e, this)
+ if (this.isMousedown) {
+ this.emit('drag', e, this)
+ }
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 15:18:57
+ * @Desc: 鼠标松开事件
+ */
+ onMouseup(e) {
+ this.isMousedown = false
+ this.emit('mouseup', e, this)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 15:46:27
+ * @Desc: 鼠标滚动
+ */
+ onMousewheel(e) {
+ e.stopPropagation()
+ e.preventDefault()
+ let dir
+ if (e.wheelDeltaY > 0) {
+ dir = 'up'
+ } else {
+ dir = 'down'
+ }
+ this.emit('mousewheel', e, dir, this)
+ }
+}
+
export default Event
\ No newline at end of file
diff --git a/src/package/mind-map/src/KeyCommand.js b/simple-mind-map/src/KeyCommand.js
similarity index 100%
rename from src/package/mind-map/src/KeyCommand.js
rename to simple-mind-map/src/KeyCommand.js
diff --git a/src/package/mind-map/src/Node.js b/simple-mind-map/src/Node.js
similarity index 90%
rename from src/package/mind-map/src/Node.js
rename to simple-mind-map/src/Node.js
index 38f1bedd..15c8d263 100644
--- a/src/package/mind-map/src/Node.js
+++ b/simple-mind-map/src/Node.js
@@ -1,397 +1,381 @@
-import Style from './Style';
-import {
- resizeImgSize
-} from './Utils'
-import {
- Image,
- Text,
- SVG,
- Circle,
- Element
-} from '@svgdotjs/svg.js'
-import btnsSvg from './svg/btns';
-
-/**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-06 11:26:00
- * @Desc: 节点类
- */
-class Node {
- /**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-06 11:26:17
- * @Desc: 构造函数
- */
- constructor(opt = {}) {
- // 原始数据
- this.originData = opt.originData
- // 原始数据里的数据部分
- this.data = opt.data
- // id
- this.uid = opt.uid
- // 控制实例
- this.mindMap = opt.mindMap
- // 渲染实例
- this.renderer = opt.renderer
- // 主题配置
- this.themeConfig = this.mindMap.themeConfig
- // 样式实例
- this.style = new Style(this, this.themeConfig)
- // 渲染器
- this.draw = opt.draw || null
- // 是否是根节点
- this.isRoot = opt.isRoot === undefined ? false : opt.isRoot
- // 是否激活
- this.isActive = opt.isActive === undefined ? false : opt.isActive
- // 是否展开
- this.expand = opt.expand === undefined ? true : opt.expand
- // 节点层级
- this.layerIndex = opt.layerIndex === undefined ? 0 : opt.layerIndex
- // 节点宽
- this.width = opt.width || 0
- // 节点高
- this.height = opt.height || 0
- // left
- this.left = opt.left || 0
- // top
- this.top = opt.top || 0
- // 父节点
- this.parent = opt.parent || null
- // 子节点
- this.children = opt.children || []
- // 全部子节点所占的高度之和
- this.childrenAreaHeight = opt.childrenAreaHeight || 0
- // 文本节点
- this.textNode = null
- // 其他数据
- Object.keys(opt.data).forEach((key) => {
- this[key] = opt.data[key]
- })
- }
-
- /**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-06 15:55:04
- * @Desc: 添加子节点
- */
- addChildren(node) {
- this.children.push(node)
- }
-
- /**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-09 09:46:23
- * @Desc: 刷新节点的宽高
- */
- refreshSize() {
- let {
- width,
- height
- } = this.getNodeRect()
- this.width = width
- this.height = height
- }
-
- /**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-06 14:52:17
- * @Desc: 计算节点尺寸信息
- */
- getNodeRect() {
- let width = this.themeConfig.paddingX * 2
- let height = this.themeConfig.paddingY * 2
- let maxWidth = 0
- if (this.img) {
- let img = this.createImgNode()
- if (img.width > maxWidth) {
- maxWidth = img.width
- }
- height += img.height
- }
- if (this.icon && this.text) {
- let icon = this.createIconNode()
- let text = this.createTextNode()
- if (icon.width + text.width > maxWidth) {
- maxWidth = icon.width + text.width
- }
- height += Math.max(text.height, icon.height)
- } else if (this.text) {
- let text = this.createTextNode()
- if (text.width > maxWidth) {
- maxWidth = text.width
- }
- height += text.height
- } else if (this.icon) {
- let icon = this.createIconNode()
- if (icon.width > maxWidth) {
- maxWidth = icon.width
- }
- height += icon.height
- }
- return {
- width: width + maxWidth,
- height
- }
- }
-
- /**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-09 14:06:17
- * @Desc: 创建图片节点
- */
- createImgNode() {
- if (!this.img) {
- return
- }
- let imgSize = this.getImgShowSize()
- return {
- node: new Image().load(this.img).size(...imgSize),
- width: imgSize[0],
- height: imgSize[1]
- }
- }
-
- /**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-09 14:08:56
- * @Desc: 创建文本节点
- */
- createTextNode() {
- if (!this.text) {
- return
- }
- let node = this.draw.text(this.text)
- this.style.text(node)
- let {
- width,
- height
- } = node.bbox()
- let cloneNode = node.clone()
- node.remove()
- return {
- node: cloneNode,
- width,
- height
- }
- }
-
- /**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-09 14:10:48
- * @Desc: 创建icon节点
- */
- createIconNode() {
- if (!this.icon) {
- return
- }
- let node = SVG('').size(this.themeConfig.iconSize, this.themeConfig.iconSize)
- return {
- node,
- width: this.themeConfig.iconSize,
- height: this.themeConfig.iconSize
- }
- }
-
- /**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-09 11:10:11
- * @Desc: 创建内容节点
- */
- createNode() {
- let {
- left,
- top,
- width,
- height
- } = this
- let paddingY = this.themeConfig.paddingY
- // 创建组
- let group = this.draw.group()
- // 节点矩形
- let _rectNode = group.rect(width, height).x(left).y(top)
- this.style.rect(_rectNode)
- // 内容节点
- let imgNode = this.createImgNode()
- let iconNode = this.createIconNode()
- let textNode = this.createTextNode()
- let imgHeight = imgNode ? imgNode.height : 0
- // 图片
- if (imgNode) {
- group.add(imgNode.node)
- imgNode.node.cx(left + width / 2).y(top + paddingY)
- }
- // icon
- if (iconNode) {
- group.add(iconNode.node)
- iconNode.node.x(left + width / 2).y(top + paddingY + imgHeight + (textNode && textNode.height > iconNode.height ? (textNode.height - iconNode.height) / 2 : 0)).dx(textNode ? -textNode.width / 2 - iconNode.width / 2 : 0)
- }
- // 文字
- if (textNode) {
- this.textNode = textNode
- group.add(textNode.node)
- textNode.node.cx(left + width / 2).y(top + paddingY + imgHeight).dx(iconNode ? iconNode.width / 2 : 0)
- }
- // 单击事件
- group.click((e) => {
- e.stopPropagation()
- if (this.isActive) {
- return;
- }
- this.mindMap.emit('before_node_active', this, this.renderer.activeNodeList)
- this.renderer.clearActive()
- this.isActive = true
- this.mindMap.execCommand('UPDATE_NODE_DATA', this, {
- isActive: this.isActive
- })
- this.renderer.activeNodeList.push(this)
- this.mindMap.render()
- this.mindMap.emit('node_active', this, this.renderer.activeNodeList)
- })
- // 双击事件
- group.dblclick(() => {
- this.showTextEditBox()
- })
- return group
- }
-
- /**
- * @Author: 王林
- * @Date: 2021-04-13 22:15:56
- * @Desc: 显示文本编辑框
- */
- showTextEditBox() {
- if (!this.text) {
- return;
- }
- this.renderer.showEditTextBox(this, this.textNode.node.node.getBoundingClientRect())
- }
-
- /**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-07 13:55:58
- * @Desc: 渲染
- */
- render() {
- // 连线
- this.drawLine()
- // 按钮
- this.drawBtn()
- // 节点
- this.draw.add(this.createNode())
- // 子节点
- if (this.children && this.children.length && this.expand) {
- this.children.forEach((child) => {
- child.render()
- })
- }
- }
-
- /**
- * @Author: 王林
- * @Date: 2021-04-10 22:01:53
- * @Desc: 连线
- */
- drawLine() {
- if (!this.expand) {
- return;
- }
- let lines = this.renderer.layout.drawLine(this)
- lines.forEach((line) => {
- this.style.line(line)
- })
- }
-
- /**
- * @Author: 王林
- * @Date: 2021-04-11 19:47:01
- * @Desc: 展开收缩按钮
- */
- drawBtn() {
- if (this.children.length <= 0 || this.isRoot) {
- return;
- }
- let g = this.draw.group()
- let iconSvg
- if (this.expand) {
- iconSvg = btnsSvg.close
- } else {
- iconSvg = btnsSvg.open
- }
- let node = SVG(iconSvg).size(20, 20)
- let fillNode = new Circle().size(20)
- this.renderer.layout.drawIcon(this, [node, fillNode])
- node.dx(0).dy(-10)
- fillNode.dx(0).dy(-10)
- this.style.iconBtn(node, fillNode)
- g.mouseover(() => {
- g.css({ cursor: 'pointer' })
- })
- g.mouseout(() => {
- g.css({ cursor: 'auto' })
- })
- g.click(() => {
- this.expand = !this.expand
- // 需要反映到实际数据上
- this.mindMap.execCommand('UPDATE_NODE_DATA', this, {
- expand: this.expand
- })
- this.mindMap.render()
- this.mindMap.emit('expand_btn_click', this)
- })
- g.add(fillNode)
- g.add(node)
- }
-
- /**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-09 10:12:51
- * @Desc: 获取图片显示宽高
- */
- getImgShowSize() {
- return resizeImgSize(this.imgWidth, this.imgHeight, this.themeConfig.imgMaxWidth, this.themeConfig.imgMaxHeight)
- }
-
- /**
- * @Author: 王林
- * @Date: 2021-05-04 21:48:49
- * @Desc: 获取某个样式
- */
- getStyle(prop, root, isActive) {
- let v = this.style.merge(prop, root, isActive)
- return v === undefined ? '' : v
- }
-
- /**
- * @Author: 王林
- * @Date: 2021-05-04 22:18:07
- * @Desc: 修改某个样式
- */
- setStyle(prop, value, isActive) {
- if (isActive) {
- this.mindMap.execCommand('UPDATE_NODE_DATA', this, {
- activeStyle: {
- ...(this.data.activeStyle || {}),
- [prop]: value
- }
- })
- } else {
- this.mindMap.execCommand('UPDATE_NODE_DATA', this, {
- [prop]: value
- })
- }
- this.mindMap.render()
- }
-}
-
+import Style from './Style'
+import {
+ resizeImgSize
+} from './utils'
+import {
+ Image,
+ Text,
+ SVG,
+ Circle,
+ Element
+} from '@svgdotjs/svg.js'
+import btnsSvg from './svg/btns'
+
+/**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-06 11:26:00
+ * @Desc: 节点类
+ */
+class Node {
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-06 11:26:17
+ * @Desc: 构造函数
+ */
+ constructor(opt = {}) {
+ // 节点数据
+ this.data = opt.data || {}
+ // id
+ this.uid = opt.uid
+ // 控制实例
+ this.mindMap = opt.mindMap
+ // 渲染实例
+ this.renderer = opt.renderer
+ // 渲染器
+ this.draw = opt.draw || null
+ // 主题配置
+ this.themeConfig = this.mindMap.themeConfig
+ // 样式实例
+ this.style = new Style(this, this.themeConfig)
+ // 是否是根节点
+ this.isRoot = opt.isRoot === undefined ? false : opt.isRoot
+ // 是否激活
+ this.isActive = opt.isActive === undefined ? false : opt.isActive
+ // 是否展开
+ this.expand = opt.expand === undefined ? true : opt.expand
+ // 节点层级
+ this.layerIndex = opt.layerIndex === undefined ? 0 : opt.layerIndex
+ // 节点宽
+ this.width = opt.width || 0
+ // 节点高
+ this.height = opt.height || 0
+ // left
+ this.left = opt.left || 0
+ // top
+ this.top = opt.top || 0
+ // 父节点
+ this.parent = opt.parent || null
+ // 子节点
+ this.children = opt.children || []
+ // 文本节点
+ this.textNode = null
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-06 15:55:04
+ * @Desc: 添加子节点
+ */
+ addChildren(node) {
+ this.children.push(node)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-09 09:46:23
+ * @Desc: 刷新节点的宽高
+ */
+ refreshSize() {
+ let {
+ width,
+ height
+ } = this.getNodeRect()
+ this.width = width
+ this.height = height
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-06 14:52:17
+ * @Desc: 计算节点尺寸信息
+ */
+ getNodeRect() {
+ let width = this.themeConfig.paddingX * 2
+ let height = this.themeConfig.paddingY * 2
+ let maxWidth = 0
+ if (this.img) {
+ let img = this.createImgNode()
+ if (img.width > maxWidth) {
+ maxWidth = img.width
+ }
+ height += img.height
+ }
+ if (this.icon && this.text) {
+ let icon = this.createIconNode()
+ let text = this.createTextNode()
+ if (icon.width + text.width > maxWidth) {
+ maxWidth = icon.width + text.width
+ }
+ height += Math.max(text.height, icon.height)
+ } else if (this.text) {
+ let text = this.createTextNode()
+ if (text.width > maxWidth) {
+ maxWidth = text.width
+ }
+ height += text.height
+ } else if (this.icon) {
+ let icon = this.createIconNode()
+ if (icon.width > maxWidth) {
+ maxWidth = icon.width
+ }
+ height += icon.height
+ }
+ return {
+ width: width + maxWidth,
+ height
+ }
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-09 14:06:17
+ * @Desc: 创建图片节点
+ */
+ createImgNode() {
+ if (!this.img) {
+ return
+ }
+ let imgSize = this.getImgShowSize()
+ return {
+ node: new Image().load(this.img).size(...imgSize),
+ width: imgSize[0],
+ height: imgSize[1]
+ }
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-09 14:08:56
+ * @Desc: 创建文本节点
+ */
+ createTextNode() {
+ if (!this.text) {
+ return
+ }
+ let node = this.draw.text(this.text)
+ this.style.text(node)
+ let {
+ width,
+ height
+ } = node.bbox()
+ let cloneNode = node.clone()
+ node.remove()
+ return {
+ node: cloneNode,
+ width,
+ height
+ }
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-09 14:10:48
+ * @Desc: 创建icon节点
+ */
+ createIconNode() {
+ if (!this.icon) {
+ return
+ }
+ let node = SVG('').size(this.themeConfig.iconSize, this.themeConfig.iconSize)
+ return {
+ node,
+ width: this.themeConfig.iconSize,
+ height: this.themeConfig.iconSize
+ }
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-09 11:10:11
+ * @Desc: 创建内容节点
+ */
+ createNode() {
+ let {
+ left,
+ top,
+ width,
+ height
+ } = this
+ let paddingY = this.themeConfig.paddingY
+ // 创建组
+ let group = this.draw.group()
+ // 节点矩形
+ let _rectNode = group.rect(width, height).x(left).y(top)
+ this.style.rect(_rectNode)
+ // 内容节点
+ let imgNode = this.createImgNode()
+ let iconNode = this.createIconNode()
+ let textNode = this.createTextNode()
+ let imgHeight = imgNode ? imgNode.height : 0
+ // 图片
+ if (imgNode) {
+ group.add(imgNode.node)
+ imgNode.node.cx(left + width / 2).y(top + paddingY)
+ }
+ // icon
+ if (iconNode) {
+ group.add(iconNode.node)
+ iconNode.node.x(left + width / 2).y(top + paddingY + imgHeight + (textNode && textNode.height > iconNode.height ? (textNode.height - iconNode.height) / 2 : 0)).dx(textNode ? -textNode.width / 2 - iconNode.width / 2 : 0)
+ }
+ // 文字
+ if (textNode) {
+ this.textNode = textNode
+ group.add(textNode.node)
+ textNode.node.cx(left + width / 2).y(top + paddingY + imgHeight).dx(iconNode ? iconNode.width / 2 : 0)
+ }
+ // 单击事件
+ group.click((e) => {
+ e.stopPropagation()
+ if (this.isActive) {
+ return;
+ }
+ this.mindMap.emit('before_node_active', this, this.renderer.activeNodeList)
+ this.renderer.clearActive()
+ this.isActive = true
+ this.mindMap.execCommand('UPDATE_NODE_DATA', this, {
+ isActive: this.isActive
+ })
+ this.renderer.activeNodeList.push(this)
+ this.mindMap.render()
+ this.mindMap.emit('node_active', this, this.renderer.activeNodeList)
+ })
+ // 双击事件
+ group.dblclick(() => {
+ this.mindMap.emit('node_dblclick', this)
+ })
+ return group
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 13:55:58
+ * @Desc: 渲染
+ */
+ render() {
+ // 连线
+ this.drawLine()
+ // 按钮
+ this.drawBtn()
+ // 节点
+ this.draw.add(this.createNode())
+ // 子节点
+ if (this.children && this.children.length && this.expand) {
+ this.children.forEach((child) => {
+ child.render()
+ })
+ }
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-10 22:01:53
+ * @Desc: 连线
+ */
+ drawLine() {
+ if (!this.expand) {
+ return;
+ }
+ let lines = this.renderer.layout.drawLine(this)
+ lines.forEach((line) => {
+ this.style.line(line)
+ })
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-11 19:47:01
+ * @Desc: 展开收缩按钮
+ */
+ drawBtn() {
+ if (this.children.length <= 0 || this.isRoot) {
+ return;
+ }
+ let g = this.draw.group()
+ let iconSvg
+ if (this.expand) {
+ iconSvg = btnsSvg.close
+ } else {
+ iconSvg = btnsSvg.open
+ }
+ let node = SVG(iconSvg).size(20, 20)
+ let fillNode = new Circle().size(20)
+ this.renderer.layout.drawIcon(this, [node, fillNode])
+ node.dx(0).dy(-10)
+ fillNode.dx(0).dy(-10)
+ this.style.iconBtn(node, fillNode)
+ g.mouseover(() => {
+ g.css({
+ cursor: 'pointer'
+ })
+ })
+ g.mouseout(() => {
+ g.css({
+ cursor: 'auto'
+ })
+ })
+ g.click(() => {
+ this.expand = !this.expand
+ // 需要反映到实际数据上
+ this.mindMap.execCommand('UPDATE_NODE_DATA', this, {
+ expand: this.expand
+ })
+ this.mindMap.render()
+ this.mindMap.emit('expand_btn_click', this)
+ })
+ g.add(fillNode)
+ g.add(node)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-09 10:12:51
+ * @Desc: 获取图片显示宽高
+ */
+ getImgShowSize() {
+ return resizeImgSize(this.imgWidth, this.imgHeight, this.themeConfig.imgMaxWidth, this.themeConfig.imgMaxHeight)
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-04 21:48:49
+ * @Desc: 获取某个样式
+ */
+ getStyle(prop, root, isActive) {
+ let v = this.style.merge(prop, root, isActive)
+ return v === undefined ? '' : v
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-04 22:18:07
+ * @Desc: 修改某个样式
+ */
+ setStyle(prop, value, isActive) {
+ if (isActive) {
+ this.mindMap.execCommand('UPDATE_NODE_DATA', this, {
+ activeStyle: {
+ ...(this.data.activeStyle || {}),
+ [prop]: value
+ }
+ })
+ } else {
+ this.mindMap.execCommand('UPDATE_NODE_DATA', this, {
+ [prop]: value
+ })
+ }
+ this.mindMap.render()
+ }
+}
+
export default Node
\ No newline at end of file
diff --git a/src/package/mind-map/src/Render.js b/simple-mind-map/src/Render.js
similarity index 56%
rename from src/package/mind-map/src/Render.js
rename to simple-mind-map/src/Render.js
index afd75d57..c27eeb2b 100644
--- a/src/package/mind-map/src/Render.js
+++ b/simple-mind-map/src/Render.js
@@ -1,256 +1,175 @@
-import merge from 'deepmerge'
-import LogicalStructure from './layouts/LogicalStructure'
-import { getStrWithBrFromHtml } from './Utils'
-
-// 布局列表
-const layouts = {
- logicalStructure: LogicalStructure
-}
-
-/**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-08 16:25:07
- * @Desc: 渲染
- */
-class Render {
- /**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-08 16:25:32
- * @Desc: 构造函数
- */
- constructor(opt = {}) {
- this.opt = opt
- this.mindMap = opt.mindMap
- this.themeConfig = this.mindMap.themeConfig
- this.draw = this.mindMap.draw
- // 渲染树,操作过程中修改的都是这里的数据
- this.renderTree = merge({}, this.mindMap.opt.data || {})
- // 当前激活的节点列表
- this.activeNodeList = []
- // 根节点
- this.root = null
- // 文本编辑框
- this.textEditNode = null
- // 文本编辑框是否显示
- this.showTextEdit = false
- // 布局
- this.layout = new (layouts[this.mindMap.opt.layout] ? layouts[this.mindMap.opt.layout] : layouts.logicalStructure)({
- mindMap: this.mindMap,
- renderer: this,
- renderTree: this.renderTree,
- themeConfig: this.mindMap.themeConfig,
- draw: this.mindMap.draw
- })
- // 绑定事件
- this.bindEvent()
- // 注册命令
- this.registerCommands()
- }
-
- /**
- * @Author: 王林
- * @Date: 2021-04-24 13:27:04
- * @Desc: 事件
- */
- bindEvent() {
- this.mindMap.on('draw_click', () => {
- // 隐藏文本编辑框
- this.hideEditTextBox()
- // 清除激活状态
- if (this.activeNodeList.length > 0) {
- this.clearActive()
- this.mindMap.render()
- this.mindMap.emit('node_active', null, [])
- }
- })
- this.mindMap.on('expand_btn_click', () => {
- this.hideEditTextBox()
- })
- this.mindMap.on('before_node_active', () => {
- this.hideEditTextBox()
- })
- this.mindMap.keyCommand.addShortcut('Enter', () => {
- this.hideEditTextBox()
- })
- }
-
- /**
- * @Author: 王林
- * @Date: 2021-05-04 13:19:06
- * @Desc: 注册命令
- */
- registerCommands() {
- this.insertNode = this.insertNode.bind(this)
- this.mindMap.command.add('INSERT_NODE', this.insertNode)
- this.insertChildNode = this.insertChildNode.bind(this)
- this.mindMap.command.add('INSERT_CHILD_NODE', this.insertChildNode)
- this.removeNode = this.removeNode.bind(this)
- this.mindMap.command.add('REMOVE_NODE', this.removeNode)
- this.updateNodeData = this.updateNodeData.bind(this)
- this.mindMap.command.add('UPDATE_NODE_DATA', this.updateNodeData)
- }
-
- /**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-08 16:27:55
- * @Desc: 渲染
- */
- render() {
- this.root = this.layout.doLayout()
- this.root.render()
- }
-
- /**
- * @Author: 王林
- * @Date: 2021-04-12 22:45:01
- * @Desc: 清楚当前激活的节点
- */
- clearActive() {
- this.activeNodeList.forEach((item) => {
- this.mindMap.execCommand('UPDATE_NODE_DATA', item, {
- isActive: false
- })
- })
- this.activeNodeList = []
- }
-
- /**
- * @Author: 王林
- * @Date: 2021-05-04 13:46:08
- * @Desc: 获取节点在同级里的索引位置
- */
- getNodeIndex(node) {
- return node.parent ? node.parent.children.findIndex((item) => {
- return item === node
- }) : 0
- }
-
- /**
- * @Author: 王林
- * @Date: 2021-05-04 13:19:54
- * @Desc: 插入同级节点
- */
- insertNode() {
- if (this.activeNodeList.length <= 0) {
- return;
- }
- let first = this.activeNodeList[0]
- if (first.isRoot) {
- this.insertChildNode()
- } else {
- let index = this.getNodeIndex(first)
- first.parent.originData.children.splice(index + 1, 0, {
- "data": {
- "text": "分支主题",
- "expand": true
- },
- "children": []
- })
- this.mindMap.render()
- }
- }
-
- /**
- * @Author: 王林
- * @Date: 2021-05-04 13:31:02
- * @Desc: 插入子节点
- */
- insertChildNode() {
- if (this.activeNodeList.length <= 0) {
- return;
- }
- let first = this.activeNodeList[0]
- first.originData.children.push({
- "data": {
- "text": "分支主题",
- "expand": true
- },
- "children": []
- })
- this.mindMap.render()
- }
-
- /**
- * @Author: 王林
- * @Date: 2021-05-04 13:40:39
- * @Desc: 移除节点
- */
- removeNode() {
- if (this.activeNodeList.length <= 0) {
- return;
- }
- this.activeNodeList.forEach((item) => {
- if (item.isRoot) {
- item.children = []
- item.originData.children = []
- } else {
- let index = this.getNodeIndex(item)
- item.parent.children.splice(index, 1)
- item.parent.originData.children.splice(index, 1)
- }
- })
- this.clearActive()
- this.mindMap.render()
- }
-
- /**
- * @Author: 王林
- * @Date: 2021-05-04 14:19:48
- * @Desc: 更新节点数据
- */
- updateNodeData(node, data) {
- Object.keys(data).forEach((key) => {
- node.data[key] = data[key]
- })
- }
-
- /**
- * @Author: 王林
- * @Date: 2021-04-13 22:13:02
- * @Desc: 显示文本编辑框
- */
- showEditTextBox(node, rect) {
- if (!this.textEditNode) {
- this.textEditNode = document.createElement('div')
- this.textEditNode.style.cssText = `position:fixed;box-sizing: border-box;background-color:#fff;box-shadow: 0 0 20px rgba(0,0,0,.5);padding: 3px 5px;margin-left: -5px;margin-top: -3px;outline: none;`
- this.textEditNode.setAttribute('contenteditable', true)
- document.body.appendChild(this.textEditNode)
- }
- node.style.domText(this.textEditNode)
- this.textEditNode.innerHTML = node.data.text.split(/\n/img).join('
')
- this.textEditNode.style.minWidth = rect.width + 10 + 'px'
- this.textEditNode.style.minHeight = rect.height + 6 + 'px'
- this.textEditNode.style.left = rect.left + 'px'
- this.textEditNode.style.top = rect.top + 'px'
- this.textEditNode.style.display = 'block'
- this.showTextEdit = true
- }
-
- /**
- * @Author: 王林
- * @Date: 2021-04-24 13:48:16
- * @Desc: 隐藏文本编辑框
- */
- hideEditTextBox() {
- if (!this.showTextEdit) {
- return
- }
- this.activeNodeList.forEach((node) => {
- let str = getStrWithBrFromHtml(this.textEditNode.innerHTML)
- node.data.text = str
- this.mindMap.render()
- })
- this.mindMap.emit('hide_text_edit', this.textEditNode, this.activeNodeList)
- this.textEditNode.style.display = 'none'
- this.textEditNode.innerHTML = ''
- this.textEditNode.style.fontFamily = 'inherit'
- this.textEditNode.style.fontSize = 'inherit'
- this.textEditNode.style.fontWeight = 'normal'
- this.showTextEdit = false
- }
-}
-
+import merge from 'deepmerge'
+import LogicalStructure from './layouts/LogicalStructure'
+import TextEdit from './TextEdit'
+
+// 布局列表
+const layouts = {
+ // 思维导图
+ logicalStructure: LogicalStructure
+}
+
+/**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 16:25:07
+ * @Desc: 渲染
+ */
+class Render {
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 16:25:32
+ * @Desc: 构造函数
+ */
+ constructor(opt = {}) {
+ this.opt = opt
+ this.mindMap = opt.mindMap
+ this.themeConfig = this.mindMap.themeConfig
+ this.draw = this.mindMap.draw
+ // 渲染树,操作过程中修改的都是这里的数据
+ this.renderTree = merge({}, this.mindMap.opt.data || {})
+ // 当前激活的节点列表
+ this.activeNodeList = []
+ // 根节点
+ this.root = null
+ // 文本编辑框
+ this.textEdit = new TextEdit(this)
+ // 布局
+ this.layout = new(layouts[this.mindMap.opt.layout] ? layouts[this.mindMap.opt.layout] : layouts.logicalStructure)(this)
+ // 注册命令
+ this.registerCommands()
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-04 13:19:06
+ * @Desc: 注册命令
+ */
+ registerCommands() {
+ this.insertNode = this.insertNode.bind(this)
+ this.mindMap.command.add('INSERT_NODE', this.insertNode)
+ this.insertChildNode = this.insertChildNode.bind(this)
+ this.mindMap.command.add('INSERT_CHILD_NODE', this.insertChildNode)
+ this.removeNode = this.removeNode.bind(this)
+ this.mindMap.command.add('REMOVE_NODE', this.removeNode)
+ this.updateNodeData = this.updateNodeData.bind(this)
+ this.mindMap.command.add('UPDATE_NODE_DATA', this.updateNodeData)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 16:27:55
+ * @Desc: 渲染
+ */
+ render() {
+ this.root = this.layout.doLayout()
+ this.root.render()
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-12 22:45:01
+ * @Desc: 清楚当前激活的节点
+ */
+ clearActive() {
+ this.activeNodeList.forEach((item) => {
+ this.mindMap.execCommand('UPDATE_NODE_DATA', item, {
+ isActive: false
+ })
+ })
+ this.activeNodeList = []
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-04 13:46:08
+ * @Desc: 获取节点在同级里的索引位置
+ */
+ getNodeIndex(node) {
+ return node.parent ? node.parent.children.findIndex((item) => {
+ return item === node
+ }) : 0
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-04 13:19:54
+ * @Desc: 插入同级节点
+ */
+ insertNode() {
+ if (this.activeNodeList.length <= 0) {
+ return;
+ }
+ let first = this.activeNodeList[0]
+ if (first.isRoot) {
+ this.insertChildNode()
+ } else {
+ let index = this.getNodeIndex(first)
+ first.parent.originData.children.splice(index + 1, 0, {
+ "data": {
+ "text": "分支主题",
+ "expand": true
+ },
+ "children": []
+ })
+ this.mindMap.render()
+ }
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-04 13:31:02
+ * @Desc: 插入子节点
+ */
+ insertChildNode() {
+ if (this.activeNodeList.length <= 0) {
+ return;
+ }
+ let first = this.activeNodeList[0]
+ first.originData.children.push({
+ "data": {
+ "text": "分支主题",
+ "expand": true
+ },
+ "children": []
+ })
+ this.mindMap.render()
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-04 13:40:39
+ * @Desc: 移除节点
+ */
+ removeNode() {
+ if (this.activeNodeList.length <= 0) {
+ return;
+ }
+ this.activeNodeList.forEach((item) => {
+ if (item.isRoot) {
+ item.children = []
+ item.originData.children = []
+ } else {
+ let index = this.getNodeIndex(item)
+ item.parent.children.splice(index, 1)
+ item.parent.originData.children.splice(index, 1)
+ }
+ })
+ this.clearActive()
+ this.mindMap.render()
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-04 14:19:48
+ * @Desc: 更新节点数据
+ */
+ updateNodeData(node, data) {
+ Object.keys(data).forEach((key) => {
+ node.data[key] = data[key]
+ })
+ }
+}
+
export default Render
\ No newline at end of file
diff --git a/src/package/mind-map/src/Style.js b/simple-mind-map/src/Style.js
similarity index 100%
rename from src/package/mind-map/src/Style.js
rename to simple-mind-map/src/Style.js
diff --git a/simple-mind-map/src/TextEdit.js b/simple-mind-map/src/TextEdit.js
new file mode 100644
index 00000000..3472eff0
--- /dev/null
+++ b/simple-mind-map/src/TextEdit.js
@@ -0,0 +1,117 @@
+import {
+ getStrWithBrFromHtml
+} from './utils'
+
+/**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-06-19 11:11:28
+ * @Desc: 节点文字编辑类
+ */
+export default class TextEdit {
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-06-19 11:22:57
+ * @Desc: 构造函数
+ */
+ constructor(renderer) {
+ this.mindMap = renderer.mindMap
+ // 文本编辑框
+ this.textEditNode = null
+ // 文本编辑框是否显示
+ this.showTextEdit = false
+ this.bindEvent()
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-24 13:27:04
+ * @Desc: 事件
+ */
+ bindEvent() {
+ this.show = this.show.bind(this)
+ // 节点双击事件
+ this.mindMap.on('node_dblclick', this.show)
+ // 点击事件
+ this.mindMap.on('draw_click', () => {
+ // 隐藏文本编辑框
+ this.hideEditTextBox()
+ // 清除激活状态
+ if (this.activeNodeList.length > 0) {
+ this.clearActive()
+ this.mindMap.render()
+ this.mindMap.emit('node_active', null, [])
+ }
+ })
+ // 展开收缩按钮点击事件
+ this.mindMap.on('expand_btn_click', () => {
+ this.hideEditTextBox()
+ })
+ // 节点激活前事件
+ this.mindMap.on('before_node_active', () => {
+ this.hideEditTextBox()
+ })
+ // 注册回车快捷键
+ this.mindMap.keyCommand.addShortcut('Enter', () => {
+ this.hideEditTextBox()
+ })
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-13 22:15:56
+ * @Desc: 显示文本编辑框
+ */
+ show(node) {
+ if (!node.text) {
+ return;
+ }
+ this.showEditTextBox(this, this.textNode.node.node.getBoundingClientRect())
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-13 22:13:02
+ * @Desc: 显示文本编辑框
+ */
+ showEditTextBox(node, rect) {
+ if (!this.textEditNode) {
+ this.textEditNode = document.createElement('div')
+ this.textEditNode.style.cssText = `position:fixed;box-sizing: border-box;background-color:#fff;box-shadow: 0 0 20px rgba(0,0,0,.5);padding: 3px 5px;margin-left: -5px;margin-top: -3px;outline: none;`
+ this.textEditNode.setAttribute('contenteditable', true)
+ document.body.appendChild(this.textEditNode)
+ }
+ node.style.domText(this.textEditNode)
+ this.textEditNode.innerHTML = node.data.text.split(/\n/img).join('
')
+ this.textEditNode.style.minWidth = rect.width + 10 + 'px'
+ this.textEditNode.style.minHeight = rect.height + 6 + 'px'
+ this.textEditNode.style.left = rect.left + 'px'
+ this.textEditNode.style.top = rect.top + 'px'
+ this.textEditNode.style.display = 'block'
+ this.showTextEdit = true
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-24 13:48:16
+ * @Desc: 隐藏文本编辑框
+ */
+ hideEditTextBox() {
+ if (!this.showTextEdit) {
+ return
+ }
+ this.activeNodeList.forEach((node) => {
+ let str = getStrWithBrFromHtml(this.textEditNode.innerHTML)
+ node.data.text = str
+ this.mindMap.render()
+ })
+ this.mindMap.emit('hide_text_edit', this.textEditNode, this.activeNodeList)
+ this.textEditNode.style.display = 'none'
+ this.textEditNode.innerHTML = ''
+ this.textEditNode.style.fontFamily = 'inherit'
+ this.textEditNode.style.fontSize = 'inherit'
+ this.textEditNode.style.fontWeight = 'normal'
+ this.showTextEdit = false
+ }
+}
\ No newline at end of file
diff --git a/src/package/mind-map/src/View.js b/simple-mind-map/src/View.js
similarity index 96%
rename from src/package/mind-map/src/View.js
rename to simple-mind-map/src/View.js
index 981fe615..713a2d4c 100644
--- a/src/package/mind-map/src/View.js
+++ b/simple-mind-map/src/View.js
@@ -1,87 +1,87 @@
-import merge from 'deepmerge'
-
-/**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-07 14:45:24
- * @Desc: 视图操作类
- */
-class View {
- /**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-07 14:45:40
- * @Desc: 构造函数
- */
- constructor(opt = {}) {
- this.opt = opt
- this.mindMap = this.opt.mindMap
- this.viewBox = {
- x: 0,
- y: 0,
- width: this.mindMap.width,
- height: this.mindMap.height
- }
- this.cacheViewBox = {
- x: 0,
- y: 0,
- width: this.mindMap.width,
- height: this.mindMap.height
- }
- this.scale = 1
- this.bind()
- }
-
- /**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-07 15:38:51
- * @Desc: 绑定
- */
- bind() {
- // 拖动视图
- this.mindMap.event.on('mousedown', () => {
- this.cacheViewBox = merge({}, this.viewBox)
- })
- this.mindMap.event.on('drag', (e, event) => {
- // 视图放大缩小后拖动的距离也要相应变化
- this.viewBox.x = this.cacheViewBox.x - event.mousemoveOffset.x * this.scale
- this.viewBox.y = this.cacheViewBox.y - event.mousemoveOffset.y * this.scale
- this.setViewBox()
- })
- // 放大缩小视图
- this.mindMap.event.on('mousewheel', (e, dir) => {
- let stepWidth = this.viewBox.width * this.mindMap.opt.scaleRatio
- let stepHeight = this.viewBox.height * this.mindMap.opt.scaleRatio
- // 放大
- if (dir === 'down') {
- this.scale += this.mindMap.opt.scaleRatio
- this.viewBox.width += stepWidth
- this.viewBox.height += stepHeight
- } else { // 缩小
- this.scale -= this.mindMap.opt.scaleRatio
- this.viewBox.width -= stepWidth
- this.viewBox.height -= stepHeight
- }
- this.setViewBox()
- })
- }
-
- /**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-07 15:43:26
- * @Desc: 设置视图
- */
- setViewBox() {
- let {
- x,
- y,
- width,
- height
- } = this.viewBox
- this.opt.draw.viewbox(x, y, width, height)
- }
-}
-
+import merge from 'deepmerge'
+
+/**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 14:45:24
+ * @Desc: 视图操作类
+ */
+class View {
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 14:45:40
+ * @Desc: 构造函数
+ */
+ constructor(opt = {}) {
+ this.opt = opt
+ this.mindMap = this.opt.mindMap
+ this.viewBox = {
+ x: 0,
+ y: 0,
+ width: this.mindMap.width,
+ height: this.mindMap.height
+ }
+ this.cacheViewBox = {
+ x: 0,
+ y: 0,
+ width: this.mindMap.width,
+ height: this.mindMap.height
+ }
+ this.scale = 1
+ this.bind()
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 15:38:51
+ * @Desc: 绑定
+ */
+ bind() {
+ // 拖动视图
+ this.mindMap.event.on('mousedown', () => {
+ this.cacheViewBox = merge({}, this.viewBox)
+ })
+ this.mindMap.event.on('drag', (e, event) => {
+ // 视图放大缩小后拖动的距离也要相应变化
+ this.viewBox.x = this.cacheViewBox.x - event.mousemoveOffset.x * this.scale
+ this.viewBox.y = this.cacheViewBox.y - event.mousemoveOffset.y * this.scale
+ this.setViewBox()
+ })
+ // 放大缩小视图
+ this.mindMap.event.on('mousewheel', (e, dir) => {
+ let stepWidth = this.viewBox.width * this.mindMap.opt.scaleRatio
+ let stepHeight = this.viewBox.height * this.mindMap.opt.scaleRatio
+ // 放大
+ if (dir === 'down') {
+ this.scale += this.mindMap.opt.scaleRatio
+ this.viewBox.width += stepWidth
+ this.viewBox.height += stepHeight
+ } else { // 缩小
+ this.scale -= this.mindMap.opt.scaleRatio
+ this.viewBox.width -= stepWidth
+ this.viewBox.height -= stepHeight
+ }
+ this.setViewBox()
+ })
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 15:43:26
+ * @Desc: 设置视图
+ */
+ setViewBox() {
+ let {
+ x,
+ y,
+ width,
+ height
+ } = this.viewBox
+ this.opt.draw.viewbox(x, y, width, height)
+ }
+}
+
export default View
\ No newline at end of file
diff --git a/src/package/mind-map/src/layouts/Base.js b/simple-mind-map/src/layouts/Base.js
similarity index 86%
rename from src/package/mind-map/src/layouts/Base.js
rename to simple-mind-map/src/layouts/Base.js
index f5324fdd..17965b64 100644
--- a/src/package/mind-map/src/layouts/Base.js
+++ b/simple-mind-map/src/layouts/Base.js
@@ -9,17 +9,17 @@ class Base {
* @Date: 2021-04-12 22:25:16
* @Desc: 构造函数
*/
- constructor(opt) {
- // 控制实例
- this.mindMap = opt.mindMap
+ constructor(renderer) {
// 渲染实例
- this.renderer = opt.renderer
+ this.renderer = renderer
+ // 控制实例
+ this.mindMap = renderer.mindMap
// 渲染树
- this.renderTree = opt.renderTree
+ this.renderTree = renderer.renderTree
// 主题配置
- this.themeConfig = opt.themeConfig
+ this.themeConfig = this.mindMap.themeConfig
// 绘图对象
- this.draw = opt.draw
+ this.draw = this.mindMap.draw
// 根节点
this.root = null
}
diff --git a/simple-mind-map/src/layouts/BubbleChart.js b/simple-mind-map/src/layouts/BubbleChart.js
new file mode 100644
index 00000000..84f73fe9
--- /dev/null
+++ b/simple-mind-map/src/layouts/BubbleChart.js
@@ -0,0 +1,186 @@
+import {
+ walk
+} from '../Utils'
+import Node from '../Node'
+import merge from 'deepmerge'
+
+/**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 16:25:07
+ * @Desc: 鱼骨图
+ */
+class Render {
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 16:25:32
+ * @Desc: 构造函数
+ */
+ constructor(opt = {}) {
+ this.opt = opt
+ this.mindMap = opt.mindMap
+ this.draw = this.mindMap.draw
+ // 渲染树
+ this.renderTree = merge({}, this.mindMap.opt.data || {})
+ // 根节点
+ this.root = null
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 16:27:55
+ * @Desc: 渲染
+ */
+ render() {
+ this.computed()
+ this.root.render()
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-06 14:04:20
+ * @Desc: 计算位置数据
+ */
+ computed() {
+ // 计算节点的width、height
+ this.computedBaseValue()
+ // 计算节点的left、top
+ this.computedLeftTopValue()
+ // 调整节点top
+ // this.adjustTopValue()
+ // 调整节点left
+ // this.adjustLeftValue()
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 09:49:32
+ * @Desc: 计算节点的width、height
+ */
+ computedBaseValue() {
+ walk(this.renderTree, null, (node, parent, isRoot, index, layerIndex) => {
+ // 设置width、height
+ let {
+ children,
+ ...props
+ } = node
+ let newNode = new Node({
+ ...props,
+ mindMap: this.mindMap,
+ draw: this.draw,
+ layerIndex
+ })
+ // 计算节点的宽高
+ newNode.refreshSize()
+ // 计算节点的top
+ if (isRoot) {
+ newNode.isRoot = true
+ newNode.left = this.mindMap.width / 2
+ newNode.top = this.mindMap.height / 2
+ this.root = newNode
+ } else {
+ newNode.parent = parent._node
+ parent._node.addChildren(newNode)
+ }
+ node._node = newNode
+ }, (node) => {
+ // 遍历完子节点返回时
+ }, true)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 09:59:25
+ * @Desc: 计算节点的left、top
+ */
+ computedLeftTopValue() {
+ let margin = Math.max(this.mindMap.opt.marginX, this.mindMap.opt.marginY)
+ walk(this.root, null, (node) => {
+ if (node.children && node.children.length) {
+ let rad = (360 / node.children.length) * (Math.PI / 180)
+ let totalRad = 0
+ node.children.forEach((item) => {
+ let r = node.width / 2 + margin + item.width / 2
+ item.left = node.left + r * Math.cos(totalRad)
+ item.top = node.top + r * Math.sin(totalRad)
+ totalRad += rad
+ })
+ }
+ }, null, true)
+ // return
+ walk(this.root, null, null, (node) => {
+ if (node.children && node.children.length) {
+ let minLeft = Infinity,
+ minTop = Infinity,
+ maxRight = -Infinity,
+ maxBottom = -Infinity
+ node.children.concat([node]).forEach((item) => {
+ if ((item.left - item.width / 2) < minLeft) {
+ minLeft = item.left - item.width / 2
+ }
+ if ((item.top - item.width / 2) < minTop) {
+ minTop = item.top - item.width / 2
+ }
+ if ((item.left + item.width / 2) > maxRight) {
+ maxRight = item.left + item.width / 2
+ }
+ if ((item.top + item.width / 2) < maxBottom) {
+ maxBottom = item.top + item.width / 2
+ }
+ })
+ let width = Math.max(maxRight - minLeft, maxBottom - minTop)
+ let difference = width - node.width
+ this.update(node, difference)
+ }
+ }, true)
+ }
+
+ update(node, difference) {
+ if (node.parent) {
+ // console.log(node.text, difference)
+ let rad = (360 / node.parent.children.length) * (Math.PI / 180)
+ let totalRad = 0
+ node.parent.children.forEach((item) => {
+ if (item === node) {
+ item.left += difference * Math.cos(totalRad)
+ item.top += difference * Math.sin(totalRad)
+ if (node.children && node.children.length) {
+ // this.updateChildren(node)
+ }
+ }
+ totalRad += rad
+ })
+
+ this.update(node.parent, difference)
+ }
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 11:25:52
+ * @Desc: 更新子节点
+ */
+ updateChildren(node, difference) {
+ let margin = Math.max(this.mindMap.opt.marginX, this.mindMap.opt.marginY)
+ walk(node, null, (node) => {
+ if (node.children && node.children.length) {
+ let rad = (360 / node.children.length) * (Math.PI / 180)
+ let totalRad = 0
+ node.children.forEach((item) => {
+ let r = node.width / 2 + margin + item.width / 2
+ item.left = node.left + r * Math.cos(totalRad)
+ item.top = node.top + r * Math.sin(totalRad)
+ totalRad += rad
+ })
+ }
+ }, null, true)
+ }
+}
+
+export default Render
\ No newline at end of file
diff --git a/simple-mind-map/src/layouts/CatalogOrganization.js b/simple-mind-map/src/layouts/CatalogOrganization.js
new file mode 100644
index 00000000..909aad42
--- /dev/null
+++ b/simple-mind-map/src/layouts/CatalogOrganization.js
@@ -0,0 +1,270 @@
+import {
+ walk
+} from '../Utils'
+import Node from '../Node'
+import merge from 'deepmerge'
+
+/**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 16:25:07
+ * @Desc: 目录组织图
+ * 思路:第一轮只计算节点的宽高,以及某个节点的所有子节点所占的高度之和,以及该节点里所有子节点中宽度最宽是多少、第二轮计算节点的left和top,需要区分二级节点和其他节点,二级节点top相同,一行依次从做向右排开,其他节点的left相同,一列从上往下依次排开
+ */
+class Render {
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 16:25:32
+ * @Desc: 构造函数
+ */
+ constructor(opt = {}) {
+ this.opt = opt
+ this.mindMap = opt.mindMap
+ this.draw = this.mindMap.draw
+ // 渲染树
+ this.renderTree = merge({}, this.mindMap.opt.data || {})
+ // 根节点
+ this.root = null
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 16:27:55
+ * @Desc: 渲染
+ */
+ render() {
+ this.computed()
+ this.root.render()
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-06 14:04:20
+ * @Desc: 计算位置数据
+ */
+ computed() {
+ // 计算节点的width、height
+ this.computedBaseValue()
+ // 计算节点的left、top
+ this.computedLeftTopValue()
+ // 调整节点top
+ this.adjustTopValue()
+ // 调整节点left
+ this.adjustLeftValue()
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 09:49:32
+ * @Desc: 计算节点的width、height
+ */
+ computedBaseValue() {
+ walk(this.renderTree, null, (node, parent, isRoot, index) => {
+ // 设置width、height
+ let {
+ children,
+ ...props
+ } = node
+ let newNode = new Node({
+ ...props,
+ mindMap: this.mindMap,
+ draw: this.draw
+ })
+ // 计算节点的宽高
+ newNode.refreshSize()
+ // 计算节点的top
+ if (isRoot) {
+ newNode.isRoot = true
+ newNode.left = (this.mindMap.width - newNode.width) / 2
+ newNode.top = (this.mindMap.height - newNode.height) / 2
+ this.root = newNode
+ } else {
+ newNode.parent = parent._node
+ parent._node.addChildren(newNode)
+ }
+ node._node = newNode
+ }, (node) => {
+ // 遍历完子节点返回时
+ // 计算节点的areaHeight,也就是子节点所占的高度之和,包括外边距
+ let len = node._node.children.length
+ if (node._node.isRoot) {
+ node._node.childrenAreaWidth = len ? node._node.children.reduce((h, cur) => {
+ return h + cur.width
+ }, 0) + (len + 1) * this.mindMap.opt.marginX : 0
+ }
+ node._node.childrenAreaHeight = len ? node._node.children.reduce((h, cur) => {
+ return h + cur.height
+ }, 0) + (len + 1) * this.mindMap.opt.marginY : 0
+ }, true)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 09:59:25
+ * @Desc: 计算节点的left、top
+ */
+ computedLeftTopValue() {
+ walk(this.root, null, (node) => {
+ if (node.children && node.children.length) {
+ if (node.isRoot) {
+ let left = node.left + node.width / 2 - node.childrenAreaWidth / 2
+ let totalLeft = left + this.mindMap.opt.marginX
+ node.children.forEach((cur) => {
+ // left
+ cur.left = totalLeft
+ totalLeft += cur.width + this.mindMap.opt.marginX
+ // top
+ cur.top = node.top + node.height + this.mindMap.opt.marginY
+ })
+ } else {
+ let totalTop = node.top + node.height + this.mindMap.opt.marginY
+ node.children.forEach((cur) => {
+ cur.left = node.left + node.width / 5 + this.mindMap.opt.marginX
+ cur.top = totalTop
+ totalTop += cur.height + this.mindMap.opt.marginY
+ })
+ }
+ }
+ }, null, true)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-12 17:07:29
+ * @Desc: 调整节点left
+ */
+ adjustLeftValue() {
+ walk(this.root, null, (node) => {
+ if (node.parent && node.parent.isRoot) {
+ let childrenAreaWidth = this.getNodeWidth(node)
+ let difference = childrenAreaWidth - node.width
+ if (difference > 0) {
+ this.updateBrothersLeftValue(node, difference / 2)
+ }
+ }
+ }, null, true)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-12 18:55:03
+ * @Desc: 计算节点的宽度,包括子节点
+ */
+ getNodeWidth(node) {
+ let widthArr = []
+ let loop = (node, width) => {
+ if (node.children.length) {
+ width += node.width / 5 + this.mindMap.opt.marginX
+ node.children.forEach((item) => {
+ loop(item, width)
+ })
+ } else {
+ width += node.width
+ widthArr.push(width)
+ }
+ }
+ loop(node, 0)
+ return Math.max(...widthArr)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-12 18:21:46
+ * @Desc: 调整兄弟节点的left
+ */
+ updateBrothersLeftValue(node, addWidth) {
+ if (node.parent) {
+ let childrenList = node.parent.children
+ let index = childrenList.findIndex((item) => {
+ return item === node
+ })
+ childrenList.forEach((item, _index) => {
+ let _offset = 0
+ if (_index > index) {
+ _offset = addWidth
+ } else {
+ _offset = -addWidth
+ }
+ item.left += _offset
+ // 同步更新子节点的位置
+ if (item.children && item.children.length) {
+ this.updateChildren(item.children, 'left', _offset)
+ }
+ })
+ // 更新父节点的位置
+ this.updateBrothersLeftValue(node.parent, addWidth)
+ }
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 10:04:05
+ * @Desc: 调整节点top,该节点之后的节点都往下进行偏移
+ */
+ adjustTopValue() {
+ let marginY = this.mindMap.opt.marginY
+ walk(this.root, null, (node) => {
+ if (!node.isRoot && !node.parent.isRoot) {
+ // 判断子节点的areaHeight是否大于该节点自身,大于则需要调整位置
+ if (node.children && node.children.length > 0) {
+ let difference = node.childrenAreaHeight - marginY
+ this.updateBrothersTopValue(node, difference)
+ }
+ }
+ }, null, true)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 14:26:03
+ * @Desc: 更新兄弟节点的top
+ */
+ updateBrothersTopValue(node, addHeight) {
+ if (node.parent && !node.parent.isRoot) {
+ let childrenList = node.parent.children
+ let index = childrenList.findIndex((item) => {
+ return item === node
+ })
+ childrenList.forEach((item, _index) => {
+ let _offset = 0
+ if (_index > index) {
+ _offset = addHeight
+ }
+ item.top += _offset
+ // 同步更新子节点的位置
+ if (item.children && item.children.length) {
+ this.updateChildren(item.children, 'top', _offset)
+ }
+ })
+ // 更新父节点的位置
+ this.updateBrothersTopValue(node.parent, addHeight)
+ }
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 11:25:52
+ * @Desc: 更新子节点属性
+ */
+ updateChildren(children, prop, offset) {
+ children.forEach((item) => {
+ item[prop] += offset
+ if (item.children && item.children.length) {
+ this.updateChildren(item.children, prop, offset)
+ }
+ })
+ }
+}
+
+export default Render
\ No newline at end of file
diff --git a/simple-mind-map/src/layouts/Fishbone.js b/simple-mind-map/src/layouts/Fishbone.js
new file mode 100644
index 00000000..b42c644b
--- /dev/null
+++ b/simple-mind-map/src/layouts/Fishbone.js
@@ -0,0 +1,376 @@
+import {
+ walk
+} from '../Utils'
+import Node from '../Node'
+import merge from 'deepmerge'
+
+/**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 16:25:07
+ * @Desc: 鱼骨图
+ */
+class Render {
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 16:25:32
+ * @Desc: 构造函数
+ */
+ constructor(opt = {}) {
+ this.opt = opt
+ this.mindMap = opt.mindMap
+ this.draw = this.mindMap.draw
+ // 渲染树
+ this.renderTree = merge({}, this.mindMap.opt.data || {})
+ // 根节点
+ this.root = null
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 16:27:55
+ * @Desc: 渲染
+ */
+ render() {
+ this.computed()
+ this.root.render()
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-06 14:04:20
+ * @Desc: 计算位置数据
+ */
+ computed() {
+ // 计算节点的width、height
+ this.computedBaseValue()
+ // 计算节点的left、top
+ this.computedLeftTopValue()
+ // 调整节点top
+ // this.adjustTopValue()
+ // 调整节点left
+ // this.adjustLeftValue()
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 09:49:32
+ * @Desc: 计算节点的width、height
+ */
+ computedBaseValue() {
+ walk(this.renderTree, null, (node, parent, isRoot, index, layerIndex) => {
+ // 生长方向
+ let dir = ''
+ if (isRoot) {
+ dir = ''
+ } else if (parent._node.isRoot) {
+ dir = index % 2 === 0 ? 'up' : 'down'
+ } else {
+ dir = parent._node.dir
+ }
+ // 设置width、height
+ let {
+ children,
+ ...props
+ } = node
+ let newNode = new Node({
+ ...props,
+ mindMap: this.mindMap,
+ draw: this.draw,
+ dir,
+ layerIndex
+ })
+ // 计算节点的宽高
+ newNode.refreshSize()
+ // 计算节点的top
+ if (isRoot) {
+ newNode.isRoot = true
+ newNode.left = (this.mindMap.width - newNode.width) / 2
+ newNode.top = (this.mindMap.height - newNode.height) / 2
+ this.root = newNode
+ } else {
+ newNode.parent = parent._node
+ parent._node.addChildren(newNode)
+ }
+ node._node = newNode
+ }, (node) => {
+ // 遍历完子节点返回时
+ let len = node._node.children.length
+ node._node.childrenAreaHeight = len ? node._node.children.reduce((h, cur) => {
+ return h + cur.height
+ }, 0) + (len + 1) * this.mindMap.opt.marginY : 0
+ }, true)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 09:59:25
+ * @Desc: 计算节点的left、top
+ */
+ computedLeftTopValue() {
+ walk(this.root, null, (node) => {
+ // 二级节点
+ if (node.isRoot && node.children && node.children.length) {
+ let totalLeft = node.left + node.width + this.mindMap.opt.marginX
+ node.children.forEach((item) => {
+ item.left = totalLeft
+ item.top = node.top + node.height / 2 - this.mindMap.opt.marginY - item.height
+ totalLeft += item.width + this.mindMap.opt.marginX
+ this.computedThirdLevelLeftTopValue(item)
+ })
+ }
+ }, null, true)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-13 09:33:04
+ * @Desc: 计算三级节点
+ */
+ computedThirdLevelLeftTopValue(node) {
+ if (node.children && node.children.length > 0) {
+ let totalLeft = node.left
+ let totalTop = node.top - this.mindMap.opt.marginY
+ node.children.forEach((item) => {
+ let h = node.height + this.mindMap.opt.marginY
+ let w = h / Math.tan(70)
+ item.left = totalLeft + w
+ totalLeft += w
+ item.top = totalTop - item.height
+ totalTop -= this.mindMap.opt.marginY + item.height
+ this.computedThirdAfterLevelLeftTopValue(item)
+ })
+ }
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-13 09:55:54
+ * @Desc: 计算三级以后的节点
+ */
+ computedThirdAfterLevelLeftTopValue(root) {
+ let marginY = this.mindMap.opt.marginY
+ let marginX = this.mindMap.opt.marginX
+ // 计算left、top
+ walk(root, null, (node) => {
+ if (node.children && node.children.length) {
+ let totalTop = node.top + node.height + marginY
+ node.children.forEach((cur) => {
+ cur.left = node.left + node.width / 5 + marginX
+ cur.top = totalTop
+ totalTop += cur.height + marginY
+ })
+ }
+ }, null, true)
+ // 调整top
+ const updateBrothersTopValue = (node, addHeight) => {
+ if (node.parent) {
+ let childrenList = node.parent.children
+ let index = childrenList.findIndex((item) => {
+ return item === node
+ })
+ childrenList.forEach((item, _index) => {
+ let _offset = 0
+ if (_index > index) {
+ _offset = addHeight
+ }
+ item.top += _offset
+ // 同步更新子节点的位置
+ if (item.children && item.children.length) {
+ this.updateChildren(item.children, 'top', _offset)
+ }
+ })
+ // 更新父节点的位置
+ updateBrothersTopValue(node.parent, addHeight)
+ }
+ }
+ walk(root, null, (node) => {
+ // 判断子节点的areaHeight是否大于该节点自身,大于则需要调整位置
+ if (node.children && node.children.length > 0) {
+ let difference = node.childrenAreaHeight - marginY
+ updateBrothersTopValue(node, difference)
+ }
+ }, null, true)
+ // 调整left
+ const updateBrothersLeftValue = (node, w, h) => {
+ if (node.parent && node.parent.layerIndex > 0) {
+ let childrenList = node.parent.children
+ let index = childrenList.findIndex((item) => {
+ return item === node
+ })
+ childrenList.forEach((item, _index) => {
+ let _w = 0
+ let _h = 0
+ if (_index >= index) {
+ _w = w
+ _h = -h
+ }
+ console.log(item.text, _w, _h)
+ item.left += _w
+ item.top += _h
+ // 同步更新子节点的位置
+ if (item.children && item.children.length) {
+ this.updateChildren(item.children, 'left', _w)
+ this.updateChildren(item.children, 'left', _h)
+ }
+ })
+ // 更新父节点的位置
+ updateBrothersLeftValue(node.parent, w, h)
+ }
+ }
+ walk(root, null, (node) => {
+ if (node.layerIndex > 1) {
+ let h = node.childrenAreaHeight - marginY
+ if (h > 0) {
+ let w = h / Math.tan(70)
+ console.log(node.text, w, h)
+ // let childrenAreaWidth = getNodeWidth(node)
+ // let differenceX = childrenAreaWidth - node.width
+ // updateBrothersLeftValue(node, w, h)
+ }
+ }
+ }, null, true)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-12 17:07:29
+ * @Desc: 调整节点left
+ */
+ adjustLeftValue() {
+ walk(this.root, null, (node) => {
+ if (node.parent && node.parent.isRoot) {
+ let childrenAreaWidth = this.getNodeWidth(node)
+ let difference = childrenAreaWidth - node.width
+ if (difference > 0) {
+ this.updateBrothersLeftValue(node, difference / 2)
+ }
+ }
+ }, null, true)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-12 18:55:03
+ * @Desc: 计算节点的宽度,包括子节点
+ */
+ getNodeWidth(node) {
+ let widthArr = []
+ let loop = (node, width) => {
+ if (node.children.length) {
+ width += node.width / 5 + this.mindMap.opt.marginX
+ node.children.forEach((item) => {
+ loop(item, width)
+ })
+ } else {
+ width += node.width
+ widthArr.push(width)
+ }
+ }
+ loop(node, 0)
+ return Math.max(...widthArr)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-12 18:21:46
+ * @Desc: 调整兄弟节点的left
+ */
+ updateBrothersLeftValue(node, addWidth) {
+ if (node.parent) {
+ let childrenList = node.parent.children
+ let index = childrenList.findIndex((item) => {
+ return item === node
+ })
+ childrenList.forEach((item, _index) => {
+ let _offset = 0
+ if (_index > index) {
+ _offset = addWidth
+ } else {
+ _offset = -addWidth
+ }
+ item.left += _offset
+ // 同步更新子节点的位置
+ if (item.children && item.children.length) {
+ this.updateChildren(item.children, 'left', _offset)
+ }
+ })
+ // 更新父节点的位置
+ this.updateBrothersLeftValue(node.parent, addWidth)
+ }
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 10:04:05
+ * @Desc: 调整节点top,该节点之后的节点都往下进行偏移
+ */
+ adjustTopValue() {
+ let marginY = this.mindMap.opt.marginY
+ walk(this.root, null, (node) => {
+ if (!node.isRoot && !node.parent.isRoot) {
+ // 判断子节点的areaHeight是否大于该节点自身,大于则需要调整位置
+ if (node.children && node.children.length > 0) {
+ let difference = node.childrenAreaHeight - marginY
+ this.updateBrothersTopValue(node, difference)
+ }
+ }
+ }, null, true)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 14:26:03
+ * @Desc: 更新兄弟节点的top
+ */
+ updateBrothersTopValue(node, addHeight) {
+ if (node.parent && !node.parent.isRoot) {
+ let childrenList = node.parent.children
+ let index = childrenList.findIndex((item) => {
+ return item === node
+ })
+ childrenList.forEach((item, _index) => {
+ let _offset = 0
+ if (_index > index) {
+ _offset = addHeight
+ }
+ item.top += _offset
+ // 同步更新子节点的位置
+ if (item.children && item.children.length) {
+ this.updateChildren(item.children, 'top', _offset)
+ }
+ })
+ // 更新父节点的位置
+ this.updateBrothersTopValue(node.parent, addHeight)
+ }
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 11:25:52
+ * @Desc: 更新子节点属性
+ */
+ updateChildren(children, prop, offset) {
+ children.forEach((item) => {
+ item[prop] += offset
+ if (item.children && item.children.length) {
+ this.updateChildren(item.children, prop, offset)
+ }
+ })
+ }
+}
+
+export default Render
\ No newline at end of file
diff --git a/src/package/mind-map/src/layouts/LogicalStructure.js b/simple-mind-map/src/layouts/LogicalStructure.js
similarity index 97%
rename from src/package/mind-map/src/layouts/LogicalStructure.js
rename to simple-mind-map/src/layouts/LogicalStructure.js
index 3c41ec35..40905e61 100644
--- a/src/package/mind-map/src/layouts/LogicalStructure.js
+++ b/simple-mind-map/src/layouts/LogicalStructure.js
@@ -1,7 +1,7 @@
import Base from './Base';
import {
walk
-} from '../Utils'
+} from '../utils'
import Node from '../Node'
/**
@@ -45,14 +45,10 @@ class LogicalStructure extends Base {
computedBaseValue() {
walk(this.renderTree, null, (node, parent, isRoot, layerIndex) => {
// 遍历子节点前设置left、width、height
- if (!node.data) {
- node.data = {}
- }
// 创建节点
let newNode = new Node({
uid: this.mindMap.uid++,
- originData: node,
- data: node.data,
+ data: node,
renderer: this.renderer,
mindMap: this.mindMap,
draw: this.draw,
diff --git a/simple-mind-map/src/layouts/MindMap.js b/simple-mind-map/src/layouts/MindMap.js
new file mode 100644
index 00000000..cba93d07
--- /dev/null
+++ b/simple-mind-map/src/layouts/MindMap.js
@@ -0,0 +1,195 @@
+import {
+ walk
+} from '../Utils'
+import Node from '../Node'
+import merge from 'deepmerge'
+
+/**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 16:25:07
+ * @Desc: 思维导图
+ * 思路:在逻辑结构图的基础上增加一个变量来记录生长方向,向左还是向右,同时在计算left的时候根据方向来计算、调整top时只考虑同方向的节点即可
+ */
+class Render {
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 16:25:32
+ * @Desc: 构造函数
+ */
+ constructor(opt = {}) {
+ this.opt = opt
+ this.mindMap = opt.mindMap
+ this.draw = this.mindMap.draw
+ // 渲染树
+ this.renderTree = merge({}, this.mindMap.opt.data || {})
+ // 根节点
+ this.root = null
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 16:27:55
+ * @Desc: 渲染
+ */
+ render() {
+ this.computed()
+ this.root.render()
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-06 14:04:20
+ * @Desc: 计算位置数据
+ */
+ computed() {
+ // 计算节点的left、width、height
+ this.computedBaseValue()
+ // 计算节点的top
+ this.computedTopValue()
+ // 调整节点top
+ this.adjustTopValue()
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 09:49:32
+ * @Desc: 计算节点的left、width、height
+ */
+ computedBaseValue() {
+ walk(this.renderTree, null, (node, parent, isRoot, index) => {
+ // 生长方向
+ let dir = ''
+ if (isRoot) {
+ dir = ''
+ } else if (parent._node.isRoot) {
+ dir = index % 2 === 0 ? 'right' : 'left'
+ } else {
+ dir = parent._node.dir
+ }
+ // 设置left、width、height
+ let {
+ children,
+ ...props
+ } = node
+ let newNode = new Node({
+ ...props,
+ mindMap: this.mindMap,
+ draw: this.draw,
+ dir
+ })
+ // 计算节点的宽高
+ newNode.refreshSize()
+ // 计算节点的left
+ if (isRoot) {
+ newNode.isRoot = true
+ newNode.left = (this.mindMap.width - newNode.width) / 2
+ newNode.top = (this.mindMap.height - newNode.height) / 2
+ this.root = newNode
+ } else {
+ newNode.left = dir === 'right' ? parent._node.left + parent._node.width + this.mindMap.opt.marginX : parent._node.left - this.mindMap.opt.marginX - newNode.width
+ newNode.parent = parent._node
+ parent._node.addChildren(newNode)
+ }
+ node._node = newNode
+ }, (node) => {
+ // 返回时计算节点的areaHeight,也就是子节点所占的高度之和,包括外边距
+ let len = node._node.children.length
+ node._node.childrenAreaHeight = len ? node._node.children.reduce((h, cur) => {
+ return h + cur.height
+ }, 0) + (len + 1) * this.mindMap.opt.marginY : 0
+ }, true)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 09:59:25
+ * @Desc: 计算节点的top
+ */
+ computedTopValue() {
+ walk(this.root, null, (node) => {
+ if (node.children && node.children.length) {
+ // 第一个子节点的top值 = 该节点中心的top值 - 子节点的高度之和的一半
+ let top = node.top + node.height / 2 - node.childrenAreaHeight / 2
+ let totalTop = top + this.mindMap.opt.marginY
+ node.children.forEach((cur) => {
+ cur.top = totalTop
+ totalTop += cur.height + this.mindMap.opt.marginY
+ })
+ }
+ }, null, true)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 10:04:05
+ * @Desc: 调整节点top
+ */
+ adjustTopValue() {
+ let margin = this.mindMap.opt.marginY * 2
+ walk(this.root, null, (node) => {
+ // 判断子节点所占的高度之和是否大于该节点自身,大于则需要调整位置
+ let difference = node.childrenAreaHeight - margin - node.height
+ if (difference > 0) {
+ this.updateBrothers(node, difference / 2)
+ }
+ }, null, true)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 14:26:03
+ * @Desc: 更新兄弟节点的top
+ */
+ updateBrothers(node, addHeight) {
+ if (node.parent) {
+ let childrenList = node.parent.children.filter((item) => {
+ return item.dir === node.dir
+ })
+ let index = childrenList.findIndex((item) => {
+ return item === node
+ })
+ childrenList.forEach((item, _index) => {
+ let _offset = 0
+ if (_index < index) {
+ _offset = -addHeight
+ } else if (_index > index) {
+ _offset = addHeight
+ }
+ item.top += _offset
+ // 同步更新子节点的位置
+ if (item.children && item.children.length) {
+ this.updateChildren(item.children, 'top', _offset)
+ }
+ })
+ // 更新父节点的位置
+ this.updateBrothers(node.parent, addHeight)
+ }
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 11:25:52
+ * @Desc: 更新子节点属性
+ */
+ updateChildren(children, prop, offset) {
+ children.forEach((item) => {
+ item[prop] += offset
+ if (item.children && item.children.length) {
+ this.updateChildren(item.children, prop, offset)
+ }
+ })
+ }
+
+
+}
+
+export default Render
\ No newline at end of file
diff --git a/simple-mind-map/src/layouts/OrganizationStructure.js b/simple-mind-map/src/layouts/OrganizationStructure.js
new file mode 100644
index 00000000..7b329b7c
--- /dev/null
+++ b/simple-mind-map/src/layouts/OrganizationStructure.js
@@ -0,0 +1,183 @@
+import {
+ walk
+} from '../Utils'
+import Node from '../Node'
+import merge from 'deepmerge'
+
+/**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 16:25:07
+ * @Desc: 组织结构图
+ * 思路:和逻辑结构图基本一样,只是方向变成向下生长,所以先计算节点的top,后计算节点的left、最后调整节点的left即可
+ */
+class Render {
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 16:25:32
+ * @Desc: 构造函数
+ */
+ constructor(opt = {}) {
+ this.opt = opt
+ this.mindMap = opt.mindMap
+ this.draw = this.mindMap.draw
+ // 渲染树
+ this.renderTree = merge({}, this.mindMap.opt.data || {})
+ // 根节点
+ this.root = null
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 16:27:55
+ * @Desc: 渲染
+ */
+ render() {
+ this.computed()
+ this.root.render()
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-06 14:04:20
+ * @Desc: 计算位置数据
+ */
+ computed() {
+ // 计算节点的top、width、height
+ this.computedBaseValue()
+ // 计算节点的left
+ this.computedLeftValue()
+ // 调整节点left
+ this.adjustLeftValue()
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 09:49:32
+ * @Desc: 计算节点的top、width、height
+ */
+ computedBaseValue() {
+ walk(this.renderTree, null, (node, parent, isRoot, index) => {
+ // 设置top、width、height
+ let {
+ children,
+ ...props
+ } = node
+ let newNode = new Node({
+ ...props,
+ mindMap: this.mindMap,
+ draw: this.draw
+ })
+ // 计算节点的宽高
+ newNode.refreshSize()
+ // 计算节点的top
+ if (isRoot) {
+ newNode.isRoot = true
+ newNode.left = (this.mindMap.width - newNode.width) / 2
+ newNode.top = (this.mindMap.height - newNode.height) / 2
+ this.root = newNode
+ } else {
+ newNode.top = parent._node.top + parent._node.height + this.mindMap.opt.marginY
+ newNode.parent = parent._node
+ parent._node.addChildren(newNode)
+ }
+ node._node = newNode
+ }, (node) => {
+ // 返回时计算节点的areaWidth,也就是子节点所占的宽度之和,包括外边距
+ let len = node._node.children.length
+ node._node.childrenAreaWidth = len ? node._node.children.reduce((h, cur) => {
+ return h + cur.width
+ }, 0) + (len + 1) * this.mindMap.opt.marginX : 0
+ }, true)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 09:59:25
+ * @Desc: 计算节点的left
+ */
+ computedLeftValue() {
+ walk(this.root, null, (node) => {
+ if (node.children && node.children.length) {
+ // 第一个子节点的left值 = 该节点中心的left值 - 子节点的宽度之和的一半
+ let left = node.left + node.width / 2 - node.childrenAreaWidth / 2
+ let totalLeft = left + this.mindMap.opt.marginX
+ node.children.forEach((cur) => {
+ cur.left = totalLeft
+ totalLeft += cur.width + this.mindMap.opt.marginX
+ })
+ }
+ }, null, true)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 10:04:05
+ * @Desc: 调整节点left
+ */
+ adjustLeftValue() {
+ let margin = this.mindMap.opt.marginX * 2
+ walk(this.root, null, (node) => {
+ // 判断子节点所占的宽度之和是否大于该节点自身,大于则需要调整位置
+ let difference = node.childrenAreaWidth - margin - node.width
+ if (difference > 0) {
+ this.updateBrothers(node, difference / 2)
+ }
+ }, null, true)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 14:26:03
+ * @Desc: 更新兄弟节点的left
+ */
+ updateBrothers(node, addWidth) {
+ if (node.parent) {
+ let childrenList = node.parent.children
+ let index = childrenList.findIndex((item) => {
+ return item === node
+ })
+ childrenList.forEach((item, _index) => {
+ let _offset = 0
+ if (_index < index) {
+ _offset = -addWidth
+ } else if (_index > index) {
+ _offset = addWidth
+ }
+ item.left += _offset
+ // 同步更新子节点的位置
+ if (item.children && item.children.length) {
+ this.updateChildren(item.children, 'left', _offset)
+ }
+ })
+ // 更新父节点的位置
+ this.updateBrothers(node.parent, addWidth)
+ }
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 11:25:52
+ * @Desc: 更新子节点属性
+ */
+ updateChildren(children, prop, offset) {
+ children.forEach((item) => {
+ item[prop] += offset
+ if (item.children && item.children.length) {
+ this.updateChildren(item.children, prop, offset)
+ }
+ })
+ }
+
+
+}
+
+export default Render
\ No newline at end of file
diff --git a/simple-mind-map/src/layouts/Structure.js b/simple-mind-map/src/layouts/Structure.js
new file mode 100644
index 00000000..2cc8c28c
--- /dev/null
+++ b/simple-mind-map/src/layouts/Structure.js
@@ -0,0 +1,11 @@
+/**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-12 17:21:04
+ * @Desc: 基类
+ */
+class Structure {
+
+}
+
+export default Structure
\ No newline at end of file
diff --git a/src/package/mind-map/src/svg/btns.js b/simple-mind-map/src/svg/btns.js
similarity index 100%
rename from src/package/mind-map/src/svg/btns.js
rename to simple-mind-map/src/svg/btns.js
diff --git a/src/package/mind-map/src/themes/blueSky.js b/simple-mind-map/src/themes/blueSky.js
similarity index 100%
rename from src/package/mind-map/src/themes/blueSky.js
rename to simple-mind-map/src/themes/blueSky.js
diff --git a/src/package/mind-map/src/themes/brainImpairedPink.js b/simple-mind-map/src/themes/brainImpairedPink.js
similarity index 100%
rename from src/package/mind-map/src/themes/brainImpairedPink.js
rename to simple-mind-map/src/themes/brainImpairedPink.js
diff --git a/src/package/mind-map/src/themes/classic.js b/simple-mind-map/src/themes/classic.js
similarity index 100%
rename from src/package/mind-map/src/themes/classic.js
rename to simple-mind-map/src/themes/classic.js
diff --git a/src/package/mind-map/src/themes/classic2.js b/simple-mind-map/src/themes/classic2.js
similarity index 100%
rename from src/package/mind-map/src/themes/classic2.js
rename to simple-mind-map/src/themes/classic2.js
diff --git a/src/package/mind-map/src/themes/classic3.js b/simple-mind-map/src/themes/classic3.js
similarity index 100%
rename from src/package/mind-map/src/themes/classic3.js
rename to simple-mind-map/src/themes/classic3.js
diff --git a/src/package/mind-map/src/themes/dark.js b/simple-mind-map/src/themes/dark.js
similarity index 100%
rename from src/package/mind-map/src/themes/dark.js
rename to simple-mind-map/src/themes/dark.js
diff --git a/src/package/mind-map/src/themes/default.js b/simple-mind-map/src/themes/default.js
similarity index 100%
rename from src/package/mind-map/src/themes/default.js
rename to simple-mind-map/src/themes/default.js
diff --git a/src/package/mind-map/src/themes/earthYellow.js b/simple-mind-map/src/themes/earthYellow.js
similarity index 100%
rename from src/package/mind-map/src/themes/earthYellow.js
rename to simple-mind-map/src/themes/earthYellow.js
diff --git a/src/package/mind-map/src/themes/freshGreen.js b/simple-mind-map/src/themes/freshGreen.js
similarity index 100%
rename from src/package/mind-map/src/themes/freshGreen.js
rename to simple-mind-map/src/themes/freshGreen.js
diff --git a/src/package/mind-map/src/themes/freshRed.js b/simple-mind-map/src/themes/freshRed.js
similarity index 100%
rename from src/package/mind-map/src/themes/freshRed.js
rename to simple-mind-map/src/themes/freshRed.js
diff --git a/src/package/mind-map/src/themes/index.js b/simple-mind-map/src/themes/index.js
similarity index 100%
rename from src/package/mind-map/src/themes/index.js
rename to simple-mind-map/src/themes/index.js
diff --git a/src/package/mind-map/src/themes/romanticPurple.js b/simple-mind-map/src/themes/romanticPurple.js
similarity index 100%
rename from src/package/mind-map/src/themes/romanticPurple.js
rename to simple-mind-map/src/themes/romanticPurple.js
diff --git a/src/package/mind-map/src/Utils.js b/simple-mind-map/src/utils/index.js
similarity index 96%
rename from src/package/mind-map/src/Utils.js
rename to simple-mind-map/src/utils/index.js
index 895abd14..d658f2e2 100644
--- a/src/package/mind-map/src/Utils.js
+++ b/simple-mind-map/src/utils/index.js
@@ -1,134 +1,134 @@
-/**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-06 14:13:17
- * @Desc: 深度优先遍历树
- */
-export const walk = (root, parent, beforeCallback, afterCallback, isRoot, layerIndex = 0) => {
- beforeCallback && beforeCallback(root, parent, isRoot, layerIndex)
- if (root.children && root.children.length > 0) {
- let _layerIndex = layerIndex + 1
- root.children.forEach((node) => {
- walk(node, root, beforeCallback, afterCallback, false, _layerIndex)
- })
- }
- afterCallback && afterCallback(root, parent, isRoot, layerIndex)
-}
-
-/**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-07 18:47:20
- * @Desc: 广度优先遍历树
- */
-export const bfsWalk = (root, callback) => {
- callback(root)
- let stack = [root]
- while (stack.length) {
- let cur = stack.shift()
- if (cur.children && cur.children.length) {
- cur.children.forEach((item) => {
- stack.push(item)
- callback(item)
- })
- }
- }
-}
-
-/**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-09 10:44:54
- * @Desc: 缩放图片尺寸
- */
-export const resizeImgSize = (width, height, maxWidth, maxHeight) => {
- let nRatio = width / height
- let arr = []
- if (maxWidth && maxHeight) {
- if (width <= maxWidth && height <= maxHeight) {
- arr = [width, height]
- } else {
- let mRatio = maxWidth / maxHeight
- if (nRatio > mRatio) { // 固定高度
- arr = [nRatio * maxHeight, maxHeight]
- } else { // 固定宽度
- arr = [maxWidth, maxWidth / nRatio]
- }
- }
- } else if (maxWidth) {
- if (width <= maxWidth) {
- arr = [width, height]
- } else {
- arr = [maxWidth, maxWidth / nRatio]
- }
- } else if (maxHeight) {
- if (height <= maxHeight) {
- arr = [width, height]
- } else {
- arr = [nRatio * maxHeight, maxHeight]
- }
- }
- return arr
-}
-
-/**
- * javascript comment
- * @Author: 王林25
- * @Date: 2021-04-09 10:18:42
- * @Desc: 缩放图片
- */
-export const resizeImg = (imgUrl, maxWidth, maxHeight) => {
- return new Promise((resolve, reject) => {
- let img = new Image()
- img.src = imgUrl
- img.onload = () => {
- let arr = resizeImgSize(img.naturalWidth, img.naturalHeight, maxWidth, maxHeight)
- resolve(arr)
- }
- img.onerror = (e) => {
- reject(e)
- }
- })
-}
-
-/**
- * @Author: 王林
- * @Date: 2021-05-04 12:26:56
- * @Desc: 从头html结构字符串里获取带换行符的字符串
- */
-export const getStrWithBrFromHtml = (str) => {
- str = str.replace(/
/img, '\n')
- let el = document.createElement('div')
- el.innerHTML = str
- str = el.textContent
- return str;
-}
-
-/**
- * @Author: 王林
- * @Date: 2021-05-04 14:45:39
- * @Desc: 极简的深拷贝
- */
-export const simpleDeepClone = (data) => {
- try {
- return JSON.parse(JSON.stringify(data))
- } catch (error) {
- return null
- }
-}
-
-/**
- * @Author: 王林
- * @Date: 2021-05-04 14:40:11
- * @Desc: 复制渲染树数据
- */
-export const copyRenderTree = (tree, root) => {
- tree.data = simpleDeepClone(root.data)
- tree.children = []
- if (root.children.length > 0) {
- root.children.forEach((item, index) => {
- tree.children[index] = copyRenderTree({}, item)
- })
- }
- return tree;
+/**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-06 14:13:17
+ * @Desc: 深度优先遍历树
+ */
+export const walk = (root, parent, beforeCallback, afterCallback, isRoot, layerIndex = 0) => {
+ beforeCallback && beforeCallback(root, parent, isRoot, layerIndex)
+ if (root.children && root.children.length > 0) {
+ let _layerIndex = layerIndex + 1
+ root.children.forEach((node) => {
+ walk(node, root, beforeCallback, afterCallback, false, _layerIndex)
+ })
+ }
+ afterCallback && afterCallback(root, parent, isRoot, layerIndex)
+}
+
+/**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 18:47:20
+ * @Desc: 广度优先遍历树
+ */
+export const bfsWalk = (root, callback) => {
+ callback(root)
+ let stack = [root]
+ while (stack.length) {
+ let cur = stack.shift()
+ if (cur.children && cur.children.length) {
+ cur.children.forEach((item) => {
+ stack.push(item)
+ callback(item)
+ })
+ }
+ }
+}
+
+/**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-09 10:44:54
+ * @Desc: 缩放图片尺寸
+ */
+export const resizeImgSize = (width, height, maxWidth, maxHeight) => {
+ let nRatio = width / height
+ let arr = []
+ if (maxWidth && maxHeight) {
+ if (width <= maxWidth && height <= maxHeight) {
+ arr = [width, height]
+ } else {
+ let mRatio = maxWidth / maxHeight
+ if (nRatio > mRatio) { // 固定高度
+ arr = [nRatio * maxHeight, maxHeight]
+ } else { // 固定宽度
+ arr = [maxWidth, maxWidth / nRatio]
+ }
+ }
+ } else if (maxWidth) {
+ if (width <= maxWidth) {
+ arr = [width, height]
+ } else {
+ arr = [maxWidth, maxWidth / nRatio]
+ }
+ } else if (maxHeight) {
+ if (height <= maxHeight) {
+ arr = [width, height]
+ } else {
+ arr = [nRatio * maxHeight, maxHeight]
+ }
+ }
+ return arr
+}
+
+/**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-09 10:18:42
+ * @Desc: 缩放图片
+ */
+export const resizeImg = (imgUrl, maxWidth, maxHeight) => {
+ return new Promise((resolve, reject) => {
+ let img = new Image()
+ img.src = imgUrl
+ img.onload = () => {
+ let arr = resizeImgSize(img.naturalWidth, img.naturalHeight, maxWidth, maxHeight)
+ resolve(arr)
+ }
+ img.onerror = (e) => {
+ reject(e)
+ }
+ })
+}
+
+/**
+ * @Author: 王林
+ * @Date: 2021-05-04 12:26:56
+ * @Desc: 从头html结构字符串里获取带换行符的字符串
+ */
+export const getStrWithBrFromHtml = (str) => {
+ str = str.replace(/
/img, '\n')
+ let el = document.createElement('div')
+ el.innerHTML = str
+ str = el.textContent
+ return str;
+}
+
+/**
+ * @Author: 王林
+ * @Date: 2021-05-04 14:45:39
+ * @Desc: 极简的深拷贝
+ */
+export const simpleDeepClone = (data) => {
+ try {
+ return JSON.parse(JSON.stringify(data))
+ } catch (error) {
+ return null
+ }
+}
+
+/**
+ * @Author: 王林
+ * @Date: 2021-05-04 14:40:11
+ * @Desc: 复制渲染树数据
+ */
+export const copyRenderTree = (tree, root) => {
+ tree.data = simpleDeepClone(root.data)
+ tree.children = []
+ if (root.children.length > 0) {
+ root.children.forEach((item, index) => {
+ tree.children[index] = copyRenderTree({}, item)
+ })
+ }
+ return tree;
}
\ No newline at end of file
diff --git a/src/package/mind-map/src/utils/keyMap.js b/simple-mind-map/src/utils/keyMap.js
similarity index 100%
rename from src/package/mind-map/src/utils/keyMap.js
rename to simple-mind-map/src/utils/keyMap.js
diff --git a/src/package/.DS_Store b/src/package/.DS_Store
deleted file mode 100644
index fa590cfd..00000000
Binary files a/src/package/.DS_Store and /dev/null differ
diff --git a/src/pages/Index/Index.vue b/src/pages/Index/Index.vue
deleted file mode 100644
index 308dabc0..00000000
--- a/src/pages/Index/Index.vue
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/src/store.js b/src/store.js
deleted file mode 100644
index b8baeb93..00000000
--- a/src/store.js
+++ /dev/null
@@ -1,68 +0,0 @@
-import Vue from 'vue'
-import Vuex from 'vuex'
-import exampleData from './package/mind-map/example/exampleData';
-
-Vue.use(Vuex)
-
-const store = new Vuex.Store({
- state: {
- userInfo: null,// 用户信息
- mindMapData: null// 思维导图数据
- },
- mutations: {
- /**
- * @Author: 王林
- * @Date: 2020-11-28 15:32:32
- * @Desc: 设置用户信息
- */
- setUserInfo(state, userInfo) {
- state.userInfo = userInfo
- },
-
- /**
- * @Author: 王林
- * @Date: 2021-04-10 14:50:01
- * @Desc: 设置思维导图数据
- */
- setMindMapData(state, data) {
- state.mindMapData = data
- }
- },
- actions: {
- /**
- * @Author: 王林
- * @Date: 2020-11-28 15:28:03
- * @Desc: 获取用户信息
- */
- async getUserInfo(ctx) {
- try {
- let { data } = await api.getUserInfo()
- ctx.commit('setUserInfo', data.data)
- } catch (error) {
- console.log(error)
- }
- },
-
- /**
- * @Author: 王林
- * @Date: 2021-04-10 14:50:40
- * @Desc: 获取思维导图数据
- */
- async getUserMindMapData(ctx) {
- try {
- let { data } = {
- data: {
- data: {
- mindMapData: exampleData
- }
- }
- }
- ctx.commit('setMindMapData', data.data)
- } catch (error) {
- console.log(error)
- }
- }
- }
-})
-
-export default store
\ No newline at end of file
diff --git a/.DS_Store b/web/.DS_Store
similarity index 100%
rename from .DS_Store
rename to web/.DS_Store
diff --git a/babel.config.js b/web/babel.config.js
similarity index 100%
rename from babel.config.js
rename to web/babel.config.js
diff --git a/package.json b/web/package.json
similarity index 94%
rename from package.json
rename to web/package.json
index 01d4412c..1b04cfd0 100644
--- a/package.json
+++ b/web/package.json
@@ -8,9 +8,7 @@
"lint": "vue-cli-service lint"
},
"dependencies": {
- "@svgdotjs/svg.js": "^3.0.16",
"core-js": "^3.6.5",
- "deepmerge": "^1.5.2",
"element-ui": "^2.15.1",
"vue": "^2.6.11",
"vue-router": "^3.5.1",
diff --git a/public/index.html b/web/public/index.html
similarity index 79%
rename from public/index.html
rename to web/public/index.html
index 37d3bac8..3cfbc940 100644
--- a/public/index.html
+++ b/web/public/index.html
@@ -10,8 +10,6 @@
-
-