docs: 📚️ update org demo (#1581)

This commit is contained in:
vector
2021-11-20 21:51:45 +08:00
committed by GitHub
parent a8c62ebcf5
commit f2e674c0b4
18 changed files with 498 additions and 489 deletions

View File

@ -0,0 +1,67 @@
import { Graph } from '@antv/x6'
const graph = new Graph({
container: document.getElementById('container')!,
grid: true,
})
graph.addNode({
shape: 'rect',
x: 80,
y: 210,
width: 120,
height: 50,
attrs: {
body: {
stroke: '#9254de',
fill: '#efdbff',
},
text: {
textWrap: {
text: '使用 textWrap 实现文本换行',
width: -10, // 宽度减少 10px
},
},
},
})
graph.addNode({
shape: 'html',
x: 250,
y: 200,
width: 160,
height: 60,
html: () => {
const wrap = document.createElement('div')
wrap.style.width = '100%'
wrap.style.height = '100%'
wrap.style.border = '2px solid #9254de'
wrap.style.background = '#efdbff'
wrap.style.display = 'flex'
wrap.style.justifyContent = 'center'
wrap.style.alignItems = 'center'
wrap.style.wordBreak = 'break-word'
wrap.style.padding = '8px'
wrap.innerText = '使用 HTML 节点实现文本换行'
return wrap
},
})
graph.addNode({
shape: 'rect',
x: 550,
y: 230,
attrs: {
body: {
ref: 'text',
refWidth: 16,
refHeight: 16,
refX: -8,
refY: -8,
stroke: '#9254de',
fill: '#efdbff',
},
},
label: '根据文本的大小确定节点的宽高',
})

View File

@ -20,9 +20,9 @@ graph.addNode({
textWrap: {
text: '使用 textWrap 实现文本换行',
width: -10, // 宽度减少 10px
}
}
}
},
},
},
})
graph.addNode({
@ -61,7 +61,7 @@ graph.addNode({
refY: -8,
stroke: '#9254de',
fill: '#efdbff',
}
},
},
label: '根据文本的大小确定节点的宽高'
})
label: '根据文本的大小确定节点的宽高',
})

View File

@ -0,0 +1,72 @@
import { Graph, Edge, EdgeView, Shape } from '@antv/x6'
const graph = new Graph({
container: document.getElementById('container')!,
grid: true,
connecting: {
validateMagnet({ cell, magnet }) {
let count = 0
const connectionCount = magnet.getAttribute('connection-count')
const max = connectionCount
? parseInt(connectionCount, 10)
: Number.MAX_SAFE_INTEGER
const outgoingEdges = graph.getOutgoingEdges(cell)
if (outgoingEdges) {
outgoingEdges.forEach((edge: Edge) => {
const edgeView = graph.findViewByCell(edge) as EdgeView
if (edgeView.sourceMagnet === magnet) {
count += 1
}
})
}
return count < max
},
createEdge() {
return new Shape.Edge({
attrs: {
line: {
stroke: 'orange',
},
},
})
},
},
})
graph.addNode({
shape: 'rect',
x: 280,
y: 280,
width: 160,
height: 60,
ports: [
{
id: 'a',
attrs: {
circle: {
magnet: true,
connectionCount: 3, // 自定义属性,控制连接桩可连接多少条边
},
},
position: 'top',
},
{
id: 'b',
attrs: {
circle: {
magnet: true,
connectionCount: 0, // 自定义属性,控制连接桩可连接多少条边
},
},
},
],
attrs: {
body: {
fill: '#f5f5f5',
stroke: '#d9d9d9',
strokeWidth: 1,
magnet: true,
connectionCount: 2, // 自定义属性,控制节点可连接多少条边
},
},
})

View File

