This commit is contained in:
2025-10-20 12:36:33 +08:00
parent 958593b35d
commit 252a99d586
9 changed files with 283 additions and 14 deletions

View File

@@ -0,0 +1,36 @@
using System;
using DMS.Core.Models.Triggers;
namespace DMS.Application.DTOs
{
public class TriggerVariableDto
{
public int Id { get; set; }
public int TriggerDefinitionId { get; set; }
public int VariableId { get; set; }
// 从核心模型转换为DTO
public static implicit operator TriggerVariableDto(TriggerVariable triggerVariable)
{
return new TriggerVariableDto
{
Id = triggerVariable.Id,
TriggerDefinitionId = triggerVariable.TriggerDefinitionId,
VariableId = triggerVariable.VariableId
};
}
// 从DTO转换为核心模型
public static implicit operator TriggerVariable(TriggerVariableDto dto)
{
return new TriggerVariable
{
Id = dto.Id,
TriggerDefinitionId = dto.TriggerDefinitionId,
VariableId = dto.VariableId
};
}
}
}

View File

@@ -48,5 +48,7 @@ public class MappingProfile : Profile
CreateMap<DbTrigger, Trigger>() CreateMap<DbTrigger, Trigger>()
.ForMember(dest => dest.Variables, opt => opt.Ignore()) // 忽略Variables属性映射因为可能需要特殊处理 .ForMember(dest => dest.Variables, opt => opt.Ignore()) // 忽略Variables属性映射因为可能需要特殊处理
.ReverseMap(); .ReverseMap();
CreateMap<DbTriggerVariable, TriggerVariable>().ReverseMap();
} }
} }

View File

@@ -355,6 +355,7 @@ public partial class App : System.Windows.Application
services.AddTransient<EmailAccountDialogViewModel>(); services.AddTransient<EmailAccountDialogViewModel>();
services.AddTransient<EmailTemplateDialogViewModel>(); services.AddTransient<EmailTemplateDialogViewModel>();
services.AddTransient<TriggerDialogViewModel>(); // 注册 TriggerEditorViewModel services.AddTransient<TriggerDialogViewModel>(); // 注册 TriggerEditorViewModel
services.AddTransient<TriggerSelectionDialogViewModel>(); // 注册 TriggerSelectionDialogViewModel
// 注册工厂 // 注册工厂
services.AddTransient<IVariableItemViewModelFactory, VariableItemViewModelFactory>(); services.AddTransient<IVariableItemViewModelFactory, VariableItemViewModelFactory>();

View File

@@ -26,7 +26,8 @@ namespace DMS.WPF.Services
{ typeof(EmailAccountDialogViewModel), typeof(EmailAccountDialog) }, { typeof(EmailAccountDialogViewModel), typeof(EmailAccountDialog) },
{ typeof(EmailTemplateDialogViewModel), typeof(EmailTemplateDialog) }, { typeof(EmailTemplateDialogViewModel), typeof(EmailTemplateDialog) },
{ typeof(TriggerDialogViewModel), typeof(TriggerDialog) }, { typeof(TriggerDialogViewModel), typeof(TriggerDialog) },
{ typeof(InputDialogViewModel), typeof(InputDialog) } { typeof(InputDialogViewModel), typeof(InputDialog) },
{ typeof(TriggerSelectionDialogViewModel), typeof(TriggerSelectionDialog) }
// Add other mappings here // Add other mappings here
// ... other dialogs // ... other dialogs
}; };

View File

@@ -0,0 +1,87 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using DMS.Application.Interfaces;
using DMS.Application.Interfaces.Database;
using DMS.Core.Models.Triggers;
using DMS.WPF.ItemViewModel;
using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
namespace DMS.WPF.ViewModels.Dialogs
{
/// <summary>
/// 触发器选择对话框的视图模型
/// </summary>
public partial class TriggerSelectionDialogViewModel : DialogViewModelBase<TriggerItem>
{
private readonly ITriggerAppService _triggerAppService;
[ObservableProperty]
private ObservableCollection<TriggerItem> _triggers = new();
[ObservableProperty]
private TriggerItem _selectedTrigger;
public TriggerSelectionDialogViewModel(ITriggerAppService triggerAppService)
{
_triggerAppService = triggerAppService;
LoadTriggersAsync();
}
/// <summary>
/// 异步加载所有触发器
/// </summary>
private async void LoadTriggersAsync()
{
try
{
var triggers = await _triggerAppService.GetAllTriggersAsync();
Triggers.Clear();
foreach (var trigger in triggers)
{
Triggers.Add(new TriggerItem
{
Id = trigger.Id,
Name = trigger.Name,
Description = trigger.Description,
IsActive = trigger.IsActive,
Action = trigger.Action,
ActionConfigurationJson = trigger.ActionConfigurationJson,
SuppressionDuration = trigger.SuppressionDuration,
LastTriggeredAt = trigger.LastTriggeredAt,
CreatedAt = trigger.CreatedAt,
UpdatedAt = trigger.UpdatedAt
});
}
}
catch (Exception ex)
{
// 记录错误日志
System.Console.WriteLine($"加载触发器失败: {ex.Message}");
}
}
/// <summary>
/// 确认选择
/// </summary>
[RelayCommand]
private void Confirm()
{
if (SelectedTrigger != null)
{
Close(SelectedTrigger);
}
}
/// <summary>
/// 取消选择
/// </summary>
[RelayCommand]
private void Cancel()
{
Close(null);
}
}
}

View File

@@ -93,11 +93,15 @@ partial class VariableTableViewModel : ViewModelBase, INavigatable
private readonly INotificationService _notificationService; private readonly INotificationService _notificationService;
private readonly ITriggerAppService _triggerAppService;
private readonly ITriggerVariableAppService _triggerVariableAppService;
public VariableTableViewModel(IMapper mapper, IDialogService dialogService, IVariableManagementService variableManagementService, public VariableTableViewModel(IMapper mapper, IDialogService dialogService, IVariableManagementService variableManagementService,
IEventService eventService, IEventService eventService,
IMqttAliasAppService mqttAliasAppService, IMqttAppService mqttAppService, IMqttAliasAppService mqttAliasAppService, IMqttAppService mqttAppService,
IWPFDataService wpfDataService, IDataStorageService dataStorageService, IWPFDataService wpfDataService, IDataStorageService dataStorageService,
INotificationService notificationService) INotificationService notificationService, ITriggerAppService triggerAppService,
ITriggerVariableAppService triggerVariableAppService)
{ {
_mapper = mapper; _mapper = mapper;
_dialogService = dialogService; _dialogService = dialogService;
@@ -108,6 +112,8 @@ partial class VariableTableViewModel : ViewModelBase, INavigatable
_wpfDataService = wpfDataService; _wpfDataService = wpfDataService;
_dataStorageService = dataStorageService; _dataStorageService = dataStorageService;
_notificationService = notificationService; _notificationService = notificationService;
_triggerAppService = triggerAppService;
_triggerVariableAppService = triggerVariableAppService;
IsLoadCompletion = false; // 初始设置为 false表示未完成加载 IsLoadCompletion = false; // 初始设置为 false表示未完成加载
@@ -172,7 +178,7 @@ partial class VariableTableViewModel : ViewModelBase, INavigatable
/// <summary> /// <summary>
/// 编辑选定的变量数据。 /// 编辑选定的变量数据。
/// 此命令通常绑定到UI中的编辑按钮或双击事件。 /// 此命令通常绑定到UI中的"编辑"按钮或双击事件。
/// </summary> /// </summary>
/// <param name="variableTable">当前操作的变量表,用于更新其内部的变量数据。</param> /// <param name="variableTable">当前操作的变量表,用于更新其内部的变量数据。</param>
[RelayCommand] [RelayCommand]
@@ -232,7 +238,7 @@ partial class VariableTableViewModel : ViewModelBase, INavigatable
/// <summary> /// <summary>
/// 从TIA Portal导出的变量表Excel文件中导入变量数据。 /// 从TIA Portal导出的变量表Excel文件中导入变量数据。
/// 此命令通常绑定到UI中的从TIA导入按钮。 /// 此命令通常绑定到UI中的"从TIA导入"按钮。
/// </summary> /// </summary>
[RelayCommand] [RelayCommand]
private async void ImprotFromTiaVarTable() private async void ImprotFromTiaVarTable()
@@ -297,7 +303,7 @@ partial class VariableTableViewModel : ViewModelBase, INavigatable
/// <summary> /// <summary>
/// 从OPC UA服务器导入变量数据。 /// 从OPC UA服务器导入变量数据。
/// 此命令通常绑定到UI中的从OPC UA导入按钮。 /// 此命令通常绑定到UI中的"从OPC UA导入"按钮。
/// </summary> /// </summary>
[RelayCommand] [RelayCommand]
private async void ImportFromOpcUaServer() private async void ImportFromOpcUaServer()
@@ -390,7 +396,7 @@ partial class VariableTableViewModel : ViewModelBase, INavigatable
/// <summary> /// <summary>
/// 添加新的变量数据。 /// 添加新的变量数据。
/// </summary> /// </summary>
/// 此命令通常绑定到UI中的添加按钮。 /// 此命令通常绑定到UI中的"添加"按钮。
/// <param name="variableTable">当前操作的变量表用于设置新变量的所属ID。</param> /// <param name="variableTable">当前操作的变量表用于设置新变量的所属ID。</param>
[RelayCommand] [RelayCommand]
private async void AddVariable() private async void AddVariable()
@@ -438,7 +444,7 @@ partial class VariableTableViewModel : ViewModelBase, INavigatable
/// <summary> /// <summary>
/// 删除选定的变量数据。 /// 删除选定的变量数据。
/// 此命令通常绑定到UI中的删除按钮。 /// 此命令通常绑定到UI中的"删除"按钮。
/// </summary> /// </summary>
/// <param name="variablesToDelete">要删除的变量数据列表。</param> /// <param name="variablesToDelete">要删除的变量数据列表。</param>
[RelayCommand] [RelayCommand]
@@ -495,7 +501,7 @@ partial class VariableTableViewModel : ViewModelBase, INavigatable
/// <summary> /// <summary>
/// 更改选定变量的轮询频率。 /// 更改选定变量的轮询频率。
/// 此命令通常绑定到UI中的修改轮询频率按钮。 /// 此命令通常绑定到UI中的"修改轮询频率"按钮。
/// </summary> /// </summary>
/// <param name="variablesToChange">要修改轮询频率的变量数据列表。</param> /// <param name="variablesToChange">要修改轮询频率的变量数据列表。</param>
[RelayCommand] [RelayCommand]
@@ -586,7 +592,7 @@ partial class VariableTableViewModel : ViewModelBase, INavigatable
/// <summary> /// <summary>
/// 为选定的变量添加MQTT服务器。 /// 为选定的变量添加MQTT服务器。
/// 此命令通常绑定到UI中的添加MQTT服务器按钮。 /// 此命令通常绑定到UI中的"添加MQTT服务器"按钮。
/// </summary> /// </summary>
/// <param name="variablesToAddMqtt">要添加MQTT服务器的变量数据列表。</param> /// <param name="variablesToAddMqtt">要添加MQTT服务器的变量数据列表。</param>
[RelayCommand] [RelayCommand]
@@ -632,9 +638,6 @@ partial class VariableTableViewModel : ViewModelBase, INavigatable
{ {
totalAffectedCount++; totalAffectedCount++;
} }
} }
if (totalAffectedCount > 0) if (totalAffectedCount > 0)
@@ -654,6 +657,69 @@ partial class VariableTableViewModel : ViewModelBase, INavigatable
} }
} }
/// <summary>
/// 为选定的变量添加触发器。
/// 此命令通常绑定到UI中的"添加触发器"按钮。
/// </summary>
/// <param name="variablesToAddTrigger">要添加触发器的变量数据列表。</param>
[RelayCommand]
public async Task AddTriggerToVariables(IList<object> variablesToAddTrigger)
{
var validVariables = variablesToAddTrigger?.OfType<VariableItem>()
.ToList();
// 检查是否有变量被选中
if (validVariables == null || !validVariables.Any())
{
_notificationService.ShowInfo("请选择要添加触发器的变量");
return;
}
try
{
// 显示触发器选择对话框,让用户选择一个触发器
var triggerSelectionViewModel = new TriggerSelectionDialogViewModel(_triggerAppService);
var selectedTrigger = await _dialogService.ShowDialogAsync(triggerSelectionViewModel);
if (selectedTrigger == null)
{
return; // 用户取消选择
}
int totalAffectedCount = 0;
// 为每个选中的变量分配触发器
foreach (var variable in validVariables)
{
var triggerVariable = new DMS.Core.Models.Triggers.TriggerVariable
{
TriggerDefinitionId = selectedTrigger.Id,
VariableId = variable.Id
};
var triggerVariableItem = await _triggerVariableAppService.AssignTriggerVariableAsync(triggerVariable);
if (triggerVariableItem is not null)
{
totalAffectedCount++;
}
}
if (totalAffectedCount > 0)
{
_notificationService.ShowSuccess(
$"已成功为 {totalAffectedCount} 个变量添加触发器: {selectedTrigger.Name}");
}
else
{
_notificationService.ShowInfo($"没有新的变量关联到触发器: {selectedTrigger.Name}。");
}
}
catch (Exception ex)
{
// 捕获并显示错误通知
_notificationService.ShowError($"添加触发器失败: {ex.Message}", ex);
}
}
/// <summary> /// <summary>
/// 修改选定变量的启用状态。 /// 修改选定变量的启用状态。
/// </summary> /// </summary>
@@ -855,7 +921,7 @@ partial class VariableTableViewModel : ViewModelBase, INavigatable
// { // {
// // 显示失败通知 // // 显示失败通知
// NotificationHelper.ShowError($"变量表:{VariableTable.Name},状态修改失败,状态:{active}"); // NotificationHelper.ShowError($"变量表:{VariableTable.Name},状态修改失败,状态:{active}");
// // _logger.LogInformation($"变量表:{VariableTable.Name},状态修改失败,状态:{active}"); // 可以选择记录日志 // // _logger.LogInformation($"变量表:{VariableTable.Name},状态修改失败,状态:{active}") // 可以选择记录日志
// } // }
} }

View File

@@ -0,0 +1,55 @@
<ui:ContentDialog
x:Class="DMS.WPF.Views.Dialogs.TriggerSelectionDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
xmlns:vmd="clr-namespace:DMS.WPF.ViewModels.Dialogs"
d:DataContext="{d:DesignInstance vmd:TriggerSelectionDialogViewModel}"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0"
Margin="0,0,0,16"
FontSize="16"
Text="请选择一个触发器"
TextWrapping="Wrap" />
<ListBox
x:Name="TriggerListBox"
Grid.Row="1"
MaxHeight="320"
ItemsSource="{Binding Triggers}"
SelectedItem="{Binding SelectedTrigger, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="4">
<TextBlock FontWeight="SemiBold" Text="{Binding Name}" />
<TextBlock FontSize="12" Text="{Binding Description}" TextWrapping="Wrap" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel
Grid.Row="2"
Margin="0,16,0,0"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button
Margin="0,0,8,0"
Command="{Binding ConfirmCommand}"
Content="确定" />
<Button
Command="{Binding CancelCommand}"
Content="取消" />
</StackPanel>
</Grid>
</ui:ContentDialog>

View File

@@ -0,0 +1,12 @@
using iNKORE.UI.WPF.Modern.Controls;
namespace DMS.WPF.Views.Dialogs
{
public partial class TriggerSelectionDialog : ContentDialog
{
public TriggerSelectionDialog()
{
InitializeComponent();
}
}
}

View File

@@ -235,6 +235,15 @@
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem
Command="{Binding AddTriggerToVariablesCommand}"
CommandParameter="{Binding PlacementTarget.SelectedItems, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
Header="添加触发器">
<MenuItem.Icon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Add}" />
</MenuItem.Icon>
</MenuItem>
<MenuItem <MenuItem
Command="{Binding ChangeHistorySettingsCommand}" Command="{Binding ChangeHistorySettingsCommand}"
CommandParameter="{Binding PlacementTarget.SelectedItems, RelativeSource={RelativeSource AncestorType=ContextMenu}}" CommandParameter="{Binding PlacementTarget.SelectedItems, RelativeSource={RelativeSource AncestorType=ContextMenu}}"