Refactor/clean architecture phase1 (#215)

* refactor(editor): 建立Clean Architecture领域模型层

* refactor(editor): 实现应用层架构 - 命令模式、用例和状态管理

* refactor(editor): 实现展示层核心Hooks

* refactor(editor): 实现基础设施层和展示层组件

* refactor(editor): 迁移画布和连接渲染到 Clean Architecture 组件

* feat(editor): 集成应用层架构和命令模式,实现撤销/重做功能

* refactor(editor): UI组件拆分

* refactor(editor): 提取快速创建菜单逻辑

* refactor(editor): 重构BehaviorTreeEditor,提取组件和Hook

* refactor(editor): 提取端口连接和键盘事件Hook

* refactor(editor): 提取拖放处理Hook

* refactor(editor): 提取画布交互Hook和工具函数

* refactor(editor): 完成核心重构

* fix(editor): 修复节点无法创建和连接

* refactor(behavior-tree,editor): 重构节点子节点约束系统,实现元数据驱动的架构
This commit is contained in:
YHH
2025-11-03 21:22:16 +08:00
committed by GitHub
parent 40cde9c050
commit adfc7e91b3
104 changed files with 8232 additions and 2506 deletions

View File

@@ -12,7 +12,11 @@ import { NodeExecutorMetadata } from '../NodeMetadata';
nodeType: NodeType.Decorator,
displayName: '反转',
description: '反转子节点的执行结果',
category: 'Decorator'
category: 'Decorator',
childrenConstraints: {
min: 1,
max: 1
}
})
export class InverterExecutor implements INodeExecutor {
execute(context: NodeExecutionContext): TaskStatus {

View File

@@ -26,6 +26,9 @@ import { NodeExecutorMetadata } from '../NodeMetadata';
description: '失败策略',
options: ['all', 'one']
}
},
childrenConstraints: {
min: 2
}
})
export class ParallelExecutor implements INodeExecutor {

View File

@@ -12,7 +12,10 @@ import { NodeExecutorMetadata } from '../NodeMetadata';
nodeType: NodeType.Composite,
displayName: '随机序列',
description: '随机顺序执行子节点,全部成功才成功',
category: 'Composite'
category: 'Composite',
childrenConstraints: {
min: 1
}
})
export class RandomSequenceExecutor implements INodeExecutor {
execute(context: NodeExecutionContext): TaskStatus {

View File

@@ -25,6 +25,10 @@ import { NodeExecutorMetadata } from '../NodeMetadata';
default: false,
description: '子节点失败时是否结束'
}
},
childrenConstraints: {
min: 1,
max: 1
}
})
export class RepeaterExecutor implements INodeExecutor {

View File

@@ -12,7 +12,10 @@ import { NodeExecutorMetadata } from '../NodeMetadata';
nodeType: NodeType.Composite,
displayName: '选择器',
description: '按顺序执行子节点,任一成功则成功',
category: 'Composite'
category: 'Composite',
childrenConstraints: {
min: 1
}
})
export class SelectorExecutor implements INodeExecutor {
execute(context: NodeExecutionContext): TaskStatus {

View File

@@ -12,7 +12,10 @@ import { NodeExecutorMetadata } from '../NodeMetadata';
nodeType: NodeType.Composite,
displayName: '序列',
description: '按顺序执行子节点,全部成功才成功',
category: 'Composite'
category: 'Composite',
childrenConstraints: {
min: 1
}
})
export class SequenceExecutor implements INodeExecutor {
execute(context: NodeExecutionContext): TaskStatus {

View File

@@ -14,6 +14,15 @@ export interface ConfigFieldDefinition {
allowMultipleConnections?: boolean;
}
/**
* 子节点约束配置
*/
export interface ChildrenConstraints {
min?: number;
max?: number;
required?: boolean;
}
/**
* 节点元数据
*/
@@ -24,6 +33,26 @@ export interface NodeMetadata {
description?: string;
category?: string;
configSchema?: Record<string, ConfigFieldDefinition>;
childrenConstraints?: ChildrenConstraints;
}
/**
* 节点元数据默认值
*/
export class NodeMetadataDefaults {
static getDefaultConstraints(nodeType: NodeType): ChildrenConstraints | undefined {
switch (nodeType) {
case NodeType.Composite:
return { min: 1 };
case NodeType.Decorator:
return { min: 1, max: 1 };
case NodeType.Action:
case NodeType.Condition:
return { max: 0 };
default:
return undefined;
}
}
}
/**

View File

@@ -1,5 +1,5 @@
import { NodeType } from '../Types/TaskStatus';
import { NodeMetadataRegistry, ConfigFieldDefinition } from '../Runtime/NodeMetadata';
import { NodeMetadataRegistry, ConfigFieldDefinition, NodeMetadata } from '../Runtime/NodeMetadata';
/**
* 节点数据JSON格式
@@ -48,7 +48,7 @@ export const PropertyType = {
* type: 'curve-editor'
* ```
*/
export type PropertyType = typeof PropertyType[keyof typeof PropertyType] | string;
export type PropertyType = (typeof PropertyType)[keyof typeof PropertyType] | string;
/**
* 属性定义(用于编辑器)
@@ -114,7 +114,7 @@ export interface PropertyDefinition {
/** 验证失败的提示信息 */
message?: string;
/** 自定义验证函数 */
validator?: string; // 函数字符串,编辑器会解析
validator?: string; // 函数字符串,编辑器会解析
/** 最小长度(字符串) */
minLength?: number;
/** 最大长度(字符串) */
@@ -141,6 +141,8 @@ export interface NodeTemplate {
className?: string;
componentClass?: Function;
requiresChildren?: boolean;
minChildren?: number;
maxChildren?: number;
defaultConfig: Partial<NodeDataJSON>;
properties: PropertyDefinition[];
}
@@ -183,7 +185,7 @@ export class NodeTemplates {
/**
* 将NodeMetadata转换为NodeTemplate
*/
private static convertMetadataToTemplate(metadata: any): NodeTemplate {
private static convertMetadataToTemplate(metadata: NodeMetadata): NodeTemplate {
const properties = this.convertConfigSchemaToProperties(metadata.configSchema || {});
const defaultConfig: Partial<NodeDataJSON> = {
@@ -217,7 +219,10 @@ export class NodeTemplates {
// 根据节点类型生成默认颜色和图标
const { icon, color } = this.getIconAndColorByType(metadata.nodeType, metadata.category || '');
return {
// 应用子节点约束
const constraints = metadata.childrenConstraints || this.getDefaultConstraintsByNodeType(metadata.nodeType);
const template: NodeTemplate = {
type: metadata.nodeType,
displayName: metadata.displayName,
category: metadata.category || this.getCategoryByNodeType(metadata.nodeType),
@@ -228,6 +233,35 @@ export class NodeTemplates {
defaultConfig,
properties
};
if (constraints) {
if (constraints.min !== undefined) {
template.minChildren = constraints.min;
template.requiresChildren = constraints.min > 0;
}
if (constraints.max !== undefined) {
template.maxChildren = constraints.max;
}
}
return template;
}
/**
* 获取节点类型的默认约束
*/
private static getDefaultConstraintsByNodeType(nodeType: NodeType): { min?: number; max?: number } | undefined {
switch (nodeType) {
case NodeType.Composite:
return { min: 1 };
case NodeType.Decorator:
return { min: 1, max: 1 };
case NodeType.Action:
case NodeType.Condition:
return { max: 0 };
default:
return undefined;
}
}
/**