diff --git a/simple-mind-map/src/Command.js b/simple-mind-map/src/Command.js index 300f9eef..0ec3549b 100644 --- a/simple-mind-map/src/Command.js +++ b/simple-mind-map/src/Command.js @@ -30,7 +30,7 @@ class Command { fn(...args) }) if (name === 'BACK' || name === 'FORWARD') { - return ; + return; } 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: 王林 * @Date: 2021-05-04 14:35:43 diff --git a/simple-mind-map/src/Event.js b/simple-mind-map/src/Event.js index cca50593..a11197c3 100644 --- a/simple-mind-map/src/Event.js +++ b/simple-mind-map/src/Event.js @@ -47,6 +47,7 @@ class Event extends EventEmitter { this.onMouseup = this.onMouseup.bind(this) this.onMousewheel = this.onMousewheel.bind(this) this.onContextmenu = this.onContextmenu.bind(this) + this.onSvgMousedown = this.onSvgMousedown.bind(this) } /** @@ -58,6 +59,7 @@ class Event extends EventEmitter { bind() { this.mindMap.svg.on('click', this.onDrawClick) this.mindMap.el.addEventListener('mousedown', this.onMousedown) + this.mindMap.svg.on('mousedown', this.onSvgMousedown) window.addEventListener('mousemove', this.onMousemove) window.addEventListener('mouseup', this.onMouseup) // 兼容火狐浏览器 @@ -93,6 +95,15 @@ class Event extends EventEmitter { this.emit('draw_click', e) } + /** + * @Author: 王林 + * @Date: 2021-07-16 13:37:30 + * @Desc: svg画布的鼠标按下事件 + */ + onSvgMousedown(e) { + this.emit('svg_mousedown', e) + } + /** * javascript comment * @Author: 王林25 diff --git a/simple-mind-map/src/Node.js b/simple-mind-map/src/Node.js index ba606ac8..c1a792b5 100644 --- a/simple-mind-map/src/Node.js +++ b/simple-mind-map/src/Node.js @@ -165,7 +165,7 @@ class Node { this._expandBtn.off(['mouseover', 'mouseout', 'click']) } 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.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) => { e.stopPropagation() diff --git a/simple-mind-map/src/Render.js b/simple-mind-map/src/Render.js index 370007fd..52e1f268 100644 --- a/simple-mind-map/src/Render.js +++ b/simple-mind-map/src/Render.js @@ -1,9 +1,10 @@ import merge from 'deepmerge' import LogicalStructure from './layouts/LogicalStructure' import MindMap from './layouts/MindMap' -import CatalogOrganization from './layouts/CatalogOrganization'; +import CatalogOrganization from './layouts/CatalogOrganization' import OrganizationStructure from './layouts/OrganizationStructure' import TextEdit from './TextEdit' +import { copyNodeTree, simpleDeepClone, walk } from './utils' // 布局列表 const layouts = { @@ -62,7 +63,7 @@ class Render { * @Desc: 设置布局结构 */ 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.mindMap.command.add('REMOVE_NODE', this.removeNode) - // 复制节点 - this.copyNode = this.copyNode.bind(this) - this.mindMap.command.add('COPY_NODE', this.copyNode) + // 粘贴节点 + this.pasteNode = this.pasteNode.bind(this) + 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.mindMap.command.add('SET_NODE_STYLE', this.setNodeStyle) @@ -123,6 +127,12 @@ class Render { // 切换节点是否展开 this.setNodeExpand = this.setNodeExpand.bind(this) 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.mindMap.command.add('SET_NODE_DATA', this.setNodeData) @@ -222,6 +232,9 @@ class Render { */ removeActiveNode(node) { let index = this.findActiveNodeIndex(node) + if (index === -1) { + return + } this.activeNodeList.splice(index, 1) } @@ -232,7 +245,7 @@ class Render { */ findActiveNodeIndex(node) { return this.activeNodeList.findIndex((item) => { - return item === node; + return item === node }) } @@ -281,7 +294,7 @@ class Render { */ insertNode() { if (this.activeNodeList.length <= 0) { - return; + return } let first = this.activeNodeList[0] if (first.isRoot) { @@ -306,7 +319,7 @@ class Render { */ insertChildNode() { if (this.activeNodeList.length <= 0) { - return; + return } this.activeNodeList.forEach((node, index) => { if (!node.nodeData.children) { @@ -344,7 +357,7 @@ class Render { let parent = node.parent let childList = parent.children let index = childList.findIndex((item) => { - return item === node; + return item === node }) if (index === -1 || index === 0) { return @@ -375,7 +388,7 @@ class Render { let parent = node.parent let childList = parent.children let index = childList.findIndex((item) => { - return item === node; + return item === node }) if (index === -1 || index === childList.length - 1) { return @@ -397,7 +410,7 @@ class Render { */ removeNode() { if (this.activeNodeList.length <= 0) { - return; + return } for (let i = 0; i < this.activeNodeList.length; i++) { let node = this.activeNodeList[i] @@ -410,10 +423,7 @@ class Render { break } else { this.removeActiveNode(node) - let index = this.getNodeIndex(node) - node.remove() - node.parent.children.splice(index, 1) - node.parent.nodeData.children.splice(index, 1) + this.removeOneNode(node) i-- } } @@ -421,18 +431,67 @@ class 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 * @Author: 王林25 * @Date: 2021-07-15 09:53:23 - * @Desc: 复制节点 + * @Desc: 复制节点,多个节点只会操作第一个节点 */ copyNode() { 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() } + /** + * @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: 王林 * @Date: 2021-07-11 17:15:33 diff --git a/simple-mind-map/src/utils/index.js b/simple-mind-map/src/utils/index.js index b67aec30..a0cc9ef5 100644 --- a/simple-mind-map/src/utils/index.js +++ b/simple-mind-map/src/utils/index.js @@ -136,6 +136,23 @@ export const copyRenderTree = (tree, root) => { 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: 王林 * @Date: 2021-07-04 09:08:43 diff --git a/web/src/pages/Edit/components/Contextmenu.vue b/web/src/pages/Edit/components/Contextmenu.vue index 7be42fe0..7aaa1ff9 100644 --- a/web/src/pages/Edit/components/Contextmenu.vue +++ b/web/src/pages/Edit/components/Contextmenu.vue @@ -4,32 +4,45 @@ v-if="isShow" :style="{ left: left + 'px', top: top + 'px' }" > -
- 插入同级节点 -
-
插入子级节点
-
- 上移节点 -
-
- 下移节点 -
-
删除节点
-
复制节点
-
剪切节点
-
粘贴节点
+ + @@ -52,6 +65,11 @@ export default { left: 0, top: 0, node: null, + copyData: null, + type: "", + isMousedown: false, + mosuedownX: 0, + mosuedownY: 0 }; }, computed: { @@ -76,7 +94,8 @@ export default { let isLast = children.findIndex((item) => { return item === this.node; - }) === children.length - 1; + }) === + children.length - 1; return isLast; }, }, @@ -85,26 +104,74 @@ export default { this.$bus.$on("node_click", this.hide); this.$bus.$on("draw_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() { this.$bus.$off("node_contextmenu", this.show); this.$bus.$off("node_click", this.hide); this.$bus.$off("draw_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: { /** * @Author: 王林 * @Date: 2021-07-14 21:38:50 - * @Desc: 显示 + * @Desc: 节点右键显示 */ show(e, node) { + this.type = "node"; this.left = e.clientX + 10; this.top = e.clientY + 10; this.isShow = true; 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: 王林 * @Date: 2021-07-14 21:37:55 @@ -114,6 +181,7 @@ export default { this.isShow = false; this.left = 0; this.top = 0; + this.type = ""; }, /** @@ -125,7 +193,25 @@ export default { if (disabled) { 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(); }, }, @@ -153,7 +239,7 @@ export default { cursor: pointer; &.danger { - color: #F56C6C; + color: #f56c6c; } &:hover { diff --git a/web/src/pages/Edit/components/Edit.vue b/web/src/pages/Edit/components/Edit.vue index 87cb0544..b955c4af 100644 --- a/web/src/pages/Edit/components/Edit.vue +++ b/web/src/pages/Edit/components/Edit.vue @@ -89,7 +89,9 @@ export default { "node_contextmenu", "node_click", "draw_click", - "expand_btn_click" + "expand_btn_click", + "svg_mousedown", + "mouseup" ].forEach((event) => { this.mindMap.on(event, (...args) => { this.$bus.$emit(event, ...args);