实现如下效果图: 蕴含, 新增, 编辑, 删除性能.
1. 首先装置 G6
2. 创立 vue 页面,
3. 在页面中引入 G6
import G6 from '@antv/g6'
const {Util} = G6
4. 创立页面容器<div id="myMap" />
5. 在 mounted 中获取容器宽高
this.mindStyle.width = this.$refs.continer.offsetWidth
this.mindStyle.height = this.$refs.continer.offsetHeight
6. 初始化 mindmap
const that = this
const borderStyle = 'height: 42, stroke: #70A2E1, radius: 6, fill: #DCE6F2, lineWidth: 2'
const labelStyle = 'fontSize: 16, marginTop: 13, marginLeft: 16, marginRight:16'
const tipStyle = 'marginTop: -15, stroke: #409EFF, fill: #409EFF, cursor: pointer'
const position = [[0, 0.5], [1, 0.5]]
G6.registerNode(
'dice-mind-map-root', {jsx: (cfg) => {const width = Util.getTextSize(cfg.label, 16)[0] + 50
return `
<group>
<rect draggable="true" style={{width: ${width}, ${borderStyle}}} keyshape>
<text style={{${labelStyle} }}>${cfg.label}</text>
${type === 'add' ? '': `<text style={{ marginLeft: ${width - 16}, ${tipStyle}, opacity: ${cfg.hover ? 0.75 : 0} }} action="add">+</text>`}
</rect>
</group>`
},
getAnchorPoints() {return position}
},
'single-node')
G6.registerNode(
'dice-mind-map-sub', {jsx: (cfg) => {const width = Util.getTextSize(cfg.label, 16)[0] + 50
return `
<group>
<rect draggable="true" style={{width: ${width},${borderStyle}}} keyshape>
<text draggable="true" style={{${labelStyle} }}>${cfg.label}</text>
${type === 'add' ? '': `<text style={{ marginLeft: ${width - 30}, ${tipStyle}, opacity: ${cfg.hover ? 0.75 : 0}, next:'inline'}} action="add">+</text>`}
${type === 'add' ? '': `<text style={{ marginLeft: ${width - 20}, ${tipStyle}, opacity: ${cfg.hover ? 0.75 : 0}, next:'inline'}} action="delete">-</text>`}
</rect>
</group>`
},
getAnchorPoints() {return position}
},
'single-node'
)
G6.registerBehavior('dice-mindmap', {getEvents() {if (type !== 'add') {
return {
'node:click': 'clickNode',
'node:dblclick': 'editNode',
'node:mouseenter': 'hoverNode',
'node:mouseleave': 'hoverNodeOut'
}
}
},
clickNode(evt) {const model = evt.item.get('model')
const name = evt.target.get('action')
switch (name) {
case 'add':
that.nodeAdd(model, evt)
break
case 'delete':
this.nodeDelete(model, evt)
break
case 'edit':
break
default:
return
}
},
editNode(evt) {that.nodeEdit(evt)
},
hoverNode(evt) {that.nodeHover(evt, true)
},
hoverNodeOut(evt) {that.nodeHover(evt, false)
}
})
this.mindmap = new G6.TreeGraph({
container: 'myMap',
width: this.mindStyle.width,
height: this.mindStyle.height,
fitView: 'autoSize',
fitViewPadding: [10, 20],
layout: {
type: 'mindmap',
direction: 'H',
getHeight: () => {return 40},
getWidth: (node) => {
return node.level === 0
? Util.getTextSize(node.label, 16)[0] + 20
: Util.getTextSize(node.label, 12)[0]
},
getVGap: () => {return 10},
getHGap: () => {return 60},
getSide: (node) => {return node.data.direction}
},
// 边的款式
defaultEdge: {
type: 'cubic-horizontal',
style: {
lineWidth: 2,
stroke: '#C3D8F2'
}
},
// 文字款式
defaultNode: {
labelCfg: {
style: {
fill: '#333',
fontSize: 20
}
},
style: {
stroke: '#72CC4A',
width: 150
}
},
minZoom: 0.5,
modes: {default: ['drag-canvas', 'zoom-canvas', 'dice-mindmap']
}
})
this.mindmap.data(that.dataFormat(this.treeNode))
this.mindmap.render()
7. 鼠标移入事件, 显示增加删除按钮
nodeHover(evt, status) {
evt.currentTarget.updateItem(evt.item, {hover: status})
},
8. 节点编辑事件, 双击能够编辑文本内容
nodeEdit(evt) {
const item = evt.item
const model = item.get('model')
const {x, y} = item.calculateBBox()
const graph = evt.currentTarget
const realPosition = evt.currentTarget.getClientByPoint(x, y)
const el = document.createElement('div')
const fontSizeMap = {
'dice-mind-map-root': 24,
'dice-mind-map-sub': 18
}
el.style.fontSize = fontSizeMap[model.type] + 'px'
el.style.position = 'fixed'
el.style.top = realPosition.y + 'px'
el.style.left = realPosition.x + 'px'
el.style.paddingLeft = '12px'
el.style.transformOrigin = 'top left'
el.style.transform = `scale(${evt.currentTarget.getZoom()})`
const input = document.createElement('input')
input.style.border = 'none'
input.value = model.label
input.style.width = Util.getTextSize(model.label, fontSizeMap[model.type])[0] + 15 + 'px'
input.className = 'dice-input'
el.className = 'dice-input'
el.appendChild(input)
document.body.appendChild(el)
const destroyEl = () => {document.body.removeChild(el)
}
const clickEvt = (event) => {if (!(event.target && event.target.className && event.target.className.includes('dice-input'))) {window.removeEventListener('mousedown', clickEvt)
window.removeEventListener('scroll', clickEvt)
graph.updateItem(item, {label: input.value})
graph.layout(false)
graph.off('wheelZoom', clickEvt)
destroyEl()}
}
graph.on('wheelZoom', clickEvt)
window.addEventListener('mousedown', clickEvt)
window.addEventListener('scroll', clickEvt)
input.addEventListener('keyup', (event) => {if (event.key === 'Enter') {
clickEvt({target: {}
})
}
})
},
9. 节点增加事件
nodeAdd(model, evt) {
const newId =
model.id + '-' +
(((model.children || []).reduce((a, b) => {const num = Number(b.id.split('-').pop())
return a < num ? num : a
}, 0) || 0) + 1)
evt.currentTarget.updateItem(evt.item, {children: (model.children || []).concat([{
id: newId,
direction: newId.charCodeAt(newId.length - 1) % 2 === 0 ? 'right' : 'left',
label: '子节点',
type: 'dice-mind-map-sub',
color: model.color
}])
})
evt.currentTarget.layout(false)
},
10. 节点删除事件
nodeDelete(model, evt) {const parent = evt.item.get('parent')
evt.currentTarget.updateItem(parent, {children: (parent.get('model').children || []).filter((e) => e.id !== model.id)
})
evt.currentTarget.layout(false)
},
11. 数据格式化
dataFormat(data) {const changeData = (d, level = 0) => {
const data = {...d}
switch (level) {
case 0:
data.type = 'dice-mind-map-root'
break
case 1:
data.type = 'dice-mind-map-sub'
break
default:
data.type = 'dice-mind-map-sub'
break
}
data.hover = false
if (level === 1 && !d.direction) {if (!d.direction) {data.direction = d.id.charCodeAt(d.id.length - 1) % 2 === 0 ? 'right' : 'left'
}
}
if (d.children) {data.children = d.children.map((child) => changeData(child, level + 1, data.color))
}
return data
}
return changeData(data)
},
12. 切换显示时, 先销毁this.mindmap.destroy()
13. 向后盾提交数据 this.mindmap.getNodes()
查看所有节点信息 this.mindmap.getNodes()[0]._cfg.model
以后思维导图的树形构造
.