You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

320 lines
9.2 KiB

using AuroraDesk.Core.Entities;
using AuroraDesk.Core.Interfaces;
using AuroraDesk.Presentation.ViewModels.Base;
using ReactiveUI;
using System;
using System.Collections.ObjectModel;
using System.Reactive;
using System.Reactive.Linq;
using Microsoft.Extensions.Logging;
namespace AuroraDesk.Presentation.ViewModels.Pages;
/// <summary>
/// 节点画布页面 ViewModel
/// </summary>
public class NodeCanvasPageViewModel : RoutableViewModel
{
private readonly INodeCanvasService _nodeCanvasService;
private readonly ILogger<NodeCanvasPageViewModel>? _logger;
private Node? _selectedNode;
private ConnectionPoint? _connectingSourcePoint;
private bool _isConnecting;
private double _canvasOffsetX;
private double _canvasOffsetY;
private double _canvasZoom = 1.0;
public ObservableCollection<Node> Nodes => _nodeCanvasService.Nodes;
public ObservableCollection<Connection> Connections => _nodeCanvasService.Connections;
private ObservableCollection<NodeTemplate> _nodeTemplates = new();
public ObservableCollection<NodeTemplate> NodeTemplates
{
get => _nodeTemplates;
set => this.RaiseAndSetIfChanged(ref _nodeTemplates, value);
}
/// <summary>
/// 选中的节点
/// </summary>
public Node? SelectedNode
{
get => _selectedNode;
set => this.RaiseAndSetIfChanged(ref _selectedNode, value);
}
/// <summary>
/// 正在连接的源连接点
/// </summary>
public ConnectionPoint? ConnectingSourcePoint
{
get => _connectingSourcePoint;
set => this.RaiseAndSetIfChanged(ref _connectingSourcePoint, value);
}
/// <summary>
/// 是否正在连接模式
/// </summary>
public bool IsConnecting
{
get => _isConnecting;
set => this.RaiseAndSetIfChanged(ref _isConnecting, value);
}
/// <summary>
/// 画布偏移X
/// </summary>
public double CanvasOffsetX
{
get => _canvasOffsetX;
set => this.RaiseAndSetIfChanged(ref _canvasOffsetX, value);
}
/// <summary>
/// 画布偏移Y
/// </summary>
public double CanvasOffsetY
{
get => _canvasOffsetY;
set => this.RaiseAndSetIfChanged(ref _canvasOffsetY, value);
}
/// <summary>
/// 画布缩放
/// </summary>
public double CanvasZoom
{
get => _canvasZoom;
set => this.RaiseAndSetIfChanged(ref _canvasZoom, value);
}
// 命令
public ReactiveCommand<Unit, Unit> ClearCanvasCommand { get; }
public ReactiveCommand<Node, Unit> DeleteNodeCommand { get; }
public ReactiveCommand<ConnectionPoint, Unit> StartConnectionCommand { get; }
public ReactiveCommand<ConnectionPoint, Unit> CompleteConnectionCommand { get; }
public ReactiveCommand<Connection, Unit> DeleteConnectionCommand { get; }
public ReactiveCommand<(double x, double y), Unit> AddNodeCommand { get; }
public ReactiveCommand<(NodeTemplate template, double x, double y), Unit> AddNodeFromTemplateCommand { get; }
public NodeCanvasPageViewModel(
IScreen screen,
INodeCanvasService nodeCanvasService,
ILogger<NodeCanvasPageViewModel>? logger = null) : base(screen, "node-canvas")
{
_nodeCanvasService = nodeCanvasService;
_logger = logger;
// 初始化节点模板
InitializeNodeTemplates();
// 初始化命令
ClearCanvasCommand = ReactiveCommand.Create(ClearCanvas);
DeleteNodeCommand = ReactiveCommand.Create<Node>(DeleteNode);
StartConnectionCommand = ReactiveCommand.Create<ConnectionPoint>(StartConnection);
CompleteConnectionCommand = ReactiveCommand.Create<ConnectionPoint>(CompleteConnection);
DeleteConnectionCommand = ReactiveCommand.Create<Connection>(DeleteConnection);
AddNodeCommand = ReactiveCommand.Create<(double x, double y)>(AddNode);
AddNodeFromTemplateCommand = ReactiveCommand.Create<(NodeTemplate template, double x, double y)>(AddNodeFromTemplate);
// 监听选中节点变化
this.WhenAnyValue(x => x.SelectedNode)
.Subscribe(node =>
{
// 取消其他节点的选中状态
foreach (var n in Nodes)
{
n.IsSelected = n == node;
}
});
_logger?.LogInformation("NodeCanvasPageViewModel 已创建");
}
/// <summary>
/// 初始化节点模板
/// </summary>
private void InitializeNodeTemplates()
{
NodeTemplates.Add(new NodeTemplate
{
Name = "power-splitter",
DisplayName = "功分器",
Description = "3功分器",
Content = "3功分42",
InputCount = 1,
OutputCount = 3,
Width = 120,
Height = 80
});
NodeTemplates.Add(new NodeTemplate
{
Name = "basic-node",
DisplayName = "基础节点",
Description = "基础节点模板",
Content = "节点",
InputCount = 1,
OutputCount = 1,
Width = 100,
Height = 60
});
}
/// <summary>
/// 添加节点到画布
/// </summary>
private void AddNode((double x, double y) position)
{
// 确保位置在合理范围内(至少为正数)
var x = Math.Max(0, position.x);
var y = Math.Max(0, position.y);
var node = new Node
{
X = x,
Y = y,
Title = $"节点 {Nodes.Count + 1}",
Content = "3功分42",
Width = 120,
Height = 80
};
// 添加连接点(右侧输出点)
for (int i = 0; i < 3; i++)
{
node.ConnectionPoints.Add(new ConnectionPoint
{
Label = (i + 1).ToString(),
Type = ConnectionPointType.Output,
Index = i,
Node = node
});
}
// 添加连接点(左侧输入点)
node.ConnectionPoints.Add(new ConnectionPoint
{
Label = "",
Type = ConnectionPointType.Input,
Index = 0,
Node = node
});
_nodeCanvasService.AddNode(node);
SelectedNode = node;
_logger?.LogDebug("添加节点: {NodeId} 在位置 ({X}, {Y})", node.Id, position.x, position.y);
}
/// <summary>
/// 删除节点
/// </summary>
private void DeleteNode(Node node)
{
if (node == null) return;
_nodeCanvasService.RemoveNode(node);
if (SelectedNode == node)
{
SelectedNode = null;
}
_logger?.LogDebug("删除节点: {NodeId}", node.Id);
}
/// <summary>
/// 开始连接
/// </summary>
private void StartConnection(ConnectionPoint connectionPoint)
{
if (connectionPoint == null || connectionPoint.Type != ConnectionPointType.Output)
return;
ConnectingSourcePoint = connectionPoint;
IsConnecting = true;
_logger?.LogDebug("开始连接,源连接点: {PointId}", connectionPoint.Id);
}
/// <summary>
/// 完成连接
/// </summary>
private void CompleteConnection(ConnectionPoint targetPoint)
{
if (targetPoint == null || ConnectingSourcePoint == null)
{
CancelConnection();
return;
}
if (targetPoint.Type != ConnectionPointType.Input)
{
CancelConnection();
return;
}
var success = _nodeCanvasService.CreateConnection(ConnectingSourcePoint, targetPoint);
if (success)
{
_logger?.LogDebug("连接成功: {SourceId} -> {TargetId}",
ConnectingSourcePoint.Id, targetPoint.Id);
}
else
{
_logger?.LogWarning("连接失败: {SourceId} -> {TargetId}",
ConnectingSourcePoint.Id, targetPoint.Id);
}
CancelConnection();
}
/// <summary>
/// 取消连接
/// </summary>
public void CancelConnection()
{
ConnectingSourcePoint = null;
IsConnecting = false;
}
/// <summary>
/// 删除连接
/// </summary>
private void DeleteConnection(Connection connection)
{
if (connection == null) return;
_nodeCanvasService.RemoveConnection(connection);
_logger?.LogDebug("删除连接: {ConnectionId}", connection.Id);
}
/// <summary>
/// 清空画布
/// </summary>
private void ClearCanvas()
{
_nodeCanvasService.Clear();
SelectedNode = null;
_logger?.LogDebug("清空画布");
}
/// <summary>
/// 更新节点位置
/// </summary>
public void UpdateNodePosition(Node node, double x, double y)
{
_nodeCanvasService.UpdateNodePosition(node, x, y);
}
/// <summary>
/// 从模板添加节点到画布
/// </summary>
private void AddNodeFromTemplate((NodeTemplate template, double x, double y) args)
{
var node = args.template.CreateNode(args.x, args.y);
_nodeCanvasService.AddNode(node);
SelectedNode = node;
_logger?.LogDebug("从模板添加节点: {TemplateName} 在位置 ({X}, {Y})",
args.template.Name, args.x, args.y);
}
}