UML Class
Loading example…
import exampleData from '../../../data/class.json'
import { Graph, ObjectExt, Cell } from '@antv/x6'
// #region 注册基础图形
Graph.registerNode(
'class',
{
inherit: 'rect',
markup: [
{
tagName: 'rect',
selector: 'body',
},
{
tagName: 'rect',
selector: 'name-rect',
},
{
tagName: 'rect',
selector: 'attrs-rect',
},
{
tagName: 'rect',
selector: 'methods-rect',
},
{
tagName: 'text',
selector: 'name-text',
},
{
tagName: 'text',
selector: 'attrs-text',
},
{
tagName: 'text',
selector: 'methods-text',
},
],
attrs: {
rect: {
width: 160,
},
body: {
stroke: '#fff',
},
'name-rect': {
fill: '#5f95ff',
stroke: '#fff',
strokeWidth: 0.5,
},
'attrs-rect': {
fill: '#eff4ff',
stroke: '#fff',
strokeWidth: 0.5,
},
'methods-rect': {
fill: '#eff4ff',
stroke: '#fff',
strokeWidth: 0.5,
},
'name-text': {
ref: 'name-rect',
refY: 0.5,
refX: 0.5,
textAnchor: 'middle',
fontWeight: 'bold',
fill: '#fff',
fontSize: 12,
},
'attrs-text': {
ref: 'attrs-rect',
refY: 0.5,
refX: 5,
textAnchor: 'left',
fill: 'black',
fontSize: 10,
},
'methods-text': {
ref: 'methods-rect',
refY: 0.5,
refX: 5,
textAnchor: 'left',
fill: 'black',
fontSize: 10,
},
},
propHooks(meta) {
const { name, attributes, methods, ...others } = meta
if (!(name && attributes && methods)) {
return meta
}
const rects = [
{ type: 'name', text: name },
{ type: 'attrs', text: attributes },
{ type: 'methods', text: methods },
]
let offsetY = 0
rects.forEach((rect) => {
const height = rect.text.length * 12 + 16
ObjectExt.setByPath(
others,
`attrs/${rect.type}-text/text`,
rect.text.join('\n'),
)
ObjectExt.setByPath(others, `attrs/${rect.type}-rect/height`, height)
ObjectExt.setByPath(
others,
`attrs/${rect.type}-rect/transform`,
'translate(0,' + offsetY + ')',
)
offsetY += height
})
others.size = { width: 160, height: offsetY }
return others
},
},
true,
)
// 继承
Graph.registerEdge(
'extends',
{
inherit: 'edge',
attrs: {
line: {
strokeWidth: 1,
targetMarker: {
name: 'path',
d: 'M 20 0 L 0 10 L 20 20 z',
fill: 'white',
offsetX: -10,
},
},
},
},
true,
)
// 实现
Graph.registerEdge(
'implement',
{
inherit: 'edge',
attrs: {
line: {
strokeWidth: 1,
strokeDasharray: '3,3',
targetMarker: {
name: 'path',
d: 'M 20 0 L 0 10 L 20 20 z',
fill: 'white',
offsetX: -10,
},
},
},
},
true,
)
// 组合
Graph.registerEdge(
'composition',
{
inherit: 'edge',
attrs: {
line: {
strokeWidth: 1,
sourceMarker: {
name: 'path',
d: 'M 30 10 L 20 16 L 10 10 L 20 4 z',
fill: 'black',
offsetX: -10,
},
targetMarker: {
name: 'path',
d: 'M 6 10 L 18 4 C 14.3333 6 10.6667 8 7 10 L 18 16 z',
fill: 'black',
offsetX: -5,
},
},
},
},
true,
)
// 聚合
Graph.registerEdge(
'aggregation',
{
inherit: 'edge',
attrs: {
line: {
strokeWidth: 1,
sourceMarker: {
name: 'path',
d: 'M 30 10 L 20 16 L 10 10 L 20 4 z',
fill: 'white',
offsetX: -10,
},
targetMarker: {
name: 'path',
d: 'M 6 10 L 18 4 C 14.3333 6 10.6667 8 7 10 L 18 16 z',
fill: 'black',
offsetX: -5,
},
},
},
},
true,
)
// 关联
Graph.registerEdge(
'association',
{
inherit: 'edge',
attrs: {
line: {
strokeWidth: 1,
targetMarker: {
name: 'path',
d: 'M 6 10 L 18 4 C 14.3333 6 10.6667 8 7 10 L 18 16 z',
fill: 'black',
offsetX: -5,
},
},
},
},
true,
)
// #endregion
const graph = new Graph({
container: document.getElementById('container')!,
})
Promise.resolve(exampleData)
.then((data) => {
const cells: Cell[] = []
const edgeShapes = [
'extends',
'composition',
'implement',
'aggregation',
'association',
]
data.forEach((item: any) => {
if (edgeShapes.includes(item.shape)) {
cells.push(graph.createEdge(item))
} else {
cells.push(graph.createNode(item))
}
})
graph.resetCells(cells)
graph.zoomToFit({ padding: 10, maxScale: 1 })
})
[
{
"id": "1",
"shape": "class",
"name": ["<<Abstract>>", "动物"],
"attributes": ["+有生命"],
"methods": ["+新陈代谢()", "+繁殖()"],
"position": {
"x": 300,
"y": 40
}
},
{
"id": "2",
"shape": "class",
"name": ["鸟"],
"attributes": ["+羽毛"],
"methods": ["+下蛋"],
"position": {
"x": 300,
"y": 200
}
},
{
"id": "3",
"shape": "extends",
"source": "2",
"target": "1"
},
{
"id": "4",
"shape": "class",
"name": ["翅膀"],
"attributes": [],
"methods": [],
"position": {
"x": 560,
"y": 212
}
},
{
"id": "5",
"shape": "composition",
"source": "2",
"target": "4",
"label": "1:2"
},
{
"id": "6",
"shape": "class",
"name": ["大雁"],
"attributes": [],
"methods": ["+飞()"],
"position": {
"x": 210,
"y": 340
}
},
{
"id": "7",
"shape": "class",
"name": ["企鹅"],
"attributes": [],
"methods": ["+下蛋()"],
"position": {
"x": 400,
"y": 340
}
},
{
"id": "8",
"shape": "extends",
"source": "2",
"target": "6"
},
{
"id": "9",
"shape": "extends",
"source": "2",
"target": "7"
},
{
"id": "10",
"shape": "class",
"name": ["<<interface>>", "飞翔"],
"attributes": [],
"methods": ["+飞()"],
"position": {
"x": 320,
"y": 500
}
},
{
"id": "11",
"shape": "implement",
"source": "6",
"target": "10"
},
{
"id": "12",
"shape": "class",
"name": ["雁群"],
"attributes": [],
"methods": ["+V字飞行()", "+一字飞行()"],
"position": {
"x": 90,
"y": 500
}
},
{
"id": "13",
"shape": "aggregation",
"source": "12",
"target": "6"
},
{
"id": "14",
"shape": "class",
"name": ["气候"],
"attributes": [],
"methods": [],
"position": {
"x": 540,
"y": 510
}
},
{
"id": "15",
"shape": "association",
"source": "7",
"target": "14"
}
]