支持在通信节点右键菜单中挂载行为树

This commit is contained in:
2026-04-14 15:09:50 +08:00
parent 835bb56851
commit 2b2a11831d
3 changed files with 173 additions and 4 deletions

View File

@@ -1,5 +1,9 @@
<template>
<a-dropdown :trigger="['contextmenu']" @openChange="handleVisibleChange">
<a-dropdown
:trigger="['contextmenu']"
:getPopupContainer="getPopupContainer"
@openChange="handleVisibleChange"
>
<a-card
:class="[
'ks-scenario-node',
@@ -55,6 +59,27 @@
<template #overlay>
<a-menu @click="handleMenuClick">
<a-sub-menu key="mount">
<template #icon>
<LinkOutlined />
</template>
<template #title>挂载</template>
<a-menu-item
v-for="tree in availableTrees"
:key="`tree-${tree.id}`"
:disabled="isTreeMounted(tree.id)"
@click="() => handleMountTree(tree)"
>
<template #icon>
<CheckOutlined v-if="isTreeMounted(tree.id)" />
</template>
{{ tree.name }}
</a-menu-item>
<a-menu-item v-if="availableTrees.length === 0" disabled>
暂无可用行为树
</a-menu-item>
</a-sub-menu>
<a-menu-divider />
<a-menu-item key="delete">
<template #icon>
<DeleteOutlined />
@@ -70,15 +95,20 @@
import { defineComponent, onMounted, onUnmounted, ref } from 'vue';
import { elementProps, type ModelElement } from '../graph';
import { DeleteOutlined, SettingOutlined } from '@ant-design/icons-vue';
import { DeleteOutlined, LinkOutlined, CheckOutlined, SettingOutlined } from '@ant-design/icons-vue';
import type { Graph } from '@antv/x6';
import { message } from 'ant-design-vue';
import { substring } from '@/utils/strings';
import { getAllBehaviorTreesBySceneId, updateBehaviorTree } from './api';
import type { BehaviorTree } from '../designer/tree';
export default defineComponent({
name: 'ModelElement',
components: {
SettingOutlined,
DeleteOutlined,
LinkOutlined,
CheckOutlined,
},
props: elementProps,
setup(_props) {
@@ -87,6 +117,17 @@ export default defineComponent({
);
const updateKey = ref(0);
const isMenuVisible = ref(false);
// 挂载行为树相关状态
const availableTrees = ref<BehaviorTree[]>([]);
// 获取 popup 容器
const getPopupContainer = () => {
if (typeof document !== 'undefined') {
return document.body;
}
return undefined;
};
// 获取画布实例
const getGraph = (): Graph | null => {
@@ -103,8 +144,35 @@ export default defineComponent({
updateKey.value++;
};
// 获取行为树名称
const getBehaviorTreeName = (treeId: number | undefined | null): string => {
if (!treeId) return '';
const tree = availableTrees.value.find(t => t.id === treeId);
return tree?.name || `行为树${treeId}`;
};
// 判断行为树是否已挂载到当前节点
const isTreeMounted = (treeId: number): boolean => {
if (!element.value) return false;
const currentTreeId = (element.value as any).behaviorTreeId as number | undefined;
return currentTreeId === treeId;
};
// 处理挂载行为树 - 当右键菜单打开时从graph中读取已缓存的行为树列表
const handleVisibleChange = (visible: boolean) => {
isMenuVisible.value = visible;
if (!visible || !element.value) return;
// 从graph对象中获取已缓存的行为树列表
const graph = _props.graph as any;
if (graph?.behaviorTrees) {
availableTrees.value = graph.behaviorTrees;
console.log('从缓存中读取行为树列表:', availableTrees.value.length, '个');
} else {
availableTrees.value = [];
console.warn('未找到缓存的行为树列表');
}
};
const handleMenuClick = ({ key }: { key: string }) => {
@@ -113,6 +181,37 @@ export default defineComponent({
}
};
// 处理挂载具体的行为树
const handleMountTree = async (tree: BehaviorTree) => {
if (!element.value) return;
try {
// 更新节点的behaviorTreeId属性
const updatedElement = { ...(element.value as any), behaviorTreeId: tree.id };
// 调用后端API更新行为树将platformId关联到该平台
const platformIdValue = (element.value as any).platformId as number | undefined;
const treeToUpdate = {
...tree,
platformId: platformIdValue ?? null
};
const updateResponse = await updateBehaviorTree(treeToUpdate);
if (updateResponse.code === 200) {
// 更新本地节点数据
if (_props.node) {
_props.node.setData(updatedElement);
}
message.success(`已成功挂载行为树: ${tree.name}`);
} else {
message.error(updateResponse.msg || '挂载失败');
}
} catch (error) {
console.error('挂载行为树失败:', error);
message.error('挂载行为树失败');
}
};
const handleDelete = () => {
if (!_props.node) return;
@@ -134,10 +233,37 @@ export default defineComponent({
onMounted(() => {
_props.node?.on('change:data', handleDataChange);
// 监听画布各种事件,操作时立即关闭菜单
const graph = getGraph();
if (graph) {
const closeMenuHandler = () => {
if (isMenuVisible.value) {
isMenuVisible.value = false;
}
};
// 监听多种可能导致菜单位置变化的事件
graph.on('pan', closeMenuHandler);
graph.on('translate', closeMenuHandler);
graph.on('scale', closeMenuHandler);
graph.on('zoom', closeMenuHandler);
graph.on('resize', closeMenuHandler);
}
});
onUnmounted(() => {
_props.node?.off('change:data', handleDataChange);
// 清理事件监听
const graph = getGraph();
if (graph) {
graph.off('pan');
graph.off('translate');
graph.off('scale');
graph.off('zoom');
graph.off('resize');
}
});
return {
@@ -145,6 +271,11 @@ export default defineComponent({
substring,
handleMenuClick,
handleVisibleChange,
availableTrees,
getBehaviorTreeName,
isTreeMounted,
handleMountTree,
getPopupContainer,
};
},
});