mirror of
https://github.com/wanglin2/mind-map.git
synced 2026-02-21 18:37:43 +08:00
1006 lines
28 KiB
JavaScript
1006 lines
28 KiB
JavaScript
import merge from 'deepmerge'
|
||
import LogicalStructure from './layouts/LogicalStructure'
|
||
import MindMap from './layouts/MindMap'
|
||
import CatalogOrganization from './layouts/CatalogOrganization'
|
||
import OrganizationStructure from './layouts/OrganizationStructure'
|
||
import Timeline from './layouts/Timeline'
|
||
import Fishbone from './layouts/Fishbone'
|
||
import TextEdit from './TextEdit'
|
||
import { copyNodeTree, simpleDeepClone, walk } from './utils'
|
||
import { shapeList } from './Shape'
|
||
import { lineStyleProps } from './themes/default'
|
||
import { CONSTANTS } from './utils/constant'
|
||
|
||
// 布局列表
|
||
const layouts = {
|
||
// 逻辑结构图
|
||
[CONSTANTS.LAYOUT.LOGICAL_STRUCTURE]: LogicalStructure,
|
||
// 思维导图
|
||
[CONSTANTS.LAYOUT.MIND_MAP]: MindMap,
|
||
// 目录组织图
|
||
[CONSTANTS.LAYOUT.CATALOG_ORGANIZATION]: CatalogOrganization,
|
||
// 组织结构图
|
||
[CONSTANTS.LAYOUT.ORGANIZATION_STRUCTURE]: OrganizationStructure,
|
||
// 时间轴
|
||
[CONSTANTS.LAYOUT.TIMELINE]: Timeline,
|
||
// 时间轴2
|
||
[CONSTANTS.LAYOUT.TIMELINE2]: Timeline,
|
||
// 鱼骨图
|
||
[CONSTANTS.LAYOUT.FISHBONE]: Fishbone,
|
||
}
|
||
|
||
// 渲染
|
||
|
||
class Render {
|
||
// 构造函数
|
||
constructor(opt = {}) {
|
||
this.opt = opt
|
||
this.mindMap = opt.mindMap
|
||
this.themeConfig = this.mindMap.themeConfig
|
||
this.draw = this.mindMap.draw
|
||
// 渲染树,操作过程中修改的都是这里的数据
|
||
this.renderTree = merge({}, this.mindMap.opt.data || {})
|
||
// 是否重新渲染
|
||
this.reRender = false
|
||
// 是否正在渲染中
|
||
this.isRendering = false
|
||
// 是否存在等待渲染
|
||
this.hasWaitRendering = false
|
||
// 用于缓存节点
|
||
this.nodeCache = {}
|
||
this.lastNodeCache = {}
|
||
// 触发render的来源
|
||
this.renderSource = ''
|
||
// 当前激活的节点列表
|
||
this.activeNodeList = []
|
||
// 根节点
|
||
this.root = null
|
||
// 文本编辑框,需要再bindEvent之前实例化,否则单击事件只能触发隐藏文本编辑框,而无法保存文本修改
|
||
this.textEdit = new TextEdit(this)
|
||
// 布局
|
||
this.setLayout()
|
||
// 绑定事件
|
||
this.bindEvent()
|
||
// 注册命令
|
||
this.registerCommands()
|
||
// 注册快捷键
|
||
this.registerShortcutKeys()
|
||
}
|
||
|
||
// 设置布局结构
|
||
setLayout() {
|
||
this.layout = new (
|
||
layouts[this.mindMap.opt.layout]
|
||
? layouts[this.mindMap.opt.layout]
|
||
: layouts[CONSTANTS.LAYOUT.LOGICAL_STRUCTURE]
|
||
)(this, this.mindMap.opt.layout)
|
||
}
|
||
|
||
// 绑定事件
|
||
bindEvent() {
|
||
// 点击事件
|
||
this.mindMap.on('draw_click', () => {
|
||
// 清除激活状态
|
||
if (this.activeNodeList.length > 0) {
|
||
this.mindMap.execCommand('CLEAR_ACTIVE_NODE')
|
||
}
|
||
})
|
||
}
|
||
|
||
// 注册命令
|
||
registerCommands() {
|
||
// 全选
|
||
this.selectAll = this.selectAll.bind(this)
|
||
this.mindMap.command.add('SELECT_ALL', this.selectAll)
|
||
// 回退
|
||
this.back = this.back.bind(this)
|
||
this.mindMap.command.add('BACK', this.back)
|
||
// 前进
|
||
this.forward = this.forward.bind(this)
|
||
this.mindMap.command.add('FORWARD', this.forward)
|
||
// 插入同级节点
|
||
this.insertNode = this.insertNode.bind(this)
|
||
this.mindMap.command.add('INSERT_NODE', this.insertNode)
|
||
// 插入子节点
|
||
this.insertChildNode = this.insertChildNode.bind(this)
|
||
this.mindMap.command.add('INSERT_CHILD_NODE', this.insertChildNode)
|
||
// 上移节点
|
||
this.upNode = this.upNode.bind(this)
|
||
this.mindMap.command.add('UP_NODE', this.upNode)
|
||
// 下移节点
|
||
this.downNode = this.downNode.bind(this)
|
||
this.mindMap.command.add('DOWN_NODE', this.downNode)
|
||
// 移动节点
|
||
this.insertAfter = this.insertAfter.bind(this)
|
||
this.mindMap.command.add('INSERT_AFTER', this.insertAfter)
|
||
this.insertBefore = this.insertBefore.bind(this)
|
||
this.mindMap.command.add('INSERT_BEFORE', this.insertBefore)
|
||
this.moveNodeTo = this.moveNodeTo.bind(this)
|
||
this.mindMap.command.add('MOVE_NODE_TO', this.moveNodeTo)
|
||
// 删除节点
|
||
this.removeNode = this.removeNode.bind(this)
|
||
this.mindMap.command.add('REMOVE_NODE', this.removeNode)
|
||
// 粘贴节点
|
||
this.pasteNode = this.pasteNode.bind(this)
|
||
this.mindMap.command.add('PASTE_NODE', this.pasteNode)
|
||
// 剪切节点
|
||
this.cutNode = this.cutNode.bind(this)
|
||
this.mindMap.command.add('CUT_NODE', this.cutNode)
|
||
// 修改节点样式
|
||
this.setNodeStyle = this.setNodeStyle.bind(this)
|
||
this.mindMap.command.add('SET_NODE_STYLE', this.setNodeStyle)
|
||
// 切换节点是否激活
|
||
this.setNodeActive = this.setNodeActive.bind(this)
|
||
this.mindMap.command.add('SET_NODE_ACTIVE', this.setNodeActive)
|
||
// 清除所有激活节点
|
||
this.clearAllActive = this.clearAllActive.bind(this)
|
||
this.mindMap.command.add('CLEAR_ACTIVE_NODE', this.clearAllActive)
|
||
// 切换节点是否展开
|
||
this.setNodeExpand = this.setNodeExpand.bind(this)
|
||
this.mindMap.command.add('SET_NODE_EXPAND', this.setNodeExpand)
|
||
// 展开所有节点
|
||
this.expandAllNode = this.expandAllNode.bind(this)
|
||
this.mindMap.command.add('EXPAND_ALL', this.expandAllNode)
|
||
// 收起所有节点
|
||
this.unexpandAllNode = this.unexpandAllNode.bind(this)
|
||
this.mindMap.command.add('UNEXPAND_ALL', this.unexpandAllNode)
|
||
// 展开到指定层级
|
||
this.expandToLevel = this.expandToLevel.bind(this)
|
||
this.mindMap.command.add('UNEXPAND_TO_LEVEL', this.expandToLevel)
|
||
// 设置节点数据
|
||
this.setNodeData = this.setNodeData.bind(this)
|
||
this.mindMap.command.add('SET_NODE_DATA', this.setNodeData)
|
||
// 设置节点文本
|
||
this.setNodeText = this.setNodeText.bind(this)
|
||
this.mindMap.command.add('SET_NODE_TEXT', this.setNodeText)
|
||
// 设置节点图片
|
||
this.setNodeImage = this.setNodeImage.bind(this)
|
||
this.mindMap.command.add('SET_NODE_IMAGE', this.setNodeImage)
|
||
// 设置节点图标
|
||
this.setNodeIcon = this.setNodeIcon.bind(this)
|
||
this.mindMap.command.add('SET_NODE_ICON', this.setNodeIcon)
|
||
// 设置节点超链接
|
||
this.setNodeHyperlink = this.setNodeHyperlink.bind(this)
|
||
this.mindMap.command.add('SET_NODE_HYPERLINK', this.setNodeHyperlink)
|
||
// 设置节点备注
|
||
this.setNodeNote = this.setNodeNote.bind(this)
|
||
this.mindMap.command.add('SET_NODE_NOTE', this.setNodeNote)
|
||
// 设置节点标签
|
||
this.setNodeTag = this.setNodeTag.bind(this)
|
||
this.mindMap.command.add('SET_NODE_TAG', this.setNodeTag)
|
||
// 添加节点概要
|
||
this.addGeneralization = this.addGeneralization.bind(this)
|
||
this.mindMap.command.add('ADD_GENERALIZATION', this.addGeneralization)
|
||
// 删除节点概要
|
||
this.removeGeneralization = this.removeGeneralization.bind(this)
|
||
this.mindMap.command.add('REMOVE_GENERALIZATION', this.removeGeneralization)
|
||
// 设置节点自定义位置
|
||
this.setNodeCustomPosition = this.setNodeCustomPosition.bind(this)
|
||
this.mindMap.command.add(
|
||
'SET_NODE_CUSTOM_POSITION',
|
||
this.setNodeCustomPosition
|
||
)
|
||
// 一键整理布局
|
||
this.resetLayout = this.resetLayout.bind(this)
|
||
this.mindMap.command.add('RESET_LAYOUT', this.resetLayout)
|
||
// 设置节点形状
|
||
this.setNodeShape = this.setNodeShape.bind(this)
|
||
this.mindMap.command.add('SET_NODE_SHAPE', this.setNodeShape)
|
||
}
|
||
|
||
// 注册快捷键
|
||
registerShortcutKeys() {
|
||
// 插入下级节点
|
||
this.mindMap.keyCommand.addShortcut('Tab', () => {
|
||
this.mindMap.execCommand('INSERT_CHILD_NODE')
|
||
})
|
||
// 插入同级节点
|
||
this.insertNodeWrap = () => {
|
||
if (this.textEdit.showTextEdit) {
|
||
return
|
||
}
|
||
this.mindMap.execCommand('INSERT_NODE')
|
||
}
|
||
this.mindMap.keyCommand.addShortcut('Enter', this.insertNodeWrap)
|
||
// 插入概要
|
||
this.mindMap.keyCommand.addShortcut('Control+s', this.addGeneralization)
|
||
// 展开/收起节点
|
||
this.toggleActiveExpand = this.toggleActiveExpand.bind(this)
|
||
this.mindMap.keyCommand.addShortcut('/', this.toggleActiveExpand)
|
||
// 删除节点
|
||
this.removeNodeWrap = () => {
|
||
this.mindMap.execCommand('REMOVE_NODE')
|
||
}
|
||
this.mindMap.keyCommand.addShortcut('Del|Backspace', this.removeNodeWrap)
|
||
// 节点编辑时某些快捷键会存在冲突,需要暂时去除
|
||
this.mindMap.on('before_show_text_edit', () => {
|
||
this.startTextEdit()
|
||
})
|
||
this.mindMap.on('hide_text_edit', () => {
|
||
this.endTextEdit()
|
||
})
|
||
// 全选
|
||
this.mindMap.keyCommand.addShortcut('Control+a', () => {
|
||
this.mindMap.execCommand('SELECT_ALL')
|
||
})
|
||
// 一键整理布局
|
||
this.mindMap.keyCommand.addShortcut('Control+l', this.resetLayout)
|
||
// 上移节点
|
||
this.mindMap.keyCommand.addShortcut('Control+Up', this.upNode)
|
||
// 下移节点
|
||
this.mindMap.keyCommand.addShortcut('Control+Down', this.downNode)
|
||
// 复制节点、剪切节点、粘贴节点的快捷键需开发者自行注册实现,可参考demo
|
||
}
|
||
|
||
// 开启文字编辑,会禁用回车键和删除键相关快捷键防止冲突
|
||
startTextEdit() {
|
||
this.mindMap.keyCommand.save()
|
||
// this.mindMap.keyCommand.removeShortcut('Del|Backspace')
|
||
// this.mindMap.keyCommand.removeShortcut('/')
|
||
// this.mindMap.keyCommand.removeShortcut('Enter', this.insertNodeWrap)
|
||
}
|
||
|
||
// 结束文字编辑,会恢复回车键和删除键相关快捷键
|
||
endTextEdit() {
|
||
this.mindMap.keyCommand.restore()
|
||
// this.mindMap.keyCommand.addShortcut('Del|Backspace', this.removeNodeWrap)
|
||
// this.mindMap.keyCommand.addShortcut('/', this.toggleActiveExpand)
|
||
// this.mindMap.keyCommand.addShortcut('Enter', this.insertNodeWrap)
|
||
}
|
||
|
||
// 渲染
|
||
render(callback = () => {}, source) {
|
||
let t = Date.now()
|
||
// 如果当前还没有渲染完毕,不再触发渲染
|
||
if (this.isRendering) {
|
||
// 等待当前渲染完毕后再进行一次渲染
|
||
this.hasWaitRendering = true
|
||
return
|
||
}
|
||
this.isRendering = true
|
||
// 触发当前重新渲染的来源
|
||
this.renderSource = source
|
||
// 节点缓存
|
||
this.lastNodeCache = this.nodeCache
|
||
this.nodeCache = {}
|
||
// 重新渲染需要清除激活状态
|
||
if (this.reRender) {
|
||
this.clearActive()
|
||
this.layout.clearNodePool()
|
||
}
|
||
// 计算布局
|
||
this.layout.doLayout(root => {
|
||
// 删除本次渲染时不再需要的节点
|
||
Object.keys(this.lastNodeCache).forEach((uid) => {
|
||
if (!this.nodeCache[uid]) {
|
||
this.lastNodeCache[uid].destroy()
|
||
if (this.lastNodeCache[uid].parent) {
|
||
this.lastNodeCache[uid].parent.removeLine()
|
||
}
|
||
}
|
||
})
|
||
// 更新根节点
|
||
this.root = root
|
||
// 渲染节点
|
||
const onEnd = () => {
|
||
this.isRendering = false
|
||
this.mindMap.emit('node_tree_render_end')
|
||
callback && callback()
|
||
if (this.hasWaitRendering) {
|
||
this.hasWaitRendering = false
|
||
this.render(callback, source)
|
||
}
|
||
}
|
||
let { enableNodeTransitionMove, nodeTransitionMoveDuration } =
|
||
this.mindMap.opt
|
||
this.root.render(() => {
|
||
let dur = Date.now() - t
|
||
if (enableNodeTransitionMove && dur <= nodeTransitionMoveDuration) {
|
||
setTimeout(() => {
|
||
onEnd()
|
||
}, nodeTransitionMoveDuration - dur);
|
||
} else {
|
||
onEnd()
|
||
}
|
||
})
|
||
})
|
||
this.mindMap.emit('node_active', null, this.activeNodeList)
|
||
}
|
||
|
||
// 清除当前激活的节点
|
||
clearActive() {
|
||
this.activeNodeList.forEach(item => {
|
||
this.setNodeActive(item, false)
|
||
})
|
||
this.activeNodeList = []
|
||
}
|
||
|
||
// 清除当前所有激活节点,并会触发事件
|
||
clearAllActive() {
|
||
if (this.activeNodeList.length <= 0) {
|
||
return
|
||
}
|
||
this.clearActive()
|
||
this.mindMap.emit('node_active', null, [])
|
||
}
|
||
|
||
// 添加节点到激活列表里
|
||
addActiveNode(node) {
|
||
let index = this.findActiveNodeIndex(node)
|
||
if (index === -1) {
|
||
this.activeNodeList.push(node)
|
||
}
|
||
}
|
||
|
||
// 在激活列表里移除某个节点
|
||
removeActiveNode(node) {
|
||
let index = this.findActiveNodeIndex(node)
|
||
if (index === -1) {
|
||
return
|
||
}
|
||
this.activeNodeList.splice(index, 1)
|
||
}
|
||
|
||
// 检索某个节点在激活列表里的索引
|
||
findActiveNodeIndex(node) {
|
||
return this.activeNodeList.findIndex(item => {
|
||
return item === node
|
||
})
|
||
}
|
||
|
||
// 获取节点在同级里的索引位置
|
||
getNodeIndex(node) {
|
||
return node.parent
|
||
? node.parent.children.findIndex(item => {
|
||
return item === node
|
||
})
|
||
: 0
|
||
}
|
||
|
||
// 全选
|
||
selectAll() {
|
||
walk(
|
||
this.root,
|
||
null,
|
||
node => {
|
||
if (!node.nodeData.data.isActive) {
|
||
node.nodeData.data.isActive = true
|
||
this.addActiveNode(node)
|
||
setTimeout(() => {
|
||
node.updateNodeShape()
|
||
}, 0)
|
||
}
|
||
},
|
||
null,
|
||
true,
|
||
0,
|
||
0
|
||
)
|
||
}
|
||
|
||
// 回退
|
||
back(step) {
|
||
this.clearAllActive()
|
||
let data = this.mindMap.command.back(step)
|
||
if (data) {
|
||
this.renderTree = data
|
||
this.mindMap.render()
|
||
}
|
||
}
|
||
|
||
// 前进
|
||
forward(step) {
|
||
this.clearAllActive()
|
||
let data = this.mindMap.command.forward(step)
|
||
if (data) {
|
||
this.renderTree = data
|
||
this.mindMap.render()
|
||
}
|
||
}
|
||
|
||
// 规范指定节点数据
|
||
formatAppointNodes(appointNodes) {
|
||
if (!appointNodes) return []
|
||
return Array.isArray(appointNodes) ? appointNodes: [appointNodes]
|
||
}
|
||
|
||
// 插入同级节点,多个节点只会操作第一个节点
|
||
insertNode(openEdit = true, appointNodes = [], appointData = null) {
|
||
appointNodes = this.formatAppointNodes(appointNodes)
|
||
if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) {
|
||
return
|
||
}
|
||
let { defaultInsertSecondLevelNodeText, defaultInsertBelowSecondLevelNodeText } = this.mindMap.opt
|
||
let list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
|
||
let first = list[0]
|
||
if (first.isRoot) {
|
||
this.insertChildNode(openEdit, appointNodes, appointData)
|
||
} else {
|
||
let text = first.layerIndex === 1 ? defaultInsertSecondLevelNodeText : defaultInsertBelowSecondLevelNodeText
|
||
if (first.layerIndex === 1) {
|
||
first.parent.destroy()
|
||
}
|
||
let index = this.getNodeIndex(first)
|
||
first.parent.nodeData.children.splice(index + 1, 0, {
|
||
inserting: openEdit,
|
||
data: {
|
||
text: text,
|
||
expand: true,
|
||
...(appointData || {})
|
||
},
|
||
children: []
|
||
})
|
||
this.mindMap.render()
|
||
}
|
||
}
|
||
|
||
// 插入子节点
|
||
insertChildNode(openEdit = true, appointNodes = [], appointData = null) {
|
||
appointNodes = this.formatAppointNodes(appointNodes)
|
||
if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) {
|
||
return
|
||
}
|
||
let { defaultInsertSecondLevelNodeText, defaultInsertBelowSecondLevelNodeText } = this.mindMap.opt
|
||
let list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
|
||
list.forEach(node => {
|
||
if (!node.nodeData.children) {
|
||
node.nodeData.children = []
|
||
}
|
||
let text = node.isRoot ? defaultInsertSecondLevelNodeText : defaultInsertBelowSecondLevelNodeText
|
||
node.nodeData.children.push({
|
||
inserting: openEdit,
|
||
data: {
|
||
text: text,
|
||
expand: true,
|
||
...(appointData || {})
|
||
},
|
||
children: []
|
||
})
|
||
// 插入子节点时自动展开子节点
|
||
node.nodeData.data.expand = true
|
||
if (node.isRoot) {
|
||
node.destroy()
|
||
}
|
||
})
|
||
this.mindMap.render()
|
||
}
|
||
|
||
// 上移节点,多个节点只会操作第一个节点
|
||
upNode() {
|
||
if (this.activeNodeList.length <= 0) {
|
||
return
|
||
}
|
||
let node = this.activeNodeList[0]
|
||
if (node.isRoot) {
|
||
return
|
||
}
|
||
let parent = node.parent
|
||
let childList = parent.children
|
||
let index = childList.findIndex(item => {
|
||
return item === node
|
||
})
|
||
if (index === -1 || index === 0) {
|
||
return
|
||
}
|
||
let insertIndex = index - 1
|
||
// 节点实例
|
||
childList.splice(index, 1)
|
||
childList.splice(insertIndex, 0, node)
|
||
// 节点数据
|
||
parent.nodeData.children.splice(index, 1)
|
||
parent.nodeData.children.splice(insertIndex, 0, node.nodeData)
|
||
this.mindMap.render()
|
||
}
|
||
|
||
// 下移节点,多个节点只会操作第一个节点
|
||
downNode() {
|
||
if (this.activeNodeList.length <= 0) {
|
||
return
|
||
}
|
||
let node = this.activeNodeList[0]
|
||
if (node.isRoot) {
|
||
return
|
||
}
|
||
let parent = node.parent
|
||
let childList = parent.children
|
||
let index = childList.findIndex(item => {
|
||
return item === node
|
||
})
|
||
if (index === -1 || index === childList.length - 1) {
|
||
return
|
||
}
|
||
let insertIndex = index + 1
|
||
// 节点实例
|
||
childList.splice(index, 1)
|
||
childList.splice(insertIndex, 0, node)
|
||
// 节点数据
|
||
parent.nodeData.children.splice(index, 1)
|
||
parent.nodeData.children.splice(insertIndex, 0, node.nodeData)
|
||
this.mindMap.render()
|
||
}
|
||
|
||
// 将节点移动到另一个节点的前面
|
||
insertBefore(node, exist) {
|
||
if (node.isRoot) {
|
||
return
|
||
}
|
||
// 如果是二级节点变成了下级节点,或是下级节点变成了二级节点,节点样式需要更新
|
||
let nodeLayerChanged = (node.layerIndex === 1 && exist.layerIndex !== 1) || (node.layerIndex !== 1 && exist.layerIndex === 1)
|
||
// 移动节点
|
||
let nodeParent = node.parent
|
||
let nodeBorthers = nodeParent.children
|
||
let nodeIndex = nodeBorthers.findIndex(item => {
|
||
return item === node
|
||
})
|
||
if (nodeIndex === -1) {
|
||
return
|
||
}
|
||
nodeBorthers.splice(nodeIndex, 1)
|
||
nodeParent.nodeData.children.splice(nodeIndex, 1)
|
||
|
||
// 目标节点
|
||
let existParent = exist.parent
|
||
let existBorthers = existParent.children
|
||
let existIndex = existBorthers.findIndex(item => {
|
||
return item === exist
|
||
})
|
||
if (existIndex === -1) {
|
||
return
|
||
}
|
||
existBorthers.splice(existIndex, 0, node)
|
||
existParent.nodeData.children.splice(existIndex, 0, node.nodeData)
|
||
this.mindMap.render(() => {
|
||
if (nodeLayerChanged) {
|
||
node.reRender()
|
||
}
|
||
})
|
||
}
|
||
|
||
// 将节点移动到另一个节点的后面
|
||
insertAfter(node, exist) {
|
||
if (node.isRoot) {
|
||
return
|
||
}
|
||
// 如果是二级节点变成了下级节点,或是下级节点变成了二级节点,节点样式需要更新
|
||
let nodeLayerChanged = (node.layerIndex === 1 && exist.layerIndex !== 1) || (node.layerIndex !== 1 && exist.layerIndex === 1)
|
||
// 移动节点
|
||
let nodeParent = node.parent
|
||
let nodeBorthers = nodeParent.children
|
||
let nodeIndex = nodeBorthers.findIndex(item => {
|
||
return item === node
|
||
})
|
||
if (nodeIndex === -1) {
|
||
return
|
||
}
|
||
nodeBorthers.splice(nodeIndex, 1)
|
||
nodeParent.nodeData.children.splice(nodeIndex, 1)
|
||
|
||
// 目标节点
|
||
let existParent = exist.parent
|
||
let existBorthers = existParent.children
|
||
let existIndex = existBorthers.findIndex(item => {
|
||
return item === exist
|
||
})
|
||
if (existIndex === -1) {
|
||
return
|
||
}
|
||
existIndex++
|
||
existBorthers.splice(existIndex, 0, node)
|
||
existParent.nodeData.children.splice(existIndex, 0, node.nodeData)
|
||
this.mindMap.render(() => {
|
||
if (nodeLayerChanged) {
|
||
node.reRender()
|
||
}
|
||
})
|
||
}
|
||
|
||
// 移除节点
|
||
removeNode(appointNodes = []) {
|
||
appointNodes = this.formatAppointNodes(appointNodes)
|
||
if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) {
|
||
return
|
||
}
|
||
let isAppointNodes = appointNodes.length > 0
|
||
let list = isAppointNodes ? appointNodes : this.activeNodeList
|
||
let root = list.find((node) => {
|
||
return node.isRoot
|
||
})
|
||
if (root) {
|
||
this.clearActive()
|
||
root.children.forEach(child => {
|
||
child.remove()
|
||
})
|
||
root.children = []
|
||
root.nodeData.children = []
|
||
} else {
|
||
for (let i = 0; i < list.length; i++) {
|
||
let node = list[i]
|
||
if (isAppointNodes) list.splice(i, 1)
|
||
if (node.isGeneralization) {
|
||
// 删除概要节点
|
||
this.setNodeData(node.generalizationBelongNode, {
|
||
generalization: null
|
||
})
|
||
node.generalizationBelongNode.update()
|
||
this.removeActiveNode(node)
|
||
i--
|
||
} else {
|
||
this.removeActiveNode(node)
|
||
this.removeOneNode(node)
|
||
i--
|
||
}
|
||
}
|
||
}
|
||
this.mindMap.emit('node_active', null, this.activeNodeList)
|
||
this.mindMap.render()
|
||
}
|
||
|
||
// 移除某个指定节点
|
||
removeOneNode(node) {
|
||
let index = this.getNodeIndex(node)
|
||
node.remove()
|
||
node.parent.children.splice(index, 1)
|
||
node.parent.nodeData.children.splice(index, 1)
|
||
}
|
||
|
||
// 复制节点,多个节点只会操作第一个节点
|
||
copyNode() {
|
||
if (this.activeNodeList.length <= 0) {
|
||
return
|
||
}
|
||
return copyNodeTree({}, this.activeNodeList[0], true)
|
||
}
|
||
|
||
// 剪切节点,多个节点只会操作第一个节点
|
||
cutNode(callback) {
|
||
if (this.activeNodeList.length <= 0) {
|
||
return
|
||
}
|
||
let node = this.activeNodeList[0]
|
||
if (node.isRoot) {
|
||
return null
|
||
}
|
||
let copyData = copyNodeTree({}, node, true)
|
||
this.removeActiveNode(node)
|
||
this.removeOneNode(node)
|
||
this.mindMap.emit('node_active', null, this.activeNodeList)
|
||
this.mindMap.render()
|
||
if (callback && typeof callback === 'function') {
|
||
callback(copyData)
|
||
}
|
||
}
|
||
|
||
// 移动一个节点作为另一个节点的子节点
|
||
moveNodeTo(node, toNode) {
|
||
if (node.isRoot) {
|
||
return
|
||
}
|
||
let copyData = copyNodeTree({}, node, false, true)
|
||
this.removeActiveNode(node)
|
||
this.removeOneNode(node)
|
||
this.mindMap.emit('node_active', null, this.activeNodeList)
|
||
toNode.nodeData.children.push(copyData)
|
||
this.mindMap.render()
|
||
if (toNode.isRoot) {
|
||
toNode.destroy()
|
||
}
|
||
}
|
||
|
||
// 粘贴节点到节点
|
||
pasteNode(data) {
|
||
if (this.activeNodeList.length <= 0) {
|
||
return
|
||
}
|
||
this.activeNodeList.forEach(item => {
|
||
item.nodeData.children.push(simpleDeepClone(data))
|
||
})
|
||
this.mindMap.render()
|
||
}
|
||
|
||
// 设置节点样式
|
||
setNodeStyle(node, prop, value, isActive) {
|
||
let data = {}
|
||
if (isActive) {
|
||
data = {
|
||
activeStyle: {
|
||
...(node.nodeData.data.activeStyle || {}),
|
||
[prop]: value
|
||
}
|
||
}
|
||
} else {
|
||
data = {
|
||
[prop]: value
|
||
}
|
||
}
|
||
// 如果开启了富文本,则需要应用到富文本上
|
||
if (this.mindMap.richText) {
|
||
let config = this.mindMap.richText.normalStyleToRichTextStyle({
|
||
[prop]: value
|
||
})
|
||
if (Object.keys(config).length > 0) {
|
||
this.mindMap.richText.showEditText(node)
|
||
this.mindMap.richText.formatAllText(config)
|
||
this.mindMap.richText.hideEditText([node])
|
||
}
|
||
}
|
||
this.setNodeDataRender(node, data)
|
||
// 更新了连线的样式
|
||
if (lineStyleProps.includes(prop)) {
|
||
;(node.parent || node).renderLine(true)
|
||
}
|
||
}
|
||
|
||
// 设置节点是否激活
|
||
setNodeActive(node, active) {
|
||
this.setNodeData(node, {
|
||
isActive: active
|
||
})
|
||
node.updateNodeShape()
|
||
}
|
||
|
||
// 设置节点是否展开
|
||
setNodeExpand(node, expand) {
|
||
this.setNodeData(node, {
|
||
expand
|
||
})
|
||
if (expand) {
|
||
// 展开
|
||
node.children.forEach(item => {
|
||
item.render()
|
||
})
|
||
node.renderLine()
|
||
node.updateExpandBtnNode()
|
||
} else {
|
||
// 收缩
|
||
node.children.forEach(item => {
|
||
item.remove()
|
||
})
|
||
node.removeLine()
|
||
node.updateExpandBtnNode()
|
||
}
|
||
this.mindMap.render()
|
||
}
|
||
|
||
// 展开所有
|
||
expandAllNode() {
|
||
walk(
|
||
this.renderTree,
|
||
null,
|
||
node => {
|
||
if (!node.data.expand) {
|
||
node.data.expand = true
|
||
}
|
||
},
|
||
null,
|
||
true,
|
||
0,
|
||
0
|
||
)
|
||
this.mindMap.render()
|
||
}
|
||
|
||
// 收起所有
|
||
unexpandAllNode() {
|
||
walk(
|
||
this.renderTree,
|
||
null,
|
||
(node, parent, isRoot) => {
|
||
node._node = null
|
||
if (!isRoot) {
|
||
node.data.expand = false
|
||
}
|
||
},
|
||
null,
|
||
true,
|
||
0,
|
||
0
|
||
)
|
||
this.mindMap.render(() => {
|
||
this.mindMap.view.reset()
|
||
})
|
||
}
|
||
|
||
// 展开到指定层级
|
||
expandToLevel(level) {
|
||
walk(
|
||
this.renderTree,
|
||
null,
|
||
(node, parent, isRoot, layerIndex) => {
|
||
node._node = null
|
||
node.data.expand = layerIndex < level
|
||
},
|
||
null,
|
||
true,
|
||
0,
|
||
0
|
||
)
|
||
this.mindMap.render()
|
||
}
|
||
|
||
// 切换激活节点的展开状态
|
||
toggleActiveExpand() {
|
||
this.activeNodeList.forEach(node => {
|
||
if (node.nodeData.children.length <= 0) {
|
||
return
|
||
}
|
||
this.toggleNodeExpand(node)
|
||
})
|
||
}
|
||
|
||
// 切换节点展开状态
|
||
toggleNodeExpand(node) {
|
||
this.mindMap.execCommand(
|
||
'SET_NODE_EXPAND',
|
||
node,
|
||
!node.nodeData.data.expand
|
||
)
|
||
}
|
||
|
||
// 设置节点文本
|
||
setNodeText(node, text, richText) {
|
||
this.setNodeDataRender(node, {
|
||
text,
|
||
richText
|
||
})
|
||
}
|
||
|
||
// 设置节点图片
|
||
setNodeImage(node, { url, title, width, height }) {
|
||
this.setNodeDataRender(node, {
|
||
image: url,
|
||
imageTitle: title || '',
|
||
imageSize: {
|
||
width,
|
||
height
|
||
}
|
||
})
|
||
}
|
||
|
||
// 设置节点图标
|
||
setNodeIcon(node, icons) {
|
||
this.setNodeDataRender(node, {
|
||
icon: icons
|
||
})
|
||
}
|
||
|
||
// 设置节点超链接
|
||
setNodeHyperlink(node, link, title = '') {
|
||
this.setNodeDataRender(node, {
|
||
hyperlink: link,
|
||
hyperlinkTitle: title
|
||
})
|
||
}
|
||
|
||
// 设置节点备注
|
||
setNodeNote(node, note) {
|
||
this.setNodeDataRender(node, {
|
||
note
|
||
})
|
||
}
|
||
|
||
// 设置节点标签
|
||
setNodeTag(node, tag) {
|
||
this.setNodeDataRender(node, {
|
||
tag
|
||
})
|
||
}
|
||
|
||
// 添加节点概要
|
||
addGeneralization(data) {
|
||
if (this.activeNodeList.length <= 0) {
|
||
return
|
||
}
|
||
this.activeNodeList.forEach(node => {
|
||
if (node.nodeData.data.generalization || node.isRoot) {
|
||
return
|
||
}
|
||
this.setNodeData(node, {
|
||
generalization: data || {
|
||
text: '概要'
|
||
}
|
||
})
|
||
node.update()
|
||
})
|
||
this.mindMap.render()
|
||
}
|
||
|
||
// 删除节点概要
|
||
removeGeneralization() {
|
||
if (this.activeNodeList.length <= 0) {
|
||
return
|
||
}
|
||
this.activeNodeList.forEach(node => {
|
||
if (!node.nodeData.data.generalization) {
|
||
return
|
||
}
|
||
this.setNodeData(node, {
|
||
generalization: null
|
||
})
|
||
node.update()
|
||
})
|
||
this.mindMap.render()
|
||
}
|
||
|
||
// 设置节点自定义位置
|
||
setNodeCustomPosition(node, left = undefined, top = undefined) {
|
||
let nodeList = [node] || this.activeNodeList
|
||
nodeList.forEach(item => {
|
||
this.setNodeData(item, {
|
||
customLeft: left,
|
||
customTop: top
|
||
})
|
||
})
|
||
}
|
||
|
||
// 一键整理布局,即去除自定义位置
|
||
resetLayout() {
|
||
walk(
|
||
this.root,
|
||
null,
|
||
node => {
|
||
node.customLeft = undefined
|
||
node.customTop = undefined
|
||
this.setNodeData(node, {
|
||
customLeft: undefined,
|
||
customTop: undefined
|
||
})
|
||
this.mindMap.render()
|
||
},
|
||
null,
|
||
true,
|
||
0,
|
||
0
|
||
)
|
||
}
|
||
|
||
// 设置节点形状
|
||
setNodeShape(node, shape) {
|
||
if (!shape || !shapeList.includes(shape)) {
|
||
return
|
||
}
|
||
let nodeList = [node] || this.activeNodeList
|
||
nodeList.forEach(item => {
|
||
this.setNodeStyle(item, 'shape', shape)
|
||
})
|
||
}
|
||
|
||
// 更新节点数据
|
||
setNodeData(node, data) {
|
||
Object.keys(data).forEach(key => {
|
||
node.nodeData.data[key] = data[key]
|
||
})
|
||
}
|
||
|
||
// 设置节点数据,并判断是否渲染
|
||
setNodeDataRender(node, data) {
|
||
this.setNodeData(node, data)
|
||
let changed = node.reRender()
|
||
if (changed) {
|
||
if (node.isGeneralization) {
|
||
// 概要节点
|
||
node.generalizationBelongNode.updateGeneralization()
|
||
}
|
||
this.mindMap.render()
|
||
}
|
||
}
|
||
|
||
// 移动节点到画布中心
|
||
moveNodeToCenter(node) {
|
||
let halfWidth = this.mindMap.width / 2
|
||
let halfHeight = this.mindMap.height / 2
|
||
let { left, top, width, height } = node
|
||
let nodeCenterX = left + width / 2
|
||
let nodeCenterY = top + height / 2
|
||
let { state } = this.mindMap.view.getTransformData()
|
||
let targetX = halfWidth - state.x
|
||
let targetY = halfHeight - state.y
|
||
let offsetX = targetX - nodeCenterX
|
||
let offsetY = targetY - nodeCenterY
|
||
this.mindMap.view.translateX(offsetX)
|
||
this.mindMap.view.translateY(offsetY)
|
||
this.mindMap.view.setScale(1)
|
||
}
|
||
}
|
||
|
||
export default Render
|