支持自由拖拽、修复一些bug

This commit is contained in:
wanglin2
2022-08-04 11:39:17 +08:00
parent 0620d31d0a
commit b4193d50a3
10 changed files with 296 additions and 125 deletions

View File

@@ -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": []
}

View File

@@ -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()
}

View File

@@ -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

View File

@@ -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()
}
}

View File

@@ -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)
}
})

View File

@@ -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)
}
}

View File

@@ -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

View File

@@ -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
// 上面的节点往上移

View File

@@ -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}`)

View File

@@ -42,6 +42,7 @@
<div class="item" @click="exec('RETURN_CENTER')">回到中心</div>
<div class="item" @click="exec('EXPAND_ALL')">展开所有</div>
<div class="item" @click="exec('UNEXPAND_ALL')">收起所有</div>
<div class="item" @click="exec('RESET_LAYOUT')">一键整理布局</div>
</template>
</div>
</template>