转载
- https://antv-x6.gitee.io/zh/examples/showcase/practices#flowchart
- https://antv-g6.gitee.io/zh/docs/manual/tutorial/elements
描述
关于节点名称修改是从G6文档中找到的。。。
- 实现了点击 node 节点就可以修改节点名称
预览
源码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
}
#stencil {
200px;
height: 100vh;
position: relative;
float: left;
}
#graph-container {
margin-left: 200px;
margin-right: 400px;
height: 100vh;
position: relative;
}
#info-container{
400px;
height: 100vh;
position: relative;
float: right;
background-color: #31BDEC;
}
#node-info{
100%;
box-sizing: border-box;
padding: 20px;
display: none;
}
</style>
<!-- 引入 layui.css -->
<link href="//unpkg.com/layui@2.7.6/dist/css/layui.css" rel="stylesheet">
</head>
<body>
<form class="layui-form" style="padding: 20px">
<div class="layui-form-item">
<div class="layui-btn-group">
<button type="button" class="layui-btn" onclick="printNodeList()">导出节点</button>
<button type="button" class="layui-btn" onclick="importNodeList()">导入节点</button>
</div>
</div>
<div class="layui-form-item">
<textarea name="desc" placeholder="请输入内容" class="layui-textarea" id="json-data"></textarea>
</div>
</form>
<!-- 左侧工具 -->
<div id="stencil"></div>
<!-- 右侧详情 -->
<div id="info-container">
<form class="layui-form" id="node-info">
<div class="layui-form-item">
<label class="layui-form-label">Label</label>
<div class="layui-input-block">
<input type="text" id="node-name" autocomplete="off" class="layui-input">
</div>
</div>
</form>
</div>
<!-- 画布容器 -->
<div id="graph-container">
</div>
<script src="//unpkg.com/layui@2.7.6/dist/layui.js"></script>
<script src="https://unpkg.com/@antv/x6@1.1.1/dist/x6.js"></script>
<script>
// #region 初始化画布
const graph = new X6.Graph({
container: document.getElementById('graph-container'),
grid: true,
mousewheel: {
enabled: true,
zoomAtMousePosition: true,
modifiers: 'ctrl',
minScale: 0.5,
maxScale: 3,
},
connecting: {
router: {
name: 'manhattan',
args: {
padding: 1,
},
},
connector: {
name: 'rounded',
args: {
radius: 8,
},
},
anchor: 'center',
connectionPoint: 'anchor',
allowBlank: false,
snap: {
radius: 20,
},
createEdge() {
return new X6.Shape.Edge({
attrs: {
line: {
stroke: '#A2B1C3',
strokeWidth: 2,
targetMarker: {
name: 'block',
12,
height: 8,
},
},
},
zIndex: 0,
})
},
validateConnection({ targetMagnet }) {
return !!targetMagnet
},
},
highlighting: {
magnetAdsorbed: {
name: 'stroke',
args: {
attrs: {
fill: '#5F95FF',
stroke: '#5F95FF',
},
},
},
},
resizing: true,
rotating: true,
selecting: {
enabled: true,
rubberband: true,
showNodeSelectionBox: true,
},
snapline: true,
keyboard: true,
clipboard: true,
})
// #endregion
// #region 初始化 stencil
const stencil = new X6.Addon.Stencil({
title: '流程图',
target: graph,
stencilGraphWidth: 200,
stencilGraphHeight: 180,
collapsable: true,
groups: [
{
title: '基础流程图',
name: 'group1',
},
{
title: '系统设计图',
name: 'group2',
graphHeight: 250,
layoutOptions: {
rowHeight: 70,
},
},
],
layoutOptions: {
columns: 2,
columnWidth: 80,
rowHeight: 55,
},
})
document.getElementById('stencil').appendChild(stencil.container)
// #endregion
// #region 快捷键与事件
// copy cut paste
graph.bindKey(['meta+c', 'ctrl+c'], () => {
const cells = graph.getSelectedCells()
if (cells.length) {
graph.copy(cells)
}
return false
})
graph.bindKey(['meta+x', 'ctrl+x'], () => {
const cells = graph.getSelectedCells()
if (cells.length) {
graph.cut(cells)
}
return false
})
graph.bindKey(['meta+v', 'ctrl+v'], () => {
if (!graph.isClipboardEmpty()) {
const cells = graph.paste({ offset: 32 })
graph.cleanSelection()
graph.select(cells)
}
return false
})
//undo redo
graph.bindKey(['meta+z', 'ctrl+z'], () => {
if (graph.history.canUndo()) {
graph.history.undo()
}
return false
})
graph.bindKey(['meta+shift+z', 'ctrl+shift+z'], () => {
if (graph.history.canRedo()) {
graph.history.redo()
}
return false
})
// select all
graph.bindKey(['meta+a', 'ctrl+a'], () => {
const nodes = graph.getNodes()
if (nodes) {
graph.select(nodes)
}
})
//delete
graph.bindKey('backspace', () => {
const cells = graph.getSelectedCells()
if (cells.length) {
graph.removeCells(cells)
}
})
// zoom
graph.bindKey(['ctrl+1', 'meta+1'], () => {
const zoom = graph.zoom()
if (zoom < 1.5) {
graph.zoom(0.1)
}
})
graph.bindKey(['ctrl+2', 'meta+2'], () => {
const zoom = graph.zoom()
if (zoom > 0.5) {
graph.zoom(-0.1)
}
})
// 控制连接桩显示/隐藏
const showPorts = (ports, show) => {
for (let i = 0, len = ports.length; i < len; i = i + 1) {
ports[i].style.visibility = show ? 'visible' : 'hidden'
}
}
graph.on('node:mouseenter', () => {
const container = document.getElementById('graph-container')
const ports = container.querySelectorAll(
'.x6-port-body',
)
showPorts(ports, true)
})
graph.on('node:mouseleave', () => {
const container = document.getElementById('graph-container')
const ports = container.querySelectorAll(
'.x6-port-body',
)
showPorts(ports, false)
})
// #endregion
// #region 初始化图形
const ports = {
groups: {
top: {
position: 'top',
attrs: {
circle: {
r: 4,
magnet: true,
stroke: '#5F95FF',
strokeWidth: 1,
fill: '#fff',
style: {
visibility: 'hidden',
},
},
},
},
right: {
position: 'right',
attrs: {
circle: {
r: 4,
magnet: true,
stroke: '#5F95FF',
strokeWidth: 1,
fill: '#fff',
style: {
visibility: 'hidden',
},
},
},
},
bottom: {
position: 'bottom',
attrs: {
circle: {
r: 4,
magnet: true,
stroke: '#5F95FF',
strokeWidth: 1,
fill: '#fff',
style: {
visibility: 'hidden',
},
},
},
},
left: {
position: 'left',
attrs: {
circle: {
r: 4,
magnet: true,
stroke: '#5F95FF',
strokeWidth: 1,
fill: '#fff',
style: {
visibility: 'hidden',
},
},
},
},
},
items: [
{
group: 'top',
},
{
group: 'right',
},
{
group: 'bottom',
},
{
group: 'left',
},
],
}
X6.Graph.registerNode(
'custom-rect',
{
inherit: 'rect',
66,
height: 36,
attrs: {
body: {
strokeWidth: 1,
stroke: '#5F95FF',
fill: '#EFF4FF',
},
text: {
fontSize: 12,
fill: '#262626',
},
},
ports: { ...ports },
},
true,
)
X6.Graph.registerNode(
'custom-polygon',
{
inherit: 'polygon',
66,
height: 36,
attrs: {
body: {
strokeWidth: 1,
stroke: '#5F95FF',
fill: '#EFF4FF',
},
text: {
fontSize: 12,
fill: '#262626',
},
},
ports: {
...ports,
items: [
{
group: 'top',
},
{
group: 'bottom',
},
],
},
},
true,
)
X6.Graph.registerNode(
'custom-circle',
{
inherit: 'circle',
45,
height: 45,
attrs: {
body: {
strokeWidth: 1,
stroke: '#5F95FF',
fill: '#EFF4FF',
},
text: {
fontSize: 12,
fill: '#262626',
},
},
ports: { ...ports },
},
true,
)
X6.Graph.registerNode(
'custom-image',
{
inherit: 'rect',
52,
height: 52,
markup: [
{
tagName: 'rect',
selector: 'body',
},
{
tagName: 'image',
},
{
tagName: 'text',
selector: 'label',
},
],
attrs: {
body: {
stroke: '#5F95FF',
fill: '#5F95FF',
},
image: {
26,
height: 26,
refX: 13,
refY: 16,
},
label: {
refX: 3,
refY: 2,
textAnchor: 'left',
textVerticalAnchor: 'top',
fontSize: 12,
fill: '#fff',
},
},
ports: { ...ports },
},
true,
)
const r1 = graph.createNode({
shape: 'custom-rect',
label: '开始',
attrs: {
body: {
rx: 20,
ry: 26,
},
},
})
const r2 = graph.createNode({
shape: 'custom-rect',
label: '过程',
})
const r3 = graph.createNode({
shape: 'custom-rect',
attrs: {
body: {
rx: 6,
ry: 6,
},
},
label: '可选过程',
})
const r4 = graph.createNode({
shape: 'custom-polygon',
attrs: {
body: {
refPoints: '0,10 10,0 20,10 10,20',
},
},
label: '决策',
})
const r5 = graph.createNode({
shape: 'custom-polygon',
attrs: {
body: {
refPoints: '10,0 40,0 30,20 0,20',
},
},
label: '数据',
})
const r6 = graph.createNode({
shape: 'custom-circle',
label: '连接',
})
stencil.load([r1, r2, r3, r4, r5, r6], 'group1')
const imageShapes = [
{
label: 'Client',
image:
'https://gw.alipayobjects.com/zos/bmw-prod/687b6cb9-4b97-42a6-96d0-34b3099133ac.svg',
},
{
label: 'Http',
image:
'https://gw.alipayobjects.com/zos/bmw-prod/dc1ced06-417d-466f-927b-b4a4d3265791.svg',
},
{
label: 'Api',
image:
'https://gw.alipayobjects.com/zos/bmw-prod/c55d7ae1-8d20-4585-bd8f-ca23653a4489.svg',
},
{
label: 'Sql',
image:
'https://gw.alipayobjects.com/zos/bmw-prod/6eb71764-18ed-4149-b868-53ad1542c405.svg',
},
{
label: 'Clound',
image:
'https://gw.alipayobjects.com/zos/bmw-prod/c36fe7cb-dc24-4854-aeb5-88d8dc36d52e.svg',
},
{
label: 'Mq',
image:
'https://gw.alipayobjects.com/zos/bmw-prod/2010ac9f-40e7-49d4-8c4a-4fcf2f83033b.svg',
},
]
const imageNodes = imageShapes.map((item) =>
graph.createNode({
shape: 'custom-image',
label: item.label,
attrs: {
image: {
'xlink:href': item.image,
},
},
}),
)
stencil.load(imageNodes, 'group2')
// #endregion
function preWork() {
// 这里协助演示的代码,在实际项目中根据实际情况进行调整
const container = document.getElementById('container')
const stencilContainer = document.createElement('div')
stencilContainer.id = 'stencil'
const graphContainer = document.createElement('div')
graphContainer.id = 'graph-container'
container.appendChild(stencilContainer)
container.appendChild(graphContainer)
insertCss(`
#container {
display: flex;
border: 1px solid #dfe3e8;
}
#stencil {
180px;
height: 100%;
position: relative;
border-right: 1px solid #dfe3e8;
}
#graph-container {
calc(100% - 180px);
height: 100%;
}
.x6-widget-stencil {
background-color: #fff;
}
.x6-widget-stencil-title {
background-color: #fff;
}
.x6-widget-stencil-group-title {
background-color: #fff !important;
}
.x6-widget-transform {
margin: -1px 0 0 -1px;
padding: 0px;
border: 1px solid #239edd;
}
.x6-widget-transform > div {
border: 1px solid #239edd;
}
.x6-widget-transform > div:hover {
background-color: #3dafe4;
}
.x6-widget-transform-active-handle {
background-color: #3dafe4;
}
.x6-widget-transform-resize {
border-radius: 0;
}
.x6-widget-selection-inner {
border: 1px solid #239edd;
}
.x6-widget-selection-box {
opacity: 0;
}
`)
}
/**
* 当前选中的节点
* */
var actionNode;
/**
* 监听lable变化
* */
// Object.defineProperty(actionNode, 'label', {
// enumerable: true,
// configurable: true,
// set(newVal){
// this.label=newVal
// console.log('set:'+this.label)
// },
// get(){
// console.log('get:'+this.label)
// return this.label
// }
// })
/**
* 节点点击事件
* */
graph.on('node:click', (event) => {
actionNode = event.node;
document.querySelector("#node-name").value = actionNode.label;
document.querySelector("#node-info").style.display = "block";
})
/**
* 空白画布点击事件
* */
graph.on('blank:click', () => {
actionNode = null;
document.querySelector("#node-info").style.display = "none";
})
/**
* 监听输入框修改事件,如果没有选择节点也不报错。
* */
document.querySelector("#node-name").addEventListener("input", (e)=>{
try{
actionNode.label = e.explicitOriginalTarget.value
}catch(e){
}
})
/**
* 导出节点
* */
function printNodeList() {
document.querySelector("#json-data").value = JSON.stringify(graph.toJSON({ diff: true }))
}
/**
* 导入节点
* */
function importNodeList() {
graph.fromJSON(JSON.parse(document.querySelector("#json-data").value));
}
</script>
</body>
</html>