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

@@ -0,0 +1,278 @@
import React from 'react';
import { Play, Pause, Square, SkipForward, RotateCcw, Trash2, Undo, Redo } from 'lucide-react';
type ExecutionMode = 'idle' | 'running' | 'paused' | 'step';
interface EditorToolbarProps {
executionMode: ExecutionMode;
canUndo: boolean;
canRedo: boolean;
onPlay: () => void;
onPause: () => void;
onStop: () => void;
onStep: () => void;
onReset: () => void;
onUndo: () => void;
onRedo: () => void;
onResetView: () => void;
onClearCanvas: () => void;
}
export const EditorToolbar: React.FC<EditorToolbarProps> = ({
executionMode,
canUndo,
canRedo,
onPlay,
onPause,
onStop,
onStep,
onReset,
onUndo,
onRedo,
onResetView,
onClearCanvas
}) => {
return (
<div style={{
position: 'absolute',
top: '10px',
left: '50%',
transform: 'translateX(-50%)',
display: 'flex',
gap: '8px',
backgroundColor: 'rgba(45, 45, 45, 0.95)',
padding: '8px',
borderRadius: '6px',
boxShadow: '0 2px 8px rgba(0,0,0,0.3)',
zIndex: 100
}}>
{/* 播放按钮 */}
<button
onClick={onPlay}
disabled={executionMode === 'running'}
style={{
padding: '8px',
backgroundColor: executionMode === 'running' ? '#2d2d2d' : '#4caf50',
border: 'none',
borderRadius: '4px',
color: executionMode === 'running' ? '#666' : '#fff',
cursor: executionMode === 'running' ? 'not-allowed' : 'pointer',
fontSize: '14px',
fontWeight: 'bold',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
title="运行 (Play)"
>
<Play size={16} />
</button>
{/* 暂停按钮 */}
<button
onClick={onPause}
disabled={executionMode === 'idle'}
style={{
padding: '8px',
backgroundColor: executionMode === 'idle' ? '#2d2d2d' : '#ff9800',
border: 'none',
borderRadius: '4px',
color: executionMode === 'idle' ? '#666' : '#fff',
cursor: executionMode === 'idle' ? 'not-allowed' : 'pointer',
fontSize: '14px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
title={executionMode === 'paused' ? '继续' : '暂停'}
>
{executionMode === 'paused' ? <Play size={16} /> : <Pause size={16} />}
</button>
{/* 停止按钮 */}
<button
onClick={onStop}
disabled={executionMode === 'idle'}
style={{
padding: '8px',
backgroundColor: executionMode === 'idle' ? '#2d2d2d' : '#f44336',
border: 'none',
borderRadius: '4px',
color: executionMode === 'idle' ? '#666' : '#fff',
cursor: executionMode === 'idle' ? 'not-allowed' : 'pointer',
fontSize: '14px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
title="停止"
>
<Square size={16} />
</button>
{/* 单步执行按钮 */}
<button
onClick={onStep}
disabled={executionMode !== 'idle' && executionMode !== 'paused'}
style={{
padding: '8px',
backgroundColor: (executionMode !== 'idle' && executionMode !== 'paused') ? '#2d2d2d' : '#2196f3',
border: 'none',
borderRadius: '4px',
color: (executionMode !== 'idle' && executionMode !== 'paused') ? '#666' : '#fff',
cursor: (executionMode !== 'idle' && executionMode !== 'paused') ? 'not-allowed' : 'pointer',
fontSize: '14px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
title="单步执行"
>
<SkipForward size={16} />
</button>
{/* 重置按钮 */}
<button
onClick={onReset}
style={{
padding: '8px',
backgroundColor: '#9e9e9e',
border: 'none',
borderRadius: '4px',
color: '#fff',
cursor: 'pointer',
fontSize: '14px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
title="重置"
>
<RotateCcw size={16} />
</button>
{/* 分隔符 */}
<div style={{
width: '1px',
backgroundColor: '#666',
margin: '4px 0'
}} />
{/* 重置视图按钮 */}
<button
onClick={onResetView}
style={{
padding: '8px 12px',
backgroundColor: '#3c3c3c',
border: 'none',
borderRadius: '4px',
color: '#cccccc',
cursor: 'pointer',
fontSize: '12px',
display: 'flex',
alignItems: 'center',
gap: '4px'
}}
title="重置视图 (滚轮缩放, Alt+拖动平移)"
>
<RotateCcw size={14} />
View
</button>
{/* 清空画布按钮 */}
<button
style={{
padding: '8px 12px',
backgroundColor: '#3c3c3c',
border: 'none',
borderRadius: '4px',
color: '#cccccc',
cursor: 'pointer',
fontSize: '12px',
display: 'flex',
alignItems: 'center',
gap: '4px'
}}
title="清空画布"
onClick={onClearCanvas}
>
<Trash2 size={14} />
</button>
{/* 分隔符 */}
<div style={{
width: '1px',
height: '24px',
backgroundColor: '#555',
margin: '0 4px'
}} />
{/* 撤销按钮 */}
<button
onClick={onUndo}
disabled={!canUndo}
style={{
padding: '8px',
backgroundColor: canUndo ? '#3c3c3c' : '#2d2d2d',
border: 'none',
borderRadius: '4px',
color: canUndo ? '#cccccc' : '#666',
cursor: canUndo ? 'pointer' : 'not-allowed',
fontSize: '14px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
title="撤销 (Ctrl+Z)"
>
<Undo size={16} />
</button>
{/* 重做按钮 */}
<button
onClick={onRedo}
disabled={!canRedo}
style={{
padding: '8px',
backgroundColor: canRedo ? '#3c3c3c' : '#2d2d2d',
border: 'none',
borderRadius: '4px',
color: canRedo ? '#cccccc' : '#666',
cursor: canRedo ? 'pointer' : 'not-allowed',
fontSize: '14px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
title="重做 (Ctrl+Shift+Z / Ctrl+Y)"
>
<Redo size={16} />
</button>
{/* 状态指示器 */}
<div style={{
padding: '8px 12px',
backgroundColor: '#1e1e1e',
borderRadius: '4px',
fontSize: '12px',
color: '#ccc',
display: 'flex',
alignItems: 'center',
gap: '6px'
}}>
<span style={{
width: '8px',
height: '8px',
borderRadius: '50%',
backgroundColor:
executionMode === 'running' ? '#4caf50' :
executionMode === 'paused' ? '#ff9800' : '#666'
}} />
{executionMode === 'idle' ? 'Idle' :
executionMode === 'running' ? 'Running' :
executionMode === 'paused' ? 'Paused' : 'Step'}
</div>
</div>
);
};