1.修改主题不再完全重新渲染;2.重构Node类核心逻辑;3.Shape类不再直接添加节点,而是返回节点
This commit is contained in:
parent
1795773af9
commit
79ae08fc9a
@ -135,7 +135,7 @@ class MindMap {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 初始渲染
|
// 初始渲染
|
||||||
this.reRender()
|
this.render()
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.command.addHistory()
|
this.command.addHistory()
|
||||||
}, 0)
|
}, 0)
|
||||||
@ -153,11 +153,11 @@ class MindMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 渲染,部分渲染
|
// 渲染,部分渲染
|
||||||
render(callback) {
|
render(callback, source = '') {
|
||||||
this.batchExecution.push('render', () => {
|
this.batchExecution.push('render', () => {
|
||||||
this.initTheme()
|
this.initTheme()
|
||||||
this.renderer.reRender = false
|
this.renderer.reRender = false
|
||||||
this.renderer.render(callback)
|
this.renderer.render(callback, source)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,7 +206,7 @@ class MindMap {
|
|||||||
setTheme(theme) {
|
setTheme(theme) {
|
||||||
this.renderer.clearAllActive()
|
this.renderer.clearAllActive()
|
||||||
this.opt.theme = theme
|
this.opt.theme = theme
|
||||||
this.reRender()
|
this.render(null, 'changeTheme')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前主题
|
// 获取当前主题
|
||||||
@ -217,7 +217,7 @@ class MindMap {
|
|||||||
// 设置主题配置
|
// 设置主题配置
|
||||||
setThemeConfig(config) {
|
setThemeConfig(config) {
|
||||||
this.opt.themeConfig = config
|
this.opt.themeConfig = config
|
||||||
this.reRender()
|
this.render(null, 'changeTheme')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取自定义主题配置
|
// 获取自定义主题配置
|
||||||
|
|||||||
@ -1,12 +1,14 @@
|
|||||||
import Style from './Style'
|
import Style from './Style'
|
||||||
import Shape from './Shape'
|
import Shape from './Shape'
|
||||||
import { resizeImgSize, asyncRun, measureText } from './utils'
|
import { asyncRun } from './utils'
|
||||||
import { Image, SVG, Circle, A, G, Rect, Text, ForeignObject } from '@svgdotjs/svg.js'
|
import { G } from '@svgdotjs/svg.js'
|
||||||
import btnsSvg from './svg/btns'
|
import nodeGeneralizationMethods from './utils/nodeGeneralization'
|
||||||
import iconsSvg from './svg/icons'
|
import nodeExpandBtnMethods from './utils/nodeExpandBtn'
|
||||||
|
import nodeCommandWrapsMethods from './utils/nodeCommandWraps'
|
||||||
|
import nodeCreateContentsMethods from './utils/nodeCreateContents'
|
||||||
|
|
||||||
|
|
||||||
// 节点类
|
// 节点类
|
||||||
|
|
||||||
class Node {
|
class Node {
|
||||||
// 构造函数
|
// 构造函数
|
||||||
constructor(opt = {}) {
|
constructor(opt = {}) {
|
||||||
@ -84,8 +86,26 @@ class Node {
|
|||||||
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.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()
|
this.getSize()
|
||||||
}
|
}
|
||||||
@ -125,28 +145,6 @@ class Node {
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查节点是否存在自定义数据
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建节点的各个内容对象数据
|
// 创建节点的各个内容对象数据
|
||||||
createNodeData() {
|
createNodeData() {
|
||||||
this._imgData = this.createImgNode()
|
this._imgData = this.createImgNode()
|
||||||
@ -155,68 +153,11 @@ class Node {
|
|||||||
this._hyperlinkData = this.createHyperlinkNode()
|
this._hyperlinkData = this.createHyperlinkNode()
|
||||||
this._tagData = this.createTagNode()
|
this._tagData = this.createTagNode()
|
||||||
this._noteData = this.createNoteNode()
|
this._noteData = this.createNoteNode()
|
||||||
this.createGeneralizationNode()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解绑所有事件
|
|
||||||
removeAllEvent() {
|
|
||||||
if (this._noteData) {
|
|
||||||
this._noteData.node.off(['mouseover', 'mouseout'])
|
|
||||||
}
|
|
||||||
if (this._expandBtn) {
|
|
||||||
this._expandBtn.off(['mouseover', 'mouseout', 'click'])
|
|
||||||
}
|
|
||||||
if (this.group) {
|
|
||||||
this.group.off([
|
|
||||||
'click',
|
|
||||||
'dblclick',
|
|
||||||
'contextmenu',
|
|
||||||
'mousedown',
|
|
||||||
'mouseup',
|
|
||||||
'mouseenter',
|
|
||||||
'mouseleave'
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 移除节点内容
|
|
||||||
removeAllNode() {
|
|
||||||
// 节点内的内容
|
|
||||||
;[
|
|
||||||
this._imgData,
|
|
||||||
this._iconData,
|
|
||||||
this._textData,
|
|
||||||
this._hyperlinkData,
|
|
||||||
this._tagData,
|
|
||||||
this._noteData
|
|
||||||
].forEach(item => {
|
|
||||||
if (item && item.node) item.node.remove()
|
|
||||||
})
|
|
||||||
this._imgData = null
|
|
||||||
this._iconData = null
|
|
||||||
this._textData = null
|
|
||||||
this._hyperlinkData = null
|
|
||||||
this._tagData = null
|
|
||||||
this._noteData = null
|
|
||||||
// 展开收缩按钮
|
|
||||||
if (this._expandBtn) {
|
|
||||||
this._expandBtn.remove()
|
|
||||||
this._expandBtn = null
|
|
||||||
}
|
|
||||||
// 组
|
|
||||||
if (this.group) {
|
|
||||||
this.group.clear()
|
|
||||||
this.group.remove()
|
|
||||||
this.group = null
|
|
||||||
}
|
|
||||||
// 概要
|
|
||||||
this.removeGeneralization()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算节点的宽高
|
// 计算节点的宽高
|
||||||
getSize() {
|
getSize() {
|
||||||
this.removeAllEvent()
|
this.updateGeneralization()
|
||||||
this.removeAllNode()
|
|
||||||
this.createNodeData()
|
this.createNodeData()
|
||||||
let { width, height } = this.getNodeRect()
|
let { width, height } = this.getNodeRect()
|
||||||
// 判断节点尺寸是否有变化
|
// 判断节点尺寸是否有变化
|
||||||
@ -293,276 +234,23 @@ class Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建图片节点
|
|
||||||
createImgNode() {
|
|
||||||
let img = this.nodeData.data.image
|
|
||||||
if (!img) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let imgSize = this.getImgShowSize()
|
|
||||||
let node = new Image().load(img).size(...imgSize)
|
|
||||||
if (this.nodeData.data.imageTitle) {
|
|
||||||
node.attr('title', this.nodeData.data.imageTitle)
|
|
||||||
}
|
|
||||||
node.on('dblclick', e => {
|
|
||||||
this.mindMap.emit('node_img_dblclick', this, e)
|
|
||||||
})
|
|
||||||
return {
|
|
||||||
node,
|
|
||||||
width: imgSize[0],
|
|
||||||
height: imgSize[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取图片显示宽高
|
|
||||||
getImgShowSize() {
|
|
||||||
return resizeImgSize(
|
|
||||||
this.nodeData.data.imageSize.width,
|
|
||||||
this.nodeData.data.imageSize.height,
|
|
||||||
this.mindMap.themeConfig.imgMaxWidth,
|
|
||||||
this.mindMap.themeConfig.imgMaxHeight
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建icon节点
|
|
||||||
createIconNode() {
|
|
||||||
let _data = this.nodeData.data
|
|
||||||
if (!_data.icon || _data.icon.length <= 0) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
let iconSize = this.mindMap.themeConfig.iconSize
|
|
||||||
return _data.icon.map(item => {
|
|
||||||
return {
|
|
||||||
node: SVG(iconsSvg.getNodeIconListIcon(item)).size(iconSize, iconSize),
|
|
||||||
width: iconSize,
|
|
||||||
height: iconSize
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建富文本节点
|
|
||||||
createRichTextNode() {
|
|
||||||
let g = new G()
|
|
||||||
let html = `<div>${this.nodeData.data.text}</div>`
|
|
||||||
let div = document.createElement('div')
|
|
||||||
div.innerHTML = html
|
|
||||||
div.style.cssText = `position: fixed; left: -999999px;`
|
|
||||||
let el = div.children[0]
|
|
||||||
el.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml')
|
|
||||||
el.style.maxWidth = this.mindMap.opt.textAutoWrapWidth + 'px'
|
|
||||||
this.mindMap.el.appendChild(div)
|
|
||||||
let { width, height } = el.getBoundingClientRect()
|
|
||||||
width = Math.ceil(width)
|
|
||||||
height = Math.ceil(height)
|
|
||||||
g.attr('data-width', width)
|
|
||||||
g.attr('data-height', height)
|
|
||||||
html = div.innerHTML
|
|
||||||
this.mindMap.el.removeChild(div)
|
|
||||||
let foreignObject = new ForeignObject()
|
|
||||||
foreignObject.width(width)
|
|
||||||
foreignObject.height(height)
|
|
||||||
foreignObject.add(SVG(html))
|
|
||||||
g.add(foreignObject)
|
|
||||||
return {
|
|
||||||
node: g,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建文本节点
|
|
||||||
createTextNode() {
|
|
||||||
if (this.nodeData.data.richText) {
|
|
||||||
return this.createRichTextNode()
|
|
||||||
}
|
|
||||||
let g = new G()
|
|
||||||
let fontSize = this.getStyle(
|
|
||||||
'fontSize',
|
|
||||||
false,
|
|
||||||
this.nodeData.data.isActive
|
|
||||||
)
|
|
||||||
let lineHeight = this.getStyle(
|
|
||||||
'lineHeight',
|
|
||||||
false,
|
|
||||||
this.nodeData.data.isActive
|
|
||||||
)
|
|
||||||
// 文本超长自动换行
|
|
||||||
let textStyle = this.style.getTextFontStyle()
|
|
||||||
let textArr = this.nodeData.data.text.split(/\n/gim)
|
|
||||||
let maxWidth = this.mindMap.opt.textAutoWrapWidth
|
|
||||||
textArr.forEach((item, index) => {
|
|
||||||
let arr = item.split('')
|
|
||||||
let lines = []
|
|
||||||
let line = []
|
|
||||||
while(arr.length) {
|
|
||||||
line.push(arr.shift())
|
|
||||||
let text = line.join('')
|
|
||||||
if (measureText(text, textStyle).width >= maxWidth) {
|
|
||||||
lines.push(text)
|
|
||||||
line = []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (line.length > 0) {
|
|
||||||
lines.push(line.join(''))
|
|
||||||
}
|
|
||||||
textArr[index] = lines.join('\n')
|
|
||||||
})
|
|
||||||
textArr = textArr.join('\n').split(/\n/gim)
|
|
||||||
textArr.forEach((item, index) => {
|
|
||||||
let node = new Text().text(item)
|
|
||||||
this.style.text(node)
|
|
||||||
node.y(fontSize * lineHeight * index)
|
|
||||||
g.add(node)
|
|
||||||
})
|
|
||||||
let { width, height } = g.bbox()
|
|
||||||
width = Math.ceil(width)
|
|
||||||
height = Math.ceil(height)
|
|
||||||
g.attr('data-width', width)
|
|
||||||
g.attr('data-height', height)
|
|
||||||
return {
|
|
||||||
node: g,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建超链接节点
|
|
||||||
createHyperlinkNode() {
|
|
||||||
let { hyperlink, hyperlinkTitle } = this.nodeData.data
|
|
||||||
if (!hyperlink) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let iconSize = this.mindMap.themeConfig.iconSize
|
|
||||||
let node = new SVG()
|
|
||||||
// 超链接节点
|
|
||||||
let a = new A().to(hyperlink).target('_blank')
|
|
||||||
a.node.addEventListener('click', e => {
|
|
||||||
e.stopPropagation()
|
|
||||||
})
|
|
||||||
if (hyperlinkTitle) {
|
|
||||||
a.attr('title', hyperlinkTitle)
|
|
||||||
}
|
|
||||||
// 添加一个透明的层,作为鼠标区域
|
|
||||||
a.rect(iconSize, iconSize).fill({ color: 'transparent' })
|
|
||||||
// 超链接图标
|
|
||||||
let iconNode = SVG(iconsSvg.hyperlink).size(iconSize, iconSize)
|
|
||||||
this.style.iconNode(iconNode)
|
|
||||||
a.add(iconNode)
|
|
||||||
node.add(a)
|
|
||||||
return {
|
|
||||||
node,
|
|
||||||
width: iconSize,
|
|
||||||
height: iconSize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建标签节点
|
|
||||||
createTagNode() {
|
|
||||||
let tagData = this.nodeData.data.tag
|
|
||||||
if (!tagData || tagData.length <= 0) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
let nodes = []
|
|
||||||
tagData.slice(0, this.mindMap.opt.maxTag).forEach((item, index) => {
|
|
||||||
let tag = new G()
|
|
||||||
// 标签文本
|
|
||||||
let text = new Text().text(item).x(8).cy(10)
|
|
||||||
this.style.tagText(text, index)
|
|
||||||
let { width } = text.bbox()
|
|
||||||
// 标签矩形
|
|
||||||
let rect = new Rect().size(width + 16, 20)
|
|
||||||
this.style.tagRect(rect, index)
|
|
||||||
tag.add(rect).add(text)
|
|
||||||
nodes.push({
|
|
||||||
node: tag,
|
|
||||||
width: width + 16,
|
|
||||||
height: 20
|
|
||||||
})
|
|
||||||
})
|
|
||||||
return nodes
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建备注节点
|
|
||||||
createNoteNode() {
|
|
||||||
if (!this.nodeData.data.note) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
let iconSize = this.mindMap.themeConfig.iconSize
|
|
||||||
let node = new SVG().attr('cursor', 'pointer')
|
|
||||||
// 透明的层,用来作为鼠标区域
|
|
||||||
node.add(new Rect().size(iconSize, iconSize).fill({ color: 'transparent' }))
|
|
||||||
// 备注图标
|
|
||||||
let iconNode = SVG(iconsSvg.note).size(iconSize, iconSize)
|
|
||||||
this.style.iconNode(iconNode)
|
|
||||||
node.add(iconNode)
|
|
||||||
// 备注tooltip
|
|
||||||
if (!this.mindMap.opt.customNoteContentShow) {
|
|
||||||
if (!this.noteEl) {
|
|
||||||
this.noteEl = document.createElement('div')
|
|
||||||
this.noteEl.style.cssText = `
|
|
||||||
position: absolute;
|
|
||||||
padding: 10px;
|
|
||||||
border-radius: 5px;
|
|
||||||
box-shadow: 0 2px 5px rgb(0 0 0 / 10%);
|
|
||||||
display: none;
|
|
||||||
background-color: #fff;
|
|
||||||
`
|
|
||||||
document.body.appendChild(this.noteEl)
|
|
||||||
}
|
|
||||||
this.noteEl.innerText = this.nodeData.data.note
|
|
||||||
}
|
|
||||||
node.on('mouseover', () => {
|
|
||||||
let { left, top } = node.node.getBoundingClientRect()
|
|
||||||
if (!this.mindMap.opt.customNoteContentShow) {
|
|
||||||
this.noteEl.style.left = left + 'px'
|
|
||||||
this.noteEl.style.top = top + iconSize + 'px'
|
|
||||||
this.noteEl.style.display = 'block'
|
|
||||||
} else {
|
|
||||||
this.mindMap.opt.customNoteContentShow.show(
|
|
||||||
this.nodeData.data.note,
|
|
||||||
left,
|
|
||||||
top + iconSize
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
node.on('mouseout', () => {
|
|
||||||
if (!this.mindMap.opt.customNoteContentShow) {
|
|
||||||
this.noteEl.style.display = 'none'
|
|
||||||
} else {
|
|
||||||
this.mindMap.opt.customNoteContentShow.hide()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return {
|
|
||||||
node,
|
|
||||||
width: iconSize,
|
|
||||||
height: iconSize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取节点形状
|
|
||||||
getShape() {
|
|
||||||
// 节点使用功能横线风格的话不支持设置形状,直接使用默认的矩形
|
|
||||||
return this.mindMap.themeConfig.nodeUseLineStyle
|
|
||||||
? 'rectangle'
|
|
||||||
: this.style.getStyle('shape', false, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 定位节点内容
|
// 定位节点内容
|
||||||
layout() {
|
layout() {
|
||||||
|
// 清除之前的内容
|
||||||
|
this.group.clear()
|
||||||
let { width, textContentItemMargin } = this
|
let { width, textContentItemMargin } = this
|
||||||
let { paddingY } = this.getPaddingVale()
|
let { paddingY } = this.getPaddingVale()
|
||||||
paddingY += this.shapePadding.paddingY
|
paddingY += this.shapePadding.paddingY
|
||||||
// 创建组
|
// 展开收起按钮
|
||||||
this.group = new G()
|
this.renderExpandBtn()
|
||||||
|
// 节点形状
|
||||||
|
this.shapeNode = this.shapeInstance.createShape()
|
||||||
|
this.group.add(this.shapeNode)
|
||||||
|
this.updateNodeShape()
|
||||||
// 概要节点添加一个带所属节点id的类名
|
// 概要节点添加一个带所属节点id的类名
|
||||||
if (this.isGeneralization && this.generalizationBelongNode) {
|
if (this.isGeneralization && this.generalizationBelongNode) {
|
||||||
this.group.addClass('generalization_' + this.generalizationBelongNode.uid)
|
this.group.addClass('generalization_' + this.generalizationBelongNode.uid)
|
||||||
}
|
}
|
||||||
this.draw.add(this.group)
|
|
||||||
this.update(true)
|
|
||||||
// 节点形状
|
|
||||||
this.shapeNode = this.shapeInstance.createShape()
|
|
||||||
this.updateNodeShape()
|
|
||||||
// 图片节点
|
// 图片节点
|
||||||
let imgHeight = 0
|
let imgHeight = 0
|
||||||
if (this._imgData) {
|
if (this._imgData) {
|
||||||
@ -634,8 +322,17 @@ class Node {
|
|||||||
: 0)
|
: 0)
|
||||||
)
|
)
|
||||||
this.group.add(textContentNested)
|
this.group.add(textContentNested)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 给节点绑定事件
|
||||||
|
bindGroupEvent() {
|
||||||
// 单击事件,选中节点
|
// 单击事件,选中节点
|
||||||
this.group.on('click', e => {
|
this.group.on('click', e => {
|
||||||
|
if (this.isMultipleChoice) {
|
||||||
|
e.stopPropagation()
|
||||||
|
this.isMultipleChoice = false
|
||||||
|
return
|
||||||
|
}
|
||||||
this.mindMap.emit('node_click', this, e)
|
this.mindMap.emit('node_click', this, e)
|
||||||
this.active(e)
|
this.active(e)
|
||||||
})
|
})
|
||||||
@ -645,8 +342,10 @@ class Node {
|
|||||||
}
|
}
|
||||||
// 多选和取消多选
|
// 多选和取消多选
|
||||||
if (e.ctrlKey) {
|
if (e.ctrlKey) {
|
||||||
|
this.isMultipleChoice = true
|
||||||
let isActive = this.nodeData.data.isActive
|
let isActive = this.nodeData.data.isActive
|
||||||
this.mindMap.renderer.setNodeActive(this, !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.renderer[isActive ? 'removeActiveNode' : 'addActiveNode'](this)
|
||||||
this.mindMap.emit(
|
this.mindMap.emit(
|
||||||
'node_active',
|
'node_active',
|
||||||
@ -708,16 +407,6 @@ class Node {
|
|||||||
this.mindMap.emit('node_active', this, this.renderer.activeNodeList)
|
this.mindMap.emit('node_active', this, this.renderer.activeNodeList)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 渲染节点到画布,会移除旧的,创建新的
|
|
||||||
renderNode() {
|
|
||||||
// 连线
|
|
||||||
this.renderLine()
|
|
||||||
this.removeAllEvent()
|
|
||||||
this.removeAllNode()
|
|
||||||
this.createNodeData()
|
|
||||||
this.layout()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新节点
|
// 更新节点
|
||||||
update(isLayout = false) {
|
update(isLayout = false) {
|
||||||
if (!this.group) {
|
if (!this.group) {
|
||||||
@ -730,9 +419,12 @@ class Node {
|
|||||||
// 需要添加展开收缩按钮
|
// 需要添加展开收缩按钮
|
||||||
this.renderExpandBtn()
|
this.renderExpandBtn()
|
||||||
} else {
|
} else {
|
||||||
|
// 更新展开收起按钮
|
||||||
this.updateExpandBtnPos()
|
this.updateExpandBtnPos()
|
||||||
}
|
}
|
||||||
|
// 更新概要
|
||||||
this.renderGeneralization()
|
this.renderGeneralization()
|
||||||
|
// 更新节点位置
|
||||||
let t = this.group.transform()
|
let t = this.group.transform()
|
||||||
if (!isLayout) {
|
if (!isLayout) {
|
||||||
this.group
|
this.group
|
||||||
@ -749,6 +441,14 @@ class Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 重新渲染节点,即重新创建节点内容、计算节点大小、计算节点内容布局、更新展开收起按钮,概要及位置
|
||||||
|
reRender() {
|
||||||
|
let sizeChange = this.getSize()
|
||||||
|
this.layout()
|
||||||
|
this.update()
|
||||||
|
return sizeChange
|
||||||
|
}
|
||||||
|
|
||||||
// 更新节点形状样式
|
// 更新节点形状样式
|
||||||
updateNodeShape() {
|
updateNodeShape() {
|
||||||
const shape = this.getShape()
|
const shape = this.getShape()
|
||||||
@ -758,12 +458,21 @@ class Node {
|
|||||||
// 递归渲染
|
// 递归渲染
|
||||||
render(callback = () => {}) {
|
render(callback = () => {}) {
|
||||||
// 节点
|
// 节点
|
||||||
if (this.initRender) {
|
// 重新渲染连线
|
||||||
this.initRender = false
|
this.renderLine()
|
||||||
this.renderNode()
|
if (!this.group) {
|
||||||
|
// 创建组
|
||||||
|
this.group = new G()
|
||||||
|
this.bindGroupEvent()
|
||||||
|
this.draw.add(this.group)
|
||||||
|
this.layout()
|
||||||
|
this.update(true)
|
||||||
} else {
|
} else {
|
||||||
// 连线
|
this.draw.add(this.group)
|
||||||
this.renderLine()
|
if (this.needLayout) {
|
||||||
|
this.needLayout = false
|
||||||
|
this.layout()
|
||||||
|
}
|
||||||
this.update()
|
this.update()
|
||||||
}
|
}
|
||||||
// 子节点
|
// 子节点
|
||||||
@ -798,11 +507,11 @@ class Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 递归删除
|
// 递归删除,只是从画布删除,节点容器还在,后续还可以重新插回画布
|
||||||
remove() {
|
remove() {
|
||||||
this.initRender = true
|
if (!this.group) return
|
||||||
this.removeAllEvent()
|
this.group.remove()
|
||||||
this.removeAllNode()
|
this.removeGeneralization()
|
||||||
this.removeLine()
|
this.removeLine()
|
||||||
// 子节点
|
// 子节点
|
||||||
if (this.children && this.children.length) {
|
if (this.children && this.children.length) {
|
||||||
@ -816,6 +525,13 @@ class Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 销毁节点,不但会从画布删除,而且原节点直接置空,后续无法再插回画布
|
||||||
|
destroy() {
|
||||||
|
if (!this.group) return
|
||||||
|
this.group.remove()
|
||||||
|
this.group = null
|
||||||
|
}
|
||||||
|
|
||||||
// 隐藏节点
|
// 隐藏节点
|
||||||
hide() {
|
hide() {
|
||||||
this.group.hide()
|
this.group.hide()
|
||||||
@ -895,6 +611,36 @@ class Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取节点形状
|
||||||
|
getShape() {
|
||||||
|
// 节点使用功能横线风格的话不支持设置形状,直接使用默认的矩形
|
||||||
|
return this.mindMap.themeConfig.nodeUseLineStyle
|
||||||
|
? '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) {
|
styleLine(line, node) {
|
||||||
let width =
|
let width =
|
||||||
@ -919,184 +665,6 @@ class Node {
|
|||||||
this._lines = []
|
this._lines = []
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否存在概要
|
|
||||||
checkHasGeneralization() {
|
|
||||||
return !!this.nodeData.data.generalization
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建概要节点
|
|
||||||
createGeneralizationNode() {
|
|
||||||
if (this.isGeneralization || !this.checkHasGeneralization()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!this._generalizationLine) {
|
|
||||||
this._generalizationLine = this.draw.path()
|
|
||||||
}
|
|
||||||
if (!this._generalizationNode) {
|
|
||||||
this._generalizationNode = new Node({
|
|
||||||
data: {
|
|
||||||
data: this.nodeData.data.generalization
|
|
||||||
},
|
|
||||||
uid: this.mindMap.uid++,
|
|
||||||
renderer: this.renderer,
|
|
||||||
mindMap: this.mindMap,
|
|
||||||
draw: this.draw,
|
|
||||||
isGeneralization: true
|
|
||||||
})
|
|
||||||
this._generalizationNodeWidth = this._generalizationNode.width
|
|
||||||
this._generalizationNodeHeight = this._generalizationNode.height
|
|
||||||
this._generalizationNode.generalizationBelongNode = this
|
|
||||||
if (this.nodeData.data.generalization.isActive) {
|
|
||||||
this.renderer.addActiveNode(this._generalizationNode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新概要节点
|
|
||||||
updateGeneralization() {
|
|
||||||
this.removeGeneralization()
|
|
||||||
this.createGeneralizationNode()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 渲染概要节点
|
|
||||||
renderGeneralization() {
|
|
||||||
if (this.isGeneralization) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!this.checkHasGeneralization()) {
|
|
||||||
this.removeGeneralization()
|
|
||||||
this._generalizationNodeWidth = 0
|
|
||||||
this._generalizationNodeHeight = 0
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (this.nodeData.data.expand === false) {
|
|
||||||
this.removeGeneralization()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.createGeneralizationNode()
|
|
||||||
this.renderer.layout.renderGeneralization(
|
|
||||||
this,
|
|
||||||
this._generalizationLine,
|
|
||||||
this._generalizationNode
|
|
||||||
)
|
|
||||||
this.style.generalizationLine(this._generalizationLine)
|
|
||||||
this._generalizationNode.render()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除概要节点
|
|
||||||
removeGeneralization() {
|
|
||||||
if (this._generalizationLine) {
|
|
||||||
this._generalizationLine.remove()
|
|
||||||
this._generalizationLine = null
|
|
||||||
}
|
|
||||||
if (this._generalizationNode) {
|
|
||||||
// 删除概要节点时要同步从激活节点里删除
|
|
||||||
this.renderer.removeActiveNode(this._generalizationNode)
|
|
||||||
this._generalizationNode.remove()
|
|
||||||
this._generalizationNode = null
|
|
||||||
}
|
|
||||||
// hack修复当激活一个节点时创建概要,然后立即激活创建的概要节点后会重复创建概要节点并且无法删除的问题
|
|
||||||
if (this.generalizationBelongNode) {
|
|
||||||
this.draw
|
|
||||||
.find('.generalization_' + this.generalizationBelongNode.uid)
|
|
||||||
.remove()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 隐藏概要节点
|
|
||||||
hideGeneralization() {
|
|
||||||
if (this._generalizationLine) {
|
|
||||||
this._generalizationLine.hide()
|
|
||||||
}
|
|
||||||
if (this._generalizationNode) {
|
|
||||||
this._generalizationNode.hide()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 显示概要节点
|
|
||||||
showGeneralization() {
|
|
||||||
if (this._generalizationLine) {
|
|
||||||
this._generalizationLine.show()
|
|
||||||
}
|
|
||||||
if (this._generalizationNode) {
|
|
||||||
this._generalizationNode.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建或更新展开收缩按钮内容
|
|
||||||
updateExpandBtnNode() {
|
|
||||||
if (this._expandBtn) {
|
|
||||||
this._expandBtn.clear()
|
|
||||||
}
|
|
||||||
let iconSvg
|
|
||||||
if (this.nodeData.data.expand === false) {
|
|
||||||
iconSvg = btnsSvg.open
|
|
||||||
} 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)
|
|
||||||
this.style.iconBtn(node, fillNode)
|
|
||||||
if (this._expandBtn) this._expandBtn.add(fillNode).add(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新展开收缩按钮位置
|
|
||||||
updateExpandBtnPos() {
|
|
||||||
if (!this._expandBtn) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.renderer.layout.renderExpandBtn(this, this._expandBtn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 展开收缩按钮
|
|
||||||
renderExpandBtn() {
|
|
||||||
if (
|
|
||||||
!this.nodeData.children ||
|
|
||||||
this.nodeData.children.length <= 0 ||
|
|
||||||
this.isRoot
|
|
||||||
) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this._expandBtn = new G()
|
|
||||||
this.updateExpandBtnNode()
|
|
||||||
this._expandBtn.on('mouseover', e => {
|
|
||||||
e.stopPropagation()
|
|
||||||
this._expandBtn.css({
|
|
||||||
cursor: 'pointer'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
this._expandBtn.on('mouseout', e => {
|
|
||||||
e.stopPropagation()
|
|
||||||
this._expandBtn.css({
|
|
||||||
cursor: 'auto'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
this._expandBtn.on('click', e => {
|
|
||||||
e.stopPropagation()
|
|
||||||
// 展开收缩
|
|
||||||
this.mindMap.execCommand(
|
|
||||||
'SET_NODE_EXPAND',
|
|
||||||
this,
|
|
||||||
!this.nodeData.data.expand
|
|
||||||
)
|
|
||||||
this.mindMap.emit('expand_btn_click', this)
|
|
||||||
})
|
|
||||||
this.group.add(this._expandBtn)
|
|
||||||
this.updateExpandBtnPos()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 移除展开收缩按钮
|
|
||||||
removeExpandBtn() {
|
|
||||||
if (this._expandBtn) {
|
|
||||||
this._expandBtn.off(['mouseover', 'mouseout', 'click'])
|
|
||||||
this._expandBtn.clear()
|
|
||||||
this._expandBtn.remove()
|
|
||||||
this._expandBtn = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检测当前节点是否是某个节点的祖先节点
|
// 检测当前节点是否是某个节点的祖先节点
|
||||||
isParent(node) {
|
isParent(node) {
|
||||||
if (this === node) {
|
if (this === node) {
|
||||||
@ -1159,55 +727,10 @@ class Node {
|
|||||||
) // 父级
|
) // 父级
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改某个样式
|
|
||||||
setStyle(prop, value, isActive) {
|
|
||||||
this.mindMap.execCommand('SET_NODE_STYLE', this, prop, value, isActive)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取数据
|
// 获取数据
|
||||||
getData(key) {
|
getData(key) {
|
||||||
return key ? this.nodeData.data[key] || '' : this.nodeData.data
|
return key ? this.nodeData.data[key] || '' : this.nodeData.data
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置数据
|
|
||||||
setData(data = {}) {
|
|
||||||
this.mindMap.execCommand('SET_NODE_DATA', this, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置文本
|
|
||||||
setText(text, richText) {
|
|
||||||
this.mindMap.execCommand('SET_NODE_TEXT', this, text, richText)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置图片
|
|
||||||
setImage(imgData) {
|
|
||||||
this.mindMap.execCommand('SET_NODE_IMAGE', this, imgData)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置图标
|
|
||||||
setIcon(icons) {
|
|
||||||
this.mindMap.execCommand('SET_NODE_ICON', this, icons)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置超链接
|
|
||||||
setHyperlink(link, title) {
|
|
||||||
this.mindMap.execCommand('SET_NODE_HYPERLINK', this, link, title)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置备注
|
|
||||||
setNote(note) {
|
|
||||||
this.mindMap.execCommand('SET_NODE_NOTE', this, note)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置标签
|
|
||||||
setTag(tag) {
|
|
||||||
this.mindMap.execCommand('SET_NODE_TAG', this, tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置形状
|
|
||||||
setShape(shape) {
|
|
||||||
this.mindMap.execCommand('SET_NODE_SHAPE', this, shape)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Node
|
export default Node
|
||||||
|
|||||||
@ -33,6 +33,8 @@ class Render {
|
|||||||
this.renderTree = merge({}, this.mindMap.opt.data || {})
|
this.renderTree = merge({}, this.mindMap.opt.data || {})
|
||||||
// 是否重新渲染
|
// 是否重新渲染
|
||||||
this.reRender = false
|
this.reRender = false
|
||||||
|
// 触发render的来源
|
||||||
|
this.renderSource = ''
|
||||||
// 当前激活的节点列表
|
// 当前激活的节点列表
|
||||||
this.activeNodeList = []
|
this.activeNodeList = []
|
||||||
// 根节点
|
// 根节点
|
||||||
@ -231,7 +233,8 @@ class Render {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 渲染
|
// 渲染
|
||||||
render(callback = () => {}) {
|
render(callback = () => {}, source) {
|
||||||
|
this.renderSource = source
|
||||||
if (this.reRender) {
|
if (this.reRender) {
|
||||||
this.clearActive()
|
this.clearActive()
|
||||||
}
|
}
|
||||||
@ -239,7 +242,7 @@ class Render {
|
|||||||
this.root = root
|
this.root = root
|
||||||
this.root.render(() => {
|
this.root.render(() => {
|
||||||
this.mindMap.emit('node_tree_render_end')
|
this.mindMap.emit('node_tree_render_end')
|
||||||
callback()
|
callback && callback()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
this.mindMap.emit('node_active', null, this.activeNodeList)
|
this.mindMap.emit('node_active', null, this.activeNodeList)
|
||||||
@ -356,7 +359,7 @@ class Render {
|
|||||||
} else {
|
} else {
|
||||||
let text = first.layerIndex === 1 ? defaultInsertSecondLevelNodeText : defaultInsertBelowSecondLevelNodeText
|
let text = first.layerIndex === 1 ? defaultInsertSecondLevelNodeText : defaultInsertBelowSecondLevelNodeText
|
||||||
if (first.layerIndex === 1) {
|
if (first.layerIndex === 1) {
|
||||||
first.parent.initRender = true
|
first.parent.destroy()
|
||||||
}
|
}
|
||||||
let index = this.getNodeIndex(first)
|
let index = this.getNodeIndex(first)
|
||||||
first.parent.nodeData.children.splice(index + 1, 0, {
|
first.parent.nodeData.children.splice(index + 1, 0, {
|
||||||
@ -397,10 +400,7 @@ class Render {
|
|||||||
// 插入子节点时自动展开子节点
|
// 插入子节点时自动展开子节点
|
||||||
node.nodeData.data.expand = true
|
node.nodeData.data.expand = true
|
||||||
if (node.isRoot) {
|
if (node.isRoot) {
|
||||||
node.initRender = true
|
node.destroy()
|
||||||
// this.mindMap.batchExecution.push('renderNode' + index, () => {
|
|
||||||
// node.renderNode()
|
|
||||||
// })
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.mindMap.render()
|
this.mindMap.render()
|
||||||
@ -492,8 +492,7 @@ class Render {
|
|||||||
existParent.nodeData.children.splice(existIndex, 0, node.nodeData)
|
existParent.nodeData.children.splice(existIndex, 0, node.nodeData)
|
||||||
this.mindMap.render(() => {
|
this.mindMap.render(() => {
|
||||||
if (nodeLayerChanged) {
|
if (nodeLayerChanged) {
|
||||||
node.getSize()
|
node.reRender()
|
||||||
node.layout()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -531,8 +530,7 @@ class Render {
|
|||||||
existParent.nodeData.children.splice(existIndex, 0, node.nodeData)
|
existParent.nodeData.children.splice(existIndex, 0, node.nodeData)
|
||||||
this.mindMap.render(() => {
|
this.mindMap.render(() => {
|
||||||
if (nodeLayerChanged) {
|
if (nodeLayerChanged) {
|
||||||
node.getSize()
|
node.reRender()
|
||||||
node.layout()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -625,7 +623,7 @@ class Render {
|
|||||||
toNode.nodeData.children.push(copyData)
|
toNode.nodeData.children.push(copyData)
|
||||||
this.mindMap.render()
|
this.mindMap.render()
|
||||||
if (toNode.isRoot) {
|
if (toNode.isRoot) {
|
||||||
toNode.renderNode()
|
toNode.destroy()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -913,8 +911,7 @@ class Render {
|
|||||||
// 设置节点数据,并判断是否渲染
|
// 设置节点数据,并判断是否渲染
|
||||||
setNodeDataRender(node, data) {
|
setNodeDataRender(node, data) {
|
||||||
this.setNodeData(node, data)
|
this.setNodeData(node, data)
|
||||||
let changed = node.getSize()
|
let changed = node.reRender()
|
||||||
node.layout()
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
if (node.isGeneralization) {
|
if (node.isGeneralization) {
|
||||||
// 概要节点
|
// 概要节点
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import { Rect, Polygon, Path } from '@svgdotjs/svg.js'
|
||||||
|
|
||||||
// 节点形状类
|
// 节点形状类
|
||||||
export default class Shape {
|
export default class Shape {
|
||||||
constructor(node) {
|
constructor(node) {
|
||||||
@ -63,7 +65,7 @@ export default class Shape {
|
|||||||
let node = null
|
let node = null
|
||||||
// 矩形
|
// 矩形
|
||||||
if (shape === 'rectangle') {
|
if (shape === 'rectangle') {
|
||||||
node = this.node.group.rect(width, height)
|
node = new Rect().size(width, height)
|
||||||
} else if (shape === 'diamond') {
|
} else if (shape === 'diamond') {
|
||||||
// 菱形
|
// 菱形
|
||||||
node = this.createDiamond()
|
node = this.createDiamond()
|
||||||
@ -105,12 +107,12 @@ export default class Shape {
|
|||||||
let bottomY = height
|
let bottomY = height
|
||||||
let leftX = 0
|
let leftX = 0
|
||||||
let leftY = halfHeight
|
let leftY = halfHeight
|
||||||
return this.node.group.polygon(`
|
return new Polygon().plot([
|
||||||
${topX}, ${topY}
|
[topX, topY],
|
||||||
${rightX}, ${rightY}
|
[rightX, rightY],
|
||||||
${bottomX}, ${bottomY}
|
[bottomX, bottomY],
|
||||||
${leftX}, ${leftY}
|
[leftX, leftY]
|
||||||
`)
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建平行四边形
|
// 创建平行四边形
|
||||||
@ -118,41 +120,41 @@ export default class Shape {
|
|||||||
let { paddingX } = this.node.getPaddingVale()
|
let { paddingX } = this.node.getPaddingVale()
|
||||||
paddingX = paddingX || this.node.shapePadding.paddingX
|
paddingX = paddingX || this.node.shapePadding.paddingX
|
||||||
let { width, height } = this.node
|
let { width, height } = this.node
|
||||||
return this.node.group.polygon(`
|
return new Polygon().plot([
|
||||||
${paddingX}, ${0}
|
[paddingX, 0],
|
||||||
${width}, ${0}
|
[width, 0],
|
||||||
${width - paddingX}, ${height}
|
[width - paddingX, height],
|
||||||
${0}, ${height}
|
[0, height]
|
||||||
`)
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建圆角矩形
|
// 创建圆角矩形
|
||||||
createRoundedRectangle() {
|
createRoundedRectangle() {
|
||||||
let { width, height } = this.node
|
let { width, height } = this.node
|
||||||
let halfHeight = height / 2
|
let halfHeight = height / 2
|
||||||
return this.node.group.path(`
|
return new Path().plot(`
|
||||||
M${halfHeight},0
|
M${halfHeight},0
|
||||||
L${width - halfHeight},0
|
L${width - halfHeight},0
|
||||||
A${height / 2},${height / 2} 0 0,1 ${width - halfHeight},${height}
|
A${height / 2},${height / 2} 0 0,1 ${width - halfHeight},${height}
|
||||||
L${halfHeight},${height}
|
L${halfHeight},${height}
|
||||||
A${height / 2},${height / 2} 0 0,1 ${halfHeight},${0}
|
A${height / 2},${height / 2} 0 0,1 ${halfHeight},${0}
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建八角矩形
|
// 创建八角矩形
|
||||||
createOctagonalRectangle() {
|
createOctagonalRectangle() {
|
||||||
let w = 5
|
let w = 5
|
||||||
let { width, height } = this.node
|
let { width, height } = this.node
|
||||||
return this.node.group.polygon(`
|
return new Polygon().plot([
|
||||||
${0}, ${w}
|
[0, w],
|
||||||
${w}, ${0}
|
[w, 0],
|
||||||
${width - w}, ${0}
|
[width - w, 0],
|
||||||
${width}, ${w}
|
[width, w],
|
||||||
${width}, ${height - w}
|
[width, height - w],
|
||||||
${width - w}, ${height}
|
[width - w, height],
|
||||||
${w}, ${height}
|
[w, height],
|
||||||
${0}, ${height - w}
|
[0, height - w]
|
||||||
`)
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建外三角矩形
|
// 创建外三角矩形
|
||||||
@ -160,14 +162,14 @@ export default class Shape {
|
|||||||
let { paddingX } = this.node.getPaddingVale()
|
let { paddingX } = this.node.getPaddingVale()
|
||||||
paddingX = paddingX || this.node.shapePadding.paddingX
|
paddingX = paddingX || this.node.shapePadding.paddingX
|
||||||
let { width, height } = this.node
|
let { width, height } = this.node
|
||||||
return this.node.group.polygon(`
|
return new Polygon().plot([
|
||||||
${paddingX}, ${0}
|
[paddingX, 0],
|
||||||
${width - paddingX}, ${0}
|
[width - paddingX, 0],
|
||||||
${width}, ${height / 2}
|
[width, height / 2],
|
||||||
${width - paddingX}, ${height}
|
[width - paddingX, height],
|
||||||
${paddingX}, ${height}
|
[paddingX, height],
|
||||||
${0}, ${height / 2}
|
[0, height / 2]
|
||||||
`)
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建内三角矩形
|
// 创建内三角矩形
|
||||||
@ -175,14 +177,14 @@ export default class Shape {
|
|||||||
let { paddingX } = this.node.getPaddingVale()
|
let { paddingX } = this.node.getPaddingVale()
|
||||||
paddingX = paddingX || this.node.shapePadding.paddingX
|
paddingX = paddingX || this.node.shapePadding.paddingX
|
||||||
let { width, height } = this.node
|
let { width, height } = this.node
|
||||||
return this.node.group.polygon(`
|
return new Polygon().plot([
|
||||||
${0}, ${0}
|
[0, 0],
|
||||||
${width}, ${0}
|
[width, 0],
|
||||||
${width - paddingX / 2}, ${height / 2}
|
[width - paddingX / 2, height / 2],
|
||||||
${width}, ${height}
|
[width, height],
|
||||||
${0}, ${height}
|
[0, height],
|
||||||
${paddingX / 2}, ${height / 2}
|
[paddingX / 2, height / 2]
|
||||||
`)
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建椭圆
|
// 创建椭圆
|
||||||
@ -190,12 +192,12 @@ export default class Shape {
|
|||||||
let { width, height } = this.node
|
let { width, height } = this.node
|
||||||
let halfWidth = width / 2
|
let halfWidth = width / 2
|
||||||
let halfHeight = height / 2
|
let halfHeight = height / 2
|
||||||
return this.node.group.path(`
|
return new Path().plot(`
|
||||||
M${halfWidth},0
|
M${halfWidth},0
|
||||||
A${halfWidth},${halfHeight} 0 0,1 ${halfWidth},${height}
|
A${halfWidth},${halfHeight} 0 0,1 ${halfWidth},${height}
|
||||||
M${halfWidth},${height}
|
M${halfWidth},${height}
|
||||||
A${halfWidth},${halfHeight} 0 0,1 ${halfWidth},${0}
|
A${halfWidth},${halfHeight} 0 0,1 ${halfWidth},${0}
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建圆
|
// 创建圆
|
||||||
@ -203,12 +205,12 @@ export default class Shape {
|
|||||||
let { width, height } = this.node
|
let { width, height } = this.node
|
||||||
let halfWidth = width / 2
|
let halfWidth = width / 2
|
||||||
let halfHeight = height / 2
|
let halfHeight = height / 2
|
||||||
return this.node.group.path(`
|
return new Path().plot(`
|
||||||
M${halfWidth},0
|
M${halfWidth},0
|
||||||
A${halfWidth},${halfHeight} 0 0,1 ${halfWidth},${height}
|
A${halfWidth},${halfHeight} 0 0,1 ${halfWidth},${height}
|
||||||
M${halfWidth},${height}
|
M${halfWidth},${height}
|
||||||
A${halfWidth},${halfHeight} 0 0,1 ${halfWidth},${0}
|
A${halfWidth},${halfHeight} 0 0,1 ${halfWidth},${0}
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,11 @@ class Base {
|
|||||||
newNode = data._node
|
newNode = data._node
|
||||||
newNode.reset()
|
newNode.reset()
|
||||||
newNode.layerIndex = layerIndex
|
newNode.layerIndex = layerIndex
|
||||||
|
// 主题或主题配置改变了需要重新计算节点大小和布局
|
||||||
|
if (this.renderer.renderSource === 'changeTheme') {
|
||||||
|
newNode.getSize()
|
||||||
|
newNode.needLayout = true
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// 创建新节点
|
// 创建新节点
|
||||||
newNode = new Node({
|
newNode = new Node({
|
||||||
|
|||||||
56
simple-mind-map/src/utils/nodeCommandWraps.js
Normal file
56
simple-mind-map/src/utils/nodeCommandWraps.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// 设置数据
|
||||||
|
function setData(data = {}) {
|
||||||
|
this.mindMap.execCommand('SET_NODE_DATA', this, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置文本
|
||||||
|
function setText(text, richText) {
|
||||||
|
this.mindMap.execCommand('SET_NODE_TEXT', this, text, richText)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置图片
|
||||||
|
function setImage(imgData) {
|
||||||
|
this.mindMap.execCommand('SET_NODE_IMAGE', this, imgData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置图标
|
||||||
|
function setIcon(icons) {
|
||||||
|
this.mindMap.execCommand('SET_NODE_ICON', this, icons)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置超链接
|
||||||
|
function setHyperlink(link, title) {
|
||||||
|
this.mindMap.execCommand('SET_NODE_HYPERLINK', this, link, title)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置备注
|
||||||
|
function setNote(note) {
|
||||||
|
this.mindMap.execCommand('SET_NODE_NOTE', this, note)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置标签
|
||||||
|
function setTag(tag) {
|
||||||
|
this.mindMap.execCommand('SET_NODE_TAG', this, tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置形状
|
||||||
|
function setShape(shape) {
|
||||||
|
this.mindMap.execCommand('SET_NODE_SHAPE', this, shape)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改某个样式
|
||||||
|
function setStyle(prop, value, isActive) {
|
||||||
|
this.mindMap.execCommand('SET_NODE_STYLE', this, prop, value, isActive)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setData,
|
||||||
|
setText,
|
||||||
|
setImage,
|
||||||
|
setIcon,
|
||||||
|
setHyperlink,
|
||||||
|
setNote,
|
||||||
|
setTag,
|
||||||
|
setShape,
|
||||||
|
setStyle
|
||||||
|
}
|
||||||
256
simple-mind-map/src/utils/nodeCreateContents.js
Normal file
256
simple-mind-map/src/utils/nodeCreateContents.js
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
import { measureText, resizeImgSize } from '../utils'
|
||||||
|
import { Image, SVG, A, G, Rect, Text, ForeignObject } from '@svgdotjs/svg.js'
|
||||||
|
import iconsSvg from '../svg/icons'
|
||||||
|
|
||||||
|
// 创建图片节点
|
||||||
|
function createImgNode() {
|
||||||
|
let img = this.nodeData.data.image
|
||||||
|
if (!img) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let imgSize = this.getImgShowSize()
|
||||||
|
let node = new Image().load(img).size(...imgSize)
|
||||||
|
if (this.nodeData.data.imageTitle) {
|
||||||
|
node.attr('title', this.nodeData.data.imageTitle)
|
||||||
|
}
|
||||||
|
node.on('dblclick', e => {
|
||||||
|
this.mindMap.emit('node_img_dblclick', this, e)
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
node,
|
||||||
|
width: imgSize[0],
|
||||||
|
height: imgSize[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取图片显示宽高
|
||||||
|
function getImgShowSize() {
|
||||||
|
return resizeImgSize(
|
||||||
|
this.nodeData.data.imageSize.width,
|
||||||
|
this.nodeData.data.imageSize.height,
|
||||||
|
this.mindMap.themeConfig.imgMaxWidth,
|
||||||
|
this.mindMap.themeConfig.imgMaxHeight
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建icon节点
|
||||||
|
function createIconNode() {
|
||||||
|
let _data = this.nodeData.data
|
||||||
|
if (!_data.icon || _data.icon.length <= 0) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
let iconSize = this.mindMap.themeConfig.iconSize
|
||||||
|
return _data.icon.map(item => {
|
||||||
|
return {
|
||||||
|
node: SVG(iconsSvg.getNodeIconListIcon(item)).size(iconSize, iconSize),
|
||||||
|
width: iconSize,
|
||||||
|
height: iconSize
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建富文本节点
|
||||||
|
function createRichTextNode() {
|
||||||
|
let g = new G()
|
||||||
|
let html = `<div>${this.nodeData.data.text}</div>`
|
||||||
|
let div = document.createElement('div')
|
||||||
|
div.innerHTML = html
|
||||||
|
div.style.cssText = `position: fixed; left: -999999px;`
|
||||||
|
let el = div.children[0]
|
||||||
|
el.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml')
|
||||||
|
el.style.maxWidth = this.mindMap.opt.textAutoWrapWidth + 'px'
|
||||||
|
this.mindMap.el.appendChild(div)
|
||||||
|
let { width, height } = el.getBoundingClientRect()
|
||||||
|
width = Math.ceil(width)
|
||||||
|
height = Math.ceil(height)
|
||||||
|
g.attr('data-width', width)
|
||||||
|
g.attr('data-height', height)
|
||||||
|
html = div.innerHTML
|
||||||
|
this.mindMap.el.removeChild(div)
|
||||||
|
let foreignObject = new ForeignObject()
|
||||||
|
foreignObject.width(width)
|
||||||
|
foreignObject.height(height)
|
||||||
|
foreignObject.add(SVG(html))
|
||||||
|
g.add(foreignObject)
|
||||||
|
return {
|
||||||
|
node: g,
|
||||||
|
width,
|
||||||
|
height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建文本节点
|
||||||
|
function createTextNode() {
|
||||||
|
if (this.nodeData.data.richText) {
|
||||||
|
return this.createRichTextNode()
|
||||||
|
}
|
||||||
|
let g = new G()
|
||||||
|
let fontSize = this.getStyle('fontSize', false, this.nodeData.data.isActive)
|
||||||
|
let lineHeight = this.getStyle(
|
||||||
|
'lineHeight',
|
||||||
|
false,
|
||||||
|
this.nodeData.data.isActive
|
||||||
|
)
|
||||||
|
// 文本超长自动换行
|
||||||
|
let textStyle = this.style.getTextFontStyle()
|
||||||
|
let textArr = this.nodeData.data.text.split(/\n/gim)
|
||||||
|
let maxWidth = this.mindMap.opt.textAutoWrapWidth
|
||||||
|
textArr.forEach((item, index) => {
|
||||||
|
let arr = item.split('')
|
||||||
|
let lines = []
|
||||||
|
let line = []
|
||||||
|
while (arr.length) {
|
||||||
|
line.push(arr.shift())
|
||||||
|
let text = line.join('')
|
||||||
|
if (measureText(text, textStyle).width >= maxWidth) {
|
||||||
|
lines.push(text)
|
||||||
|
line = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (line.length > 0) {
|
||||||
|
lines.push(line.join(''))
|
||||||
|
}
|
||||||
|
textArr[index] = lines.join('\n')
|
||||||
|
})
|
||||||
|
textArr = textArr.join('\n').split(/\n/gim)
|
||||||
|
textArr.forEach((item, index) => {
|
||||||
|
let node = new Text().text(item)
|
||||||
|
this.style.text(node)
|
||||||
|
node.y(fontSize * lineHeight * index)
|
||||||
|
g.add(node)
|
||||||
|
})
|
||||||
|
let { width, height } = g.bbox()
|
||||||
|
width = Math.ceil(width)
|
||||||
|
height = Math.ceil(height)
|
||||||
|
g.attr('data-width', width)
|
||||||
|
g.attr('data-height', height)
|
||||||
|
return {
|
||||||
|
node: g,
|
||||||
|
width,
|
||||||
|
height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建超链接节点
|
||||||
|
function createHyperlinkNode() {
|
||||||
|
let { hyperlink, hyperlinkTitle } = this.nodeData.data
|
||||||
|
if (!hyperlink) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let iconSize = this.mindMap.themeConfig.iconSize
|
||||||
|
let node = new SVG()
|
||||||
|
// 超链接节点
|
||||||
|
let a = new A().to(hyperlink).target('_blank')
|
||||||
|
a.node.addEventListener('click', e => {
|
||||||
|
e.stopPropagation()
|
||||||
|
})
|
||||||
|
if (hyperlinkTitle) {
|
||||||
|
a.attr('title', hyperlinkTitle)
|
||||||
|
}
|
||||||
|
// 添加一个透明的层,作为鼠标区域
|
||||||
|
a.rect(iconSize, iconSize).fill({ color: 'transparent' })
|
||||||
|
// 超链接图标
|
||||||
|
let iconNode = SVG(iconsSvg.hyperlink).size(iconSize, iconSize)
|
||||||
|
this.style.iconNode(iconNode)
|
||||||
|
a.add(iconNode)
|
||||||
|
node.add(a)
|
||||||
|
return {
|
||||||
|
node,
|
||||||
|
width: iconSize,
|
||||||
|
height: iconSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建标签节点
|
||||||
|
function createTagNode() {
|
||||||
|
let tagData = this.nodeData.data.tag
|
||||||
|
if (!tagData || tagData.length <= 0) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
let nodes = []
|
||||||
|
tagData.slice(0, this.mindMap.opt.maxTag).forEach((item, index) => {
|
||||||
|
let tag = new G()
|
||||||
|
// 标签文本
|
||||||
|
let text = new Text().text(item).x(8).cy(10)
|
||||||
|
this.style.tagText(text, index)
|
||||||
|
let { width } = text.bbox()
|
||||||
|
// 标签矩形
|
||||||
|
let rect = new Rect().size(width + 16, 20)
|
||||||
|
this.style.tagRect(rect, index)
|
||||||
|
tag.add(rect).add(text)
|
||||||
|
nodes.push({
|
||||||
|
node: tag,
|
||||||
|
width: width + 16,
|
||||||
|
height: 20
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return nodes
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建备注节点
|
||||||
|
function createNoteNode() {
|
||||||
|
if (!this.nodeData.data.note) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
let iconSize = this.mindMap.themeConfig.iconSize
|
||||||
|
let node = new SVG().attr('cursor', 'pointer')
|
||||||
|
// 透明的层,用来作为鼠标区域
|
||||||
|
node.add(new Rect().size(iconSize, iconSize).fill({ color: 'transparent' }))
|
||||||
|
// 备注图标
|
||||||
|
let iconNode = SVG(iconsSvg.note).size(iconSize, iconSize)
|
||||||
|
this.style.iconNode(iconNode)
|
||||||
|
node.add(iconNode)
|
||||||
|
// 备注tooltip
|
||||||
|
if (!this.mindMap.opt.customNoteContentShow) {
|
||||||
|
if (!this.noteEl) {
|
||||||
|
this.noteEl = document.createElement('div')
|
||||||
|
this.noteEl.style.cssText = `
|
||||||
|
position: absolute;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 0 2px 5px rgb(0 0 0 / 10%);
|
||||||
|
display: none;
|
||||||
|
background-color: #fff;
|
||||||
|
`
|
||||||
|
document.body.appendChild(this.noteEl)
|
||||||
|
}
|
||||||
|
this.noteEl.innerText = this.nodeData.data.note
|
||||||
|
}
|
||||||
|
node.on('mouseover', () => {
|
||||||
|
let { left, top } = node.node.getBoundingClientRect()
|
||||||
|
if (!this.mindMap.opt.customNoteContentShow) {
|
||||||
|
this.noteEl.style.left = left + 'px'
|
||||||
|
this.noteEl.style.top = top + iconSize + 'px'
|
||||||
|
this.noteEl.style.display = 'block'
|
||||||
|
} else {
|
||||||
|
this.mindMap.opt.customNoteContentShow.show(
|
||||||
|
this.nodeData.data.note,
|
||||||
|
left,
|
||||||
|
top + iconSize
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
node.on('mouseout', () => {
|
||||||
|
if (!this.mindMap.opt.customNoteContentShow) {
|
||||||
|
this.noteEl.style.display = 'none'
|
||||||
|
} else {
|
||||||
|
this.mindMap.opt.customNoteContentShow.hide()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
node,
|
||||||
|
width: iconSize,
|
||||||
|
height: iconSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
createImgNode,
|
||||||
|
getImgShowSize,
|
||||||
|
createIconNode,
|
||||||
|
createRichTextNode,
|
||||||
|
createTextNode,
|
||||||
|
createHyperlinkNode,
|
||||||
|
createTagNode,
|
||||||
|
createNoteNode
|
||||||
|
}
|
||||||
84
simple-mind-map/src/utils/nodeExpandBtn.js
Normal file
84
simple-mind-map/src/utils/nodeExpandBtn.js
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import btnsSvg from '../svg/btns'
|
||||||
|
import { SVG, Circle, G } from '@svgdotjs/svg.js'
|
||||||
|
|
||||||
|
// 创建或更新展开收缩按钮内容
|
||||||
|
function updateExpandBtnNode() {
|
||||||
|
if (this._expandBtn) {
|
||||||
|
this._expandBtn.clear()
|
||||||
|
}
|
||||||
|
let iconSvg
|
||||||
|
if (this.nodeData.data.expand === false) {
|
||||||
|
iconSvg = btnsSvg.open
|
||||||
|
} 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)
|
||||||
|
this.style.iconBtn(node, fillNode)
|
||||||
|
if (this._expandBtn) this._expandBtn.add(fillNode).add(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新展开收缩按钮位置
|
||||||
|
function updateExpandBtnPos() {
|
||||||
|
if (!this._expandBtn) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.renderer.layout.renderExpandBtn(this, this._expandBtn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建展开收缩按钮
|
||||||
|
function renderExpandBtn() {
|
||||||
|
if (
|
||||||
|
!this.nodeData.children ||
|
||||||
|
this.nodeData.children.length <= 0 ||
|
||||||
|
this.isRoot
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (this._expandBtn) {
|
||||||
|
this.group.add(this._expandBtn)
|
||||||
|
} else {
|
||||||
|
this._expandBtn = new G()
|
||||||
|
this._expandBtn.on('mouseover', e => {
|
||||||
|
e.stopPropagation()
|
||||||
|
this._expandBtn.css({
|
||||||
|
cursor: 'pointer'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this._expandBtn.on('mouseout', e => {
|
||||||
|
e.stopPropagation()
|
||||||
|
this._expandBtn.css({
|
||||||
|
cursor: 'auto'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this._expandBtn.on('click', e => {
|
||||||
|
e.stopPropagation()
|
||||||
|
// 展开收缩
|
||||||
|
this.mindMap.execCommand(
|
||||||
|
'SET_NODE_EXPAND',
|
||||||
|
this,
|
||||||
|
!this.nodeData.data.expand
|
||||||
|
)
|
||||||
|
this.mindMap.emit('expand_btn_click', this)
|
||||||
|
})
|
||||||
|
this.group.add(this._expandBtn)
|
||||||
|
}
|
||||||
|
this.updateExpandBtnNode()
|
||||||
|
this.updateExpandBtnPos()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除展开收缩按钮
|
||||||
|
function removeExpandBtn() {
|
||||||
|
if (this._expandBtn) {
|
||||||
|
this._expandBtn.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
updateExpandBtnNode,
|
||||||
|
updateExpandBtnPos,
|
||||||
|
renderExpandBtn,
|
||||||
|
removeExpandBtn
|
||||||
|
}
|
||||||
115
simple-mind-map/src/utils/nodeGeneralization.js
Normal file
115
simple-mind-map/src/utils/nodeGeneralization.js
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
import Node from '../Node'
|
||||||
|
|
||||||
|
// 检查是否存在概要
|
||||||
|
function checkHasGeneralization () {
|
||||||
|
return !!this.nodeData.data.generalization
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建概要节点
|
||||||
|
function createGeneralizationNode () {
|
||||||
|
if (this.isGeneralization || !this.checkHasGeneralization()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!this._generalizationLine) {
|
||||||
|
this._generalizationLine = this.draw.path()
|
||||||
|
}
|
||||||
|
if (!this._generalizationNode) {
|
||||||
|
this._generalizationNode = new Node({
|
||||||
|
data: {
|
||||||
|
data: this.nodeData.data.generalization
|
||||||
|
},
|
||||||
|
uid: this.mindMap.uid++,
|
||||||
|
renderer: this.renderer,
|
||||||
|
mindMap: this.mindMap,
|
||||||
|
draw: this.draw,
|
||||||
|
isGeneralization: true
|
||||||
|
})
|
||||||
|
this._generalizationNodeWidth = this._generalizationNode.width
|
||||||
|
this._generalizationNodeHeight = this._generalizationNode.height
|
||||||
|
this._generalizationNode.generalizationBelongNode = this
|
||||||
|
if (this.nodeData.data.generalization.isActive) {
|
||||||
|
this.renderer.addActiveNode(this._generalizationNode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新概要节点
|
||||||
|
function updateGeneralization () {
|
||||||
|
this.removeGeneralization()
|
||||||
|
this.createGeneralizationNode()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 渲染概要节点
|
||||||
|
function renderGeneralization () {
|
||||||
|
if (this.isGeneralization) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!this.checkHasGeneralization()) {
|
||||||
|
this.removeGeneralization()
|
||||||
|
this._generalizationNodeWidth = 0
|
||||||
|
this._generalizationNodeHeight = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (this.nodeData.data.expand === false) {
|
||||||
|
this.removeGeneralization()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.createGeneralizationNode()
|
||||||
|
this.renderer.layout.renderGeneralization(
|
||||||
|
this,
|
||||||
|
this._generalizationLine,
|
||||||
|
this._generalizationNode
|
||||||
|
)
|
||||||
|
this.style.generalizationLine(this._generalizationLine)
|
||||||
|
this._generalizationNode.render()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除概要节点
|
||||||
|
function removeGeneralization () {
|
||||||
|
if (this._generalizationLine) {
|
||||||
|
this._generalizationLine.remove()
|
||||||
|
this._generalizationLine = null
|
||||||
|
}
|
||||||
|
if (this._generalizationNode) {
|
||||||
|
// 删除概要节点时要同步从激活节点里删除
|
||||||
|
this.renderer.removeActiveNode(this._generalizationNode)
|
||||||
|
this._generalizationNode.remove()
|
||||||
|
this._generalizationNode = null
|
||||||
|
}
|
||||||
|
// hack修复当激活一个节点时创建概要,然后立即激活创建的概要节点后会重复创建概要节点并且无法删除的问题
|
||||||
|
if (this.generalizationBelongNode) {
|
||||||
|
this.draw
|
||||||
|
.find('.generalization_' + this.generalizationBelongNode.uid)
|
||||||
|
.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 隐藏概要节点
|
||||||
|
function hideGeneralization () {
|
||||||
|
if (this._generalizationLine) {
|
||||||
|
this._generalizationLine.hide()
|
||||||
|
}
|
||||||
|
if (this._generalizationNode) {
|
||||||
|
this._generalizationNode.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示概要节点
|
||||||
|
function showGeneralization () {
|
||||||
|
if (this._generalizationLine) {
|
||||||
|
this._generalizationLine.show()
|
||||||
|
}
|
||||||
|
if (this._generalizationNode) {
|
||||||
|
this._generalizationNode.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
checkHasGeneralization,
|
||||||
|
createGeneralizationNode,
|
||||||
|
updateGeneralization,
|
||||||
|
renderGeneralization,
|
||||||
|
removeGeneralization,
|
||||||
|
hideGeneralization,
|
||||||
|
showGeneralization
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user