|
|
@@ -3,8 +3,9 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
- import { ref, onMounted, onBeforeUnmount, watch } from 'vue';
|
|
|
- import { Graph, treeToGraphData, register, ExtensionCategory, NodeEvent, Polyline, CanvasEvent } from '@antv/g6';
|
|
|
+ import { ref, onMounted, onBeforeUnmount, watch, nextTick } from 'vue';
|
|
|
+ import { Graph, treeToGraphData, NodeEvent, CanvasEvent } from '@antv/g6';
|
|
|
+ // import { Graph, treeToGraphData, register, ExtensionCategory, NodeEvent, Polyline, CanvasEvent } from '@antv/g6';
|
|
|
|
|
|
/**
|
|
|
* @description: 为了确保图的正确渲染和交互,建议按照 G6 标准数据结构组织数据。
|
|
|
@@ -30,29 +31,23 @@
|
|
|
let data = treeToGraphData(props.treeData); // 通过 treeToGraphData 方法,将树形结构数据转换为 G6 的标准数据结构
|
|
|
const container = ref<HTMLDivElement | null>(null); // 图表容器引用
|
|
|
let graph: any = null;
|
|
|
-
|
|
|
- // 自定义edge样式
|
|
|
- class AntLine extends Polyline {
|
|
|
- onCreate() {
|
|
|
- const shape = this.shapeMap.key;
|
|
|
- shape.animate([{ lineDashOffset: 20 }, { lineDashOffset: 0 }], {
|
|
|
- duration: 500,
|
|
|
- iterations: Infinity,
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
- register(ExtensionCategory.EDGE, 'ant-line', AntLine);
|
|
|
+ let pending = false; // 防止并发 initGraph
|
|
|
|
|
|
// 初始化图表
|
|
|
- const initGraph = () => {
|
|
|
- if (!container.value) return;
|
|
|
+ const initGraph = async () => {
|
|
|
+ if (pending || !container.value) return;
|
|
|
+ pending = true;
|
|
|
|
|
|
// 销毁旧实例
|
|
|
if (graph) {
|
|
|
+ graph.off();
|
|
|
graph.destroy();
|
|
|
graph = null;
|
|
|
}
|
|
|
|
|
|
+ // 等待下一帧,确保销毁完成
|
|
|
+ await nextTick();
|
|
|
+
|
|
|
// 创建新的 G6 实例
|
|
|
graph = new Graph({
|
|
|
container: container.value,
|
|
|
@@ -107,17 +102,17 @@
|
|
|
animation: true, // 启用布局动画
|
|
|
},
|
|
|
autoFit: {
|
|
|
- type: 'view', // 自适应类型:'view' 或 'center'
|
|
|
- options: {
|
|
|
- // 仅适用于 'view' 类型
|
|
|
- when: 'always', // 何时适配:'overflow'(仅当内容溢出时) 或 'always'(总是适配)
|
|
|
- direction: 'both', // 适配方向:'x'、'y' 或 'both'
|
|
|
- },
|
|
|
- animation: {
|
|
|
- // 自适应动画效果
|
|
|
- duration: 1000, // 动画持续时间(毫秒)
|
|
|
- easing: 'ease-in-out', // 动画缓动函数
|
|
|
- },
|
|
|
+ type: 'center', // 自适应类型:'view' 或 'center'
|
|
|
+ // options: {
|
|
|
+ // // 仅适用于 'view' 类型
|
|
|
+ // when: 'always', // 何时适配:'overflow'(仅当内容溢出时) 或 'always'(总是适配)
|
|
|
+ // direction: 'both', // 适配方向:'x'、'y' 或 'both'
|
|
|
+ // },
|
|
|
+ // animation: {
|
|
|
+ // // 自适应动画效果
|
|
|
+ // duration: 1000, // 动画持续时间(毫秒)
|
|
|
+ // easing: 'ease-in-out', // 动画缓动函数
|
|
|
+ // },
|
|
|
},
|
|
|
autoResize: true, // 自动调整大小
|
|
|
behaviors: [
|
|
|
@@ -149,16 +144,18 @@
|
|
|
});
|
|
|
|
|
|
graph.on(CanvasEvent.CLICK, () => {
|
|
|
- graph.fitCenter();
|
|
|
+ graph?.fitCenter();
|
|
|
emits('canvas-click');
|
|
|
});
|
|
|
|
|
|
window.addEventListener('resize', handleResize);
|
|
|
+
|
|
|
+ pending = false;
|
|
|
};
|
|
|
|
|
|
// 处理窗口大小变化
|
|
|
const handleResize = () => {
|
|
|
- if (container.value) {
|
|
|
+ if (graph && container.value) {
|
|
|
graph.resize(container.value.offsetWidth, container.value.offsetHeight);
|
|
|
graph.fitCenter();
|
|
|
}
|
|
|
@@ -171,7 +168,7 @@
|
|
|
data = treeToGraphData(props.treeData);
|
|
|
initGraph();
|
|
|
},
|
|
|
- { deep: true, immediate: true },
|
|
|
+ { deep: true },
|
|
|
);
|
|
|
|
|
|
// 生命周期钩子
|
|
|
@@ -185,6 +182,7 @@
|
|
|
if (graph) {
|
|
|
graph.off(); // 移除所有事件监听
|
|
|
graph.destroy();
|
|
|
+ graph = null;
|
|
|
}
|
|
|
});
|
|
|
</script>
|