@ -1,36 +1,20 @@
{
"demos": [
{
"filename": "flex-image.ts",
"title": {
"zh": "根据图片大小缩放节点",
"en": "Resize Node According to the Image Size"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*AhNyQ5453mEAAAAAAAAAAAAAARQnAQ"
},
{
"filename": "change-mode.tsx",
"title": {
"zh": "切换交互模式",
"en": "Switch Interacting"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*YmthRqMeb7AAAAAAAAAAAAAAARQnAQ"
},
{
"filename": "validate-connection.ts",
"title": {
"zh": "限制连接边的数量",
"en": "Limit Connected Edges"
"zh": "链接桩验证/高亮/自动吸附",
"en": "Validattion/Highlight/Absorb of Port"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*f_ztRpTun3AAAAAAAAAAAAAAARQnAQ"
"screenshot": "https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*Gi4OSoXvPzgAAAAAAAAAAAAAARQnAQ"
},
{
"filename": "text-wrap.ts",
"filename": "path-editor.ts",
"title": {
"zh": "文本换行",
"en": "Text Wrap"
"zh": "路径编辑器",
"en": "Path Editor"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*-gIBRKiYraoAAAAAAAAAAAAAARQnAQ"
"screenshot": "https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*drmQQb_VAUsAAAAAAAAAAAAAARQnAQ"
}
]
}

View File

@ -6,7 +6,6 @@ const container = document.getElementById('container')!
const graph = new Graph({
container,
grid: true,
})
const init = (pos: { x: number; y: number }) => {
@ -29,7 +28,7 @@ const init = (pos: { x: number; y: number }) => {
attrs: {
line: {
targetMarker: null,
stroke: '#A2B1C3',
stroke: '#5F95FF',
strokeWidth: 2,
},
},

View File

@ -1,70 +1,257 @@
import { Graph, Edge, EdgeView, Shape } from '@antv/x6'
import { Graph, Edge, Shape, NodeView } from '@antv/x6'
// 定义节点
class MyShape extends Shape.Rect {
getInPorts() {
return this.getPortsByGroup('in')
}
getOutPorts() {
return this.getPortsByGroup('out')
}
getUsedInPorts(graph: Graph) {
const incomingEdges = graph.getIncomingEdges(this) || []
return incomingEdges.map((edge: Edge) => {
const portId = edge.getTargetPortId()
return this.getPort(portId!)
})
}
getNewInPorts(length: number) {
return Array.from(
{
length,
},
() => {
return {
group: 'in',
}
},
)
}
updateInPorts(graph: Graph) {
const minNumberOfPorts = 2
const ports = this.getInPorts()
const usedPorts = this.getUsedInPorts(graph)
const newPorts = this.getNewInPorts(
Math.max(minNumberOfPorts - usedPorts.length, 1),
)
if (
ports.length === minNumberOfPorts &&
ports.length - usedPorts.length > 0
) {
// noop
} else if (ports.length === usedPorts.length) {
this.addPorts(newPorts)
} else if (ports.length + 1 > usedPorts.length) {
this.prop(
['ports', 'items'],
this.getOutPorts().concat(usedPorts).concat(newPorts),
{
rewrite: true,
},
)
}
return this
}
}
MyShape.config({
attrs: {
root: {
magnet: false,
},
body: {
fill: '#EFF4FF',
stroke: '#5F95FF',
strokeWidth: 1,
},
},
ports: {
items: [
{
group: 'out',
},
],
groups: {
in: {
position: {
name: 'top',
},
attrs: {
portBody: {
magnet: 'passive',
r: 6,
stroke: '#5F95FF',
fill: '#fff',
strokeWidth: 1,
},
},
},
out: {
position: {
name: 'bottom',
},
attrs: {
portBody: {
magnet: true,
r: 6,
fill: '#fff',
stroke: '#5F95FF',
strokeWidth: 1,
},
},
},
},
},
portMarkup: [
{
tagName: 'circle',
selector: 'portBody',
},
],
})
// 高亮
const magnetAvailabilityHighlighter = {
name: 'stroke',
args: {
attrs: {
fill: '#fff',
stroke: '#47C769',
},
},
}
// 画布
const graph = new Graph({
container: document.getElementById('container')!,
grid: true,
highlighting: {
magnetAvailable: magnetAvailabilityHighlighter,
magnetAdsorbed: {
name: 'stroke',
args: {
attrs: {
fill: '#fff',
stroke: '#31d0c6',
},
},
},
},
connecting: {
validateMagnet({ cell, magnet }) {
let count = 0
const connectionCount = magnet.getAttribute('connection-count')
const max = connectionCount ? parseInt(connectionCount, 10) : Number.MAX_SAFE_INTEGER
const outgoingEdges = graph.getOutgoingEdges(cell)
if (outgoingEdges) {
outgoingEdges.forEach((edge: Edge) => {
const edgeView = graph.findViewByCell(edge) as EdgeView
if (edgeView.sourceMagnet === magnet) {
count += 1
}
})
}
return count < max
snap: true,
allowBlank: false,
allowLoop: false,
highlight: true,
connector: 'rounded',
connectionPoint: 'boundary',
router: {
name: 'er',
args: {
direction: 'V',
},
},
createEdge() {
return new Shape.Edge({
attrs: {
line: {
stroke: 'orange',
line: {
stroke: '#A2B1C3',
strokeWidth: 1,
targetMarker: {
name: 'classic',
size: 7,
},
},
},
},
})
}
},
validateConnection({ sourceView, targetView, targetMagnet }) {
if (!targetMagnet) {
return false
}
if (targetMagnet.getAttribute('port-group') !== 'in') {
return false
}
if (targetView) {
const node = targetView.cell
if (node instanceof MyShape) {
const portId = targetMagnet.getAttribute('port')
const usedInPorts = node.getUsedInPorts(graph)
if (usedInPorts.find((port) => port && port.id === portId)) {
return false
}
}
}
return true
},
},
})
graph.addNode(
new MyShape().resize(120, 40).position(200, 50).updateInPorts(graph),
)
graph.addNode(
new MyShape().resize(120, 40).position(400, 50).updateInPorts(graph),
)
graph.addNode(
new MyShape().resize(120, 40).position(300, 250).updateInPorts(graph),
)
function update(view: NodeView) {
const cell = view.cell
if (cell instanceof MyShape) {
cell.getInPorts().forEach((port) => {
const portNode = view.findPortElem(port.id!, 'portBody')
view.unhighlight(portNode, {
highlighter: magnetAvailabilityHighlighter,
})
})
cell.updateInPorts(graph)
}
}
graph.on('edge:connected', ({ previousView, currentView }) => {
if (previousView) {
update(previousView as NodeView)
}
if (currentView) {
update(currentView as NodeView)
}
})
graph.addNode({
shape: 'rect',
x: 280,
y: 280,
width: 160,
height: 60,
ports: [
graph.on('edge:removed', ({ edge, options }) => {
if (!options.ui) {
return
}
const target = edge.getTargetCell()
if (target instanceof MyShape) {
target.updateInPorts(graph)
}
})
graph.on('edge:mouseenter', ({ edge }) => {
edge.addTools([
'source-arrowhead',
'target-arrowhead',
{
id: 'a',
attrs: {
circle: {
magnet: true,
connectionCount: 3, // 自定义属性,控制连接桩可连接多少条边
}
name: 'button-remove',
args: {
distance: -30,
},
position: 'top'
},
{
id: 'b',
attrs: {
circle: {
magnet: true,
connectionCount: 0, // 自定义属性,控制连接桩可连接多少条边
}
}
},
],
attrs: {
body: {
fill: '#f5f5f5',
stroke: '#d9d9d9',
strokeWidth: 1,
magnet: true,
connectionCount: 2, // 自定义属性,控制节点可连接多少条边
},
},
})
])
})
graph.on('edge:mouseleave', ({ edge }) => {
edge.removeTools()
})

View File

@ -1,5 +1,5 @@
---
title: FAQ
title: Common Functions
order: 20
redirect_from:
- /zh/examples/practices

View File

@ -1,5 +1,5 @@
---
title: 常见问题
title: 常用功能
order: 20
redirect_from:
- /zh/examples/practices

View File

@ -1,5 +1,4 @@
import { Graph, Cell } from '@antv/x6'
////import ELK, { ElkNode, ElkExtendedEdge, ElkEdge } from 'elkjs/lib/elk-api.js';
import ELK, { ElkNode, ElkEdge, ElkExtendedEdge } from 'elkjs'
Graph.registerNode(
@ -174,7 +173,7 @@ fetch('../data/elkdata.json')
addEdges(res.edges || [])
graph.resetCells(cells)
graph.zoomToFit({
padding: 10,
padding: 20,
maxScale: 1,
})
})

View File

@ -11,8 +11,8 @@
{
"filename": "dag.tsx",
"title": {
"zh": "人工智能建模流程",
"en": "Flow for AI Model"
"zh": "人工智能建模 DAG 图",
"en": "DAG for AI Model"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*RPiGRaSus3UAAAAAAAAAAAAAARQnAQ"
},
@ -56,29 +56,13 @@
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*Z3ebTKy0w9cAAAAAAAAAAAAAARQnAQ"
},
{
"filename": "validate-connection.ts",
"title": {
"zh": "链接桩验证/高亮/自动吸附",
"en": "Validattion/Highlight/Absorb of Port"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*chF2SIucKCUAAAAAAAAAAAAAARQnAQ"
},
{
"filename": "tree.ts",
"title": {
"zh": "展开/折叠树",
"en": "Expand/Collapse the Tree"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*dJiNTJ2h3GAAAAAAAAAAAAAAARQnAQ"
},
{
"filename": "org.ts",
"title": {
"zh": "组织架构图",
"en": "Organizational Charts"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*mxDSTpxbMYYAAAAAAAAAAAAAARQnAQ"
"screenshot": "https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*FWx5SYDzLw4AAAAAAAAAAAAAARQnAQ"
},
{
"filename": "orgchart.ts",
@ -86,15 +70,7 @@
"zh": "组织架构图(自动布局)",
"en": "Organizational Charts"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*w5SUSIvTxPAAAAAAAAAAAAAAARQnAQ"
},
{
"filename": "path-editor.ts",
"title": {
"zh": "路径编辑器",
"en": "Path Editor"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*dz6aS5gtapUAAAAAAAAAAAAAARQnAQ"
"screenshot": "https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*-1_wQ6zLmMYAAAAAAAAAAAAAARQnAQ"
},
{
"filename": "class.ts",
@ -103,6 +79,14 @@
"en": "UML Class"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*OaCpR7t_mVoAAAAAAAAAAAAAARQnAQ"
},
{
"filename": "tree.ts",
"title": {
"zh": "展开/折叠树",
"en": "Expand/Collapse the Tree"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*XDnNRqnj4WkAAAAAAAAAAAAAARQnAQ"
}
]
}

View File

@ -39,6 +39,7 @@ Graph.registerNode(
'xlink:href':
'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*SYCuQ6HHs5cAAAAAAAAAAAAAARQnAQ',
event: 'add:topic',
class: 'topic-image',
},
label: {
fontSize: 14,
@ -379,11 +380,11 @@ graph.bindKey('tab', (e) => {
render()
insertCss(`
.x6-node image {
.topic-image {
visibility: hidden;
cursor: pointer;
}
.x6-node:hover image {
.x6-node:hover .topic-image {
visibility: visible;
}
.x6-node-selected rect {

View File

@ -27,9 +27,9 @@ Graph.registerNode(
body: {
refWidth: '100%',
refHeight: '100%',
fill: '#FFFFFF',
stroke: '#000000',
strokeWidth: 2,
fill: '#5F95FF',
stroke: '#5F95FF',
strokeWidth: 1,
rx: 10,
ry: 10,
pointerEvents: 'visiblePainted',
@ -43,6 +43,7 @@ Graph.registerNode(
rank: {
refX: 0.9,
refY: 0.2,
fill: '#fff',
fontFamily: 'Courier New',
fontSize: 14,
textAnchor: 'end',
@ -51,6 +52,7 @@ Graph.registerNode(
name: {
refX: 0.9,
refY: 0.6,
fill: '#fff',
fontFamily: 'Courier New',
fontSize: 14,
fontWeight: '800',
@ -69,8 +71,8 @@ Graph.registerEdge(
line: {
fill: 'none',
strokeLinejoin: 'round',
strokeWidth: '2',
stroke: '#4b4a67',
strokeWidth: 2,
stroke: '#A2B1C3',
sourceMarker: null,
targetMarker: null,
},
@ -80,8 +82,7 @@ Graph.registerEdge(
)
const graph = new Graph({
container: document.getElementById('container'),
grid: true,
container: document.getElementById('container')!,
connecting: {
anchor: 'orth',
},
@ -93,31 +94,23 @@ function member(
rank: string,
name: string,
image: string,
background: string,
textColor: string = '#000',
) {
return graph.addNode({
x,
y,
shape: 'org-node',
attrs: {
body: {
fill: background,
stroke: 'none',
},
avatar: {
opacity: 0.7,
'xlink:href': image,
},
rank: {
text: rank,
fill: textColor,
wordSpacing: '-5px',
letterSpacing: 0,
},
name: {
text: name,
fill: textColor,
fontSize: 13,
fontFamily: 'Arial',
letterSpacing: 0,
@ -129,8 +122,12 @@ function member(
function link(source: Node, target: Node, vertices: Point.PointLike[]) {
return graph.addEdge({
vertices,
source: { cell: source },
target: { cell: target },
source: {
cell: source,
},
target: {
cell: target,
},
shape: 'org-edge',
})
}
@ -140,47 +137,57 @@ const male =
const female =
'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*f6hhT75YjkIAAAAAAAAAAAAAARQnAQ'
const bart = member(300, 70, 'CEO', 'Bart Simpson', male, '#30d0c6')
const homer = member(
90,
200,
'VP Marketing',
'Homer Simpson',
male,
'#7c68fd',
'#f1f1f1',
)
const marge = member(
300,
200,
'VP Sales',
'Marge Simpson',
female,
'#7c68fd',
'#f1f1f1',
)
const lisa = member(
500,
200,
'VP Production',
'Lisa Simpson',
female,
'#7c68fd',
'#f1f1f1',
)
const maggie = member(400, 350, 'Manager', 'Maggie Simpson', female, '#feb563')
const lenny = member(190, 350, 'Manager', 'Lenny Leonard', male, '#feb563')
const carl = member(190, 500, 'Manager', 'Carl Carlson', male, '#feb563')
const bart = member(300, 70, 'CEO', 'Bart Simpson', male)
const homer = member(90, 200, 'VP Marketing', 'Homer Simpson', male)
const marge = member(300, 200, 'VP Sales', 'Marge Simpson', female)
const lisa = member(500, 200, 'VP Production', 'Lisa Simpson', female)
const maggie = member(400, 350, 'Manager', 'Maggie Simpson', female)
const lenny = member(190, 350, 'Manager', 'Lenny Leonard', male)
const carl = member(190, 500, 'Manager', 'Carl Carlson', male)
link(bart, marge, [{ x: 385, y: 180 }])
link(bart, marge, [
{
x: 385,
y: 180,
},
])
link(bart, homer, [
{ x: 385, y: 180 },
{ x: 175, y: 180 },
{
x: 385,
y: 180,
},
{
x: 175,
y: 180,
},
])
link(bart, lisa, [
{ x: 385, y: 180 },
{ x: 585, y: 180 },
{
x: 385,
y: 180,
},
{
x: 585,
y: 180,
},
])
link(homer, lenny, [{ x: 175, y: 380 }])
link(homer, carl, [{ x: 175, y: 530 }])
link(marge, maggie, [{ x: 385, y: 380 }])
link(homer, lenny, [
{
x: 175,
y: 380,
},
])
link(homer, carl, [
{
x: 175,
y: 530,
},
])
link(marge, maggie, [
{
x: 385,
y: 380,
},
])
graph.zoomToFit({ padding: 20, maxScale: 1 })

View File

@ -90,9 +90,9 @@ Graph.registerNode(
ry: 10,
refWidth: '100%',
refHeight: '100%',
fill: '#FFF',
stroke: '#000',
strokeWidth: 0,
fill: '#5F95FF',
stroke: '#5F95FF',
strokeWidth: 1,
pointerEvents: 'visiblePainted',
},
'.image': {
@ -105,6 +105,7 @@ Graph.registerNode(
'.rank': {
refX: 0.95,
refY: 0.5,
fill: '#fff',
fontFamily: 'Courier New',
fontSize: 13,
textAnchor: 'end',
@ -113,6 +114,7 @@ Graph.registerNode(
'.name': {
refX: 0.95,
refY: 0.7,
fill: '#fff',
fontFamily: 'Arial',
fontSize: 14,
fontWeight: '600',
@ -131,13 +133,13 @@ Graph.registerNode(
'.btn > circle': {
r: 10,
fill: 'transparent',
stroke: '#333',
stroke: '#fff',
strokeWidth: 1,
},
'.btn.add > text': {
fontSize: 20,
fontWeight: 800,
stroke: '#000',
fill: '#fff',
x: -5.5,
y: 7,
fontFamily: 'Times New Roman',
@ -146,7 +148,7 @@ Graph.registerNode(
'.btn.del > text': {
fontSize: 28,
fontWeight: 500,
stroke: '#000',
fill: '#fff',
x: -4.5,
y: 6,
fontFamily: 'Times New Roman',
@ -164,8 +166,8 @@ Graph.registerEdge(
zIndex: -1,
attrs: {
line: {
stroke: '#585858',
strokeWidth: 3,
strokeWidth: 2,
stroke: '#A2B1C3',
sourceMarker: null,
targetMarker: null,
},
@ -183,10 +185,8 @@ const dir = 'LR' // LR RL TB BT
// 创建画布
const graph = new Graph({
container: document.getElementById('container'),
grid: true,
container: document.getElementById('container')!,
scroller: true,
snapline: true,
interacting: false,
})
@ -194,13 +194,10 @@ const graph = new Graph({
function setup() {
graph.on('node:add', ({ e, node }) => {
e.stopPropagation()
const bg = Color.randomHex()
const member = createNode(
'Employee',
'New Employee',
Math.random() < 0.5 ? male : female,
bg,
Color.invert(bg, true),
)
graph.freeze()
graph.addCell([member, createEdge(node, member)])
@ -288,28 +285,17 @@ function layout() {
graph.unfreeze()
}
function createNode(
rank: string,
name: string,
image: string,
background: string,
textColor = '#000',
) {
function createNode(rank: string, name: string, image: string) {
return graph.createNode({
shape: 'org-node',
attrs: {
'.card': { fill: background },
'.image': { xlinkHref: image },
'.rank': {
fill: textColor,
text: Dom.breakText(rank, { width: 160, height: 45 }),
},
'.name': {
fill: textColor,
text: Dom.breakText(name, { width: 160, height: 45 }),
},
'.btn > circle': { stroke: textColor },
'.btn > text': { fill: textColor, stroke: textColor },
},
})
}
@ -323,27 +309,12 @@ function createEdge(source: Cell, target: Cell) {
}
const nodes = [
createNode('Founder & Chairman', 'Pierre Omidyar', male, '#31d0c6'),
createNode('President & CEO', 'Margaret C. Whitman', female, '#31d0c6'),
createNode('President, PayPal', 'Scott Thompson', male, '#7c68fc'),
createNode(
'President, Ebay Global Marketplaces',
'Devin Wenig',
male,
'#7c68fc',
),
createNode(
'Senior Vice President Human Resources',
'Jeffrey S. Skoll',
male,
'#fe854f',
),
createNode(
'Senior Vice President Controller',
'Steven P. Westly',
male,
'#feb663',
),
createNode('Founder & Chairman', 'Pierre Omidyar', male),
createNode('President & CEO', 'Margaret C. Whitman', female),
createNode('President, PayPal', 'Scott Thompson', male),
createNode('President, Ebay Global Marketplaces', 'Devin Wenig', male),
createNode('Senior Vice President Human Resources', 'Jeffrey S. Skoll', male),
createNode('Senior Vice President Controller', 'Steven P. Westly', male),
]
const edges = [

View File

@ -73,8 +73,8 @@ TreeNode.config({
refWidth: '100%',
refHeight: '100%',
strokeWidth: 1,
fill: '#ffffff',
stroke: '#a0a0a0',
fill: '#EFF4FF',
stroke: '#5F95FF',
},
label: {
textWrap: {
@ -92,7 +92,7 @@ TreeNode.config({
refY: '50%',
},
button: {
fill: '#4C65DD',
fill: '#5F95FF',
stroke: 'none',
x: -10,
y: -10,
@ -124,7 +124,7 @@ TreeEdge.config({
zIndex: 1,
attrs: {
line: {
stroke: '#a0a0a0',
stroke: '#A2B1C3',
strokeWidth: 1,
targetMarker: null,
},
@ -138,15 +138,11 @@ Edge.registry.register('tree-edge', TreeEdge, true)
// 初始化画布
const graph = new Graph({
container: document.getElementById('container')!,
grid: 1,
async: true,
frozen: true,
scroller: true,
interacting: false,
sorting: 'approx',
background: {
color: '#f5f5f5',
},
connecting: {
anchor: 'orth',
connector: 'rounded',

View File

@ -1,258 +0,0 @@
import { Graph, Edge, Shape, NodeView } from '@antv/x6'
// 定义节点
class MyShape extends Shape.Rect {
getInPorts() {
return this.getPortsByGroup('in')
}
getOutPorts() {
return this.getPortsByGroup('out')
}
getUsedInPorts(graph: Graph) {
const incomingEdges = graph.getIncomingEdges(this) || []
return incomingEdges.map((edge: Edge) => {
const portId = edge.getTargetPortId()
return this.getPort(portId!)
})
}
getNewInPorts(length: number) {
return Array.from(
{
length,
},
() => {
return {
group: 'in',
}
},
)
}
updateInPorts(graph: Graph) {
const minNumberOfPorts = 2
const ports = this.getInPorts()
const usedPorts = this.getUsedInPorts(graph)
const newPorts = this.getNewInPorts(
Math.max(minNumberOfPorts - usedPorts.length, 1),
)
if (
ports.length === minNumberOfPorts &&
ports.length - usedPorts.length > 0
) {
// noop
} else if (ports.length === usedPorts.length) {
this.addPorts(newPorts)
} else if (ports.length + 1 > usedPorts.length) {
this.prop(
['ports', 'items'],
this.getOutPorts().concat(usedPorts).concat(newPorts),
{
rewrite: true,
},
)
}
return this
}
}
MyShape.config({
attrs: {
root: {
magnet: false,
},
body: {
fill: '#f5f5f5',
stroke: '#d9d9d9',
strokeWidth: 1,
},
},
ports: {
items: [
{
group: 'out',
},
],
groups: {
in: {
position: {
name: 'top',
},
attrs: {
portBody: {
magnet: 'passive',
r: 6,
stroke: '#ffa940',
fill: '#fff',
strokeWidth: 2,
},
},
},
out: {
position: {
name: 'bottom',
},
attrs: {
portBody: {
magnet: true,
r: 6,
fill: '#fff',
stroke: '#3199FF',
strokeWidth: 2,
},
},
},
},
},
portMarkup: [
{
tagName: 'circle',
selector: 'portBody',
},
],
})
// 高亮
const magnetAvailabilityHighlighter = {
name: 'stroke',
args: {
attrs: {
fill: '#fff',
stroke: '#47C769',
},
},
}
// 画布
const graph = new Graph({
grid: true,
container: document.getElementById('container')!,
highlighting: {
magnetAvailable: magnetAvailabilityHighlighter,
magnetAdsorbed: {
name: 'stroke',
args: {
attrs: {
fill: '#fff',
stroke: '#31d0c6',
},
},
},
},
connecting: {
snap: true,
allowBlank: false,
allowLoop: false,
highlight: true,
connector: 'rounded',
connectionPoint: 'boundary',
router: {
name: 'er',
args: {
direction: 'V',
},
},
createEdge() {
return new Shape.Edge({
attrs: {
line: {
stroke: '#a0a0a0',
strokeWidth: 1,
targetMarker: {
name: 'classic',
size: 7,
},
},
},
})
},
validateConnection({ sourceView, targetView, targetMagnet }) {
if (!targetMagnet) {
return false
}
if (targetMagnet.getAttribute('port-group') !== 'in') {
return false
}
if (targetView) {
const node = targetView.cell
if (node instanceof MyShape) {
const portId = targetMagnet.getAttribute('port')
const usedInPorts = node.getUsedInPorts(graph)
if (usedInPorts.find((port) => port && port.id === portId)) {
return false
}
}
}
return true
},
},
})
graph.addNode(
new MyShape().resize(120, 40).position(200, 50).updateInPorts(graph),
)
graph.addNode(
new MyShape().resize(120, 40).position(400, 50).updateInPorts(graph),
)
graph.addNode(
new MyShape().resize(120, 40).position(300, 250).updateInPorts(graph),
)
function update(view: NodeView) {
const cell = view.cell
if (cell instanceof MyShape) {
cell.getInPorts().forEach((port) => {
const portNode = view.findPortElem(port.id!, 'portBody')
view.unhighlight(portNode, {
highlighter: magnetAvailabilityHighlighter,
})
})
cell.updateInPorts(graph)
}
}
graph.on('edge:connected', ({ previousView, currentView }) => {
if (previousView) {
update(previousView as NodeView)
}
if (currentView) {
update(currentView as NodeView)
}
})
graph.on('edge:removed', ({ edge, options }) => {
if (!options.ui) {
return
}
const target = edge.getTargetCell()
if (target instanceof MyShape) {
target.updateInPorts(graph)
}
})
graph.on('edge:mouseenter', ({ edge }) => {
edge.addTools([
'source-arrowhead',
'target-arrowhead',
{
name: 'button-remove',
args: {
distance: -30,
},
},
])
})
graph.on('edge:mouseleave', ({ edge }) => {
edge.removeTools()
})

View File

@ -1,5 +1,5 @@
---
title: 业务实践
title: 业务场景
order: 10
redirect_from:
- /zh/examples/practices