基本完成

This commit is contained in:
wanglin 2021-07-16 13:42:37 +08:00
parent 7982a1373f
commit a798a40fab
7 changed files with 290 additions and 52 deletions

View File

@ -30,7 +30,7 @@ class Command {
fn(...args) fn(...args)
}) })
if (name === 'BACK' || name === 'FORWARD') { if (name === 'BACK' || name === 'FORWARD') {
return ; return;
} }
this.addHistory() this.addHistory()
} }
@ -49,6 +49,28 @@ class Command {
] ]
} }
/**
* @Author: 王林
* @Date: 2021-07-15 23:02:41
* @Desc: 移除命令
*/
remove(name, fn) {
if (!this.commands[name]) {
return
}
if (!fn) {
this.commands[name] = []
delete this.commands[name]
} else {
let index = this.commands[name].find((item) => {
return item === fn;
})
if (index !== -1) {
this.commands[name].splice(index, 1)
}
}
}
/** /**
* @Author: 王林 * @Author: 王林
* @Date: 2021-05-04 14:35:43 * @Date: 2021-05-04 14:35:43

View File

@ -47,6 +47,7 @@ class Event extends EventEmitter {
this.onMouseup = this.onMouseup.bind(this) this.onMouseup = this.onMouseup.bind(this)
this.onMousewheel = this.onMousewheel.bind(this) this.onMousewheel = this.onMousewheel.bind(this)
this.onContextmenu = this.onContextmenu.bind(this) this.onContextmenu = this.onContextmenu.bind(this)
this.onSvgMousedown = this.onSvgMousedown.bind(this)
} }
/** /**
@ -58,6 +59,7 @@ class Event extends EventEmitter {
bind() { bind() {
this.mindMap.svg.on('click', this.onDrawClick) this.mindMap.svg.on('click', this.onDrawClick)
this.mindMap.el.addEventListener('mousedown', this.onMousedown) this.mindMap.el.addEventListener('mousedown', this.onMousedown)
this.mindMap.svg.on('mousedown', this.onSvgMousedown)
window.addEventListener('mousemove', this.onMousemove) window.addEventListener('mousemove', this.onMousemove)
window.addEventListener('mouseup', this.onMouseup) window.addEventListener('mouseup', this.onMouseup)
// 兼容火狐浏览器 // 兼容火狐浏览器
@ -93,6 +95,15 @@ class Event extends EventEmitter {
this.emit('draw_click', e) this.emit('draw_click', e)
} }
/**
* @Author: 王林
* @Date: 2021-07-16 13:37:30
* @Desc: svg画布的鼠标按下事件
*/
onSvgMousedown(e) {
this.emit('svg_mousedown', e)
}
/** /**
* javascript comment * javascript comment
* @Author: 王林25 * @Author: 王林25

View File

@ -165,7 +165,7 @@ class Node {
this._expandBtn.off(['mouseover', 'mouseout', 'click']) this._expandBtn.off(['mouseover', 'mouseout', 'click'])
} }
if (this.group) { if (this.group) {
this.group.off(['click', 'dblclick', 'contextmenu']) this.group.off(['click', 'dblclick', 'contextmenu', 'mousedown', 'mouseup'])
} }
} }
@ -551,6 +551,14 @@ class Node {
this.mindMap.emit('node_click', this) this.mindMap.emit('node_click', this)
this.active(e) this.active(e)
}) })
this.group.on('mousedown', (e) => {
e.stopPropagation()
this.mindMap.emit('node_mousedown', this)
})
this.group.on('mouseup', (e) => {
e.stopPropagation()
this.mindMap.emit('node_mouseup', this)
})
// 双击事件 // 双击事件
this.group.on('dblclick', (e) => { this.group.on('dblclick', (e) => {
e.stopPropagation() e.stopPropagation()

View File

@ -1,9 +1,10 @@
import merge from 'deepmerge' import merge from 'deepmerge'
import LogicalStructure from './layouts/LogicalStructure' import LogicalStructure from './layouts/LogicalStructure'
import MindMap from './layouts/MindMap' import MindMap from './layouts/MindMap'
import CatalogOrganization from './layouts/CatalogOrganization'; import CatalogOrganization from './layouts/CatalogOrganization'
import OrganizationStructure from './layouts/OrganizationStructure' import OrganizationStructure from './layouts/OrganizationStructure'
import TextEdit from './TextEdit' import TextEdit from './TextEdit'
import { copyNodeTree, simpleDeepClone, walk } from './utils'
// 布局列表 // 布局列表
const layouts = { const layouts = {
@ -62,7 +63,7 @@ class Render {
* @Desc: 设置布局结构 * @Desc: 设置布局结构
*/ */
setLayout() { setLayout() {
this.layout = new(layouts[this.mindMap.opt.layout] ? layouts[this.mindMap.opt.layout] : layouts.logicalStructure)(this) this.layout = new (layouts[this.mindMap.opt.layout] ? layouts[this.mindMap.opt.layout] : layouts.logicalStructure)(this)
} }
/** /**
@ -108,9 +109,12 @@ class Render {
// 删除节点 // 删除节点
this.removeNode = this.removeNode.bind(this) this.removeNode = this.removeNode.bind(this)
this.mindMap.command.add('REMOVE_NODE', this.removeNode) this.mindMap.command.add('REMOVE_NODE', this.removeNode)
// 复制节点 // 粘贴节点
this.copyNode = this.copyNode.bind(this) this.pasteNode = this.pasteNode.bind(this)
this.mindMap.command.add('COPY_NODE', this.copyNode) this.mindMap.command.add('PASTE_NODE', this.pasteNode)
// 剪切节点
this.cutNode = this.cutNode.bind(this)
this.mindMap.command.add('CUT_NODE', this.cutNode)
// 修改节点样式 // 修改节点样式
this.setNodeStyle = this.setNodeStyle.bind(this) this.setNodeStyle = this.setNodeStyle.bind(this)
this.mindMap.command.add('SET_NODE_STYLE', this.setNodeStyle) this.mindMap.command.add('SET_NODE_STYLE', this.setNodeStyle)
@ -123,6 +127,12 @@ class Render {
// 切换节点是否展开 // 切换节点是否展开
this.setNodeExpand = this.setNodeExpand.bind(this) this.setNodeExpand = this.setNodeExpand.bind(this)
this.mindMap.command.add('SET_NODE_EXPAND', this.setNodeExpand) this.mindMap.command.add('SET_NODE_EXPAND', this.setNodeExpand)
// 展开所有节点
this.expandAllNode = this.expandAllNode.bind(this)
this.mindMap.command.add('EXPAND_ALL', this.expandAllNode)
// 收起所有节点
this.unexpandAllNode = this.unexpandAllNode.bind(this)
this.mindMap.command.add('UNEXPAND_ALL', this.unexpandAllNode)
// 设置节点数据 // 设置节点数据
this.setNodeData = this.setNodeData.bind(this) this.setNodeData = this.setNodeData.bind(this)
this.mindMap.command.add('SET_NODE_DATA', this.setNodeData) this.mindMap.command.add('SET_NODE_DATA', this.setNodeData)
@ -222,6 +232,9 @@ class Render {
*/ */
removeActiveNode(node) { removeActiveNode(node) {
let index = this.findActiveNodeIndex(node) let index = this.findActiveNodeIndex(node)
if (index === -1) {
return
}
this.activeNodeList.splice(index, 1) this.activeNodeList.splice(index, 1)
} }
@ -232,7 +245,7 @@ class Render {
*/ */
findActiveNodeIndex(node) { findActiveNodeIndex(node) {
return this.activeNodeList.findIndex((item) => { return this.activeNodeList.findIndex((item) => {
return item === node; return item === node
}) })
} }
@ -281,7 +294,7 @@ class Render {
*/ */
insertNode() { insertNode() {
if (this.activeNodeList.length <= 0) { if (this.activeNodeList.length <= 0) {
return; return
} }
let first = this.activeNodeList[0] let first = this.activeNodeList[0]
if (first.isRoot) { if (first.isRoot) {
@ -306,7 +319,7 @@ class Render {
*/ */
insertChildNode() { insertChildNode() {
if (this.activeNodeList.length <= 0) { if (this.activeNodeList.length <= 0) {
return; return
} }
this.activeNodeList.forEach((node, index) => { this.activeNodeList.forEach((node, index) => {
if (!node.nodeData.children) { if (!node.nodeData.children) {
@ -344,7 +357,7 @@ class Render {
let parent = node.parent let parent = node.parent
let childList = parent.children let childList = parent.children
let index = childList.findIndex((item) => { let index = childList.findIndex((item) => {
return item === node; return item === node
}) })
if (index === -1 || index === 0) { if (index === -1 || index === 0) {
return return
@ -375,7 +388,7 @@ class Render {
let parent = node.parent let parent = node.parent
let childList = parent.children let childList = parent.children
let index = childList.findIndex((item) => { let index = childList.findIndex((item) => {
return item === node; return item === node
}) })
if (index === -1 || index === childList.length - 1) { if (index === -1 || index === childList.length - 1) {
return return
@ -397,7 +410,7 @@ class Render {
*/ */
removeNode() { removeNode() {
if (this.activeNodeList.length <= 0) { if (this.activeNodeList.length <= 0) {
return; return
} }
for (let i = 0; i < this.activeNodeList.length; i++) { for (let i = 0; i < this.activeNodeList.length; i++) {
let node = this.activeNodeList[i] let node = this.activeNodeList[i]
@ -410,10 +423,7 @@ class Render {
break break
} else { } else {
this.removeActiveNode(node) this.removeActiveNode(node)
let index = this.getNodeIndex(node) this.removeOneNode(node)
node.remove()
node.parent.children.splice(index, 1)
node.parent.nodeData.children.splice(index, 1)
i-- i--
} }
} }
@ -421,18 +431,67 @@ class Render {
this.mindMap.render() this.mindMap.render()
} }
/**
* @Author: 王林
* @Date: 2021-07-15 22:46:27
* @Desc: 移除某个指定节点
*/
removeOneNode(node) {
let index = this.getNodeIndex(node)
node.remove()
node.parent.children.splice(index, 1)
node.parent.nodeData.children.splice(index, 1)
}
/** /**
* javascript comment * javascript comment
* @Author: 王林25 * @Author: 王林25
* @Date: 2021-07-15 09:53:23 * @Date: 2021-07-15 09:53:23
* @Desc: 复制节点 * @Desc: 复制节点多个节点只会操作第一个节点
*/ */
copyNode() { copyNode() {
if (this.activeNodeList.length <= 0) { if (this.activeNodeList.length <= 0) {
return; return
} }
let copyData = [] return copyNodeTree({}, this.activeNodeList[0])
}
/**
* @Author: 王林
* @Date: 2021-07-15 22:36:45
* @Desc: 剪切节点多个节点只会操作第一个节点
*/
cutNode(callback) {
if (this.activeNodeList.length <= 0) {
return
}
let node = this.activeNodeList[0]
if (node.isRoot) {
return null
}
let copyData = copyNodeTree({}, node)
this.removeActiveNode(node)
this.removeOneNode(node)
this.mindMap.emit('node_active', null, this.activeNodeList)
this.mindMap.render()
if (callback && typeof callback === 'function') {
callback(copyData)
}
}
/**
* @Author: 王林
* @Date: 2021-07-15 20:09:39
* @Desc: 粘贴节点到节点
*/
pasteNode(data) {
if (this.activeNodeList.length <= 0) {
return
}
this.activeNodeList.forEach((item) => {
item.nodeData.children.push(simpleDeepClone(data))
})
this.mindMap.render()
} }
/** /**
@ -494,6 +553,39 @@ class Render {
this.mindMap.render() this.mindMap.render()
} }
/**
* @Author: 王林
* @Date: 2021-07-15 23:23:37
* @Desc: 展开所有
*/
expandAllNode() {
walk(this.renderTree, null, (node) => {
if (!node.data.expand) {
node.data.expand = true
}
}, null, true, 0, 0)
this.mindMap.render()
this.root.children.forEach((item) => {
item.updateExpandBtnNode()
})
}
/**
* @Author: 王林
* @Date: 2021-07-15 23:27:14
* @Desc: 收起所有
*/
unexpandAllNode() {
this.root.children.forEach((item) => {
this.setNodeExpand(item, false)
})
walk(this.renderTree, null, (node, parent, isRoot) => {
if (!isRoot) {
node.data.expand = false
}
}, null, true, 0, 0)
}
/** /**
* @Author: 王林 * @Author: 王林
* @Date: 2021-07-11 17:15:33 * @Date: 2021-07-11 17:15:33

View File

@ -136,6 +136,23 @@ export const copyRenderTree = (tree, root) => {
return tree; return tree;
} }
/**
* @Author: 王林
* @Date: 2021-05-04 14:40:11
* @Desc: 复制节点树数据
*/
export const copyNodeTree = (tree, root) => {
tree.data = simpleDeepClone(root.nodeData.data)
tree.data.isActive = false
tree.children = []
if (root.children && root.children.length > 0) {
root.children.forEach((item, index) => {
tree.children[index] = copyNodeTree({}, item)
})
}
return tree;
}
/** /**
* @Author: 王林 * @Author: 王林
* @Date: 2021-07-04 09:08:43 * @Date: 2021-07-04 09:08:43

View File

@ -4,32 +4,45 @@
v-if="isShow" v-if="isShow"
:style="{ left: left + 'px', top: top + 'px' }" :style="{ left: left + 'px', top: top + 'px' }"
> >
<div <template v-if="type === 'node'">
class="item" <div
@click="exec('INSERT_NODE', insertNodeBtnDisabled)" class="item"
:class="{ disabled: insertNodeBtnDisabled }" @click="exec('INSERT_NODE', insertNodeBtnDisabled)"
> :class="{ disabled: insertNodeBtnDisabled }"
插入同级节点 >
</div> 插入同级节点
<div class="item" @click="exec('INSERT_CHILD_NODE')">插入子级节点</div> </div>
<div <div class="item" @click="exec('INSERT_CHILD_NODE')">插入子级节点</div>
class="item" <div
@click="exec('UP_NODE')" class="item"
:class="{ disabled: upNodeBtnDisabled }" @click="exec('UP_NODE')"
> :class="{ disabled: upNodeBtnDisabled }"
上移节点 >
</div> 上移节点
<div </div>
class="item" <div
@click="exec('DOWN_NODE')" class="item"
:class="{ disabled: downNodeBtnDisabled }" @click="exec('DOWN_NODE')"
> :class="{ disabled: downNodeBtnDisabled }"
下移节点 >
</div> 下移节点
<div class="item danger" @click="exec('REMOVE_NODE')">删除节点</div> </div>
<div class="item">复制节点</div> <div class="item danger" @click="exec('REMOVE_NODE')">删除节点</div>
<div class="item">剪切节点</div> <div class="item" @click="exec('COPY_NODE')">复制节点</div>
<div class="item">粘贴节点</div> <div class="item" @click="exec('CUT_NODE')">剪切节点</div>
<div
class="item"
:class="{ disabled: copyData === null }"
@click="exec('PASTE_NODE')"
>
粘贴节点
</div>
</template>
<template v-if="type === 'svg'">
<div class="item" @click="exec('RETURN_CENTER')">回到中心</div>
<div class="item" @click="exec('EXPAND_ALL')">展开所有</div>
<div class="item" @click="exec('UNEXPAND_ALL')">收起所有</div>
</template>
</div> </div>
</template> </template>
@ -52,6 +65,11 @@ export default {
left: 0, left: 0,
top: 0, top: 0,
node: null, node: null,
copyData: null,
type: "",
isMousedown: false,
mosuedownX: 0,
mosuedownY: 0
}; };
}, },
computed: { computed: {
@ -76,7 +94,8 @@ export default {
let isLast = let isLast =
children.findIndex((item) => { children.findIndex((item) => {
return item === this.node; return item === this.node;
}) === children.length - 1; }) ===
children.length - 1;
return isLast; return isLast;
}, },
}, },
@ -85,26 +104,74 @@ export default {
this.$bus.$on("node_click", this.hide); this.$bus.$on("node_click", this.hide);
this.$bus.$on("draw_click", this.hide); this.$bus.$on("draw_click", this.hide);
this.$bus.$on("expand_btn_click", this.hide); this.$bus.$on("expand_btn_click", this.hide);
this.$bus.$on("svg_mousedown", this.onMousedown);
this.$bus.$on("mouseup", this.onMouseup);
}, },
beforeDestroy() { beforeDestroy() {
this.$bus.$off("node_contextmenu", this.show); this.$bus.$off("node_contextmenu", this.show);
this.$bus.$off("node_click", this.hide); this.$bus.$off("node_click", this.hide);
this.$bus.$off("draw_click", this.hide); this.$bus.$off("draw_click", this.hide);
this.$bus.$off("expand_btn_click", this.hide); this.$bus.$off("expand_btn_click", this.hide);
this.$bus.$on("svg_mousedown", this.onMousedown);
this.$bus.$on("mouseup", this.onMouseup);
}, },
methods: { methods: {
/** /**
* @Author: 王林 * @Author: 王林
* @Date: 2021-07-14 21:38:50 * @Date: 2021-07-14 21:38:50
* @Desc: 显示 * @Desc: 节点右键显示
*/ */
show(e, node) { show(e, node) {
this.type = "node";
this.left = e.clientX + 10; this.left = e.clientX + 10;
this.top = e.clientY + 10; this.top = e.clientY + 10;
this.isShow = true; this.isShow = true;
this.node = node; this.node = node;
}, },
/**
* @Author: 王林
* @Date: 2021-07-16 13:27:48
* @Desc: 鼠标按下事件
*/
onMousedown(e) {
if (e.which !== 3) {
return;
}
this.mosuedownX = e.clientX
this.mosuedownY = e.clientY
this.isMousedown = true;
},
/**
* @Author: 王林
* @Date: 2021-07-16 13:27:53
* @Desc: 鼠标松开事件
*/
onMouseup(e) {
if (!this.isMousedown) {
return;
}
this.isMousedown = false
if (Math.abs(this.mosuedownX - e.clientX) > 3 || Math.abs(this.mosuedownY - e.clientY) > 3) {
this.hide()
return;
}
this.show2(e)
},
/**
* @Author: 王林
* @Date: 2021-07-15 22:54:08
* @Desc: 画布右键显示
*/
show2(e) {
this.type = "svg";
this.left = e.clientX + 10;
this.top = e.clientY + 10;
this.isShow = true;
},
/** /**
* @Author: 王林 * @Author: 王林
* @Date: 2021-07-14 21:37:55 * @Date: 2021-07-14 21:37:55
@ -114,6 +181,7 @@ export default {
this.isShow = false; this.isShow = false;
this.left = 0; this.left = 0;
this.top = 0; this.top = 0;
this.type = "";
}, },
/** /**
@ -125,7 +193,25 @@ export default {
if (disabled) { if (disabled) {
return; return;
} }
this.$bus.$emit("execCommand", key); switch (key) {
case "COPY_NODE":
this.copyData = this.mindMap.renderer.copyNode();
break;
case "CUT_NODE":
this.$bus.$emit("execCommand", key, (copyData) => {
this.copyData = copyData;
});
break;
case "PASTE_NODE":
this.$bus.$emit("execCommand", key, this.copyData);
break;
case "RETURN_CENTER":
this.mindMap.view.reset();
break;
default:
this.$bus.$emit("execCommand", key);
break;
}
this.hide(); this.hide();
}, },
}, },
@ -153,7 +239,7 @@ export default {
cursor: pointer; cursor: pointer;
&.danger { &.danger {
color: #F56C6C; color: #f56c6c;
} }
&:hover { &:hover {

View File

@ -89,7 +89,9 @@ export default {
"node_contextmenu", "node_contextmenu",
"node_click", "node_click",
"draw_click", "draw_click",
"expand_btn_click" "expand_btn_click",
"svg_mousedown",
"mouseup"
].forEach((event) => { ].forEach((event) => {
this.mindMap.on(event, (...args) => { this.mindMap.on(event, (...args) => {
this.$bus.$emit(event, ...args); this.$bus.$emit(event, ...args);