新增装饰节点选中功能
This commit is contained in:
@@ -16,6 +16,7 @@ export function useAppState() {
|
||||
const nodeTemplates_ = ref(nodeTemplates);
|
||||
const treeNodes = ref<TreeNode[]>([]);
|
||||
const selectedNodeId = ref<string | null>(null);
|
||||
const selectedConditionNodeId = ref<string | null>(null); // 选中的条件节点ID
|
||||
const nodeSearchText = ref('');
|
||||
|
||||
// 调试:检查条件节点模板
|
||||
@@ -95,6 +96,7 @@ export function useAppState() {
|
||||
nodeTemplates: nodeTemplates_,
|
||||
treeNodes,
|
||||
selectedNodeId,
|
||||
selectedConditionNodeId,
|
||||
nodeSearchText,
|
||||
|
||||
// 画布状态
|
||||
|
||||
@@ -43,6 +43,7 @@ export function useBehaviorTreeEditor() {
|
||||
appState.nodeSearchText,
|
||||
appState.treeNodes,
|
||||
appState.selectedNodeId,
|
||||
appState.selectedConditionNodeId,
|
||||
appState.checkingStatus,
|
||||
appState.isInstalling,
|
||||
appState.isInstalled,
|
||||
@@ -456,6 +457,49 @@ export function useBehaviorTreeEditor() {
|
||||
autoLayout,
|
||||
validateTree,
|
||||
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,
|
||||
startConditionDrag: conditionAttachment.startConditionDrag,
|
||||
|
||||
@@ -13,6 +13,7 @@ export function useComputedProperties(
|
||||
nodeSearchText: Ref<string>,
|
||||
treeNodes: Ref<TreeNode[]>,
|
||||
selectedNodeId: Ref<string | null>,
|
||||
selectedConditionNodeId: Ref<string | null>,
|
||||
checkingStatus: Ref<boolean>,
|
||||
isInstalling: Ref<boolean>,
|
||||
isInstalled: Ref<boolean>,
|
||||
@@ -79,6 +80,27 @@ export function useComputedProperties(
|
||||
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 = () => {
|
||||
return getRootNode(treeNodes.value);
|
||||
@@ -140,6 +162,8 @@ export function useComputedProperties(
|
||||
filteredConditionNodes,
|
||||
filteredECSNodes,
|
||||
selectedNode,
|
||||
selectedConditionNode,
|
||||
activeNode,
|
||||
rootNode,
|
||||
installStatusClass,
|
||||
installStatusText,
|
||||
|
||||
@@ -87,6 +87,21 @@
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
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 {
|
||||
@@ -99,6 +114,17 @@
|
||||
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 {
|
||||
background: none;
|
||||
border: none;
|
||||
|
||||
@@ -263,16 +263,22 @@
|
||||
<!-- 条件装饰器的条件显示 -->
|
||||
<div v-if="node.type === 'conditional-decorator'" class="condition-attachment-area">
|
||||
<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-text">{{ getConditionDisplayText(node) }}</span>
|
||||
<span class="edit-hint">📝</span>
|
||||
</div>
|
||||
<button
|
||||
class="remove-condition-btn"
|
||||
@click.stop="removeConditionFromDecorator(node)"
|
||||
title="移除条件"
|
||||
>×</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="condition-placeholder">
|
||||
<span class="placeholder-text">🎯 拖拽条件到此处</span>
|
||||
</div>
|
||||
@@ -346,32 +352,34 @@
|
||||
<h3>⚙️ 属性面板</h3>
|
||||
</div>
|
||||
|
||||
<div v-if="selectedNode" class="node-properties">
|
||||
<div v-if="activeNode" class="node-properties">
|
||||
<div class="property-section">
|
||||
<h4>基本信息</h4>
|
||||
<div class="property-item">
|
||||
<label>节点名称:</label>
|
||||
<input
|
||||
type="text"
|
||||
:value="selectedNode.name"
|
||||
:value="activeNode.name"
|
||||
@input="updateNodeProperty('name', $event.target.value)"
|
||||
:key="selectedNode.id + '_name'"
|
||||
:key="activeNode.id + '_name'"
|
||||
:disabled="activeNode.isConditionNode"
|
||||
>
|
||||
</div>
|
||||
<div class="property-item">
|
||||
<label>描述:</label>
|
||||
<textarea
|
||||
:value="selectedNode.description"
|
||||
:value="activeNode.description"
|
||||
@input="updateNodeProperty('description', $event.target.value)"
|
||||
:key="selectedNode.id + '_description'"
|
||||
:key="activeNode.id + '_description'"
|
||||
:disabled="activeNode.isConditionNode"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="property-section" v-if="selectedNode.properties">
|
||||
<div class="property-section" v-if="activeNode.properties">
|
||||
<h4>节点属性</h4>
|
||||
<div
|
||||
v-for="(prop, key) in selectedNode.properties"
|
||||
v-for="(prop, key) in activeNode.properties"
|
||||
:key="key"
|
||||
class="property-item"
|
||||
>
|
||||
@@ -381,21 +389,21 @@
|
||||
type="text"
|
||||
:value="prop.value"
|
||||
@input="updateNodeProperty('properties.' + key + '.value', $event.target.value)"
|
||||
:key="selectedNode.id + '_' + key + '_string'"
|
||||
:key="activeNode.id + '_' + key + '_string'"
|
||||
>
|
||||
<input
|
||||
v-else-if="prop.type === 'number'"
|
||||
type="number"
|
||||
:value="prop.value"
|
||||
@input="updateNodeProperty('properties.' + key + '.value', parseFloat($event.target.value) || 0)"
|
||||
:key="selectedNode.id + '_' + key + '_number'"
|
||||
:key="activeNode.id + '_' + key + '_number'"
|
||||
>
|
||||
<input
|
||||
v-else-if="prop.type === 'boolean'"
|
||||
type="checkbox"
|
||||
:checked="prop.value"
|
||||
@change="updateNodeProperty('properties.' + key + '.value', $event.target.checked)"
|
||||
:key="selectedNode.id + '_' + key + '_boolean'"
|
||||
:key="activeNode.id + '_' + key + '_boolean'"
|
||||
>
|
||||
<textarea
|
||||
v-else-if="prop.type === 'code'"
|
||||
@@ -404,13 +412,13 @@
|
||||
rows="6"
|
||||
class="code-input"
|
||||
placeholder="请输入代码..."
|
||||
:key="selectedNode.id + '_' + key + '_code'"
|
||||
:key="activeNode.id + '_' + key + '_code'"
|
||||
></textarea>
|
||||
<select
|
||||
v-else-if="prop.type === 'select'"
|
||||
:value="prop.value"
|
||||
@change="updateNodeProperty('properties.' + key + '.value', $event.target.value)"
|
||||
:key="selectedNode.id + '_' + key + '_select_' + prop.value"
|
||||
:key="activeNode.id + '_' + key + '_select_' + prop.value"
|
||||
>
|
||||
<option
|
||||
v-for="option in prop.options"
|
||||
@@ -427,7 +435,7 @@
|
||||
|
||||
<div class="property-section">
|
||||
<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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user