diff --git a/simple-mind-map/example/exampleData.js b/simple-mind-map/example/exampleData.js index 29c56c2c..5a1bb5cd 100644 --- a/simple-mind-map/example/exampleData.js +++ b/simple-mind-map/example/exampleData.js @@ -32,110 +32,110 @@ const data1 = { }, "children": [{ "data": { - "text": "子节点", + "text": "分支主题", ...createFullData() }, "children": [{ "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, "children": [{ "data": { - "text": "子节点", + "text": "分支主题", ...createFullData() }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, "children": [{ "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, "children": [{ "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, "children": [{ "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, "children": [{ "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }] }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }] }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }] }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }] }, { "data": { - "text": "子节点", + "text": "分支主题", }, }] }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }] }] @@ -147,36 +147,36 @@ const data1 = { }, "children": [{ "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, "children": [{ "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }] }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }] }, @@ -187,40 +187,40 @@ const data1 = { }, "children": [{ "data": { - "text": "子节点", + "text": "分支主题", }, "children": [{ "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, "children": [{ "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }] }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }] }] @@ -232,225 +232,225 @@ const data1 = { }, "children": [{ "data": { - "text": "子节点", + "text": "分支主题", }, "children": [{ "data": { - "text": "子节点", + "text": "分支主题", }, "children": [{ "data": { - "text": "子节点", + "text": "分支主题", }, "children": [{ "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }] }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }] }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, "children": [{ "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, "children": [{ "data": { - "text": "子节点", + "text": "分支主题", }, "children": [{ "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }] }, { "data": { - "text": "子节点", + "text": "分支主题", }, "children": [{ "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, "children": [{ "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }] }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }] }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }] }] }, { "data": { - "text": "子节点", + "text": "分支主题", }, "children": [{ "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, "children": [{ "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, "children": [{ "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }] }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }] }, { "data": { - "text": "子节点", + "text": "分支主题", }, "children": [{ "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }] }, { "data": { - "text": "子节点", + "text": "分支主题", }, "children": [{ "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }, { "data": { - "text": "子节点", + "text": "分支主题", }, }] }] @@ -767,13 +767,13 @@ const data3 = { "children": [ { "data": { - "text": "子节点" + "text": "分支主题" }, "children": [] }, { "data": { - "text": "子节点" + "text": "分支主题" }, "children": [] } @@ -881,13 +881,13 @@ const data5 = { "children": [ { "data": { - "text": "子节点" + "text": "分支主题" }, "children": [] }, { "data": { - "text": "子节点" + "text": "分支主题" }, "children": [] } diff --git a/simple-mind-map/src/Drag.js b/simple-mind-map/src/Drag.js index 85561405..3f7268b2 100644 --- a/simple-mind-map/src/Drag.js +++ b/simple-mind-map/src/Drag.js @@ -129,11 +129,12 @@ class Drag extends Base { * @Date: 2021-11-23 19:38:02 * @Desc: 鼠标松开事件 */ - onMouseup() { + onMouseup(e) { if (!this.isMousedown) { return; } this.isMousedown = false + let _nodeIsDrag = this.node.isDrag this.node.isDrag = false this.node.show() this.removeCloneNode() @@ -147,6 +148,26 @@ class Drag extends Base { } else if (this.nextNode) { // 存在下一个相邻节点,作为其前一个兄弟节点 this.mindMap.renderer.setNodeActive(this.nextNode, false) this.mindMap.execCommand('INSERT_BEFORE', this.node, this.nextNode) + } else if (_nodeIsDrag) { + // 自定义位置 + let { + x, + y + } = this.mindMap.toPos(e.clientX - this.offsetX, e.clientY - this.offsetY) + let { + scaleX, + scaleY, + translateX, + translateY + } = this.drawTransform + x = (x - translateX) / scaleX + y = (y - translateY) / scaleY + this.node.left = x + this.node.top = y + this.node.customLeft = x + this.node.customTop = y + this.mindMap.execCommand('SET_NODE_CUSTOM_POSITION', this.node, x, y) + this.mindMap.render() } this.reset() } diff --git a/simple-mind-map/src/Node.js b/simple-mind-map/src/Node.js index 3c383ae3..117fc6c2 100644 --- a/simple-mind-map/src/Node.js +++ b/simple-mind-map/src/Node.js @@ -55,9 +55,12 @@ class Node { // 节点高 this.height = opt.height || 0 // left - this.left = opt.left || 0 + this._left = opt.left || 0 // top - this.top = opt.top || 0 + this._top = opt.top || 0 + // 自定义位置 + this.customLeft = opt.data.data.customLeft || undefined + this.customTop = opt.data.data.customTop || undefined // 是否正在拖拽中 this.isDrag = false // 父节点 @@ -101,6 +104,23 @@ class Node { this.getSize() } + // 支持自定义位置 + get left() { + return this.customLeft || this._left + } + + set left(val) { + this._left = val + } + + get top() { + return this.customTop || this._top + } + + set top(val) { + this._top = val + } + /** * @Author: 王林 * @Date: 2021-07-12 07:40:47 @@ -139,6 +159,33 @@ class Node { return data } + /** + * javascript comment + * @Author: 王林25 + * @Date: 2022-08-02 19:53:40 + * @Desc: 检查节点是否存在自定义数据 + */ + hasCustomPosition() { + return this.customLeft !== undefined && this.customTop !== undefined + } + + /** + * javascript comment + * @Author: 王林25 + * @Date: 2022-08-04 09:06:56 + * @Desc: 检查节点是否存在自定义位置的祖先节点 + */ + ancestorHasCustomPosition() { + let node = this + while(node) { + if (node.hasCustomPosition()) { + return true + } + node = node.parent + } + return false + } + /** * javascript comment * @Author: 王林25 diff --git a/simple-mind-map/src/Render.js b/simple-mind-map/src/Render.js index 0d3323ec..e209760b 100644 --- a/simple-mind-map/src/Render.js +++ b/simple-mind-map/src/Render.js @@ -169,6 +169,12 @@ class Render { // 删除节点概要 this.removeGeneralization = this.removeGeneralization.bind(this) this.mindMap.command.add('REMOVE_GENERALIZATION', this.removeGeneralization) + // 设置节点自定义位置 + this.setNodeCustomPosition = this.setNodeCustomPosition.bind(this) + this.mindMap.command.add('SET_NODE_CUSTOM_POSITION', this.setNodeCustomPosition) + // 一键整理布局 + this.resetLayout = this.resetLayout.bind(this) + this.mindMap.command.add('RESET_LAYOUT', this.resetLayout) } /** @@ -388,13 +394,14 @@ class Render { if (first.isRoot) { this.insertChildNode() } else { + let text = first.layerIndex === 1 ? '二级节点' : '分支主题' if (first.layerIndex === 1) { first.parent.initRender = true } let index = this.getNodeIndex(first) first.parent.nodeData.children.splice(index + 1, 0, { "data": { - "text": "分支主题", + "text": text, "expand": true }, "children": [] @@ -416,9 +423,10 @@ class Render { if (!node.nodeData.children) { node.nodeData.children = [] } + let text = node.isRoot ? '二级节点' : '分支主题' node.nodeData.children.push({ "data": { - "text": "分支主题", + "text": text, "expand": true }, "children": [] @@ -916,6 +924,40 @@ class Render { this.mindMap.render() } + /** + * javascript comment + * @Author: 王林25 + * @Date: 2022-08-02 19:04:24 + * @Desc: 设置节点自定义位置 + */ + setNodeCustomPosition(node, left = undefined, top = undefined) { + let nodeList = [node] || this.activeNodeList + nodeList.forEach((item) => { + this.setNodeData(item, { + customLeft: left, + customTop: top + }) + }) + } + + /** + * javascript comment + * @Author: 王林25 + * @Date: 2022-08-02 20:02:50 + * @Desc: 一键整理布局,即去除自定义位置 + */ + resetLayout() { + walk(this.root, null, (node) => { + node.customLeft = undefined + node.customTop = undefined + this.setNodeData(node, { + customLeft: undefined, + customTop: undefined + }) + this.mindMap.render() + }, null, true, 0, 0) + } + /** * @Author: 王林 * @Date: 2021-05-04 14:19:48 @@ -937,6 +979,10 @@ class Render { let changed = node.getSize() node.renderNode() if (changed) { + if (node.isGeneralization) { + // 概要节点 + node.generalizationBelongNode.updateGeneralization() + } this.mindMap.render() } } diff --git a/simple-mind-map/src/layouts/Base.js b/simple-mind-map/src/layouts/Base.js index b57ca829..f07e2ff9 100644 --- a/simple-mind-map/src/layouts/Base.js +++ b/simple-mind-map/src/layouts/Base.js @@ -119,7 +119,7 @@ class Base { updateChildren(children, prop, offset) { children.forEach((item) => { item[prop] += offset - if (item.children && item.children.length) { + if (item.children && item.children.length && !item.hasCustomPosition()) {// 适配自定义位置 this.updateChildren(item.children, prop, offset) } }) diff --git a/simple-mind-map/src/layouts/CatalogOrganization.js b/simple-mind-map/src/layouts/CatalogOrganization.js index 54c60858..38536840 100644 --- a/simple-mind-map/src/layouts/CatalogOrganization.js +++ b/simple-mind-map/src/layouts/CatalogOrganization.js @@ -165,15 +165,18 @@ class CatalogOrganization extends Base { let index = childrenList.findIndex((item) => { return item === node }) - // 第一个或最后一个节点自身也需要移动,否则两边不对称 - if (index === 0 || index === childrenList.length - 1) { + // 存在大于一个节点时,第一个或最后一个节点自身也需要移动,否则两边不对称 + if ((index === 0 || index === childrenList.length - 1) && childrenList.length > 1) { let _offset = index === 0 ? -addWidth : addWidth node.left += _offset - if (node.children && node.children.length) { + if (node.children && node.children.length && !node.hasCustomPosition()) { this.updateChildren(node.children, 'left', _offset) } } childrenList.forEach((item, _index) => { + if (item.hasCustomPosition()) {// 适配自定义位置 + return + } let _offset = 0 if (_index < index) { // 左边的节点往左移 _offset = -addWidth @@ -204,6 +207,9 @@ class CatalogOrganization extends Base { return item === node }) childrenList.forEach((item, _index) => { + if (item.hasCustomPosition()) {// 适配自定义位置 + return + } let _offset = 0 // 下面的节点往下移 if (_index > index) { @@ -239,44 +245,80 @@ class CatalogOrganization extends Base { let len = node.children.length let marginX = this.getMarginX(node.layerIndex + 1) if (node.isRoot) { + // 根节点 let x1 = left + width / 2 let y1 = top + height let s1 = marginX * 0.7 - let minx = 0 - let maxx = 0 + let minx = Infinity + let maxx = -Infinity node.children.forEach((item, index) => { let x2 = item.left +item.width / 2 let y2 = item.top - if (index === 0) { + if (x2 < minx) { minx = x2 - } else if (index >= len - 1) { + } + if (x2 > maxx) { maxx = x2 } - let path = `M ${x2},${y1 + s1} L ${x2},${y2}` + let path = `M ${x2},${y1 + s1} L ${x2},${y1 + s1 > y2 ? y2 + item.height : y2}` + // 竖线 lines[index].plot(path) }) + minx = Math.min(minx, x1) + maxx = Math.max(maxx, x1) // 父节点的竖线 let line1 = this.draw.path() node.style.line(line1) line1.plot(`M ${x1},${y1} L ${x1},${y1 + s1}`) node._lines.push(line1) // 水平线 - if (len > 1) { + if (len > 0) { let lin2 = this.draw.path() node.style.line(lin2) lin2.plot(`M ${minx},${y1 + s1} L ${maxx},${y1 + s1}`) node._lines.push(lin2) } } else { + // 非根节点 let y1 = top + height - let maxy = 0 + let maxy = -Infinity let x2 = node.left + node.width * 0.3 node.children.forEach((item, index) => { + // 为了适配自定义位置,下面做了各种位置的兼容 let y2 = item.top + item.height / 2 - if (index >= len - 1) { + if (y2 > maxy) { maxy = y2 } - let path = `M ${x2},${y2} L ${x2 + node.width * 0.2},${y2}` + // 水平线 + let path = '' + let _left = item.left + let _isLeft = item.left + item.width < x2 + let _isXCenter = false + if (_isLeft) { + // 水平位置在父节点左边 + _left = item.left + item.width + } else if (item.left < x2 && item.left + item.width > x2) { + // 水平位置在父节点之间 + _isXCenter = true + y2 = item.top + maxy = y2 + } + if (y2 > top && y2 < y1) { + // 自定义位置的情况:垂直位置节点在父节点之间 + path = `M ${_isLeft ? node.left : node.left + node.width},${y2} L ${_left},${y2}` + } else if (y2 < y1) { + // 自定义位置的情况:垂直位置节点在父节点上面 + if (_isXCenter) { + y2 = item.top + item.height + _left = x2 + } + path = `M ${x2},${top} L ${x2},${y2} L ${_left},${y2}` + } else { + if (_isXCenter) { + _left = x2 + } + path = `M ${x2},${y2} L ${_left},${y2}` + } lines[index].plot(path) }) // 竖线 @@ -284,7 +326,12 @@ class CatalogOrganization extends Base { let lin2 = this.draw.path() expandBtnSize = len > 0 ? expandBtnSize : 0 node.style.line(lin2) - lin2.plot(`M ${x2},${y1 + expandBtnSize} L ${x2},${maxy}`) + if (maxy < y1 + expandBtnSize) { + lin2.hide() + } else { + lin2.plot(`M ${x2},${y1 + expandBtnSize} L ${x2},${maxy}`) + lin2.show() + } node._lines.push(lin2) } } diff --git a/simple-mind-map/src/layouts/LogicalStructure.js b/simple-mind-map/src/layouts/LogicalStructure.js index ccd85a26..faafbc45 100644 --- a/simple-mind-map/src/layouts/LogicalStructure.js +++ b/simple-mind-map/src/layouts/LogicalStructure.js @@ -120,7 +120,7 @@ class LogicalStructure extends Base { return item === node }) childrenList.forEach((item, _index) => { - if (item === node) { + if (item === node || item.hasCustomPosition()) {// 适配自定义位置 return } let _offset = 0 diff --git a/simple-mind-map/src/layouts/MindMap.js b/simple-mind-map/src/layouts/MindMap.js index 72277893..faac21e3 100644 --- a/simple-mind-map/src/layouts/MindMap.js +++ b/simple-mind-map/src/layouts/MindMap.js @@ -155,6 +155,9 @@ class MindMap extends Base { return item === node }) childrenList.forEach((item, _index) => { + if (item.hasCustomPosition()) {// 适配自定义位置 + return + } let _offset = 0 let addHeight = item.dir === 'left' ? leftAddHeight : rightAddHeight // 上面的节点往上移 diff --git a/simple-mind-map/src/layouts/OrganizationStructure.js b/simple-mind-map/src/layouts/OrganizationStructure.js index 331e2676..cdb6e396 100644 --- a/simple-mind-map/src/layouts/OrganizationStructure.js +++ b/simple-mind-map/src/layouts/OrganizationStructure.js @@ -121,6 +121,9 @@ class OrganizationStructure extends Base { return item === node }) childrenList.forEach((item, _index) => { + if (item.hasCustomPosition()) {// 适配自定义位置 + return + } let _offset = 0 // 上面的节点往上移 if (_index < index) { @@ -160,20 +163,23 @@ class OrganizationStructure extends Base { let y1 = top + height let marginX = this.getMarginX(node.layerIndex + 1) let s1 = marginX * 0.7 - let minx = 0 - let maxx = 0 + let minx = Infinity + let maxx = -Infinity let len = node.children.length node.children.forEach((item, index) => { let x2 = item.left + item.width / 2 - let y2 = item.top - if (index === 0) { + let y2 = y1 + s1 > item.top ? item.top + item.height : item.top + if (x2 < minx) { minx = x2 - } else if (index >= len - 1) { + } + if (x2 > maxx) { maxx = x2 } let path = `M ${x2},${y1 + s1} L ${x2},${y2}` lines[index].plot(path) }) + minx = Math.min(x1, minx) + maxx = Math.max(x1, maxx) // 父节点的竖线 let line1 = this.draw.path() node.style.line(line1) @@ -181,7 +187,7 @@ class OrganizationStructure extends Base { line1.plot(`M ${x1},${y1 + expandBtnSize} L ${x1},${y1 + s1}`) node._lines.push(line1) // 水平线 - if (len > 1) { + if (len > 0) { let lin2 = this.draw.path() node.style.line(lin2) lin2.plot(`M ${minx},${y1 + s1} L ${maxx},${y1 + s1}`) diff --git a/web/src/pages/Edit/components/Contextmenu.vue b/web/src/pages/Edit/components/Contextmenu.vue index 7aaa1ff9..600c27a5 100644 --- a/web/src/pages/Edit/components/Contextmenu.vue +++ b/web/src/pages/Edit/components/Contextmenu.vue @@ -42,6 +42,7 @@
回到中心
展开所有
收起所有
+
一键整理布局