mirror of
https://github.com/wanglin2/mind-map.git
synced 2026-02-17 22:08:25 +08:00
833 lines
23 KiB
JavaScript
833 lines
23 KiB
JavaScript
import Style from './Style'
|
|
import Shape from './Shape'
|
|
import { asyncRun, nodeToHTML } from '../../../utils'
|
|
import { G, Rect, ForeignObject, SVG } from '@svgdotjs/svg.js'
|
|
import nodeGeneralizationMethods from './nodeGeneralization'
|
|
import nodeExpandBtnMethods from './nodeExpandBtn'
|
|
import nodeCommandWrapsMethods from './nodeCommandWraps'
|
|
import nodeCreateContentsMethods from './nodeCreateContents'
|
|
import { CONSTANTS } from '../../../constants/constant'
|
|
|
|
// 节点类
|
|
class Node {
|
|
// 构造函数
|
|
constructor(opt = {}) {
|
|
// 节点数据
|
|
this.nodeData = this.handleData(opt.data || {})
|
|
// id
|
|
this.uid = opt.uid
|
|
// 控制实例
|
|
this.mindMap = opt.mindMap
|
|
// 渲染实例
|
|
this.renderer = opt.renderer
|
|
// 渲染器
|
|
this.draw = opt.draw || null
|
|
// 样式实例
|
|
this.style = new Style(this)
|
|
// 形状实例
|
|
this.shapeInstance = new Shape(this)
|
|
this.shapePadding = {
|
|
paddingX: 0,
|
|
paddingY: 0
|
|
}
|
|
// 是否是根节点
|
|
this.isRoot = opt.isRoot === undefined ? false : opt.isRoot
|
|
// 是否是概要节点
|
|
this.isGeneralization =
|
|
opt.isGeneralization === undefined ? false : opt.isGeneralization
|
|
this.generalizationBelongNode = null
|
|
// 节点层级
|
|
this.layerIndex = opt.layerIndex === undefined ? 0 : opt.layerIndex
|
|
// 节点宽
|
|
this.width = opt.width || 0
|
|
// 节点高
|
|
this.height = opt.height || 0
|
|
// left
|
|
this._left = opt.left || 0
|
|
// top
|
|
this._top = opt.top || 0
|
|
// 自定义位置
|
|
this.customLeft = opt.data.data.customLeft || undefined
|
|
this.customTop = opt.data.data.customTop || undefined
|
|
// 是否正在拖拽中
|
|
this.isDrag = false
|
|
// 父节点
|
|
this.parent = opt.parent || null
|
|
// 子节点
|
|
this.children = opt.children || []
|
|
// 节点内容的容器
|
|
this.group = null
|
|
this.shapeNode = null // 节点形状节点
|
|
// 节点内容对象
|
|
this._customNodeContent = null
|
|
this._imgData = null
|
|
this._iconData = null
|
|
this._textData = null
|
|
this._hyperlinkData = null
|
|
this._tagData = null
|
|
this._noteData = null
|
|
this.noteEl = null
|
|
this._expandBtn = null
|
|
this._lastExpandBtnType = null
|
|
this._showExpandBtn = false
|
|
this._openExpandNode = null
|
|
this._closeExpandNode = null
|
|
this._fillExpandNode = null
|
|
this._lines = []
|
|
this._generalizationLine = null
|
|
this._generalizationNode = null
|
|
this._unVisibleRectRegionNode = null
|
|
this._isMouseenter = false
|
|
// 尺寸信息
|
|
this._rectInfo = {
|
|
imgContentWidth: 0,
|
|
imgContentHeight: 0,
|
|
textContentWidth: 0,
|
|
textContentHeight: 0
|
|
}
|
|
// 概要节点的宽高
|
|
this._generalizationNodeWidth = 0
|
|
this._generalizationNodeHeight = 0
|
|
// 各种文字信息的间距
|
|
this.textContentItemMargin = this.mindMap.opt.textContentMargin
|
|
// 图片和文字节点的间距
|
|
this.blockContentMargin = this.mindMap.opt.imgTextMargin
|
|
// 展开收缩按钮尺寸
|
|
this.expandBtnSize = this.mindMap.opt.expandBtnSize
|
|
// 是否是多选节点
|
|
this.isMultipleChoice = false
|
|
// 是否需要重新layout
|
|
this.needLayout = false
|
|
// 概要相关方法
|
|
Object.keys(nodeGeneralizationMethods).forEach(item => {
|
|
this[item] = nodeGeneralizationMethods[item].bind(this)
|
|
})
|
|
// 展开收起按钮相关方法
|
|
Object.keys(nodeExpandBtnMethods).forEach(item => {
|
|
this[item] = nodeExpandBtnMethods[item].bind(this)
|
|
})
|
|
// 命令的相关方法
|
|
Object.keys(nodeCommandWrapsMethods).forEach(item => {
|
|
this[item] = nodeCommandWrapsMethods[item].bind(this)
|
|
})
|
|
// 创建节点内容的相关方法
|
|
Object.keys(nodeCreateContentsMethods).forEach(item => {
|
|
this[item] = nodeCreateContentsMethods[item].bind(this)
|
|
})
|
|
// 初始化
|
|
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
|
|
}
|
|
|
|
// 复位部分布局时会重新设置的数据
|
|
reset() {
|
|
this.children = []
|
|
this.parent = null
|
|
this.isRoot = false
|
|
this.layerIndex = 0
|
|
this.left = 0
|
|
this.top = 0
|
|
}
|
|
|
|
// 处理数据
|
|
handleData(data) {
|
|
data.data.expand = data.data.expand === false ? false : true
|
|
data.data.isActive = data.data.isActive === true ? true : false
|
|
data.children = data.children || []
|
|
return data
|
|
}
|
|
|
|
// 创建节点的各个内容对象数据
|
|
createNodeData() {
|
|
// 自定义节点内容
|
|
let { isUseCustomNodeContent, customCreateNodeContent } = this.mindMap.opt
|
|
if (isUseCustomNodeContent && customCreateNodeContent) {
|
|
this._customNodeContent = customCreateNodeContent(this)
|
|
}
|
|
// 如果没有返回内容,那么还是使用内置的节点内容
|
|
if (this._customNodeContent) return
|
|
this._imgData = this.createImgNode()
|
|
this._iconData = this.createIconNode()
|
|
this._textData = this.createTextNode()
|
|
this._hyperlinkData = this.createHyperlinkNode()
|
|
this._tagData = this.createTagNode()
|
|
this._noteData = this.createNoteNode()
|
|
}
|
|
|
|
// 计算节点的宽高
|
|
getSize() {
|
|
this.updateGeneralization()
|
|
this.createNodeData()
|
|
let { width, height } = this.getNodeRect()
|
|
// 判断节点尺寸是否有变化
|
|
let changed = this.width !== width || this.height !== height
|
|
this.width = width
|
|
this.height = height
|
|
return changed
|
|
}
|
|
|
|
// 计算节点尺寸信息
|
|
getNodeRect() {
|
|
// 自定义节点内容
|
|
if (this.isUseCustomNodeContent()) {
|
|
let rect = this.measureCustomNodeContentSize(this._customNodeContent)
|
|
return {
|
|
width: rect.width,
|
|
height: rect.height
|
|
}
|
|
}
|
|
// 宽高
|
|
let imgContentWidth = 0
|
|
let imgContentHeight = 0
|
|
let textContentWidth = 0
|
|
let textContentHeight = 0
|
|
// 存在图片
|
|
if (this._imgData) {
|
|
this._rectInfo.imgContentWidth = imgContentWidth = this._imgData.width
|
|
this._rectInfo.imgContentHeight = imgContentHeight = this._imgData.height
|
|
}
|
|
// 图标
|
|
if (this._iconData.length > 0) {
|
|
textContentWidth += this._iconData.reduce((sum, cur) => {
|
|
textContentHeight = Math.max(textContentHeight, cur.height)
|
|
return (sum += cur.width + this.textContentItemMargin)
|
|
}, 0)
|
|
}
|
|
// 文字
|
|
if (this._textData) {
|
|
textContentWidth += this._textData.width
|
|
textContentHeight = Math.max(textContentHeight, this._textData.height)
|
|
}
|
|
// 超链接
|
|
if (this._hyperlinkData) {
|
|
textContentWidth += this._hyperlinkData.width
|
|
textContentHeight = Math.max(
|
|
textContentHeight,
|
|
this._hyperlinkData.height
|
|
)
|
|
}
|
|
// 标签
|
|
if (this._tagData.length > 0) {
|
|
textContentWidth += this._tagData.reduce((sum, cur) => {
|
|
textContentHeight = Math.max(textContentHeight, cur.height)
|
|
return (sum += cur.width + this.textContentItemMargin)
|
|
}, 0)
|
|
}
|
|
// 备注
|
|
if (this._noteData) {
|
|
textContentWidth += this._noteData.width
|
|
textContentHeight = Math.max(textContentHeight, this._noteData.height)
|
|
}
|
|
// 文字内容部分的尺寸
|
|
this._rectInfo.textContentWidth = textContentWidth
|
|
this._rectInfo.textContentHeight = textContentHeight
|
|
// 间距
|
|
let margin =
|
|
imgContentHeight > 0 && textContentHeight > 0
|
|
? this.blockContentMargin
|
|
: 0
|
|
let { paddingX, paddingY } = this.getPaddingVale()
|
|
// 纯内容宽高
|
|
let _width = Math.max(imgContentWidth, textContentWidth)
|
|
let _height = imgContentHeight + textContentHeight
|
|
// 计算节点形状需要的附加内边距
|
|
let { paddingX: shapePaddingX, paddingY: shapePaddingY } =
|
|
this.shapeInstance.getShapePadding(_width, _height, paddingX, paddingY)
|
|
this.shapePadding.paddingX = shapePaddingX
|
|
this.shapePadding.paddingY = shapePaddingY
|
|
return {
|
|
width: _width + paddingX * 2 + shapePaddingX * 2,
|
|
height: _height + paddingY * 2 + margin + shapePaddingY * 2
|
|
}
|
|
}
|
|
|
|
// 定位节点内容
|
|
layout() {
|
|
// 清除之前的内容
|
|
this.group.clear()
|
|
let { width, height, textContentItemMargin } = this
|
|
let { paddingY } = this.getPaddingVale()
|
|
paddingY += this.shapePadding.paddingY
|
|
// 节点形状
|
|
this.shapeNode = this.shapeInstance.createShape()
|
|
this.group.add(this.shapeNode)
|
|
this.updateNodeShape()
|
|
// 渲染一个隐藏的矩形区域,用来触发展开收起按钮的显示
|
|
this.renderExpandBtnPlaceholderRect()
|
|
// 概要节点添加一个带所属节点id的类名
|
|
if (this.isGeneralization && this.generalizationBelongNode) {
|
|
this.group.addClass('generalization_' + this.generalizationBelongNode.uid)
|
|
}
|
|
// 如果存在自定义节点内容,那么使用自定义节点内容
|
|
if (this.isUseCustomNodeContent()) {
|
|
let foreignObject = new ForeignObject()
|
|
foreignObject.width(width)
|
|
foreignObject.height(height)
|
|
foreignObject.add(SVG(this._customNodeContent))
|
|
this.group.add(foreignObject)
|
|
return
|
|
}
|
|
// 图片节点
|
|
let imgHeight = 0
|
|
if (this._imgData) {
|
|
imgHeight = this._imgData.height
|
|
this.group.add(this._imgData.node)
|
|
this._imgData.node.cx(width / 2).y(paddingY)
|
|
}
|
|
// 内容节点
|
|
let textContentNested = new G()
|
|
let textContentOffsetX = 0
|
|
// icon
|
|
let iconNested = new G()
|
|
if (this._iconData && this._iconData.length > 0) {
|
|
let iconLeft = 0
|
|
this._iconData.forEach(item => {
|
|
item.node
|
|
.x(textContentOffsetX + iconLeft)
|
|
.y((this._rectInfo.textContentHeight - item.height) / 2)
|
|
iconNested.add(item.node)
|
|
iconLeft += item.width + textContentItemMargin
|
|
})
|
|
textContentNested.add(iconNested)
|
|
textContentOffsetX += iconLeft
|
|
}
|
|
// 文字
|
|
if (this._textData) {
|
|
this._textData.node.attr('data-offsetx', textContentOffsetX)
|
|
this._textData.node.x(textContentOffsetX).y(0)
|
|
textContentNested.add(this._textData.node)
|
|
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
|
|
}
|
|
// 标签
|
|
let tagNested = new G()
|
|
if (this._tagData && this._tagData.length > 0) {
|
|
let tagLeft = 0
|
|
this._tagData.forEach(item => {
|
|
item.node
|
|
.x(textContentOffsetX + tagLeft)
|
|
.y((this._rectInfo.textContentHeight - item.height) / 2)
|
|
tagNested.add(item.node)
|
|
tagLeft += item.width + textContentItemMargin
|
|
})
|
|
textContentNested.add(tagNested)
|
|
textContentOffsetX += tagLeft
|
|
}
|
|
// 备注
|
|
if (this._noteData) {
|
|
this._noteData.node
|
|
.x(textContentOffsetX)
|
|
.y((this._rectInfo.textContentHeight - this._noteData.height) / 2)
|
|
textContentNested.add(this._noteData.node)
|
|
textContentOffsetX += this._noteData.width
|
|
}
|
|
// 文字内容整体
|
|
textContentNested.translate(
|
|
width / 2 - textContentNested.bbox().width / 2,
|
|
imgHeight +
|
|
paddingY +
|
|
(imgHeight > 0 && this._rectInfo.textContentHeight > 0
|
|
? this.blockContentMargin
|
|
: 0)
|
|
)
|
|
this.group.add(textContentNested)
|
|
}
|
|
|
|
// 渲染展开收起按钮的隐藏占位元素
|
|
renderExpandBtnPlaceholderRect() {
|
|
if (!this.mindMap.opt.alwaysShowExpandBtn) {
|
|
let { width, height } = this
|
|
if (!this._unVisibleRectRegionNode) {
|
|
this._unVisibleRectRegionNode = new Rect()
|
|
this._unVisibleRectRegionNode.fill({
|
|
color: 'transparent'
|
|
})
|
|
}
|
|
this.group.add(this._unVisibleRectRegionNode)
|
|
this.renderer.layout.renderExpandBtnRect(this._unVisibleRectRegionNode, this.expandBtnSize, width, height, this)
|
|
}
|
|
}
|
|
|
|
// 给节点绑定事件
|
|
bindGroupEvent() {
|
|
// 单击事件,选中节点
|
|
this.group.on('click', e => {
|
|
this.mindMap.emit('node_click', this, e)
|
|
if (this.isMultipleChoice) {
|
|
e.stopPropagation()
|
|
this.isMultipleChoice = false
|
|
return
|
|
}
|
|
this.active(e)
|
|
})
|
|
this.group.on('mousedown', e => {
|
|
if (this.isRoot && e.which === 3) {
|
|
e.stopPropagation()
|
|
}
|
|
if (!this.isRoot) {
|
|
e.stopPropagation()
|
|
}
|
|
// 多选和取消多选
|
|
if (e.ctrlKey && this.mindMap.opt.enableCtrlKeyNodeSelection) {
|
|
this.isMultipleChoice = true
|
|
let isActive = this.nodeData.data.isActive
|
|
if (!isActive)
|
|
this.mindMap.emit(
|
|
'before_node_active',
|
|
this,
|
|
this.renderer.activeNodeList
|
|
)
|
|
this.mindMap.execCommand('SET_NODE_ACTIVE', this, !isActive)
|
|
this.mindMap.renderer[isActive ? 'removeActiveNode' : 'addActiveNode'](
|
|
this
|
|
)
|
|
this.mindMap.emit(
|
|
'node_active',
|
|
isActive ? null : this,
|
|
this.mindMap.renderer.activeNodeList
|
|
)
|
|
}
|
|
this.mindMap.emit('node_mousedown', this, e)
|
|
})
|
|
this.group.on('mouseup', e => {
|
|
if (!this.isRoot) {
|
|
e.stopPropagation()
|
|
}
|
|
this.mindMap.emit('node_mouseup', this, e)
|
|
})
|
|
this.group.on('mouseenter', e => {
|
|
this._isMouseenter = true
|
|
// 显示展开收起按钮
|
|
this.showExpandBtn()
|
|
this.mindMap.emit('node_mouseenter', this, e)
|
|
})
|
|
this.group.on('mouseleave', e => {
|
|
this._isMouseenter = false
|
|
this.hideExpandBtn()
|
|
this.mindMap.emit('node_mouseleave', this, e)
|
|
})
|
|
// 双击事件
|
|
this.group.on('dblclick', e => {
|
|
if (this.mindMap.opt.readonly) {
|
|
return
|
|
}
|
|
e.stopPropagation()
|
|
this.mindMap.emit('node_dblclick', this, e)
|
|
})
|
|
// 右键菜单事件
|
|
this.group.on('contextmenu', e => {
|
|
// 按住ctrl键点击鼠标左键不知为何触发的是contextmenu事件
|
|
if (this.mindMap.opt.readonly || e.ctrlKey) {
|
|
// || this.isGeneralization
|
|
return
|
|
}
|
|
e.stopPropagation()
|
|
e.preventDefault()
|
|
if (this.nodeData.data.isActive) {
|
|
this.renderer.clearActive()
|
|
}
|
|
this.active(e)
|
|
this.mindMap.emit('node_contextmenu', e, this)
|
|
})
|
|
}
|
|
|
|
// 激活节点
|
|
active(e) {
|
|
if (this.mindMap.opt.readonly) {
|
|
return
|
|
}
|
|
e && e.stopPropagation()
|
|
if (this.nodeData.data.isActive) {
|
|
return
|
|
}
|
|
this.mindMap.emit('before_node_active', this, this.renderer.activeNodeList)
|
|
this.renderer.clearActive()
|
|
this.mindMap.execCommand('SET_NODE_ACTIVE', this, true)
|
|
this.renderer.addActiveNode(this)
|
|
this.mindMap.emit('node_active', this, this.renderer.activeNodeList)
|
|
}
|
|
|
|
// 更新节点
|
|
update(isLayout = false) {
|
|
if (!this.group) {
|
|
return
|
|
}
|
|
let {
|
|
alwaysShowExpandBtn
|
|
} = this.mindMap.opt
|
|
if (alwaysShowExpandBtn) {
|
|
// 需要移除展开收缩按钮
|
|
if (this._expandBtn && this.nodeData.children.length <= 0) {
|
|
this.removeExpandBtn()
|
|
} else {
|
|
// 更新展开收起按钮
|
|
this.renderExpandBtn()
|
|
}
|
|
} else {
|
|
let { isActive, expand } = this.nodeData.data
|
|
// 展开状态且非激活状态,且当前鼠标不在它上面,才隐藏
|
|
if (expand && !isActive && !this._isMouseenter) {
|
|
this.hideExpandBtn()
|
|
} else {
|
|
this.showExpandBtn()
|
|
}
|
|
}
|
|
// 更新概要
|
|
this.renderGeneralization()
|
|
// 更新节点位置
|
|
let t = this.group.transform()
|
|
// 如果节点位置没有变化,则返回
|
|
if (this.left === t.translateX && this.top === t.translateY) return
|
|
this.group.translate(this.left - t.translateX, this.top - t.translateY)
|
|
}
|
|
|
|
// 重新渲染节点,即重新创建节点内容、计算节点大小、计算节点内容布局、更新展开收起按钮,概要及位置
|
|
reRender() {
|
|
let sizeChange = this.getSize()
|
|
this.layout()
|
|
this.update()
|
|
return sizeChange
|
|
}
|
|
|
|
// 更新节点形状样式
|
|
updateNodeShape() {
|
|
if (!this.shapeNode) return
|
|
const shape = this.getShape()
|
|
this.style[shape === CONSTANTS.SHAPE.RECTANGLE ? 'rect' : 'shape'](
|
|
this.shapeNode
|
|
)
|
|
}
|
|
|
|
// 递归渲染
|
|
render(callback = () => {}) {
|
|
// 节点
|
|
// 重新渲染连线
|
|
this.renderLine()
|
|
let isLayout = false
|
|
if (!this.group) {
|
|
isLayout = true
|
|
// 创建组
|
|
this.group = new G()
|
|
this.group.css({
|
|
cursor: 'default'
|
|
})
|
|
this.bindGroupEvent()
|
|
this.draw.add(this.group)
|
|
this.layout()
|
|
this.update(isLayout)
|
|
} else {
|
|
this.draw.add(this.group)
|
|
if (this.needLayout) {
|
|
this.needLayout = false
|
|
this.layout()
|
|
}
|
|
if (this.needRerenderExpandBtnPlaceholderRect) {
|
|
this.needRerenderExpandBtnPlaceholderRect = false
|
|
this.renderExpandBtnPlaceholderRect()
|
|
}
|
|
this.update()
|
|
}
|
|
// 子节点
|
|
if (
|
|
this.children &&
|
|
this.children.length &&
|
|
this.nodeData.data.expand !== false
|
|
) {
|
|
let index = 0
|
|
asyncRun(
|
|
this.children.map(item => {
|
|
return () => {
|
|
item.render(() => {
|
|
index++
|
|
if (index >= this.children.length) {
|
|
callback()
|
|
}
|
|
})
|
|
}
|
|
})
|
|
)
|
|
} else {
|
|
callback()
|
|
}
|
|
// 手动插入的节点立即获得焦点并且开启编辑模式
|
|
if (this.nodeData.inserting) {
|
|
delete this.nodeData.inserting
|
|
this.active()
|
|
setTimeout(() => {
|
|
this.mindMap.emit('node_dblclick', this, null, true)
|
|
}, 0)
|
|
}
|
|
}
|
|
|
|
// 递归删除,只是从画布删除,节点容器还在,后续还可以重新插回画布
|
|
remove() {
|
|
if (!this.group) return
|
|
this.group.remove()
|
|
this.removeGeneralization()
|
|
this.removeLine()
|
|
// 子节点
|
|
if (this.children && this.children.length) {
|
|
asyncRun(
|
|
this.children.map(item => {
|
|
return () => {
|
|
item.remove()
|
|
}
|
|
})
|
|
)
|
|
}
|
|
}
|
|
|
|
// 销毁节点,不但会从画布删除,而且原节点直接置空,后续无法再插回画布
|
|
destroy() {
|
|
if (!this.group) return
|
|
this.group.remove()
|
|
this.removeGeneralization()
|
|
this.removeLine()
|
|
this.group = null
|
|
}
|
|
|
|
// 隐藏节点
|
|
hide() {
|
|
this.group.hide()
|
|
this.hideGeneralization()
|
|
if (this.parent) {
|
|
let index = this.parent.children.indexOf(this)
|
|
this.parent._lines[index] && this.parent._lines[index].hide()
|
|
this._lines.forEach(item => {
|
|
item.hide()
|
|
})
|
|
}
|
|
// 子节点
|
|
if (this.children && this.children.length) {
|
|
asyncRun(
|
|
this.children.map(item => {
|
|
return () => {
|
|
item.hide()
|
|
}
|
|
})
|
|
)
|
|
}
|
|
}
|
|
|
|
// 显示节点
|
|
show() {
|
|
if (!this.group) {
|
|
return
|
|
}
|
|
this.group.show()
|
|
this.showGeneralization()
|
|
if (this.parent) {
|
|
let index = this.parent.children.indexOf(this)
|
|
this.parent._lines[index] && this.parent._lines[index].show()
|
|
this._lines.forEach(item => {
|
|
item.show()
|
|
})
|
|
}
|
|
// 子节点
|
|
if (this.children && this.children.length) {
|
|
asyncRun(
|
|
this.children.map(item => {
|
|
return () => {
|
|
item.show()
|
|
}
|
|
})
|
|
)
|
|
}
|
|
}
|
|
|
|
// 连线
|
|
renderLine(deep = false) {
|
|
if (this.nodeData.data.expand === false) {
|
|
return
|
|
}
|
|
let childrenLen = this.nodeData.children.length
|
|
// 切换为鱼骨结构时,清空根节点和二级节点的连线
|
|
if (
|
|
this.mindMap.opt.layout === CONSTANTS.LAYOUT.FISHBONE &&
|
|
(this.isRoot || this.layerIndex === 1)
|
|
) {
|
|
childrenLen = 0
|
|
}
|
|
if (childrenLen > this._lines.length) {
|
|
// 创建缺少的线
|
|
new Array(childrenLen - this._lines.length).fill(0).forEach(() => {
|
|
this._lines.push(this.draw.path())
|
|
})
|
|
} else if (childrenLen < this._lines.length) {
|
|
// 删除多余的线
|
|
this._lines.slice(childrenLen).forEach(line => {
|
|
line.remove()
|
|
})
|
|
this._lines = this._lines.slice(0, childrenLen)
|
|
}
|
|
// 画线
|
|
this.renderer.layout.renderLine(
|
|
this,
|
|
this._lines,
|
|
(line, node) => {
|
|
// 添加样式
|
|
this.styleLine(line, node)
|
|
},
|
|
this.style.getStyle('lineStyle', true)
|
|
)
|
|
// 子级的连线也需要更新
|
|
if (deep && this.children && this.children.length > 0) {
|
|
this.children.forEach(item => {
|
|
item.renderLine(deep)
|
|
})
|
|
}
|
|
}
|
|
|
|
// 获取节点形状
|
|
getShape() {
|
|
// 节点使用功能横线风格的话不支持设置形状,直接使用默认的矩形
|
|
return this.mindMap.themeConfig.nodeUseLineStyle
|
|
? CONSTANTS.SHAPE.RECTANGLE
|
|
: this.style.getStyle('shape', false, false)
|
|
}
|
|
|
|
// 检查节点是否存在自定义数据
|
|
hasCustomPosition() {
|
|
return this.customLeft !== undefined && this.customTop !== undefined
|
|
}
|
|
|
|
// 检查节点是否存在自定义位置的祖先节点
|
|
ancestorHasCustomPosition() {
|
|
let node = this
|
|
while (node) {
|
|
if (node.hasCustomPosition()) {
|
|
return true
|
|
}
|
|
node = node.parent
|
|
}
|
|
return false
|
|
}
|
|
|
|
// 添加子节点
|
|
addChildren(node) {
|
|
this.children.push(node)
|
|
}
|
|
|
|
// 设置连线样式
|
|
styleLine(line, node) {
|
|
let width =
|
|
node.getSelfInhertStyle('lineWidth') || node.getStyle('lineWidth', true)
|
|
let color =
|
|
node.getSelfInhertStyle('lineColor') || node.getStyle('lineColor', true)
|
|
let dasharray =
|
|
node.getSelfInhertStyle('lineDasharray') ||
|
|
node.getStyle('lineDasharray', true)
|
|
this.style.line(line, {
|
|
width,
|
|
color,
|
|
dasharray
|
|
})
|
|
}
|
|
|
|
// 移除连线
|
|
removeLine() {
|
|
this._lines.forEach(line => {
|
|
line.remove()
|
|
})
|
|
this._lines = []
|
|
}
|
|
|
|
// 检测当前节点是否是某个节点的祖先节点
|
|
isParent(node) {
|
|
if (this === node) {
|
|
return false
|
|
}
|
|
let parent = node.parent
|
|
while (parent) {
|
|
if (this === parent) {
|
|
return true
|
|
}
|
|
parent = parent.parent
|
|
}
|
|
return false
|
|
}
|
|
|
|
// 检测当前节点是否是某个节点的兄弟节点
|
|
isBrother(node) {
|
|
if (!this.parent || this === node) {
|
|
return false
|
|
}
|
|
return this.parent.children.find(item => {
|
|
return item === node
|
|
})
|
|
}
|
|
|
|
// 获取padding值
|
|
getPaddingVale() {
|
|
let { isActive } = this.nodeData.data
|
|
return {
|
|
paddingX: this.getStyle('paddingX', true, isActive),
|
|
paddingY: this.getStyle('paddingY', true, isActive)
|
|
}
|
|
}
|
|
|
|
// 获取某个样式
|
|
getStyle(prop, root, isActive) {
|
|
let v = this.style.merge(prop, root, isActive)
|
|
return v === undefined ? '' : v
|
|
}
|
|
|
|
// 获取自定义样式
|
|
getSelfStyle(prop) {
|
|
return this.style.getSelfStyle(prop)
|
|
}
|
|
|
|
// 获取最近一个存在自身自定义样式的祖先节点的自定义样式
|
|
getParentSelfStyle(prop) {
|
|
if (this.parent) {
|
|
return (
|
|
this.parent.getSelfStyle(prop) || this.parent.getParentSelfStyle(prop)
|
|
)
|
|
}
|
|
return null
|
|
}
|
|
|
|
// 获取自身可继承的自定义样式
|
|
getSelfInhertStyle(prop) {
|
|
return (
|
|
this.getSelfStyle(prop) || // 自身
|
|
this.getParentSelfStyle(prop)
|
|
) // 父级
|
|
}
|
|
|
|
// 获取数据
|
|
getData(key) {
|
|
return key ? this.nodeData.data[key] || '' : this.nodeData.data
|
|
}
|
|
|
|
// 是否存在自定义样式
|
|
hasCustomStyle() {
|
|
return this.style.hasCustomStyle()
|
|
}
|
|
}
|
|
|
|
export default Node
|