diff --git a/DMS.Application/Services/Triggers/ITriggerEvaluationService.cs b/DMS.Application/Services/Triggers/ITriggerEvaluationService.cs index 556aeba..447c73d 100644 --- a/DMS.Application/Services/Triggers/ITriggerEvaluationService.cs +++ b/DMS.Application/Services/Triggers/ITriggerEvaluationService.cs @@ -14,6 +14,6 @@ namespace DMS.Application.Services.Triggers /// 变量 ID /// 变量的当前值 /// 任务 - Task EvaluateTriggersAsync(Guid variableId, object currentValue); + Task EvaluateTriggersAsync(int variableId, object currentValue); } } \ No newline at end of file diff --git a/DMS.Application/Services/Triggers/Impl/TriggerActionExecutor.cs b/DMS.Application/Services/Triggers/Impl/TriggerActionExecutor.cs index 4231ad2..b105d4d 100644 --- a/DMS.Application/Services/Triggers/Impl/TriggerActionExecutor.cs +++ b/DMS.Application/Services/Triggers/Impl/TriggerActionExecutor.cs @@ -4,6 +4,8 @@ using System.Text.Json; using System.Threading.Tasks; using DMS.Application.DTOs.Triggers; using DMS.Application.Services.Triggers; +using DMS.Core.Interfaces.Services; +using DMS.Core.Models.Triggers; using Microsoft.Extensions.Logging; // 使用标准日志接口 namespace DMS.Application.Services.Triggers.Impl @@ -108,7 +110,7 @@ namespace DMS.Application.Services.Triggers.Impl .Replace("{UpperBound}", context.Trigger.UpperBound?.ToString() ?? "N/A") .Replace("{Timestamp}", DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss")); - await _emailService.SendEmailAsync(recipients, subject, body); + // await _emailService.SendEmailAsync(recipients, subject, body); } /* diff --git a/DMS.Application/Services/Triggers/Impl/TriggerEvaluationService.cs b/DMS.Application/Services/Triggers/Impl/TriggerEvaluationService.cs index ece17b6..82eacd3 100644 --- a/DMS.Application/Services/Triggers/Impl/TriggerEvaluationService.cs +++ b/DMS.Application/Services/Triggers/Impl/TriggerEvaluationService.cs @@ -8,6 +8,7 @@ using ThreadingTimer = System.Threading.Timer; using TimersTimer = System.Timers.Timer; using DMS.Application.DTOs.Triggers; using DMS.Application.Services.Triggers; +using DMS.Core.Models.Triggers; using Microsoft.Extensions.Logging; // 使用 Microsoft.Extensions.Logging.ILogger namespace DMS.Application.Services.Triggers.Impl @@ -39,61 +40,61 @@ namespace DMS.Application.Services.Triggers.Impl /// /// 评估与指定变量关联的所有激活状态的触发器 /// - public async Task EvaluateTriggersAsync(Guid variableId, object currentValue) + public async Task EvaluateTriggersAsync(int variableId, object currentValue) { - try - { - var triggers = await _triggerManagementService.GetTriggersForVariableAsync(variableId); + // try + // { + // var triggers = await _triggerManagementService.GetTriggersForVariableAsync(variableId); // 注意:这里不再通过 _variableAppService 获取 VariableDto, // 而是在调用 ExecuteActionAsync 时,由上层(DataEventService)提供。 // 如果需要 VariableDto 信息,可以在 ExecuteActionAsync 的 TriggerContext 中携带。 - _logger.LogDebug($"Evaluating {triggers.Count(t => t.IsActive)} active triggers for variable ID: {variableId}"); - - foreach (var trigger in triggers.Where(t => t.IsActive)) - { - if (!IsWithinSuppressionWindow(trigger)) // Check suppression first - { - if (EvaluateCondition(trigger, currentValue)) - { - // 创建一个临时的上下文对象,其中 VariableDto 可以为 null, - // 因为我们目前没有从 _variableAppService 获取它。 - // 在实际应用中,你可能需要通过某种方式获取 VariableDto。 - var context = new TriggerContext(trigger, currentValue, null); - - await _actionExecutor.ExecuteActionAsync(context); - - // Update last triggered time and start suppression timer if needed - trigger.LastTriggeredAt = DateTime.UtcNow; - // For simplicity, we'll assume it's updated periodically or on next load. - // In a production scenario, you'd likely want to persist this back to the database. - - // Start suppression timer if duration is set (in-memory suppression) - if (trigger.SuppressionDuration.HasValue) - { - // 使用 ThreadingTimer 避免歧义 - var timer = new ThreadingTimer(_ => - { - trigger.LastTriggeredAt = null; // Reset suppression flag after delay - _logger.LogInformation($"Suppression lifted for trigger {trigger.Id}"); - // Note: Modifying 'trigger' directly affects the object in the list returned by GetTriggersForVariableAsync(). - // This works for in-memory state but won't persist changes. Consider updating DB explicitly if needed. - }, null, trigger.SuppressionDuration.Value, Timeout.InfiniteTimeSpan); // Single shot timer - - // Replace any existing timer for this trigger ID - _suppressionTimers.AddOrUpdate(trigger.Id, timer, (key, oldTimer) => { - oldTimer?.Dispose(); - return timer; - }); - } - } - } - } - } - catch (Exception ex) - { - _logger.LogError(ex, "An error occurred while evaluating triggers for variable ID: {VariableId}", variableId); - } + // _logger.LogDebug($"Evaluating {triggers.Count(t => t.IsActive)} active triggers for variable ID: {variableId}"); + // + // foreach (var trigger in triggers.Where(t => t.IsActive)) + // { + // if (!IsWithinSuppressionWindow(trigger)) // Check suppression first + // { + // if (EvaluateCondition(trigger, currentValue)) + // { + // // 创建一个临时的上下文对象,其中 VariableDto 可以为 null, + // // 因为我们目前没有从 _variableAppService 获取它。 + // // 在实际应用中,你可能需要通过某种方式获取 VariableDto。 + // var context = new TriggerContext(trigger, currentValue, null); + // + // await _actionExecutor.ExecuteActionAsync(context); + // + // // Update last triggered time and start suppression timer if needed + // trigger.LastTriggeredAt = DateTime.UtcNow; + // // For simplicity, we'll assume it's updated periodically or on next load. + // // In a production scenario, you'd likely want to persist this back to the database. + // + // // Start suppression timer if duration is set (in-memory suppression) + // if (trigger.SuppressionDuration.HasValue) + // { + // // 使用 ThreadingTimer 避免歧义 + // var timer = new ThreadingTimer(_ => + // { + // trigger.LastTriggeredAt = null; // Reset suppression flag after delay + // _logger.LogInformation($"Suppression lifted for trigger {trigger.Id}"); + // // Note: Modifying 'trigger' directly affects the object in the list returned by GetTriggersForVariableAsync(). + // // This works for in-memory state but won't persist changes. Consider updating DB explicitly if needed. + // }, null, trigger.SuppressionDuration.Value, Timeout.InfiniteTimeSpan); // Single shot timer + // + // // Replace any existing timer for this trigger ID + // _suppressionTimers.AddOrUpdate(trigger.Id, timer, (key, oldTimer) => { + // oldTimer?.Dispose(); + // return timer; + // }); + // } + // } + // } + // } + // } + // catch (Exception ex) + // { + // _logger.LogError(ex, "An error occurred while evaluating triggers for variable ID: {VariableId}", variableId); + // } } /// diff --git a/DMS.Infrastructure/Profiles/MappingProfile.cs b/DMS.Infrastructure/Profiles/MappingProfile.cs index 84bbf91..c218956 100644 --- a/DMS.Infrastructure/Profiles/MappingProfile.cs +++ b/DMS.Infrastructure/Profiles/MappingProfile.cs @@ -2,6 +2,7 @@ using System; using AutoMapper; using DMS.Infrastructure.Entities; using DMS.Core.Models; +using DMS.Core.Models.Triggers; namespace DMS.Infrastructure.Profiles; @@ -42,5 +43,17 @@ public class MappingProfile : Profile CreateMap().ReverseMap(); CreateMap().ReverseMap(); CreateMap().ReverseMap(); + + // --- 触发器映射 --- + CreateMap() + .ForMember(dest => dest.SuppressionDuration, + opt => opt.MapFrom(src => src.SuppressionDurationTicks.HasValue ? + TimeSpan.FromTicks(src.SuppressionDurationTicks.Value) : + (TimeSpan?)null)) + .ReverseMap() + .ForMember(dest => dest.SuppressionDurationTicks, + opt => opt.MapFrom(src => src.SuppressionDuration.HasValue ? + src.SuppressionDuration.Value.Ticks : + (long?)null)); } } diff --git a/DMS.Infrastructure/Repositories/BaseRepository.cs b/DMS.Infrastructure/Repositories/BaseRepository.cs index d297c36..16f506f 100644 --- a/DMS.Infrastructure/Repositories/BaseRepository.cs +++ b/DMS.Infrastructure/Repositories/BaseRepository.cs @@ -117,6 +117,23 @@ public abstract class BaseRepository return entity; } + /// + /// 异步根据主键 ID (Guid类型) 获取单个实体。 + /// + /// 实体的主键 ID (Guid类型)。 + /// 返回找到的实体,如果未找到则返回 null。 + public virtual async Task GetByIdAsync(Guid id) + { + var stopwatch = new Stopwatch(); + stopwatch.Start(); + var entity = await Db.Queryable() + .In(id) + .FirstAsync(); + stopwatch.Stop(); + _logger.LogInformation($"GetById {typeof(TEntity).Name}耗时:{stopwatch.ElapsedMilliseconds}ms"); + return entity; + } + /// /// 异步根据主键 ID 列表批量删除实体。 /// diff --git a/DMS.Infrastructure/Repositories/InitializeRepository.cs b/DMS.Infrastructure/Repositories/InitializeRepository.cs index a1d5c65..9730bff 100644 --- a/DMS.Infrastructure/Repositories/InitializeRepository.cs +++ b/DMS.Infrastructure/Repositories/InitializeRepository.cs @@ -51,6 +51,7 @@ public class InitializeRepository : IInitializeRepository _db.CodeFirst.InitTables(); _db.CodeFirst.InitTables(); _db.CodeFirst.InitTables(); + _db.CodeFirst.InitTables(); } /// @@ -139,26 +140,32 @@ public class InitializeRepository : IInitializeRepository }, new DbMenu { - Id = 5, Header = "日志历史", Icon = "\uE7BA", ParentId = 0, - MenuType = MenuType.MainMenu, TargetViewKey = "LogHistoryView", + Id = 5, Header = "触发器", Icon = "\uE7BA", ParentId = 0, + MenuType = MenuType.MainMenu, TargetViewKey = "TriggersView", DisplayOrder = 5 }, new DbMenu { - Id = 6, Header = "邮件管理", Icon = "\uE715", ParentId = 0, - MenuType = MenuType.MainMenu, TargetViewKey = "EmailManagementView", + Id = 6, Header = "日志历史", Icon = "\uE7BA", ParentId = 0, + MenuType = MenuType.MainMenu, TargetViewKey = "LogHistoryView", DisplayOrder = 6 }, new DbMenu { - Id = 7, Header = "设置", Icon = "\uE713", ParentId = 0, - MenuType = MenuType.MainMenu, TargetViewKey = "SettingView", + Id = 7, Header = "邮件管理", Icon = "\uE715", ParentId = 0, + MenuType = MenuType.MainMenu, TargetViewKey = "EmailManagementView", DisplayOrder = 7 }, new DbMenu { - Id = 8, Header = "关于", Icon = "\uE946", ParentId = 0, - MenuType = MenuType.MainMenu, TargetViewKey = "", DisplayOrder = 8 + Id = 8, Header = "设置", Icon = "\uE713", ParentId = 0, + MenuType = MenuType.MainMenu, TargetViewKey = "SettingView", + DisplayOrder = 8 + }, + new DbMenu + { + Id = 9, Header = "关于", Icon = "\uE946", ParentId = 0, + MenuType = MenuType.MainMenu, TargetViewKey = "", DisplayOrder = 9 } // 假设有一个AboutView }; diff --git a/DMS.Infrastructure/Repositories/RepositoryManager.cs b/DMS.Infrastructure/Repositories/RepositoryManager.cs index 79ec674..9f45ff5 100644 --- a/DMS.Infrastructure/Repositories/RepositoryManager.cs +++ b/DMS.Infrastructure/Repositories/RepositoryManager.cs @@ -3,7 +3,6 @@ using DMS.Core.Interfaces; using DMS.Core.Interfaces.Repositories; using DMS.Core.Interfaces.Repositories.Triggers; // 引入新的接口 using DMS.Infrastructure.Data; -using DMS.Infrastructure.Repositories.Triggers; // 引入实现类所在的命名空间 using SqlSugar; namespace DMS.Infrastructure.Repositories; diff --git a/DMS.Infrastructure/Repositories/TriggerRepository.cs b/DMS.Infrastructure/Repositories/TriggerRepository.cs new file mode 100644 index 0000000..c482a89 --- /dev/null +++ b/DMS.Infrastructure/Repositories/TriggerRepository.cs @@ -0,0 +1,97 @@ +using System.Diagnostics; +using AutoMapper; +using DMS.Core.Interfaces.Repositories.Triggers; +using DMS.Core.Models.Triggers; +using DMS.Infrastructure.Data; +using DMS.Infrastructure.Entities; +using Microsoft.Extensions.Logging; + +namespace DMS.Infrastructure.Repositories +{ + /// + /// 触发器仓储实现类,负责触发器数据的持久化操作。 + /// 继承自 并实现 接口。 + /// + public class TriggerRepository : BaseRepository, ITriggerRepository + { + private readonly IMapper _mapper; + + /// + /// 构造函数,注入 AutoMapper 和 SqlSugarDbContext。 + /// + /// AutoMapper 实例,用于实体模型和数据库模型之间的映射。 + /// SqlSugar 数据库上下文,用于数据库操作。 + /// 日志记录器实例。 + public TriggerRepository(IMapper mapper, SqlSugarDbContext dbContext, ILogger logger) + : base(dbContext, logger) + { + _mapper = mapper; + } + + /// + /// 获取所有触发器定义 + /// + public async Task> GetAllAsync() + { + var dbList = await base.GetAllAsync(); + return _mapper.Map>(dbList); + } + + /// + /// 根据 ID 获取触发器定义 + /// + public async Task GetByIdAsync(Guid id) + { + var dbTrigger = await base.GetByIdAsync(id); + return _mapper.Map(dbTrigger); + } + + /// + /// 添加一个新的触发器定义 + /// + public async Task AddAsync(TriggerDefinition trigger) + { + var dbTrigger = await base.AddAsync(_mapper.Map(trigger)); + return _mapper.Map(dbTrigger, trigger); + } + + /// + /// 更新一个已存在的触发器定义 + /// + public async Task UpdateAsync(TriggerDefinition trigger) + { + var rowsAffected = await base.UpdateAsync(_mapper.Map(trigger)); + return rowsAffected > 0 ? trigger : null; + } + + /// + /// 删除一个触发器定义 + /// + public async Task DeleteAsync(Guid id) + { + var stopwatch = new Stopwatch(); + stopwatch.Start(); + var rowsAffected = await Db.Deleteable() + .In(id) + .ExecuteCommandAsync(); + stopwatch.Stop(); + _logger.LogInformation($"Delete {typeof(DbTriggerDefinition).Name},ID={id},耗时:{stopwatch.ElapsedMilliseconds}ms"); + return rowsAffected > 0; + } + + /// + /// 获取与指定变量关联的所有触发器定义 + /// + public async Task> GetByVariableIdAsync(Guid variableId) + { + var stopwatch = new Stopwatch(); + stopwatch.Start(); + var dbList = await Db.Queryable() + .Where(t => t.VariableId == variableId) + .ToListAsync(); + stopwatch.Stop(); + _logger.LogInformation($"GetByVariableId {typeof(DbTriggerDefinition).Name},VariableId={variableId},耗时:{stopwatch.ElapsedMilliseconds}ms"); + return _mapper.Map>(dbList); + } + } +} \ No newline at end of file diff --git a/DMS.Infrastructure/Repositories/Triggers/Impl/SqlSugarTriggerRepository.cs b/DMS.Infrastructure/Repositories/Triggers/Impl/SqlSugarTriggerRepository.cs deleted file mode 100644 index fc30272..0000000 --- a/DMS.Infrastructure/Repositories/Triggers/Impl/SqlSugarTriggerRepository.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using DMS.Core.Models.Triggers; -using SqlSugar; - -namespace DMS.Infrastructure.Repositories.Triggers.Impl -{ - /// - /// 基于 SqlSugar 的触发器仓储实现 - /// - public class SqlSugarTriggerRepository : ITriggerRepository - { - private readonly ISqlSugarClient _db; - - public SqlSugarTriggerRepository(ISqlSugarClient db) - { - _db = db ?? throw new ArgumentNullException(nameof(db)); - } - - /// - /// 获取所有触发器定义 - /// - public async Task> GetAllAsync() - { - return await _db.Queryable().ToListAsync(); - } - - /// - /// 根据 ID 获取触发器定义 - /// - public async Task GetByIdAsync(Guid id) - { - return await _db.Queryable().InSingleAsync(id); - } - - /// - /// 添加一个新的触发器定义 - /// - public async Task AddAsync(TriggerDefinition trigger) - { - var insertedId = await _db.Insertable(trigger).ExecuteReturnSnowflakeIdAsync(); - trigger.Id = insertedId; - return trigger; - } - - /// - /// 更新一个已存在的触发器定义 - /// - public async Task UpdateAsync(TriggerDefinition trigger) - { - var rowsAffected = await _db.Updateable(trigger).ExecuteCommandAsync(); - return rowsAffected > 0 ? trigger : null; - } - - /// - /// 删除一个触发器定义 - /// - public async Task DeleteAsync(Guid id) - { - var rowsAffected = await _db.Deleteable().In(id).ExecuteCommandAsync(); - return rowsAffected > 0; - } - - /// - /// 获取与指定变量关联的所有触发器定义 - /// - public async Task> GetByVariableIdAsync(Guid variableId) - { - return await _db.Queryable() - .Where(t => t.VariableId == variableId) - .ToListAsync(); - } - } -} \ No newline at end of file diff --git a/DMS.WPF/App.xaml b/DMS.WPF/App.xaml index b66dcd1..da96fa7 100644 --- a/DMS.WPF/App.xaml +++ b/DMS.WPF/App.xaml @@ -5,7 +5,7 @@ xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:triggers="clr-namespace:DMS.Core.Models.Triggers;assembly=DMS.Core" xmlns:converters="clr-namespace:DMS.WPF.Converters" - xmlns:localConverters="clr-namespace:DMS.WPF.Converters" + xmlns:localConverters="clr-namespace:DMS.WPF.Converters" xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"> diff --git a/DMS.WPF/App.xaml.cs b/DMS.WPF/App.xaml.cs index cf21434..0dbae03 100644 --- a/DMS.WPF/App.xaml.cs +++ b/DMS.WPF/App.xaml.cs @@ -4,8 +4,11 @@ using AutoMapper.Internal; using DMS.Application.Interfaces; using DMS.Application.Services; using DMS.Application.Services.Processors; +using DMS.Application.Services.Triggers; +using DMS.Application.Services.Triggers.Impl; using DMS.Core.Interfaces; using DMS.Core.Interfaces.Repositories; +using DMS.Core.Interfaces.Repositories.Triggers; using DMS.Core.Interfaces.Services; using DMS.Infrastructure.Configuration; using DMS.Infrastructure.Configurations; @@ -14,6 +17,7 @@ using DMS.Infrastructure.Interfaces; using DMS.Infrastructure.Interfaces.Services; using DMS.Infrastructure.Repositories; using DMS.Infrastructure.Services; +using DMS.WPF.Converters; using DMS.WPF.Helper; using DMS.WPF.Interfaces; using DMS.WPF.Logging; @@ -21,12 +25,14 @@ using DMS.WPF.Services; using DMS.WPF.ViewModels; using DMS.WPF.ViewModels.Dialogs; using DMS.WPF.ViewModels.Items; +using DMS.WPF.ViewModels.Triggers; using DMS.WPF.Views; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using NLog; using NLog.Web; +using SqlSugar; using ILogger = Microsoft.Extensions.Logging.ILogger; using LogLevel = Microsoft.Extensions.Logging.LogLevel; @@ -204,6 +210,8 @@ public partial class App : System.Windows.Application return new SqlSugarDbContext(appSettings); }); + + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); @@ -216,7 +224,7 @@ public partial class App : System.Windows.Application services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); // 添加这行 - services.AddSingleton(); // 注册触发器仓储 + services.AddSingleton(); // 注册触发器仓储 services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); @@ -321,7 +329,7 @@ public partial class App : System.Windows.Application services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient(); // 注册 TriggerEditorViewModel + services.AddTransient(); // 注册 TriggerEditorViewModel // 注册View视图 services.AddSingleton(); @@ -335,6 +343,7 @@ public partial class App : System.Windows.Application services.AddScoped(); services.AddScoped(); services.AddSingleton(); + services.AddSingleton(); // 注册 TriggersView } diff --git a/DMS.WPF/DMS.WPF.csproj b/DMS.WPF/DMS.WPF.csproj index 819ab41..8f78734 100644 --- a/DMS.WPF/DMS.WPF.csproj +++ b/DMS.WPF/DMS.WPF.csproj @@ -144,6 +144,16 @@ Wpf Designer + + MSBuild:Compile + Wpf + Designer + + + MSBuild:Compile + Wpf + Designer + @@ -166,6 +176,7 @@ + diff --git a/DMS.WPF/Services/DialogService.cs b/DMS.WPF/Services/DialogService.cs index f425004..5883643 100644 --- a/DMS.WPF/Services/DialogService.cs +++ b/DMS.WPF/Services/DialogService.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using System.Windows; using DMS.WPF.Interfaces; +using DMS.WPF.ViewModels.Triggers; using DMS.WPF.Views; using iNKORE.UI.WPF.Modern.Controls; @@ -29,7 +30,8 @@ namespace DMS.WPF.Services { typeof(HistorySettingsDialogViewModel), typeof(HistorySettingsDialog) }, { typeof(AlarmSettingsDialogViewModel), typeof(AlarmSettingsDialog) }, { typeof(EmailAccountDialogViewModel), typeof(EmailAccountDialog) }, - { typeof(EmailTemplateDialogViewModel), typeof(EmailTemplateDialog) } + { typeof(EmailTemplateDialogViewModel), typeof(EmailTemplateDialog) }, + { typeof(TriggerDialogViewModel), typeof(TriggerDialog) } // Add other mappings here // ... other dialogs }; diff --git a/DMS.WPF/Services/NavigationService.cs b/DMS.WPF/Services/NavigationService.cs index ef8ea54..52eca4e 100644 --- a/DMS.WPF/Services/NavigationService.cs +++ b/DMS.WPF/Services/NavigationService.cs @@ -1,6 +1,7 @@ using DMS.WPF.Interfaces; using DMS.WPF.ViewModels; using DMS.WPF.ViewModels.Items; +using DMS.WPF.ViewModels.Triggers; using Microsoft.Extensions.DependencyInjection; namespace DMS.WPF.Services; @@ -96,6 +97,8 @@ public class NavigationService : INavigationService return App.Current.Services.GetRequiredService(); case "EmailManagementView": return App.Current.Services.GetRequiredService(); + case "TriggersView": + return App.Current.Services.GetRequiredService(); default: return null; } diff --git a/DMS.WPF/ViewModels/Triggers/TriggerEditorViewModel.cs b/DMS.WPF/ViewModels/Dialogs/TriggerDialogViewModel.cs similarity index 81% rename from DMS.WPF/ViewModels/Triggers/TriggerEditorViewModel.cs rename to DMS.WPF/ViewModels/Dialogs/TriggerDialogViewModel.cs index a1b5e73..287ace7 100644 --- a/DMS.WPF/ViewModels/Triggers/TriggerEditorViewModel.cs +++ b/DMS.WPF/ViewModels/Dialogs/TriggerDialogViewModel.cs @@ -1,21 +1,19 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; using System.Text.Json; -using System.Threading.Tasks; +using System.Windows; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; +using DMS.Application.DTOs; using DMS.Application.DTOs.Triggers; +using DMS.Application.Interfaces; using DMS.Core.Models.Triggers; using DMS.WPF.Interfaces; -using DMS.WPF.Services; -namespace DMS.WPF.ViewModels.Triggers +namespace DMS.WPF.ViewModels.Dialogs { /// /// 触发器编辑器视图模型 /// - public partial class TriggerEditorViewModel : DialogViewModelBase + public partial class TriggerDialogViewModel : DialogViewModelBase { private readonly IVariableAppService _variableAppService; // To populate variable selection dropdown private readonly IDialogService _dialogService; @@ -29,18 +27,15 @@ namespace DMS.WPF.ViewModels.Triggers // Properties for easier binding in XAML for SendEmail action config [ObservableProperty] - [Required(ErrorMessage = "收件人不能为空")] private string _emailRecipients = ""; [ObservableProperty] - [Required(ErrorMessage = "邮件主题模板不能为空")] private string _emailSubjectTemplate = ""; [ObservableProperty] - [Required(ErrorMessage = "邮件内容模板不能为空")] private string _emailBodyTemplate = ""; - public TriggerEditorViewModel( + public TriggerDialogViewModel( IVariableAppService variableAppService, IDialogService dialogService, INotificationService notificationService) @@ -54,7 +49,7 @@ namespace DMS.WPF.ViewModels.Triggers /// 初始化视图模型(传入待编辑的触发器) /// /// 待编辑的触发器 DTO - public override async Task OnInitializedAsync(object? parameter) + public async Task OnInitializedAsync(object? parameter) { if (parameter is TriggerDefinitionDto triggerDto) { @@ -84,7 +79,7 @@ namespace DMS.WPF.ViewModels.Triggers } catch (Exception ex) { - _notificationService.ShowWarning($"无法解析邮件配置: {ex.Message}"); + _notificationService.ShowWarn($"无法解析邮件配置: {ex.Message}"); } } } @@ -97,7 +92,7 @@ namespace DMS.WPF.ViewModels.Triggers { try { - var variables = await _variableAppService.GetAllAsync(); + var variables = await _variableAppService.GetAllVariablesAsync(); AvailableVariables = variables ?? new List(); } catch (Exception ex) @@ -116,13 +111,13 @@ namespace DMS.WPF.ViewModels.Triggers // Basic validation if (Trigger.VariableId == Guid.Empty) { - _notificationService.ShowWarning("请选择关联的变量"); + _notificationService.ShowWarn("请选择关联的变量"); return; } if (string.IsNullOrWhiteSpace(Trigger.Description)) { - _notificationService.ShowWarning("请输入触发器描述"); + _notificationService.ShowWarn("请输入触发器描述"); return; } @@ -135,7 +130,7 @@ namespace DMS.WPF.ViewModels.Triggers case ConditionType.NotEqualTo: if (!Trigger.Threshold.HasValue) { - _notificationService.ShowWarning($"{Trigger.Condition} 条件需要设置阈值"); + _notificationService.ShowWarn($"{Trigger.Condition} 条件需要设置阈值"); return; } break; @@ -143,12 +138,12 @@ namespace DMS.WPF.ViewModels.Triggers case ConditionType.OutOfRange: if (!Trigger.LowerBound.HasValue || !Trigger.UpperBound.HasValue) { - _notificationService.ShowWarning($"{Trigger.Condition} 条件需要设置下限和上限"); + _notificationService.ShowWarn($"{Trigger.Condition} 条件需要设置下限和上限"); return; } if (Trigger.LowerBound > Trigger.UpperBound) { - _notificationService.ShowWarning("下限必须小于或等于上限"); + _notificationService.ShowWarn("下限必须小于或等于上限"); return; } break; @@ -159,19 +154,19 @@ namespace DMS.WPF.ViewModels.Triggers { if (string.IsNullOrWhiteSpace(EmailRecipients)) { - _notificationService.ShowWarning("请输入至少一个收件人邮箱地址"); + _notificationService.ShowWarn("请输入至少一个收件人邮箱地址"); return; } if (string.IsNullOrWhiteSpace(EmailSubjectTemplate)) { - _notificationService.ShowWarning("请输入邮件主题模板"); + _notificationService.ShowWarn("请输入邮件主题模板"); return; } if (string.IsNullOrWhiteSpace(EmailBodyTemplate)) { - _notificationService.ShowWarning("请输入邮件内容模板"); + _notificationService.ShowWarn("请输入邮件内容模板"); return; } @@ -199,7 +194,7 @@ namespace DMS.WPF.ViewModels.Triggers } // Close dialog with the updated trigger DTO - await CloseDialogAsync(Trigger); + await Close(Trigger); } /// @@ -208,7 +203,7 @@ namespace DMS.WPF.ViewModels.Triggers [RelayCommand] private async Task CancelAsync() { - await CloseDialogAsync(null); // Return null to indicate cancellation + await Close(null); // Return null to indicate cancellation } } } \ No newline at end of file diff --git a/DMS.WPF/ViewModels/Triggers/TriggersViewModel.cs b/DMS.WPF/ViewModels/TriggersViewModel.cs similarity index 87% rename from DMS.WPF/ViewModels/Triggers/TriggersViewModel.cs rename to DMS.WPF/ViewModels/TriggersViewModel.cs index d48187b..62e1691 100644 --- a/DMS.WPF/ViewModels/Triggers/TriggersViewModel.cs +++ b/DMS.WPF/ViewModels/TriggersViewModel.cs @@ -8,6 +8,8 @@ using DMS.Application.DTOs.Triggers; using DMS.Application.Services.Triggers; using DMS.WPF.Interfaces; using DMS.WPF.Services; +using DMS.WPF.ViewModels.Dialogs; +using Microsoft.Extensions.DependencyInjection; namespace DMS.WPF.ViewModels.Triggers { @@ -70,7 +72,10 @@ namespace DMS.WPF.ViewModels.Triggers UpdatedAt = DateTime.UtcNow }; - var result = await _dialogService.ShowDialogAsync("编辑触发器", newTrigger); + TriggerDialogViewModel viewModel = App.Current.Services.GetRequiredService(); + await viewModel.OnInitializedAsync(newTrigger); + + var result = await _dialogService.ShowDialogAsync(viewModel); if (result != null) { try @@ -95,7 +100,7 @@ namespace DMS.WPF.ViewModels.Triggers { if (SelectedTrigger == null) { - _notificationService.ShowWarning("请先选择一个触发器"); + _notificationService.ShowWarn("请先选择一个触发器"); return; } @@ -117,8 +122,10 @@ namespace DMS.WPF.ViewModels.Triggers CreatedAt = SelectedTrigger.CreatedAt, UpdatedAt = SelectedTrigger.UpdatedAt }; + TriggerDialogViewModel viewModel = App.Current.Services.GetRequiredService(); + await viewModel.OnInitializedAsync(triggerToEdit); - var result = await _dialogService.ShowDialogAsync("编辑触发器", triggerToEdit); + var result = await _dialogService.ShowDialogAsync(viewModel); if (result != null) { try @@ -154,11 +161,11 @@ namespace DMS.WPF.ViewModels.Triggers { if (SelectedTrigger == null) { - _notificationService.ShowWarning("请先选择一个触发器"); + _notificationService.ShowWarn("请先选择一个触发器"); return; } - var confirm = await _dialogService.ShowConfirmDialogAsync("确认删除", $"确定要删除触发器 '{SelectedTrigger.Description}' 吗?"); + var confirm = await _dialogService.ShowDialogAsync(new ConfirmDialogViewModel("确认删除", $"确定要删除触发器 '{SelectedTrigger.Description}' 吗?","删除")); if (confirm) { try diff --git a/DMS.WPF/Views/Dialogs/TriggerDialog.xaml b/DMS.WPF/Views/Dialogs/TriggerDialog.xaml new file mode 100644 index 0000000..67d6376 --- /dev/null +++ b/DMS.WPF/Views/Dialogs/TriggerDialog.xaml @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DMS.WPF/Views/Dialogs/TriggerDialog.xaml.cs b/DMS.WPF/Views/Dialogs/TriggerDialog.xaml.cs new file mode 100644 index 0000000..ab3ddb8 --- /dev/null +++ b/DMS.WPF/Views/Dialogs/TriggerDialog.xaml.cs @@ -0,0 +1,32 @@ +using System.Windows.Controls; +using DMS.WPF.Helper; +using iNKORE.UI.WPF.Modern.Controls; + +namespace DMS.WPF.Views.Dialogs +{ + /// + /// TriggerDialog.xaml 的交互逻辑 + /// + public partial class TriggerDialog : ContentDialog + { + private const int ContentAreaMaxWidth = 1000; + private const int ContentAreaMaxHeight = 800; + + public TriggerDialog() + { + InitializeComponent(); + this.Opened += OnOpened; + } + + private void OnOpened(ContentDialog sender, ContentDialogOpenedEventArgs args) + { + // 修改对话框内容的最大宽度和最大高度 + var backgroundElementBorder = VisualTreeFinder.FindVisualChildByName(this, "BackgroundElement"); + if (backgroundElementBorder != null) + { + backgroundElementBorder.MaxWidth = ContentAreaMaxWidth; + backgroundElementBorder.MaxHeight = ContentAreaMaxHeight; + } + } + } +} \ No newline at end of file diff --git a/DMS.WPF/Views/MainView.xaml b/DMS.WPF/Views/MainView.xaml index 2b3574b..9fd77cb 100644 --- a/DMS.WPF/Views/MainView.xaml +++ b/DMS.WPF/Views/MainView.xaml @@ -12,6 +12,7 @@ xmlns:tb="http://hardcodet.net/taskbar" xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" xmlns:vm="clr-namespace:DMS.WPF.ViewModels" + xmlns:vmt="clr-namespace:DMS.WPF.ViewModels.Triggers" Title="设备管理系统" Width="1080" Height="800" @@ -112,6 +113,10 @@ + + + + diff --git a/DMS.WPF/Views/Triggers/TriggerEditorView.xaml b/DMS.WPF/Views/Triggers/TriggerEditorView.xaml deleted file mode 100644 index 6f364c7..0000000 --- a/DMS.WPF/Views/Triggers/TriggerEditorView.xaml +++ /dev/null @@ -1,183 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -