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.
738 lines
24 KiB
738 lines
24 KiB
using AuroraDesk.Core.Entities;
|
|
using AuroraDesk.Core.Interfaces;
|
|
using AuroraDesk.Presentation.ViewModels.Base;
|
|
using ReactiveUI;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Collections.Specialized;
|
|
using System.Reactive;
|
|
using System.Reactive.Disposables;
|
|
using System.Reactive.Linq;
|
|
using System.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 IDisposable? _selectedNodeSubscription;
|
|
private ConnectionPoint? _connectingSourcePoint;
|
|
private bool _isConnecting;
|
|
private double _canvasOffsetX;
|
|
private double _canvasOffsetY;
|
|
private double _canvasZoom = 1.0;
|
|
private int _selectedLeftConnectorCount;
|
|
private int _selectedRightConnectorCount;
|
|
|
|
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);
|
|
}
|
|
|
|
public IReadOnlyList<int> ConnectorCountOptions => _connectorCountOptions;
|
|
|
|
/// <summary>
|
|
/// 选中的节点
|
|
/// </summary>
|
|
private const double DefaultConnectorSize = 10d;
|
|
private const int MinConnectorCount = 0;
|
|
private const int MaxConnectorCount = 4;
|
|
private const int DefaultConnectorCount = 3;
|
|
private const string DefaultLeftConnectorColor = "#2D9CDB";
|
|
private const string DefaultRightConnectorColor = "#EB5757";
|
|
private readonly IReadOnlyList<int> _connectorCountOptions = Enumerable.Range(MinConnectorCount, MaxConnectorCount - MinConnectorCount + 1).ToList();
|
|
|
|
public Node? SelectedNode
|
|
{
|
|
get => _selectedNode;
|
|
set
|
|
{
|
|
if (ReferenceEquals(_selectedNode, value))
|
|
{
|
|
return;
|
|
}
|
|
|
|
this.RaiseAndSetIfChanged(ref _selectedNode, value);
|
|
|
|
_selectedNodeSubscription?.Dispose();
|
|
_selectedNodeSubscription = null;
|
|
|
|
RefreshSelectedConnectorCounts();
|
|
|
|
if (value != null)
|
|
{
|
|
var disposables = new CompositeDisposable();
|
|
|
|
NotifyCollectionChangedEventHandler? handler = null;
|
|
handler = (_, __) =>
|
|
{
|
|
RefreshSelectedConnectorCounts();
|
|
this.RaisePropertyChanged(nameof(SelectedLeftConnectorCount));
|
|
this.RaisePropertyChanged(nameof(SelectedRightConnectorCount));
|
|
this.RaisePropertyChanged(nameof(SelectedConnectorMode));
|
|
};
|
|
|
|
value.ConnectionPoints.CollectionChanged += handler;
|
|
disposables.Add(Disposable.Create(() => value.ConnectionPoints.CollectionChanged -= handler));
|
|
|
|
_selectedNodeSubscription = disposables;
|
|
}
|
|
|
|
this.RaisePropertyChanged(nameof(SelectedConnectorMode));
|
|
this.RaisePropertyChanged(nameof(SelectedLeftConnectorCount));
|
|
this.RaisePropertyChanged(nameof(SelectedRightConnectorCount));
|
|
}
|
|
}
|
|
|
|
public ConnectorAttachmentMode SelectedConnectorMode
|
|
{
|
|
get
|
|
{
|
|
if (SelectedNode == null)
|
|
{
|
|
return ConnectorAttachmentMode.None;
|
|
}
|
|
|
|
var hasLeft = _selectedLeftConnectorCount > 0;
|
|
var hasRight = _selectedRightConnectorCount > 0;
|
|
|
|
return (hasLeft, hasRight) switch
|
|
{
|
|
(false, false) => ConnectorAttachmentMode.None,
|
|
(true, false) => ConnectorAttachmentMode.LeftOnly,
|
|
(false, true) => ConnectorAttachmentMode.RightOnly,
|
|
_ => ConnectorAttachmentMode.Both
|
|
};
|
|
}
|
|
set
|
|
{
|
|
if (SelectedNode == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (SelectedConnectorMode == value)
|
|
{
|
|
return;
|
|
}
|
|
|
|
switch (value)
|
|
{
|
|
case ConnectorAttachmentMode.None:
|
|
SetConnectorCount(SelectedNode, ConnectionPointType.Input, 0);
|
|
SetConnectorCount(SelectedNode, ConnectionPointType.Output, 0);
|
|
break;
|
|
case ConnectorAttachmentMode.LeftOnly:
|
|
if (SelectedLeftConnectorCount == 0)
|
|
{
|
|
SetConnectorCount(SelectedNode, ConnectionPointType.Input, 1);
|
|
}
|
|
SetConnectorCount(SelectedNode, ConnectionPointType.Output, 0);
|
|
break;
|
|
case ConnectorAttachmentMode.RightOnly:
|
|
SetConnectorCount(SelectedNode, ConnectionPointType.Input, 0);
|
|
if (SelectedRightConnectorCount == 0)
|
|
{
|
|
SetConnectorCount(SelectedNode, ConnectionPointType.Output, 1);
|
|
}
|
|
break;
|
|
case ConnectorAttachmentMode.Both:
|
|
if (SelectedLeftConnectorCount == 0)
|
|
{
|
|
SetConnectorCount(SelectedNode, ConnectionPointType.Input, 1);
|
|
}
|
|
if (SelectedRightConnectorCount == 0)
|
|
{
|
|
SetConnectorCount(SelectedNode, ConnectionPointType.Output, 1);
|
|
}
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
this.RaisePropertyChanged(nameof(SelectedConnectorMode));
|
|
}
|
|
}
|
|
|
|
public int SelectedLeftConnectorCount
|
|
{
|
|
get => _selectedLeftConnectorCount;
|
|
set
|
|
{
|
|
if (SelectedNode == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
SetConnectorCount(SelectedNode, ConnectionPointType.Input, value);
|
|
}
|
|
}
|
|
|
|
public int SelectedRightConnectorCount
|
|
{
|
|
get => _selectedRightConnectorCount;
|
|
set
|
|
{
|
|
if (SelectedNode == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
SetConnectorCount(SelectedNode, ConnectionPointType.Output, 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 ReactiveCommand<NodeTemplate, Unit> AddNodeFromTemplateAtCenterCommand { get; }
|
|
public ReactiveCommand<NodeTemplate, Unit> AddNodeToCanvasCommand { get; }
|
|
public ReactiveCommand<Unit, Unit> ZoomInCommand { get; }
|
|
public ReactiveCommand<Unit, Unit> ZoomOutCommand { get; }
|
|
public ReactiveCommand<Unit, Unit> ResetZoomCommand { get; }
|
|
public ReactiveCommand<double, Unit> SetZoomCommand { 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);
|
|
AddNodeFromTemplateAtCenterCommand = ReactiveCommand.Create<NodeTemplate>(template => AddNodeFromTemplateAtCenter(template));
|
|
AddNodeToCanvasCommand = ReactiveCommand.Create<NodeTemplate>(AddNodeToCanvas);
|
|
ZoomInCommand = ReactiveCommand.Create(ZoomIn);
|
|
ZoomOutCommand = ReactiveCommand.Create(ZoomOut);
|
|
ResetZoomCommand = ReactiveCommand.Create(ResetZoom);
|
|
SetZoomCommand = ReactiveCommand.Create<double>(SetZoom);
|
|
|
|
// 监听选中节点变化
|
|
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.Clear();
|
|
|
|
NodeTemplates.Add(new NodeTemplate
|
|
{
|
|
Name = "rectangle-vertical",
|
|
DisplayName = "竖向矩形",
|
|
Description = "高>宽的矩形组件",
|
|
Content = string.Empty,
|
|
InputCount = 3,
|
|
OutputCount = 3,
|
|
Width = 90,
|
|
Height = 160,
|
|
LeftConnectorSize = DefaultConnectorSize,
|
|
RightConnectorSize = DefaultConnectorSize,
|
|
LeftConnectorColor = "#2D9CDB",
|
|
RightConnectorColor = "#EB5757",
|
|
LeftConnectorPlacement = ConnectorPlacementMode.Outside,
|
|
RightConnectorPlacement = ConnectorPlacementMode.Outside
|
|
});
|
|
|
|
NodeTemplates.Add(new NodeTemplate
|
|
{
|
|
Name = "rectangle-horizontal",
|
|
DisplayName = "横向矩形",
|
|
Description = "宽>高的矩形组件",
|
|
Content = string.Empty,
|
|
InputCount = 3,
|
|
OutputCount = 3,
|
|
Width = 200,
|
|
Height = 90,
|
|
LeftConnectorSize = DefaultConnectorSize,
|
|
RightConnectorSize = DefaultConnectorSize,
|
|
LeftConnectorColor = "#2D9CDB",
|
|
RightConnectorColor = "#EB5757",
|
|
LeftConnectorPlacement = ConnectorPlacementMode.Outside,
|
|
RightConnectorPlacement = ConnectorPlacementMode.Outside
|
|
});
|
|
}
|
|
|
|
/// <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 = string.Empty,
|
|
Width = 200,
|
|
Height = 90,
|
|
LeftConnectorSize = DefaultConnectorSize,
|
|
RightConnectorSize = DefaultConnectorSize,
|
|
LeftConnectorColor = "#2D9CDB",
|
|
RightConnectorColor = "#EB5757",
|
|
LeftConnectorPlacement = ConnectorPlacementMode.Outside,
|
|
RightConnectorPlacement = ConnectorPlacementMode.Outside
|
|
};
|
|
|
|
// 默认提供输入和输出连接点
|
|
for (int i = 0; i < DefaultConnectorCount; i++)
|
|
{
|
|
node.ConnectionPoints.Add(new ConnectionPoint
|
|
{
|
|
Label = string.Empty,
|
|
Type = ConnectionPointType.Input,
|
|
Index = i,
|
|
Node = node,
|
|
Diameter = node.LeftConnectorSize,
|
|
Color = node.LeftConnectorColor,
|
|
Placement = node.LeftConnectorPlacement
|
|
});
|
|
}
|
|
|
|
for (int i = 0; i < DefaultConnectorCount; i++)
|
|
{
|
|
node.ConnectionPoints.Add(new ConnectionPoint
|
|
{
|
|
Label = (i + 1).ToString(),
|
|
Type = ConnectionPointType.Output,
|
|
Index = i,
|
|
Node = node,
|
|
Diameter = node.RightConnectorSize,
|
|
Color = node.RightConnectorColor,
|
|
Placement = node.RightConnectorPlacement
|
|
});
|
|
}
|
|
|
|
_nodeCanvasService.AddNode(node);
|
|
SelectedNode = node;
|
|
_logger?.LogDebug("添加节点: {NodeId} 在位置 ({X}, {Y})", node.Id, position.x, position.y);
|
|
}
|
|
|
|
private void SetConnectorCount(Node node, ConnectionPointType type, int desiredCount)
|
|
{
|
|
UpdateConnectorCount(node, type, desiredCount);
|
|
RefreshSelectedConnectorCounts();
|
|
|
|
if (type == ConnectionPointType.Input)
|
|
{
|
|
this.RaisePropertyChanged(nameof(SelectedLeftConnectorCount));
|
|
}
|
|
else
|
|
{
|
|
this.RaisePropertyChanged(nameof(SelectedRightConnectorCount));
|
|
}
|
|
|
|
this.RaisePropertyChanged(nameof(SelectedConnectorMode));
|
|
}
|
|
|
|
private void RefreshSelectedConnectorCounts()
|
|
{
|
|
if (SelectedNode == null)
|
|
{
|
|
_selectedLeftConnectorCount = 0;
|
|
_selectedRightConnectorCount = 0;
|
|
return;
|
|
}
|
|
|
|
_selectedLeftConnectorCount = SelectedNode.ConnectionPoints.Count(p => p.Type == ConnectionPointType.Input);
|
|
_selectedRightConnectorCount = SelectedNode.ConnectionPoints.Count(p => p.Type == ConnectionPointType.Output);
|
|
}
|
|
|
|
private void UpdateConnectorCount(Node node, ConnectionPointType type, int desiredCount)
|
|
{
|
|
if (node == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
desiredCount = Math.Clamp(desiredCount, MinConnectorCount, MaxConnectorCount);
|
|
|
|
var existing = node.ConnectionPoints
|
|
.Where(p => p.Type == type)
|
|
.OrderBy(p => p.Index)
|
|
.ToList();
|
|
|
|
if (existing.Count > desiredCount)
|
|
{
|
|
for (int i = existing.Count - 1; i >= desiredCount; i--)
|
|
{
|
|
node.ConnectionPoints.Remove(existing[i]);
|
|
}
|
|
}
|
|
else if (existing.Count < desiredCount)
|
|
{
|
|
for (int i = existing.Count; i < desiredCount; i++)
|
|
{
|
|
node.ConnectionPoints.Add(CreateConnectionPoint(node, type, i));
|
|
}
|
|
}
|
|
|
|
RenumberConnectionPoints(node, type);
|
|
}
|
|
|
|
private ConnectionPoint CreateConnectionPoint(Node node, ConnectionPointType type, int index)
|
|
{
|
|
var existingSameSide = node.ConnectionPoints
|
|
.Where(p => p.Type == type)
|
|
.OrderBy(p => p.Index)
|
|
.LastOrDefault();
|
|
|
|
var defaultDiameter = existingSameSide?.Diameter ?? (type == ConnectionPointType.Input ? node.LeftConnectorSize : node.RightConnectorSize);
|
|
if (defaultDiameter <= 0)
|
|
{
|
|
defaultDiameter = DefaultConnectorSize;
|
|
}
|
|
|
|
var defaultColor = existingSameSide?.Color ?? (type == ConnectionPointType.Input ? node.LeftConnectorColor : node.RightConnectorColor);
|
|
if (string.IsNullOrWhiteSpace(defaultColor))
|
|
{
|
|
defaultColor = type == ConnectionPointType.Input ? DefaultLeftConnectorColor : DefaultRightConnectorColor;
|
|
}
|
|
|
|
var defaultPlacement = existingSameSide?.Placement ?? (type == ConnectionPointType.Input ? node.LeftConnectorPlacement : node.RightConnectorPlacement);
|
|
|
|
return new ConnectionPoint
|
|
{
|
|
Type = type,
|
|
Index = index,
|
|
Label = type == ConnectionPointType.Output ? (index + 1).ToString() : string.Empty,
|
|
Node = node,
|
|
Diameter = defaultDiameter,
|
|
Color = defaultColor,
|
|
Placement = defaultPlacement
|
|
};
|
|
}
|
|
|
|
private void RenumberConnectionPoints(Node node, ConnectionPointType type)
|
|
{
|
|
var points = node.ConnectionPoints
|
|
.Where(p => p.Type == type)
|
|
.OrderBy(p => p.Index)
|
|
.ToList();
|
|
|
|
for (int i = 0; i < points.Count; i++)
|
|
{
|
|
points[i].Index = i;
|
|
if (type == ConnectionPointType.Output)
|
|
{
|
|
points[i].Label = (i + 1).ToString();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <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)
|
|
{
|
|
System.Diagnostics.Debug.WriteLine($"[ViewModel] AddNodeFromTemplate 被调用");
|
|
System.Diagnostics.Debug.WriteLine($"[ViewModel] 模板: {args.template.Name}, 位置: ({args.x}, {args.y})");
|
|
|
|
try
|
|
{
|
|
var node = args.template.CreateNode(args.x, args.y);
|
|
System.Diagnostics.Debug.WriteLine($"[ViewModel] 节点创建成功: {node.Title}, ID: {node.Id}");
|
|
|
|
_nodeCanvasService.AddNode(node);
|
|
System.Diagnostics.Debug.WriteLine($"[ViewModel] 节点已添加到服务,当前节点数量: {Nodes.Count}");
|
|
|
|
SelectedNode = node;
|
|
System.Diagnostics.Debug.WriteLine($"[ViewModel] 节点已设置为选中状态");
|
|
|
|
_logger?.LogDebug("从模板添加节点: {TemplateName} 在位置 ({X}, {Y})",
|
|
args.template.Name, args.x, args.y);
|
|
|
|
System.Diagnostics.Debug.WriteLine($"[ViewModel] AddNodeFromTemplate 完成");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
System.Diagnostics.Debug.WriteLine($"[ViewModel] AddNodeFromTemplate 异常: {ex.Message}");
|
|
System.Diagnostics.Debug.WriteLine($"[ViewModel] 堆栈: {ex.StackTrace}");
|
|
_logger?.LogError(ex, "添加节点时发生错误");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 从模板添加节点到画布中心
|
|
/// </summary>
|
|
private void AddNodeFromTemplateAtCenter(NodeTemplate template)
|
|
{
|
|
// 默认位置:画布中心区域
|
|
double centerX = 400;
|
|
double centerY = 300;
|
|
|
|
var node = template.CreateNode(centerX, centerY);
|
|
_nodeCanvasService.AddNode(node);
|
|
SelectedNode = node;
|
|
_logger?.LogDebug("从模板添加节点到中心: {TemplateName} 在位置 ({X}, {Y})",
|
|
template.Name, centerX, centerY);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 添加节点到画布(简化版本,默认位置)
|
|
/// </summary>
|
|
private void AddNodeToCanvas(NodeTemplate template)
|
|
{
|
|
System.Diagnostics.Debug.WriteLine($"[ViewModel] AddNodeToCanvas 被调用,模板: {template?.Name}");
|
|
|
|
if (template == null)
|
|
{
|
|
System.Diagnostics.Debug.WriteLine($"[ViewModel] 错误: template为null");
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
// 默认位置:画布中心区域
|
|
double centerX = 400;
|
|
double centerY = 300;
|
|
|
|
var node = template.CreateNode(centerX, centerY);
|
|
System.Diagnostics.Debug.WriteLine($"[ViewModel] 节点创建成功: {node.Title}, ID: {node.Id}, 位置: ({centerX}, {centerY})");
|
|
|
|
_nodeCanvasService.AddNode(node);
|
|
System.Diagnostics.Debug.WriteLine($"[ViewModel] 节点已添加到服务,当前节点数量: {Nodes.Count}");
|
|
|
|
SelectedNode = node;
|
|
System.Diagnostics.Debug.WriteLine($"[ViewModel] 节点已设置为选中状态");
|
|
|
|
_logger?.LogDebug("添加节点到画布: {TemplateName} 在位置 ({X}, {Y})",
|
|
template.Name, centerX, centerY);
|
|
|
|
System.Diagnostics.Debug.WriteLine($"[ViewModel] AddNodeToCanvas 完成");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
System.Diagnostics.Debug.WriteLine($"[ViewModel] AddNodeToCanvas 异常: {ex.Message}");
|
|
System.Diagnostics.Debug.WriteLine($"[ViewModel] 堆栈: {ex.StackTrace}");
|
|
_logger?.LogError(ex, "添加节点时发生错误");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 放大
|
|
/// </summary>
|
|
private void ZoomIn()
|
|
{
|
|
var newZoom = Math.Min(_canvasZoom * 1.2, 3.0); // 最大3倍
|
|
CanvasZoom = newZoom;
|
|
_logger?.LogDebug("放大到: {Zoom}", newZoom);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 缩小
|
|
/// </summary>
|
|
private void ZoomOut()
|
|
{
|
|
var newZoom = Math.Max(_canvasZoom / 1.2, 0.1); // 最小0.1倍
|
|
CanvasZoom = newZoom;
|
|
_logger?.LogDebug("缩小到: {Zoom}", newZoom);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 重置缩放
|
|
/// </summary>
|
|
private void ResetZoom()
|
|
{
|
|
CanvasZoom = 1.0;
|
|
_logger?.LogDebug("重置缩放到: 1.0");
|
|
}
|
|
|
|
/// <summary>
|
|
/// 设置缩放
|
|
/// </summary>
|
|
private void SetZoom(double zoom)
|
|
{
|
|
var newZoom = Math.Max(0.1, Math.Min(zoom, 3.0)); // 限制在0.1到3.0之间
|
|
CanvasZoom = newZoom;
|
|
_logger?.LogDebug("设置缩放到: {Zoom}", newZoom);
|
|
}
|
|
}
|
|
|
|
|