新增装饰节点选中功能
This commit is contained in:
@@ -16,6 +16,7 @@ export function useAppState() {
|
|||||||
const nodeTemplates_ = ref(nodeTemplates);
|
const nodeTemplates_ = ref(nodeTemplates);
|
||||||
const treeNodes = ref<TreeNode[]>([]);
|
const treeNodes = ref<TreeNode[]>([]);
|
||||||
const selectedNodeId = ref<string | null>(null);
|
const selectedNodeId = ref<string | null>(null);
|
||||||
|
const selectedConditionNodeId = ref<string | null>(null); // 选中的条件节点ID
|
||||||
const nodeSearchText = ref('');
|
const nodeSearchText = ref('');
|
||||||
|
|
||||||
// 调试:检查条件节点模板
|
// 调试:检查条件节点模板
|
||||||
@@ -95,6 +96,7 @@ export function useAppState() {
|
|||||||
nodeTemplates: nodeTemplates_,
|
nodeTemplates: nodeTemplates_,
|
||||||
treeNodes,
|
treeNodes,
|
||||||
selectedNodeId,
|
selectedNodeId,
|
||||||
|
selectedConditionNodeId,
|
||||||
nodeSearchText,
|
nodeSearchText,
|
||||||
|
|
||||||
// 画布状态
|
// 画布状态
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export function useBehaviorTreeEditor() {
|
|||||||
appState.nodeSearchText,
|
appState.nodeSearchText,
|
||||||
appState.treeNodes,
|
appState.treeNodes,
|
||||||
appState.selectedNodeId,
|
appState.selectedNodeId,
|
||||||
|
appState.selectedConditionNodeId,
|
||||||
appState.checkingStatus,
|
appState.checkingStatus,
|
||||||
appState.isInstalling,
|
appState.isInstalling,
|
||||||
appState.isInstalled,
|
appState.isInstalled,
|
||||||
@@ -456,6 +457,49 @@ export function useBehaviorTreeEditor() {
|
|||||||
autoLayout,
|
autoLayout,
|
||||||
validateTree,
|
validateTree,
|
||||||
clearAllConnections,
|
clearAllConnections,
|
||||||
|
// 节点选择相关
|
||||||
|
selectNode: (nodeId: string) => {
|
||||||
|
// 选中普通节点时,取消条件节点的选中
|
||||||
|
appState.selectedNodeId.value = nodeId;
|
||||||
|
appState.selectedConditionNodeId.value = null;
|
||||||
|
console.log('🎯 选中节点:', nodeId);
|
||||||
|
},
|
||||||
|
selectConditionNode: (decoratorNode: any) => {
|
||||||
|
// 选中条件节点时,取消装饰器节点的选中
|
||||||
|
appState.selectedNodeId.value = null;
|
||||||
|
appState.selectedConditionNodeId.value = decoratorNode.id;
|
||||||
|
console.log('📝 选中条件节点进行编辑:', decoratorNode.attachedCondition?.name);
|
||||||
|
},
|
||||||
|
// 统一的属性更新方法(支持普通节点和条件节点)
|
||||||
|
updateNodeProperty: (path: string, value: any) => {
|
||||||
|
// 如果选中的是条件节点,更新装饰器节点的属性
|
||||||
|
if (appState.selectedConditionNodeId.value) {
|
||||||
|
const decoratorNode = appState.getNodeByIdLocal(appState.selectedConditionNodeId.value);
|
||||||
|
if (decoratorNode) {
|
||||||
|
// 使用通用方法更新属性
|
||||||
|
const keys = path.split('.');
|
||||||
|
let current: any = decoratorNode;
|
||||||
|
|
||||||
|
// 导航到目标属性的父对象
|
||||||
|
for (let i = 0; i < keys.length - 1; i++) {
|
||||||
|
const key = keys[i];
|
||||||
|
if (!(key in current) || typeof current[key] !== 'object' || current[key] === null) {
|
||||||
|
current[key] = {};
|
||||||
|
}
|
||||||
|
current = current[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置最终值
|
||||||
|
const finalKey = keys[keys.length - 1];
|
||||||
|
current[finalKey] = value;
|
||||||
|
|
||||||
|
console.log('📝 更新条件属性:', path, '=', value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 普通节点属性更新
|
||||||
|
nodeOps.updateNodeProperty(path, value);
|
||||||
|
}
|
||||||
|
},
|
||||||
// 条件吸附功能
|
// 条件吸附功能
|
||||||
conditionDragState: conditionAttachment.dragState,
|
conditionDragState: conditionAttachment.dragState,
|
||||||
startConditionDrag: conditionAttachment.startConditionDrag,
|
startConditionDrag: conditionAttachment.startConditionDrag,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export function useComputedProperties(
|
|||||||
nodeSearchText: Ref<string>,
|
nodeSearchText: Ref<string>,
|
||||||
treeNodes: Ref<TreeNode[]>,
|
treeNodes: Ref<TreeNode[]>,
|
||||||
selectedNodeId: Ref<string | null>,
|
selectedNodeId: Ref<string | null>,
|
||||||
|
selectedConditionNodeId: Ref<string | null>,
|
||||||
checkingStatus: Ref<boolean>,
|
checkingStatus: Ref<boolean>,
|
||||||
isInstalling: Ref<boolean>,
|
isInstalling: Ref<boolean>,
|
||||||
isInstalled: Ref<boolean>,
|
isInstalled: Ref<boolean>,
|
||||||
@@ -79,6 +80,27 @@ export function useComputedProperties(
|
|||||||
return node || null;
|
return node || null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 当前选中的条件节点(用于编辑条件属性)
|
||||||
|
const selectedConditionNode = computed(() => {
|
||||||
|
if (!selectedConditionNodeId.value) return null;
|
||||||
|
const decoratorNode = treeNodes.value.find(n => n.id === selectedConditionNodeId.value);
|
||||||
|
if (!decoratorNode || !decoratorNode.attachedCondition) return null;
|
||||||
|
|
||||||
|
// 创建一个虚拟的条件节点对象,用于属性编辑
|
||||||
|
return {
|
||||||
|
id: decoratorNode.id + '_condition',
|
||||||
|
name: decoratorNode.attachedCondition.name + '(条件)',
|
||||||
|
type: decoratorNode.attachedCondition.type,
|
||||||
|
icon: decoratorNode.attachedCondition.icon,
|
||||||
|
properties: decoratorNode.properties || {},
|
||||||
|
isConditionNode: true,
|
||||||
|
parentDecorator: decoratorNode
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// 当前显示在属性面板的节点(普通节点或条件节点)
|
||||||
|
const activeNode = computed(() => selectedConditionNode.value || selectedNode.value);
|
||||||
|
|
||||||
// 根节点
|
// 根节点
|
||||||
const rootNode = () => {
|
const rootNode = () => {
|
||||||
return getRootNode(treeNodes.value);
|
return getRootNode(treeNodes.value);
|
||||||
@@ -140,6 +162,8 @@ export function useComputedProperties(
|
|||||||
filteredConditionNodes,
|
filteredConditionNodes,
|
||||||
filteredECSNodes,
|
filteredECSNodes,
|
||||||
selectedNode,
|
selectedNode,
|
||||||
|
selectedConditionNode,
|
||||||
|
activeNode,
|
||||||
rootNode,
|
rootNode,
|
||||||
installStatusClass,
|
installStatusClass,
|
||||||
installStatusText,
|
installStatusText,
|
||||||
|
|||||||
@@ -87,6 +87,21 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: 3px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.condition-info:hover {
|
||||||
|
background: rgba(255, 215, 0, 0.2);
|
||||||
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
.condition-info.condition-selected {
|
||||||
|
background: rgba(255, 215, 0, 0.3);
|
||||||
|
border: 1px solid #ffd700;
|
||||||
|
box-shadow: 0 0 0 2px rgba(255, 215, 0, 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
.condition-icon {
|
.condition-icon {
|
||||||
@@ -99,6 +114,17 @@
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.edit-hint {
|
||||||
|
font-size: 10px;
|
||||||
|
color: #a0aec0;
|
||||||
|
margin-left: auto;
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.condition-info:hover .edit-hint {
|
||||||
|
color: #ffd700;
|
||||||
|
}
|
||||||
|
|
||||||
.remove-condition-btn {
|
.remove-condition-btn {
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
|
|||||||
@@ -263,15 +263,21 @@
|
|||||||
<!-- 条件装饰器的条件显示 -->
|
<!-- 条件装饰器的条件显示 -->
|
||||||
<div v-if="node.type === 'conditional-decorator'" class="condition-attachment-area">
|
<div v-if="node.type === 'conditional-decorator'" class="condition-attachment-area">
|
||||||
<div v-if="node.attachedCondition" class="attached-condition">
|
<div v-if="node.attachedCondition" class="attached-condition">
|
||||||
<div class="condition-info">
|
<div
|
||||||
|
class="condition-info"
|
||||||
|
:class="{ 'condition-selected': selectedConditionNodeId === node.id }"
|
||||||
|
@click.stop="selectConditionNode(node)"
|
||||||
|
title="点击编辑条件属性"
|
||||||
|
>
|
||||||
<span class="condition-icon">{{ node.attachedCondition.icon }}</span>
|
<span class="condition-icon">{{ node.attachedCondition.icon }}</span>
|
||||||
<span class="condition-text">{{ getConditionDisplayText(node) }}</span>
|
<span class="condition-text">{{ getConditionDisplayText(node) }}</span>
|
||||||
<button
|
<span class="edit-hint">📝</span>
|
||||||
class="remove-condition-btn"
|
|
||||||
@click.stop="removeConditionFromDecorator(node)"
|
|
||||||
title="移除条件"
|
|
||||||
>×</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
<button
|
||||||
|
class="remove-condition-btn"
|
||||||
|
@click.stop="removeConditionFromDecorator(node)"
|
||||||
|
title="移除条件"
|
||||||
|
>×</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="condition-placeholder">
|
<div v-else class="condition-placeholder">
|
||||||
<span class="placeholder-text">🎯 拖拽条件到此处</span>
|
<span class="placeholder-text">🎯 拖拽条件到此处</span>
|
||||||
@@ -346,32 +352,34 @@
|
|||||||
<h3>⚙️ 属性面板</h3>
|
<h3>⚙️ 属性面板</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="selectedNode" class="node-properties">
|
<div v-if="activeNode" class="node-properties">
|
||||||
<div class="property-section">
|
<div class="property-section">
|
||||||
<h4>基本信息</h4>
|
<h4>基本信息</h4>
|
||||||
<div class="property-item">
|
<div class="property-item">
|
||||||
<label>节点名称:</label>
|
<label>节点名称:</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
:value="selectedNode.name"
|
:value="activeNode.name"
|
||||||
@input="updateNodeProperty('name', $event.target.value)"
|
@input="updateNodeProperty('name', $event.target.value)"
|
||||||
:key="selectedNode.id + '_name'"
|
:key="activeNode.id + '_name'"
|
||||||
|
:disabled="activeNode.isConditionNode"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="property-item">
|
<div class="property-item">
|
||||||
<label>描述:</label>
|
<label>描述:</label>
|
||||||
<textarea
|
<textarea
|
||||||
:value="selectedNode.description"
|
:value="activeNode.description"
|
||||||
@input="updateNodeProperty('description', $event.target.value)"
|
@input="updateNodeProperty('description', $event.target.value)"
|
||||||
:key="selectedNode.id + '_description'"
|
:key="activeNode.id + '_description'"
|
||||||
|
:disabled="activeNode.isConditionNode"
|
||||||
></textarea>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="property-section" v-if="selectedNode.properties">
|
<div class="property-section" v-if="activeNode.properties">
|
||||||
<h4>节点属性</h4>
|
<h4>节点属性</h4>
|
||||||
<div
|
<div
|
||||||
v-for="(prop, key) in selectedNode.properties"
|
v-for="(prop, key) in activeNode.properties"
|
||||||
:key="key"
|
:key="key"
|
||||||
class="property-item"
|
class="property-item"
|
||||||
>
|
>
|
||||||
@@ -381,21 +389,21 @@
|
|||||||
type="text"
|
type="text"
|
||||||
:value="prop.value"
|
:value="prop.value"
|
||||||
@input="updateNodeProperty('properties.' + key + '.value', $event.target.value)"
|
@input="updateNodeProperty('properties.' + key + '.value', $event.target.value)"
|
||||||
:key="selectedNode.id + '_' + key + '_string'"
|
:key="activeNode.id + '_' + key + '_string'"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
v-else-if="prop.type === 'number'"
|
v-else-if="prop.type === 'number'"
|
||||||
type="number"
|
type="number"
|
||||||
:value="prop.value"
|
:value="prop.value"
|
||||||
@input="updateNodeProperty('properties.' + key + '.value', parseFloat($event.target.value) || 0)"
|
@input="updateNodeProperty('properties.' + key + '.value', parseFloat($event.target.value) || 0)"
|
||||||
:key="selectedNode.id + '_' + key + '_number'"
|
:key="activeNode.id + '_' + key + '_number'"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
v-else-if="prop.type === 'boolean'"
|
v-else-if="prop.type === 'boolean'"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
:checked="prop.value"
|
:checked="prop.value"
|
||||||
@change="updateNodeProperty('properties.' + key + '.value', $event.target.checked)"
|
@change="updateNodeProperty('properties.' + key + '.value', $event.target.checked)"
|
||||||
:key="selectedNode.id + '_' + key + '_boolean'"
|
:key="activeNode.id + '_' + key + '_boolean'"
|
||||||
>
|
>
|
||||||
<textarea
|
<textarea
|
||||||
v-else-if="prop.type === 'code'"
|
v-else-if="prop.type === 'code'"
|
||||||
@@ -404,13 +412,13 @@
|
|||||||
rows="6"
|
rows="6"
|
||||||
class="code-input"
|
class="code-input"
|
||||||
placeholder="请输入代码..."
|
placeholder="请输入代码..."
|
||||||
:key="selectedNode.id + '_' + key + '_code'"
|
:key="activeNode.id + '_' + key + '_code'"
|
||||||
></textarea>
|
></textarea>
|
||||||
<select
|
<select
|
||||||
v-else-if="prop.type === 'select'"
|
v-else-if="prop.type === 'select'"
|
||||||
:value="prop.value"
|
:value="prop.value"
|
||||||
@change="updateNodeProperty('properties.' + key + '.value', $event.target.value)"
|
@change="updateNodeProperty('properties.' + key + '.value', $event.target.value)"
|
||||||
:key="selectedNode.id + '_' + key + '_select_' + prop.value"
|
:key="activeNode.id + '_' + key + '_select_' + prop.value"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
v-for="option in prop.options"
|
v-for="option in prop.options"
|
||||||
@@ -427,7 +435,7 @@
|
|||||||
|
|
||||||
<div class="property-section">
|
<div class="property-section">
|
||||||
<h4>节点配置</h4>
|
<h4>节点配置</h4>
|
||||||
<pre class="config-preview">{{ selectedNode ? JSON.stringify(selectedNode, null, 2) : '{}' }}</pre>
|
<pre class="config-preview">{{ activeNode ? JSON.stringify(activeNode, null, 2) : '{}' }}</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user