2024-09-18 21:03:12 +08:00
|
|
|
<template>
|
2025-02-23 16:39:52 +08:00
|
|
|
<div class="graph-container" ref="container"></div>
|
|
|
|
|
</template>
|
2024-09-18 21:03:12 +08:00
|
|
|
|
2025-02-23 16:39:52 +08:00
|
|
|
<script setup>
|
|
|
|
|
import { Graph } from "@antv/g6";
|
|
|
|
|
import { onMounted, watch, ref } from 'vue';
|
2024-09-18 21:03:12 +08:00
|
|
|
|
2025-02-23 16:39:52 +08:00
|
|
|
const props = defineProps({
|
|
|
|
|
graphData: {
|
|
|
|
|
type: Object,
|
|
|
|
|
required: true,
|
|
|
|
|
default: () => ({ nodes: [], edges: [] })
|
|
|
|
|
}
|
|
|
|
|
});
|
2024-09-18 21:03:12 +08:00
|
|
|
|
2025-02-23 16:39:52 +08:00
|
|
|
const container = ref(null);
|
|
|
|
|
let graphInstance = null;
|
2024-09-18 21:03:12 +08:00
|
|
|
|
2025-02-23 16:39:52 +08:00
|
|
|
const initGraph = () => {
|
|
|
|
|
graphInstance = new Graph({
|
|
|
|
|
container: container.value,
|
|
|
|
|
width: container.value.offsetWidth,
|
|
|
|
|
height: container.value.offsetHeight,
|
|
|
|
|
autoFit: true,
|
|
|
|
|
autoResize: true,
|
|
|
|
|
layout: {
|
|
|
|
|
type: 'd3-force',
|
|
|
|
|
preventOverlap: true,
|
|
|
|
|
kr: 20,
|
|
|
|
|
collide: {
|
|
|
|
|
strength: 1.0,
|
2024-09-18 21:03:12 +08:00
|
|
|
},
|
2025-02-23 16:39:52 +08:00
|
|
|
},
|
|
|
|
|
node: {
|
|
|
|
|
type: 'circle',
|
|
|
|
|
style: {
|
|
|
|
|
labelText: (d) => d.data.label,
|
|
|
|
|
size: 70,
|
2024-09-18 21:03:12 +08:00
|
|
|
},
|
2025-02-23 16:39:52 +08:00
|
|
|
palette: {
|
|
|
|
|
field: 'label',
|
|
|
|
|
color: 'tableau',
|
2024-09-18 21:03:12 +08:00
|
|
|
},
|
2025-02-23 16:39:52 +08:00
|
|
|
},
|
|
|
|
|
edge: {
|
|
|
|
|
type: 'line',
|
|
|
|
|
style: {
|
|
|
|
|
labelText: (d) => d.data.label,
|
|
|
|
|
labelBackground: '#fff',
|
|
|
|
|
endArrow: true,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
behaviors: ['drag-element', 'zoom-canvas', 'drag-canvas'],
|
|
|
|
|
});
|
|
|
|
|
};
|
2024-09-18 21:03:12 +08:00
|
|
|
|
2025-02-23 16:39:52 +08:00
|
|
|
const renderGraph = () => {
|
|
|
|
|
if (!graphInstance) {
|
|
|
|
|
initGraph();
|
|
|
|
|
}
|
2024-09-18 21:03:12 +08:00
|
|
|
|
2025-02-23 16:39:52 +08:00
|
|
|
const formattedData = {
|
|
|
|
|
nodes: props.graphData.nodes.map(node => ({
|
|
|
|
|
id: node.id,
|
|
|
|
|
data: { label: node.name }
|
|
|
|
|
})),
|
|
|
|
|
edges: props.graphData.edges.map(edge => ({
|
|
|
|
|
source: edge.source_id,
|
|
|
|
|
target: edge.target_id,
|
|
|
|
|
data: { label: edge.type }
|
|
|
|
|
}))
|
2024-09-18 21:03:12 +08:00
|
|
|
};
|
|
|
|
|
|
2025-02-23 16:39:52 +08:00
|
|
|
graphInstance.setData(formattedData);
|
|
|
|
|
graphInstance.render();
|
|
|
|
|
};
|
2024-09-18 21:03:12 +08:00
|
|
|
|
2025-02-23 16:39:52 +08:00
|
|
|
onMounted(() => {
|
|
|
|
|
renderGraph();
|
|
|
|
|
window.addEventListener('resize', renderGraph);
|
|
|
|
|
});
|
2024-09-18 21:03:12 +08:00
|
|
|
|
2025-02-23 16:39:52 +08:00
|
|
|
watch(() => props.graphData, renderGraph, { deep: true });
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.graph-container {
|
|
|
|
|
background: #F7F7F7;
|
|
|
|
|
border-radius: 16px;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 600px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
</style>
|