ts
复制代码
<script setup lang="ts">
import type { Edge, Graph as X6Graph, Node } from '@antv/x6'
import { Graph } from '@antv/x6'
import { useResizeObserver, debounce } from 'vue-amazing-ui'
interface Props {
width?: string | number // 容器宽度
height?: string | number // 容器高度
}
const props = withDefaults(defineProps<Props>(), {
width: '100%',
height: '100%'
})
const chartWidth = computed(() => {
if (typeof props.width === 'number') {
return `${props.width}px`
}
return props.width
})
const chartHeight = computed(() => {
if (typeof props.height === 'number') {
return `${props.height}px`
}
return props.height
})
const chartRef = useTemplateRef('chartRef')
let graph: X6Graph | null = null
type DagNodeData = {
label: string
type: 'source' | 'transform' | 'sink' | 'hello' | string
}
function getDefaultNodePorts() {
return {
groups: {
in: {
position: 'left',
zIndex: 1,
attrs: {
circle: { r: 4, magnet: false, stroke: '#5F95FF', strokeWidth: 1, fill: '#fff' }
}
},
out: {
position: 'right',
zIndex: 1,
attrs: {
circle: { r: 4, magnet: false, stroke: '#52c41a', strokeWidth: 1, fill: '#fff' }
}
}
},
items: [
{ id: 'in-1', group: 'in' },
{ id: 'out-1', group: 'out' }
]
}
}
function getGraphData() {
const nodes: Node.Metadata[] = [
{
id: 'source-1',
shape: 'rect', // 节点图形 'rect' | 'circle' | 'ellipse' | 'polygon' | 'polyline' | 'path' | 'image' | 'html' (HTML 节点,使用 foreignObject 渲染 HTML 片段)
x: 80, // 节点位置 x 坐标,单位为 px
y: 140, // 节点位置 y 坐标,单位为 px
width: 140, // 节点宽度,单位为 px
height: 42, // 节点高度,单位为 px
angle: 0, // 节点旋转角度,单位为度
data: { label: 'Source: Kafka', type: 'source' } as DagNodeData,
attrs: {
body: { fill: '#e6f4ff', stroke: '#91caff', rx: 6, ry: 6 },
label: { text: 'Source: Kafka', fill: '#1f1f1f' }
},
ports: getDefaultNodePorts()
},
{
id: 'transform-1',
shape: 'rect',
x: 340,
y: 120,
width: 160,
height: 42,
data: { label: 'Transform: Clean', type: 'transform' } as DagNodeData,
attrs: {
body: { fill: '#f6ffed', stroke: '#b7eb8f', rx: 6, ry: 6 },
label: { text: 'Transform: Clean', fill: '#1f1f1f' }
},
ports: getDefaultNodePorts()
},
{
id: 'transform-2',
shape: 'rect',
x: 340,
y: 200,
width: 160,
height: 42,
data: { label: 'Transform: Enrich', type: 'transform' } as DagNodeData,
attrs: {
body: { fill: '#f6ffed', stroke: '#b7eb8f', rx: 6, ry: 6 },
label: { text: 'Transform: Enrich', fill: '#1f1f1f' }
},
ports: getDefaultNodePorts()
},
{
id: 'sink-1',
shape: 'rect',
x: 620,
y: 160,
width: 140,
height: 42,
data: { label: 'Sink: ClickHouse', type: 'sink' } as DagNodeData,
attrs: {
body: { fill: '#fff7e6', stroke: '#ffadd2', rx: 6, ry: 6 },
label: { text: 'Sink: ClickHouse', fill: '#1f1f1f' }
},
ports: getDefaultNodePorts()
},
{
id: 'hello-1',
shape: 'rect',
x: 880,
y: 60,
width: 140,
height: 42,
data: { label: 'Hello: World', type: 'hello' } as DagNodeData,
attrs: {
body: { fill: '#fff0f6', stroke: '#ffadd2', rx: 6, ry: 6 },
label: { text: 'Hello: World', fill: '#1f1f1f' }
},
ports: getDefaultNodePorts()
},
{
id: 'hello-2',
shape: 'rect',
x: 880,
y: 160,
width: 140,
height: 42,
data: { label: 'Hello: Forest', type: 'hello' } as DagNodeData,
attrs: {
body: { fill: '#fff0f6', stroke: '#ffadd2', rx: 6, ry: 6 },
label: { text: 'Hello: Forest', fill: '#1f1f1f' }
},
ports: getDefaultNodePorts()
},
{
id: 'hello-3',
shape: 'rect',
x: 880,
y: 260,
width: 140,
height: 42,
data: { label: 'Hello: Sea', type: 'hello' } as DagNodeData,
attrs: {
body: { fill: '#fff0f6', stroke: '#ffadd2', rx: 6, ry: 6 },
label: { text: 'Hello: Sea', fill: '#1f1f1f' }
},
ports: getDefaultNodePorts()
}
]
const edges: Edge.Metadata[] = [
{
id: 'edge-1',
source: { cell: 'source-1', port: 'out-1' },
target: { cell: 'transform-1', port: 'in-1' },
connector: { name: 'smooth' }
},
{
id: 'edge-2',
source: { cell: 'source-1', port: 'out-1' },
target: { cell: 'transform-2', port: 'in-1' },
connector: { name: 'smooth' }
},
{
id: 'edge-3',
source: { cell: 'transform-1', port: 'out-1' },
target: { cell: 'sink-1', port: 'in-1' },
connector: { name: 'smooth' }
},
{
id: 'edge-4',
source: { cell: 'transform-2', port: 'out-1' },
target: { cell: 'sink-1', port: 'in-1' },
connector: { name: 'smooth' }
},
{
id: 'edge-5',
source: { cell: 'sink-1', port: 'out-1' },
target: { cell: 'hello-1', port: 'in-1' },
connector: { name: 'smooth' }
},
{
id: 'edge-6',
source: { cell: 'sink-1', port: 'out-1' },
target: { cell: 'hello-2', port: 'in-1' },
connector: { name: 'smooth' }
},
{
id: 'edge-7',
source: { cell: 'sink-1', port: 'out-1' },
target: { cell: 'hello-3', port: 'in-1' },
connector: { name: 'smooth' }
}
]
return { nodes, edges }
}
function initGraph() {
if (!chartRef.value) return
graph = new Graph({
container: chartRef.value,
grid: {
visible: true, // 绘制网格,默认绘制 dot 类型网格
size: 10, // 网格大小,单位 px
type: 'dot', // 网格类型,可选 'dot' | 'fixedDot' | 'mesh' | 'doubleMesh' | ''
args: {
color: '#ddd', // 网点颜色
thickness: 1 // 网点大小
}
},
background: { color: '#fafafa' }, // 背景
panning: true, // 是否可以拖拽平移
mousewheel: true, // 鼠标滚轮缩放
interacting: { nodeMovable: true, edgeMovable: false, vertexMovable: false, edgeLabelMovable: false },
autoResize: true // 监听容器大小改变,并自动更新画布大小
})
// 点击事件
graph.on('node:click', ({ node }) => {
const data = (node.getData ? node.getData() : node.getData) as DagNodeData | undefined
console.log(`节点被点击:${data?.label}`)
})
const data = getGraphData()
graph.fromJSON(data) // 渲染元素
fitView()
centerView()
useResizeObserver(
chartRef,
debounce(() => {
console.log('centerView')
fitView()
centerView()
}, 100) as ResizeObserverCallback
)
}
// 将画布缩放级别增加 0.1
function zoomIn() {
graph?.zoom(0.1)
}
// 将画布缩放级别减少 0.1
function zoomOut() {
graph?.zoom(-0.1)
}
// 将画布中元素缩小或者放大一定级别,让画布正好容纳所有元素,可以通过 maxScale 配置最大缩放级别
function fitView() {
graph?.zoomToFit({ padding: 20, maxScale: 1 })
}
// 将画布中元素居中展示
function centerView() {
graph?.centerContent()
}
onMounted(() => {
initGraph()
})
onBeforeUnmount(() => {
graph?.dispose()
graph = null
})
</script>
<template>
<div class="dag-container">
<div
ref="chartRef"
class="graph-container"
:style="`--chart-width: ${chartWidth}; --chart-height: ${chartHeight};`"
></div>
</div>
</template>
<style lang="less" scoped>
.dag-container {
.graph-container {
width: var(--chart-width) !important;
height: var(--chart-height) !important;
padding: 16px 24px;
border-radius: 4px;
}
}
</style>