以下是整理后的 AntV X6 + Vue 3 节点开发全流程指南,包含完整代码示例和关键实现细节:
环境搭建与项目初始化
使用 Vite 快速创建 Vue 3 项目:
bash
npm create vite@latest x6-vue-demo
cd x6-vue-demo
npm install
安装核心依赖:
bash
npm install @antv/x6 @antv/layout @antv/x6-vue-shape element-plus
配置 Element Plus(在 main.js/ts 中):
javascript
import { createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')
自定义节点组件开发
创建 NodeView.vue
组件:
vue
<template>
<div class="node" @click="handleClick">
<div class="node-title">{{ nodeData.name }}</div>
<div class="node-status">状态: {{ nodeData.status || "未执行" }}</div>
</div>
</template>
<script setup>
import { ref, onBeforeUnmount } from "vue"
const props = defineProps({ getNode: Function })
const node = props.getNode()
const nodeData = ref(node ? node.getData() : {})
const handleChange = ({ current }) => {
nodeData.value = { ...current }
}
if (node) node.on("change:data", handleChange)
onBeforeUnmount(() => {
if (node) node.off("change:data", handleChange)
})
const handleClick = () => {
alert(`点击了: ${nodeData.value.name}`)
}
</script>
<style scoped>
.node {
width: 120px;
height: 50px;
background: #409eff;
color: #fff;
border-radius: 6px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 12px;
cursor: pointer;
}
.node-title { font-weight: bold; }
.node-status { font-size: 10px; margin-top: 4px; }
</style>
主页面实现
创建 GraphDemo.vue
核心逻辑:
vue
<template>
<div>
<el-button type="primary" @click="setStatus('成功')">父节点成功</el-button>
<el-button type="danger" @click="setStatus('失败')">父节点失败</el-button>
<el-button @click="setStatus('等待')">父节点等待</el-button>
<div class="graph-container" ref="graphRef"></div>
<TeleportContainer />
</div>
</template>
<script setup>
import { ref, onMounted, defineComponent } from "vue"
import { Graph } from "@antv/x6"
import { register, getTeleport } from "@antv/x6-vue-shape"
import { DagreLayout } from "@antv/layout"
import NodeView from "../components/NodeView.vue"
const graphRef = ref(null)
let graph = null
const TeleportContainer = defineComponent(getTeleport())
const initGraph = () => {
register({
shape: "vue-node",
component: NodeView,
getComponentProps(node) {
return { getNode: () => node }
}
})
graph = new Graph({
container: graphRef.value,
background: { color: "#f9f9f9" },
grid: true
})
const taskList = [
{ id: "1", name: "任务A", status: "等待" },
{ id: "2", name: "任务B", status: "等待" },
{ id: "3", name: "任务C", status: "等待" }
]
const relations = [
{ source: "1", target: "2" },
{ source: "1", target: "3" }
]
const nodes = taskList.map((task) => ({
id: task.id,
shape: "vue-node",
width: 120,
height: 50,
data: task
}))
const edges = relations.map((rel) => ({
source: rel.source,
target: rel.target,
attrs: {
line: {
stroke: "#999",
strokeWidth: 2,
targetMarker: { name: "classic", width: 12, height: 8 }
}
}
}))
const layout = new DagreLayout({
type: "dagre",
rankdir: "LR",
nodesep: 40,
ranksep: 60
})
graph.fromJSON(layout.layout({ nodes, edges }))
updateEdgesColor()
}
const updateEdgesColor = () => {
graph.getEdges().forEach((edge) => {
const sourceNode = graph.getCellById(edge.getSourceCellId())
const status = sourceNode?.getData()?.status || "等待"
edge.setAttrs({
line: {
stroke:
status === "成功" ? "green" :
status === "失败" ? "red" : "gray"
}
})
})
}
const setStatus = (status) => {
const parentNode = graph.getCellById("1")
parentNode.setData({ ...parentNode.getData(), status })
updateEdgesColor()
}
onMounted(() => initGraph())
</script>
<style scoped>
.graph-container {
width: 100%;
height: 500px;
border: 1px solid #ddd;
margin-top: 10px;
}
</style>
关键功能说明
自动布局实现
通过 DagreLayout
自动计算节点位置,参数说明:
rankdir: "LR"
控制布局方向(Left to Right)nodesep
和ranksep
控制节点间距
动态边颜色控制
updateEdgesColor
方法根据源节点状态改变连线颜色:
- 成功状态:绿色边
- 失败状态:红色边
- 默认状态:灰色边
节点数据响应式
通过监听 change:data
事件实现节点状态实时更新,注意在组件卸载时移除事件监听
效果说明
运行后可见:
- 三个采用自动布局排列的节点
- 父节点(任务A)控制两个子节点的边颜色
- 点击按钮可切换父节点状态(成功/失败/等待)
- 点击节点会显示节点名称提示框