diff --git a/modeler/src/utils/event.ts b/modeler/src/utils/event.ts index abb89e3..369f3a6 100644 --- a/modeler/src/utils/event.ts +++ b/modeler/src/utils/event.ts @@ -1,7 +1,7 @@ /* * This file is part of the kernelstudio package. * - * (c) 2014-2025 zlin + * (c) 2014-2026 zlin * * For the full copyright and license information, please view the LICENSE file * that was distributed with this source code. @@ -23,14 +23,14 @@ export class EventError { export class EventListener { - private readonly _listeners: Record; + private _listeners: Record; constructor(listeners: Record = {}) { this._listeners = listeners; } all(): Record { - return {...this._listeners}; // 返回副本,避免外部直接修改 + return { ...this._listeners }; // 返回副本,避免外部直接修改 } has(name: string): boolean { @@ -55,16 +55,24 @@ export class EventListener { [...listeners].forEach(f => f(options)); } } + + clear(){ + for (const key in this._listeners) { + if (this._listeners.hasOwnProperty(key)) { + delete this._listeners[key]; + } + } + } } export const safePreventDefault = (event: any) => { if (event && typeof event.preventDefault === 'function') { event.preventDefault(); } -} +}; export const safeStopPropagation = (event: any) => { if (event && typeof event.stopPropagation === 'function') { event.stopPropagation(); } -} \ No newline at end of file +}; diff --git a/modeler/src/views/decision/designer/designer.vue b/modeler/src/views/decision/designer/designer.vue index 714859a..2433cff 100644 --- a/modeler/src/views/decision/designer/designer.vue +++ b/modeler/src/views/decision/designer/designer.vue @@ -114,8 +114,16 @@ export default defineComponent({ fitToScreen, centerContent, resizeCanvas, + destroyGraph, + clearGraph, } = useGraphCanvas(); + const destroy = ()=> { + window.removeEventListener('resize', handleResize); + destroyGraph(); + graph.value = null; + } + const loadPlatforms = () => { platforms.value = []; findAllBasicPlatforms().then(r => { @@ -213,6 +221,8 @@ export default defineComponent({ }; const handleSelectTree = (tree: BehaviorTree) => { + destroyGraph(); + console.info('handleSelectTree', tree); findOneTreeById(tree.id).then(r => { if (r.data) { @@ -233,7 +243,9 @@ export default defineComponent({ graph: nodeGraph, }; currentTreeEditing.value = true; - createElements(); + nextTick(() => { + initGraph(); + }); } else { message.error(r.msg ?? '行为树不存在.'); } @@ -241,12 +253,8 @@ export default defineComponent({ }; const createElements = () => { + clearGraph(); nextTick(() => { - try { - graph.value?.clearCells(); - } catch (e: any) { - console.error('clear cells error, cause:', e); - } setTimeout(() => { if (currentBehaviorTree.value?.graph && graph.value) { if (currentBehaviorTree.value?.graph.nodes) { @@ -274,6 +282,8 @@ export default defineComponent({ }; const handleCreateTree = () => { + destroyGraph(); + currentBehaviorTree.value = { id: 0, name: '行为树', @@ -294,7 +304,9 @@ export default defineComponent({ selectedModelNode.value = null; selectedNodeTaskElement.value = null; - createElements(); + nextTick(() => { + initGraph(); + }); }; // 初始化X6画布 @@ -417,18 +429,7 @@ export default defineComponent({ }); // 清理 - onBeforeUnmount(() => { - window.removeEventListener('resize', handleResize); - if (graph.value) { - try { - graph.value.clearCells(); - } catch (error) { - console.warn('销毁画布时出错:', error); - } - graph.value = null; - console.log('画布已销毁'); - } - }); + onBeforeUnmount(() => destroy()); return { platforms, diff --git a/modeler/src/views/decision/graph/hooks.ts b/modeler/src/views/decision/graph/hooks.ts index c404426..41377fd 100644 --- a/modeler/src/views/decision/graph/hooks.ts +++ b/modeler/src/views/decision/graph/hooks.ts @@ -7,7 +7,7 @@ * that was distributed with this source code. */ -import { computed, type ComputedRef, ref, type Ref } from 'vue'; +import { computed, type ComputedRef, ref, type Ref, nextTick } from 'vue'; import { type Dom, Graph, Node } from '@antv/x6'; import type { NodeViewPositionEventArgs } from '@antv/x6/es/view/node/type'; import { createGraphCanvas } from './canvas'; @@ -24,6 +24,8 @@ export interface UseGraphCanvas { graph: ComputedRef; zoomIn: () => void; zoomOut: () => void; + destroyGraph: () => void; + clearGraph: () => void; fitToScreen: () => void; centerContent: () => void; resizeCanvas: () => void; @@ -39,6 +41,36 @@ export const useGraphCanvas = (readonly: boolean = false): UseGraphCanvas => { const eventListener = new EventListener(); const currentZoom = ref(0); + + const clearGraph = ()=> { + if (graph.value) { + try { + graph.value.off(); + graph.value.clearCells(); + } catch (e) { + console.error('清空画布失败:', e); + } + } + } + + const destroyGraph = ()=> { + eventListener.clear(); + if (graph.value) { + clearGraph(); + + // 等待 Vue 完成卸载 + nextTick(() => { + graph.value?.dispose(); // 销毁 Graph 实例 + graph.value = null; + if (container.value) { + container.value.innerHTML = ''; // 清空容器内容 + } + }); + } else if (container.value) { + container.value.innerHTML = ''; + } + } + const handleGraphEvent = (name: string, fn: Function) => { eventListener.on(name, fn); }; @@ -243,6 +275,8 @@ export const useGraphCanvas = (readonly: boolean = false): UseGraphCanvas => { return { container, readonly, + destroyGraph, + clearGraph, graph: graphInstance, eventListener, currentZoom,