'基本完成'

This commit is contained in:
wanglin2 2021-07-13 16:39:50 +08:00
parent 58703597e3
commit 364aed858f
27 changed files with 439 additions and 382 deletions

View File

@ -793,8 +793,8 @@ export default {
// 自定义配置...
}
},
"layout": "logicalStructure",
// "layout": "mindMap",
// "layout": "logicalStructure"
// "layout": "catalogOrganization"
"layout": "organizationStructure"
// "layout": "organizationStructure"
}

View File

@ -9,6 +9,9 @@ import Command from './src/Command'
import BatchExecution from './src/BatchExecution'
import Export from './src/Export'
import Select from './src/Select'
import {
layoutValueList
} from './src/utils/constant'
import {
SVG
} from '@svgdotjs/svg.js'
@ -32,7 +35,11 @@ const defaultOpt = {
// 节点里图片和文字的间距
imgTextMargin: 5,
// 节点里各种文字信息的间距,如图标和文字的间距
textContentMargin: 2
textContentMargin: 2,
// 多选节点时鼠标移动到边缘时的画布移动偏移量
selectTranslateStep: 3,
// 多选节点时鼠标移动距边缘多少距离时开始偏移
selectTranslateLimit: 20
}
/**
@ -123,7 +130,7 @@ class MindMap {
*/
handleOpt(opt) {
// 检查布局配置
if (!['logicalStructure', 'mindMap', 'catalogOrganization', 'organizationStructure'].includes(opt.layout)) {
if (!layoutValueList.includes(opt.layout)) {
opt.layout = 'logicalStructure'
}
// 检查主题配置
@ -248,6 +255,32 @@ class MindMap {
return prop === undefined ? this.themeConfig : this.themeConfig[prop]
}
/**
* javascript comment
* @Author: 王林25
* @Date: 2021-07-13 16:17:06
* @Desc: 获取当前布局结构
*/
getLayout() {
return this.opt.layout
}
/**
* javascript comment
* @Author: 王林25
* @Date: 2021-07-13 16:17:33
* @Desc: 设置布局结构
*/
setLayout(layout) {
// 检查布局配置
if (!layoutValueList.includes(layout)) {
layout = 'logicalStructure'
}
this.opt.layout = layout
this.renderer.setLayout()
this.render()
}
/**
* @Author: 王林
* @Date: 2021-05-04 13:01:00
@ -262,7 +295,7 @@ class MindMap {
* @Date: 2021-07-01 22:06:38
* @Desc: 导出
*/
async export(...args) {
async export (...args) {
let result = await this.doExport.export(...args)
return result
}

View File

@ -78,15 +78,13 @@ class Node {
textContentHeight: 0
}
// 各种文字信息的间距
this._textContentItemMargin = this.mindMap.opt.textContentMargin
this.textContentItemMargin = this.mindMap.opt.textContentMargin
// 图片和文字节点的间距
this._blockContentMargin = this.mindMap.opt.imgTextMargin
this.blockContentMargin = this.mindMap.opt.imgTextMargin
// 展开收缩按钮尺寸
this._expandBtnSize = this.mindMap.opt.expandBtnSize
this.expandBtnSize = this.mindMap.opt.expandBtnSize
// 初始渲染
this._initRender = true
// 更新的时候的钩子
this.updateHooks = []
this.initRender = true
// 初始化
this.createNodeData()
this.getSize()
@ -241,7 +239,7 @@ class Node {
if (this._iconData.length > 0) {
textContentWidth += this._iconData.reduce((sum, cur) => {
textContentHeight = Math.max(textContentHeight, cur.height)
return sum += cur.width + this._textContentItemMargin
return sum += cur.width + this.textContentItemMargin
}, 0)
}
// 文字
@ -258,7 +256,7 @@ class Node {
if (this._tagData.length > 0) {
textContentWidth += this._tagData.reduce((sum, cur) => {
textContentHeight = Math.max(textContentHeight, cur.height)
return sum += cur.width + this._textContentItemMargin
return sum += cur.width + this.textContentItemMargin
}, 0)
}
// 备注
@ -270,7 +268,7 @@ class Node {
this._rectInfo.textContentWidth = textContentWidth
this._rectInfo.textContentHeight = textContentHeight
// 间距
let margin = imgContentHeight > 0 && textContentHeight > 0 ? this._blockContentMargin : 0
let margin = imgContentHeight > 0 && textContentHeight > 0 ? this.blockContentMargin : 0
let { paddingX, paddingY } = this.getPaddingVale()
return {
width: Math.max(imgContentWidth, textContentWidth) + paddingX * 2,
@ -481,7 +479,7 @@ class Node {
let {
width,
height,
_textContentItemMargin
textContentItemMargin
} = this
let { paddingY } = this.getPaddingVale()
// 创建组
@ -507,7 +505,7 @@ class Node {
this._iconData.forEach((item) => {
item.node.x(textContentOffsetX + iconLeft).y((this._rectInfo.textContentHeight - item.height) / 2)
iconNested.add(item.node)
iconLeft += item.width + _textContentItemMargin
iconLeft += item.width + textContentItemMargin
})
textContentNested.add(iconNested)
textContentOffsetX += iconLeft
@ -516,13 +514,13 @@ class Node {
if (this._textData) {
this._textData.node.x(textContentOffsetX).y(0)
textContentNested.add(this._textData.node)
textContentOffsetX += this._textData.width + _textContentItemMargin
textContentOffsetX += this._textData.width + textContentItemMargin
}
// 超链接
if (this._hyperlinkData) {
this._hyperlinkData.node.x(textContentOffsetX).y((this._rectInfo.textContentHeight - this._hyperlinkData.height) / 2)
textContentNested.add(this._hyperlinkData.node)
textContentOffsetX += this._hyperlinkData.width + _textContentItemMargin
textContentOffsetX += this._hyperlinkData.width + textContentItemMargin
}
// 标签
let tagNested = new G()
@ -531,7 +529,7 @@ class Node {
this._tagData.forEach((item) => {
item.node.x(textContentOffsetX + tagLeft).y((this._rectInfo.textContentHeight - item.height) / 2)
tagNested.add(item.node)
tagLeft += item.width + _textContentItemMargin
tagLeft += item.width + textContentItemMargin
})
textContentNested.add(tagNested)
textContentOffsetX += tagLeft
@ -545,7 +543,7 @@ class Node {
// 文字内容整体
textContentNested.translate(
width / 2 - textContentNested.bbox().width / 2,
imgHeight + paddingY + (imgHeight > 0 && this._rectInfo.textContentHeight > 0 ? this._blockContentMargin : 0)
imgHeight + paddingY + (imgHeight > 0 && this._rectInfo.textContentHeight > 0 ? this.blockContentMargin : 0)
)
this.group.add(textContentNested)
// 单击事件,选中节点
@ -606,11 +604,8 @@ class Node {
this.removeExpandBtn()
} else if (!this._expandBtn && this.nodeData.children.length > 0) {// 需要添加展开收缩按钮
this.renderExpandBtn()
}
if (!layout) {
this.updateHooks.forEach((hook) => {
hook(this)
})
} else {
this.updateExpandBtnPos()
}
let t = this.group.transform()
if (!layout) {
@ -630,8 +625,8 @@ class Node {
// 连线
this.renderLine()
// 节点
if (this._initRender) {
this._initRender = false
if (this.initRender) {
this.initRender = false
this.renderNode()
} else {
this.update()
@ -652,7 +647,7 @@ class Node {
* @Desc: 递归删除
*/
remove() {
this._initRender = true
this.initRender = true
this.removeAllEvent()
this.removeAllNode()
this.removeLine()
@ -723,10 +718,10 @@ class Node {
} else {
iconSvg = btnsSvg.close
}
let node = SVG(iconSvg).size(this._expandBtnSize, this._expandBtnSize)
let fillNode = new Circle().size(this._expandBtnSize)
node.x(0).y(-this._expandBtnSize / 2)
fillNode.x(0).y(-this._expandBtnSize / 2)
let node = SVG(iconSvg).size(this.expandBtnSize, this.expandBtnSize)
let fillNode = new Circle().size(this.expandBtnSize)
node.x(0).y(-this.expandBtnSize / 2)
fillNode.x(0).y(-this.expandBtnSize / 2)
this.style.iconBtn(node, fillNode)
this._expandBtn.add(fillNode).add(node)
}
@ -741,7 +736,6 @@ class Node {
if (!this._expandBtn) {
return
}
console.log('更新')
this.renderer.layout.renderExpandBtn(this, this._expandBtn)
}
@ -775,7 +769,7 @@ class Node {
this.mindMap.emit('expand_btn_click', this)
})
this.group.add(this._expandBtn)
this.renderer.layout.renderExpandBtn(this, this._expandBtn)
this.updateExpandBtnPos()
}
/**

View File

@ -44,7 +44,7 @@ class Render {
// 根节点
this.root = null
// 布局
this.layout = new (layouts[this.mindMap.opt.layout] ? layouts[this.mindMap.opt.layout] : layouts.logicalStructure)(this)
this.setLayout()
// 绑定事件
this.bindEvent()
// 注册命令
@ -55,6 +55,16 @@ class Render {
this.textEdit = new TextEdit(this)
}
/**
* javascript comment
* @Author: 王林25
* @Date: 2021-07-13 16:20:07
* @Desc: 设置布局结构
*/
setLayout() {
this.layout = new (layouts[this.mindMap.opt.layout] ? layouts[this.mindMap.opt.layout] : layouts.logicalStructure)(this)
}
/**
* @Author: 王林
* @Date: 2021-06-20 10:34:06
@ -371,9 +381,7 @@ class Render {
this.setNodeData(node, {
isActive: active
})
let s = Date.now()
node.renderNode()
console.log(Date.now() - s)
}
/**

View File

@ -73,12 +73,27 @@ class Select {
* @Desc: 鼠标移动到边缘后移动画布
*/
move (x, y) {
if (x >= this.mindMap.elRect.right - 20) {
console.log('小于')
let step = this.mindMap.opt.selectTranslateStep
let limit = this.mindMap.opt.selectTranslateLimit
// 左边缘
if (x <= this.mindMap.elRect.left + limit) {
this.mouseDownX += step
this.mindMap.view.translateX(step)
}
if (y >= this.mindMap.elRect.bottom - 20) {
console.log('小于')
this.mindMap.view.translateY(-3)
// 右边缘
if (x >= this.mindMap.elRect.right - limit) {
this.mouseDownX -= step
this.mindMap.view.translateX(-step)
}
// 上边缘
if (y <= this.mindMap.elRect.top + limit) {
this.mouseDownY += step
this.mindMap.view.translateY(step)
}
// 下边缘
if (y >= this.mindMap.elRect.bottom - limit) {
this.mouseDownY -= step
this.mindMap.view.translateY(-step)
}
}

View File

@ -60,10 +60,23 @@ class View {
})
}
translateX() {
/**
* javascript comment
* @Author: 王林25
* @Date: 2021-07-13 15:49:06
* @Desc: 平移x方向
*/
translateX(step) {
this.x += step
this.transform()
}
/**
* javascript comment
* @Author: 王林25
* @Date: 2021-07-13 15:48:52
* @Desc: 平移y方向
*/
translateY(step) {
this.y += step
this.transform()

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@ -29,9 +29,9 @@ class CatalogOrganization extends Base {
let task = [() => {
this.computedBaseValue()
}, () => {
this.computedLeftValue()
this.computedLeftTopValue()
}, () => {
// this.adjustTopValue()
this.adjustLeftTopValue()
}, () => {
callback(this.root)
}]
@ -53,18 +53,20 @@ class CatalogOrganization extends Base {
newNode.top = (this.mindMap.height - newNode.height) / 2
} else {
// 非根节点
// 定位到父节点下方
newNode.top = parent._node.top + parent._node.height + this.getMarginX(layerIndex)
if (parent._node.isRoot) {
newNode.top = parent._node.top + parent._node.height + this.getMarginX(layerIndex)
}
}
if (!cur.data.expand) {
return true;
}
}, (cur, parent, isRoot, layerIndex) => {
// 返回时计算节点的areaWidth和areaHeight也就是子节点所占的高度之和包括外边距
let len = cur.data.expand === false ? 0 : cur._node.children.length
cur._node.childrenAreaWidth = len ? cur._node.children.reduce((h, item) => {
return h + item.width
}, 0) + (len + 1) * this.getMarginY(layerIndex + 1) : 0
if (isRoot) {
let len = cur.data.expand === false ? 0 : cur._node.children.length
cur._node.childrenAreaWidth = len ? cur._node.children.reduce((h, item) => {
return h + item.width
}, 0) + (len + 1) * this.getMarginX(layerIndex + 1) : 0
}
}, true, 0)
}
@ -72,19 +74,28 @@ class CatalogOrganization extends Base {
* javascript comment
* @Author: 王林25
* @Date: 2021-04-08 09:59:25
* @Desc: 遍历节点树计算节点的left
* @Desc: 遍历节点树计算节点的lefttop
*/
computedLeftValue() {
computedLeftTopValue() {
walk(this.root, null, (node, parent, isRoot, layerIndex) => {
if (node.nodeData.data.expand && node.children && node.children.length) {
let marginX = this.getMarginY(layerIndex + 1)
// 第一个子节点的left值 = 该节点中心的left值 - 子节点的宽度之和的一半
let left = node.left + node.width / 2 - node.childrenAreaWidth / 2
let totalLeft = left + marginX
node.children.forEach((cur) => {
cur.left = totalLeft
totalLeft += cur.width + marginX
})
let marginX = this.getMarginX(layerIndex + 1)
let marginY = this.getMarginY(layerIndex + 1)
if (isRoot) {
let left = node.left + node.width / 2 - node.childrenAreaWidth / 2
let totalLeft = left + marginX
node.children.forEach((cur) => {
cur.left = totalLeft
totalLeft += cur.width + marginX
})
} else {
let totalTop = node.top + node.height + marginY + node.expandBtnSize
node.children.forEach((cur) => {
cur.left = node.left + node.width * 0.5
cur.top = totalTop
totalTop += cur.height + marginY + node.expandBtnSize
})
}
}
}, null, true)
}
@ -93,17 +104,29 @@ class CatalogOrganization extends Base {
* javascript comment
* @Author: 王林25
* @Date: 2021-04-08 10:04:05
* @Desc: 调整节点top
* @Desc: 调整节点lefttop
*/
adjustTopValue() {
adjustLeftTopValue() {
walk(this.root, null, (node, parent, isRoot, layerIndex) => {
if (!node.nodeData.data.expand) {
return;
}
// 判断子节点所占的高度之和是否大于该节点自身,大于则需要调整位置
let difference = node.childrenAreaHeight - this.getMarginY(layerIndex + 1) * 2 - node.height
if (difference > 0) {
this.updateBrothers(node, difference / 2)
// 调整left
if (parent && parent.isRoot) {
let areaWidth = this.getNodeAreaWidth(node)
let difference = areaWidth - node.width
if (difference > 0) {
this.updateBrothersLeft(node, difference / 2)
}
}
// 调整top
let len = node.children.length
if (parent && !parent.isRoot && len > 0) {
let marginY = this.getMarginY(layerIndex + 1)
let totalHeight = node.children.reduce((h, item) => {
return h + item.height
}, 0) + (len + 1) * marginY + len * node.expandBtnSize
this.updateBrothersTop(node, totalHeight)
}
}, null, true)
}
@ -111,21 +134,80 @@ class CatalogOrganization extends Base {
/**
* javascript comment
* @Author: 王林25
* @Date: 2021-04-07 14:26:03
* @Desc: 更新兄弟节点的top
* @Date: 2021-04-12 18:55:03
* @Desc: 递归计算节点的宽度
*/
updateBrothers(node, addHeight) {
getNodeAreaWidth(node) {
let widthArr = []
let loop = (node, width) => {
if (node.children.length) {
width += node.width / 2
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-07-13 11:12:51
* @Desc: 调整兄弟节点的left
*/
updateBrothersLeft(node, addWidth) {
if (node.parent) {
let childrenList = node.parent.children
let index = childrenList.findIndex((item) => {
return item === node
})
// 第一个或最后一个节点自身也需要移动,否则两边不对称
if (index === 0 || index === childrenList.length - 1) {
let _offset = index === 0 ? -addWidth : addWidth
node.left += _offset
if (node.children && node.children.length) {
this.updateChildren(node.children, 'left', _offset)
}
}
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.updateBrothersLeft(node.parent, addWidth)
}
}
/**
* javascript comment
* @Author: 王林25
* @Date: 2021-04-07 14:26:03
* @Desc: 调整兄弟节点的top
*/
updateBrothersTop(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
} else if (_index > index) { // 下面的节点往下移
// 下面的节点往下移
if (_index > index) {
_offset = addHeight
}
item.top += _offset
@ -135,7 +217,7 @@ class CatalogOrganization extends Base {
}
})
// 更新父节点的位置
this.updateBrothers(node.parent, addHeight)
this.updateBrothersTop(node.parent, addHeight)
}
}
@ -145,7 +227,6 @@ class CatalogOrganization extends Base {
* @Desc: 绘制连线连接该节点到其子节点
*/
renderLine(node, lines) {
return
if (node.children.length <= 0) {
return [];
}
@ -153,21 +234,61 @@ class CatalogOrganization extends Base {
left,
top,
width,
height
height,
expandBtnSize
} = node
node.children.forEach((item, index) => {
let x1 = node.layerIndex === 0 ? left + width / 2 : left + width + 20
let y1 = node.layerIndex === 0 ? top + height / 2 : top + height / 2
let x2 = item.left
let y2 = item.top + item.height / 2
let path = ''
if (node.isRoot) {
path = this.quadraticCurvePath(x1, y1, x2, y2)
} else {
path = this.cubicBezierPath(x1, y1, x2, y2)
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
node.children.forEach((item, index) => {
let x2 = item.left +item.width / 2
let y2 = item.top
if (index === 0) {
minx = x2
} else if (index >= len - 1) {
maxx = x2
}
let path = `M ${x2},${y1 + s1} L ${x2},${y2}`
lines[index].plot(path)
})
// 父节点的竖线
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) {
let lin2 = this.draw.path()
node.style.line(lin2)
lin2.plot(`M ${minx},${y1 + s1} L ${maxx},${y1 + s1}`)
node._lines.push(lin2)
}
lines[index].plot(path)
})
} else {
let y1 = top + height
let maxy = 0
let x2 = node.left + node.width * 0.3
node.children.forEach((item, index) => {
let y2 = item.top + item.height / 2
if (index >= len - 1) {
maxy = y2
}
let path = `M ${x2},${y2} L ${x2 + node.width * 0.2},${y2}`
lines[index].plot(path)
})
// 竖线
if (len > 0) {
let lin2 = this.draw.path()
expandBtnSize = len > 0 ? expandBtnSize : 0
node.style.line(lin2)
lin2.plot(`M ${x2},${y1 + expandBtnSize} L ${x2},${maxy}`)
node._lines.push(lin2)
}
}
}
/**
@ -176,12 +297,19 @@ class CatalogOrganization extends Base {
* @Desc: 渲染按钮
*/
renderExpandBtn(node, btn) {
return
let {
width,
height
height,
expandBtnSize,
isRoot
} = node
btn.translate(width, height / 2)
if (!isRoot) {
let {
translateX,
translateY
} = btn.transform()
btn.translate(width * 0.3 - expandBtnSize / 2 - translateX, height + expandBtnSize / 2 - translateY)
}
}
}

View File

@ -179,7 +179,11 @@ class LogicalStructure extends Base {
width,
height
} = node
btn.translate(width, height / 2)
let {
translateX,
translateY
} = btn.transform()
btn.translate(width - translateX, height / 2 - translateY)
}
}

View File

@ -48,12 +48,6 @@ class MindMap extends Base {
computedBaseValue() {
walk(this.renderer.renderTree, null, (cur, parent, isRoot, layerIndex, index) => {
let newNode = this.createNode(cur, parent, isRoot, layerIndex)
// 更新时展开收缩按钮位置可能会变化,需要更新
if (newNode.updateHooks.length <= 0) {
newNode.updateHooks.push((node) => {
node.updateExpandBtnPos()
})
}
// 根节点定位在画布中心位置
if (isRoot) {
newNode.left = (this.mindMap.width - newNode.width) / 2
@ -195,10 +189,10 @@ class MindMap extends Base {
top,
width,
height,
_expandBtnSize
expandBtnSize
} = node
node.children.forEach((item, index) => {
let x1 = node.layerIndex === 0 ? left + width / 2 : item.dir === 'left' ? left - _expandBtnSize : left + width + 20
let x1 = node.layerIndex === 0 ? left + width / 2 : item.dir === 'left' ? left - expandBtnSize : left + width + 20
let y1 = node.layerIndex === 0 ? top + height / 2 : top + height / 2
let x2 = item.dir === 'left' ? item.left + item.width : item.left
let y2 = item.top + item.height / 2
@ -221,13 +215,13 @@ class MindMap extends Base {
let {
width,
height,
_expandBtnSize
expandBtnSize
} = node
let {
translateX,
translateY
} = btn.transform()
let x = (node.dir === 'left' ? 0 - _expandBtnSize : width) - translateX
let x = (node.dir === 'left' ? 0 - expandBtnSize : width) - translateX
let y = height / 2 - translateY
btn.translate(x, y)
}

View File

@ -154,7 +154,7 @@ class OrganizationStructure extends Base {
top,
width,
height,
_expandBtnSize,
expandBtnSize,
isRoot
} = node
let x1 = left + width / 2
@ -178,8 +178,8 @@ class OrganizationStructure extends Base {
// 父节点的竖线
let line1 = this.draw.path()
node.style.line(line1)
_expandBtnSize = len > 0 && !isRoot ? _expandBtnSize : 0
line1.plot(`M ${x1},${y1 + _expandBtnSize} L ${x1},${y1 + s1}`)
expandBtnSize = len > 0 && !isRoot ? expandBtnSize : 0
line1.plot(`M ${x1},${y1 + expandBtnSize} L ${x1},${y1 + s1}`)
node._lines.push(line1)
// 水平线
if (len > 1) {
@ -199,9 +199,13 @@ class OrganizationStructure extends Base {
let {
width,
height,
_expandBtnSize
expandBtnSize
} = node
btn.translate(width / 2 - _expandBtnSize / 2, height + _expandBtnSize / 2)
let {
translateX,
translateY
} = btn.transform()
btn.translate(width / 2 - expandBtnSize / 2 - translateX, height + expandBtnSize / 2 - translateY)
}
}

View File

@ -1,270 +0,0 @@
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: 计算节点的widthheight
*/
computedBaseValue() {
walk(this.renderTree, null, (node, parent, isRoot) => {
// 设置width、height
let {
children,
...props
} = node
let newNode = new Node({
...props,
mindMap: this.mindMap,
draw: this.draw
})
// 计算节点的宽高
newNode.getSize()
// 计算节点的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: 计算节点的lefttop
*/
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

View File

@ -26,6 +26,36 @@ export const tagColorList = [
}
]
/**
* javascript comment
* @Author: 王林25
* @Date: 2021-07-13 15:56:28
* @Desc: 布局结构列表
*/
export const layoutList = [
{
name: '逻辑结构图',
value: 'logicalStructure',
img: require('../assets/logicalStructure.jpg')
},
{
name: '思维导图',
value: 'mindMap',
img: require('../assets/mindMap.jpg')
},
{
name: '组织结构图',
value: 'organizationStructure',
img: require('../assets/organizationStructure.jpg')
},
{
name: '目录组织图',
value: 'catalogOrganization',
img: require('../assets/catalogOrganization.jpg')
}
]
export const layoutValueList = ['logicalStructure', 'mindMap', 'catalogOrganization', 'organizationStructure']
/**
* @Author: 王林
* @Date: 2021-06-24 22:58:42
@ -52,6 +82,36 @@ export const themeList = [
value: 'pinkGrape',
img: require('../assets/pinkGrape.jpg')
},
{
name: '薄荷',
value: 'mint',
img: require('../assets/mint.jpg')
},
{
name: '金色vip',
value: 'gold',
img: require('../assets/gold.jpg')
},
{
name: '活力橙',
value: 'vitalityOrange',
img: require('../assets/vitalityOrange.jpg')
},
{
name: '绿叶',
value: 'greenLeaf',
img: require('../assets/greenLeaf.jpg')
},
{
name: '暗色2',
value: 'dark2',
img: require('../assets/dark2.jpg')
},
{
name: '天清绿',
value: 'skyGreen',
img: require('../assets/skyGreen.jpg')
},
{
name: '脑图经典2',
value: 'classic2',

View File

@ -1,9 +1,25 @@
<template>
<Sidebar ref="sidebar" title="结构"> </Sidebar>
<Sidebar ref="sidebar" title="结构">
<div class="layoutList">
<div
class="layoutItem"
v-for="item in layoutList"
:key="item.value"
@click="useLayout(item)"
:class="{ active: item.value === layout }"
>
<div class="imgBox">
<img :src="item.img" alt="" />
</div>
<div class="name">{{ item.name }}</div>
</div>
</div>
</Sidebar>
</template>
<script>
import Sidebar from "./Sidebar";
import { layoutList } from "simple-mind-map/src/utils/constant";
/**
* @Author: 王林
@ -15,19 +31,77 @@ export default {
components: {
Sidebar,
},
props: {
mindMap: {
type: Object,
},
},
data() {
return {};
return {
layoutList,
layout: ''
};
},
created() {
this.$bus.$on("showStructure", () => {
this.$refs.sidebar.show = false;
this.$nextTick(() => {
this.layout = this.mindMap.getLayout();
this.$refs.sidebar.show = true;
});
});
},
methods: {
/**
* @Author: 王林
* @Date: 2021-06-24 23:04:38
* @Desc: 使用主题
*/
useLayout(layout) {
this.layout = layout.value;
this.mindMap.setLayout(layout.value);
},
},
};
</script>
<style lang="less" scoped>
.layoutList {
padding: 20px;
.layoutItem {
width: 100%;
cursor: pointer;
border-bottom: 1px solid #e9e9e9;
margin-bottom: 20px;
padding-bottom: 20px;
transition: all 0.2s;
border: 1px solid transparent;
&:last-of-type {
border: none;
}
&:hover {
box-shadow: 0 1px 2px -2px rgba(0, 0, 0, 0.16),
0 3px 6px 0 rgba(0, 0, 0, 0.12), 0 5px 12px 4px rgba(0, 0, 0, 0.09);
}
&.active {
border: 1px solid #67c23a;
}
.imgBox {
width: 100%;
img {
width: 100%;
}
}
.name {
text-align: center;
font-size: 14px;
}
}
}
</style>