1 feat: 添加变量导入模板配置和工厂模式实现

2
    3 -
      在AppSettings中添加VariableImportTemplate配置项,用于设置变量导入的默认参数(IsActive、PollingIn
      terval、IsHistoryEnabled、HistoryDeadband)
    4 - 修改AppSettings.Load()方法,优化配置加载逻辑
    5 -
      实现IVariableItemViewModelFactory和VariableItemViewModelFactory,使用工厂模式创建VariableItemVie
      wModel实例
    6 - 在ImportOpcUaDialogViewModel中使用工厂创建VariableItemViewModel实例,以应用默认配置
    7 -
      在SettingViewModel和SettingView中添加变量导入设置界面和相关属性(VariablePollingInterval、Variab
      leIsActive、VariableIsHistoryEnabled、VariableHistoryDeadband)
    8 - 移除VariableItemViewModel构造函数中的轮询间隔默认值设置,改由工厂模式设置
    9 - 优化SplashViewModel中配置加载逻辑
   10 - 移除MainView.xaml.cs中已注释的代码
   11 - 调整VariableTableView.xaml的UI布局和菜单结构
   12 - 注册IVariableItemViewModelFactory服务

  这些修改主要实现了几个关键功能:
   1. 引入了工厂模式来创建VariableItemViewModel实例,确保所有新创建的变量项都应用默认配置
   2. 添加了变量导入模板设置,用户可以在设置界面自定义导入变量的默认属性
   3. 对相关UI进行了调整和优化
This commit is contained in:
2025-10-04 20:46:39 +08:00
parent ec1f94a898
commit d4562e600e
11 changed files with 249 additions and 133 deletions

View File

@@ -7,6 +7,11 @@ namespace DMS.Application.Configurations
public class AppSettings public class AppSettings
{ {
private static readonly string SettingsFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ConfigJsonFileName);
private const string ConfigJsonFileName = "dms_config.json";
public class Database public class Database
{ {
public string DbType { get; set; } = "MySql"; public string DbType { get; set; } = "MySql";
@@ -19,28 +24,38 @@ namespace DMS.Application.Configurations
public Database Db { get; set; } = new Database(); public Database Db { get; set; } = new Database();
public Variable VariableImportTemplate = new Variable()
{
IsActive = true,
PollingInterval = 30000,
IsHistoryEnabled = true,
HistoryDeadband = 0
};
public string Theme { get; set; } = "跟随系统"; public string Theme { get; set; } = "跟随系统";
public bool EnableS7Service { get; set; } = true;
public bool EnableMqttService { get; set; } = true;
public bool EnableOpcUaService { get; set; } = true;
public bool MinimizeToTrayOnClose { get; set; } = true; public bool MinimizeToTrayOnClose { get; set; } = true;
public List<MenuBean> Menus { get; set; } = new List<MenuBean>();
public int DefaultPollingInterval { get; set; } = 30000; // 默认轮询间隔30秒
private static readonly string SettingsFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ConfigJsonFileName);
private const string ConfigJsonFileName = "dms_config.json"; public void Load()
public AppSettings? Load()
{ {
if (File.Exists(SettingsFilePath)) if (File.Exists(SettingsFilePath))
{ {
string json = File.ReadAllText(SettingsFilePath); string json = File.ReadAllText(SettingsFilePath);
AppSettings? appSettings = JsonConvert.DeserializeObject<AppSettings>(json); AppSettings? appSettings = JsonConvert.DeserializeObject<AppSettings>(json);
return appSettings ?? throw new InvalidOperationException("加载配置文件出现了错误。"); if (appSettings != null)
{
this.Db= appSettings.Db;
this.VariableImportTemplate = appSettings.VariableImportTemplate;
this.Theme=appSettings.Theme;
this.MinimizeToTrayOnClose=appSettings.MinimizeToTrayOnClose;
}
else
{
throw new InvalidOperationException("加载配置文件出现了错误。");
}
} }
return null;
} }
public void Save() public void Save()

View File

@@ -25,6 +25,7 @@ using DMS.Infrastructure.Services.Mqtt;
using DMS.Infrastructure.Services.OpcUa; using DMS.Infrastructure.Services.OpcUa;
using DMS.Infrastructure.Services.S7; using DMS.Infrastructure.Services.S7;
using DMS.WPF.Converters; using DMS.WPF.Converters;
using DMS.WPF.Factories;
using DMS.WPF.Helper; using DMS.WPF.Helper;
using DMS.WPF.Interfaces; using DMS.WPF.Interfaces;
using DMS.WPF.Logging; using DMS.WPF.Logging;
@@ -347,6 +348,9 @@ public partial class App : System.Windows.Application
services.AddTransient<EmailTemplateDialogViewModel>(); services.AddTransient<EmailTemplateDialogViewModel>();
services.AddTransient<TriggerDialogViewModel>(); // 注册 TriggerEditorViewModel services.AddTransient<TriggerDialogViewModel>(); // 注册 TriggerEditorViewModel
// 注册工厂
services.AddTransient<IVariableItemViewModelFactory, VariableItemViewModelFactory>();
// 注册View视图 // 注册View视图
services.AddSingleton<SplashWindow>(); services.AddSingleton<SplashWindow>();
services.AddSingleton<SettingView>(); services.AddSingleton<SettingView>();

View File

@@ -0,0 +1,55 @@
using DMS.Application.DTOs;
using DMS.WPF.ViewModels.Items;
using DMS.Application.Configurations;
using Microsoft.Extensions.DependencyInjection;
namespace DMS.WPF.Factories
{
/// <summary>
/// VariableItemViewModel工厂类
/// 专门用于构建VariableItemViewModel实例
/// </summary>
public interface IVariableItemViewModelFactory
{
/// <summary>
/// 从VariableDto创建VariableItemViewModel实例
/// </summary>
/// <param name="variableDto">变量数据传输对象</param>
/// <returns>VariableItemViewModel实例</returns>
VariableItemViewModel CreateNewVariableItemViewModel();
}
public class VariableItemViewModelFactory : IVariableItemViewModelFactory
{
private readonly IServiceProvider _serviceProvider;
private readonly AppSettings _appSettings;
public VariableItemViewModelFactory(AppSettings appSettings)
{
_appSettings = appSettings;
}
/// <summary>
/// 从VariableDto创建VariableItemViewModel实例
/// </summary>
/// <param name="variableDto">变量数据传输对象</param>
/// <returns>VariableItemViewModel实例</returns>
public VariableItemViewModel CreateNewVariableItemViewModel()
{
var viewModel = new VariableItemViewModel()
{
IsActive = _appSettings.VariableImportTemplate.IsActive,
IsHistoryEnabled = _appSettings.VariableImportTemplate.IsHistoryEnabled,
HistoryDeadband = _appSettings.VariableImportTemplate.HistoryDeadband,
PollingInterval = _appSettings.VariableImportTemplate.PollingInterval,
};
return viewModel;
}
}
}

View File

@@ -6,6 +6,7 @@ using CommunityToolkit.Mvvm.Input;
using DMS.Core.Enums; using DMS.Core.Enums;
using DMS.Infrastructure.Interfaces.Services; using DMS.Infrastructure.Interfaces.Services;
using DMS.Infrastructure.Models; using DMS.Infrastructure.Models;
using DMS.WPF.Factories;
using DMS.WPF.Interfaces; using DMS.WPF.Interfaces;
using DMS.WPF.Services; using DMS.WPF.Services;
using DMS.WPF.ViewModels.Items; using DMS.WPF.ViewModels.Items;
@@ -79,6 +80,7 @@ public partial class ImportOpcUaDialogViewModel : DialogViewModelBase<List<Varia
/// OPC UA服务接口实例 /// OPC UA服务接口实例
/// </summary> /// </summary>
private readonly IOpcUaService _opcUaService; private readonly IOpcUaService _opcUaService;
private readonly IVariableItemViewModelFactory _variableItemViewModelFactory;
/// <summary> /// <summary>
/// 对象映射器实例 /// 对象映射器实例
@@ -102,9 +104,11 @@ public partial class ImportOpcUaDialogViewModel : DialogViewModelBase<List<Varia
/// <param name="opcUaService">OPC UA服务接口实例</param> /// <param name="opcUaService">OPC UA服务接口实例</param>
/// <param name="mapper">对象映射器实例</param> /// <param name="mapper">对象映射器实例</param>
/// <param name="notificationService">通知服务实例</param> /// <param name="notificationService">通知服务实例</param>
public ImportOpcUaDialogViewModel(IOpcUaService opcUaService, IMapper mapper, INotificationService notificationService) public ImportOpcUaDialogViewModel(IOpcUaService opcUaService, IVariableItemViewModelFactory variableItemViewModelFactory,
IMapper mapper, INotificationService notificationService)
{ {
_opcUaService = opcUaService; _opcUaService = opcUaService;
_variableItemViewModelFactory = variableItemViewModelFactory;
_mapper = mapper; _mapper = mapper;
_notificationService = notificationService; _notificationService = notificationService;
// 初始化根节点 // 初始化根节点
@@ -332,15 +336,15 @@ public partial class ImportOpcUaDialogViewModel : DialogViewModelBase<List<Varia
// 判断节点类型是否为变量 // 判断节点类型是否为变量
if (child.NodeClass == NodeClass.Variable) if (child.NodeClass == NodeClass.Variable)
{ {
var variableItem = _variableItemViewModelFactory.CreateNewVariableItemViewModel();
variableItem.Name = child.DisplayName;
variableItem.OpcUaNodeId = child.NodeId.ToString();
variableItem.Protocol = ProtocolType.OpcUa;
variableItem.DataType = child.DataType;
// 创建并添加变量项到变量列表 // 创建并添加变量项到变量列表
OpcUaNodeVariables.Add(new VariableItemViewModel OpcUaNodeVariables.Add(variableItem);
{
Name = child.DisplayName, // 变量名称
OpcUaNodeId = child.NodeId.ToString(), // OPC UA节点ID
Protocol = ProtocolType.OpcUa, // 协议类型
DataType = child.DataType, // C#数据类型
IsActive = true // 默认激活状态
});
} }
// 如果是递归模式且节点不是变量,则递归浏览子节点 // 如果是递归模式且节点不是变量,则递归浏览子节点
else if (isRecursive) else if (isRecursive)

View File

@@ -122,9 +122,6 @@ public partial class VariableItemViewModel : ObservableObject
public VariableItemViewModel() public VariableItemViewModel()
{ {
// 使用默认值,实际的默认值应该从外部传入或通过其他方式设置
// 保持构造函数轻量级,不加载配置文件
_pollingInterval = App.Current.Services.GetRequiredService<AppSettings>().DefaultPollingInterval; // 默认值,可通过外部设置覆盖
} }

View File

@@ -135,20 +135,64 @@ public partial class SettingViewModel : ViewModelBase
} }
} }
public int DefaultPollingInterval public int VariablePollingInterval
{ {
get => _settings.DefaultPollingInterval; get => _settings.VariableImportTemplate.PollingInterval;
set set
{ {
if (_settings.DefaultPollingInterval != value) if (_settings.VariableImportTemplate.PollingInterval != value)
{ {
_settings.DefaultPollingInterval = value; _settings.VariableImportTemplate.PollingInterval = value;
OnPropertyChanged(); OnPropertyChanged();
_settings.Save(); _settings.Save();
} }
} }
} }
public bool VariableIsActive
{
get => _settings.VariableImportTemplate.IsActive;
set
{
if (_settings.VariableImportTemplate.IsActive != value)
{
_settings.VariableImportTemplate.IsActive = value;
OnPropertyChanged();
_settings.Save();
}
}
}
public bool VariableIsHistoryEnabled
{
get => _settings.VariableImportTemplate.IsHistoryEnabled;
set
{
if (_settings.VariableImportTemplate.IsHistoryEnabled != value)
{
_settings.VariableImportTemplate.IsHistoryEnabled = value;
OnPropertyChanged();
_settings.Save();
}
}
}
public double VariableHistoryDeadband
{
get => _settings.VariableImportTemplate.HistoryDeadband;
set
{
if (_settings.VariableImportTemplate.HistoryDeadband != value)
{
_settings.VariableImportTemplate.HistoryDeadband = value;
OnPropertyChanged();
_settings.Save();
}
}
}
[RelayCommand] [RelayCommand]
private async Task TestConnection() private async Task TestConnection()
{ {

View File

@@ -52,10 +52,7 @@ public partial class SplashViewModel : ObservableObject
try try
{ {
LoadingMessage = "正在加载系统配置..."; LoadingMessage = "正在加载系统配置...";
if (_appSettings.Load() == null) _appSettings.Load();
{
//程序第一次启动
}
_logger.LogInformation("正在初始化数据库..."); _logger.LogInformation("正在初始化数据库...");
LoadingMessage = "正在初始化数据库..."; LoadingMessage = "正在初始化数据库...";

View File

@@ -11,7 +11,6 @@ using DMS.Core.Events;
using DMS.Core.Models; using DMS.Core.Models;
using DMS.WPF.Interfaces; using DMS.WPF.Interfaces;
using DMS.WPF.ViewModels.Dialogs; using DMS.WPF.ViewModels.Dialogs;
using DMS.WPF.ViewModels.Dialogs;
using DMS.WPF.ViewModels.Items; using DMS.WPF.ViewModels.Items;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using ObservableCollections; using ObservableCollections;
@@ -329,6 +328,8 @@ partial class VariableTableViewModel : ViewModelBase, INavigatable
return; // 用户取消或没有选择任何变量 return; // 用户取消或没有选择任何变量
} }
// 将导入的变量转换为DTO并设置必要的属性 // 将导入的变量转换为DTO并设置必要的属性
var importedVariableDtos = _mapper.Map<List<VariableDto>>(importedVariables); var importedVariableDtos = _mapper.Map<List<VariableDto>>(importedVariables);
foreach (var variableDto in importedVariableDtos) foreach (var variableDto in importedVariableDtos)

View File

@@ -31,17 +31,6 @@ public partial class MainView : Window
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{ {
// var settings = Config.AppSettings.Load();
// if (settings.MinimizeToTrayOnClose)
// {
// // Hide the window instead of closing it
// e.Cancel = true;
// Hide();
// }
// else
// {
// Application.Current.Shutdown();
// }
} }
public void ShowApplication() public void ShowApplication()

View File

@@ -84,10 +84,25 @@
</ui:SettingsExpander> </ui:SettingsExpander>
<ui:SettingsExpander> <ui:SettingsExpander Description="设置变量默认导入的状态" Header="变量导入设置">
<ui:SettingsExpander.HeaderIcon> <ui:SettingsExpander.HeaderIcon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.ScreenTime}" /> <ui:FontIcon FontFamily="{StaticResource MdFontIcons}" Glyph="&#xF02FA;" />
</ui:SettingsExpander.HeaderIcon> </ui:SettingsExpander.HeaderIcon>
<ui:SettingsExpander.Items>
<ui:SettingsCard Description="默认变量读取的间隔时间" Header="轮询时间">
<TextBox MinWidth="200" Text="{Binding VariablePollingInterval}" />
</ui:SettingsCard>
<ui:SettingsCard Description="默认变量是否启用" Header="是否启用">
<CheckBox MinWidth="200" IsChecked="{Binding VariableIsActive}" />
</ui:SettingsCard>
<ui:SettingsCard Description="默认变量是否开启历史记录" Header="是否保存">
<CheckBox MinWidth="200" IsChecked="{Binding VariableIsHistoryEnabled}" />
</ui:SettingsCard>
<ui:SettingsCard Description="默认变量历史记录变化返回,正负超过设定的范围才保存" Header="保存范围">
<TextBox MinWidth="200" Text="{Binding VariableHistoryDeadband}" />
</ui:SettingsCard>
</ui:SettingsExpander.Items>
</ui:SettingsExpander> </ui:SettingsExpander>

View File

@@ -41,30 +41,31 @@
<DockPanel> <DockPanel>
<ikw:SimpleStackPanel DockPanel.Dock="Top" Margin="10" > <ikw:SimpleStackPanel Margin="10" DockPanel.Dock="Top">
<Grid > <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<!-- 返回设备详情按钮 --> <!-- 返回设备详情按钮 -->
<Button Grid.Column="0" <Button
Grid.Column="0"
Margin="0,0,10,0"
Command="{Binding NavigateToDeviceDetailCommand}" Command="{Binding NavigateToDeviceDetailCommand}"
Style="{StaticResource ButtonDefault}" Style="{StaticResource ButtonDefault}"
ToolTip="返回设备详情" ToolTip="返回设备详情">
Margin="0,0,10,0">
<Button.Content> <Button.Content>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<ui:FontIcon <ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.ArrowLeft8}" />
Icon="{x:Static ui:SegoeFluentIcons.ArrowLeft8}" />
</StackPanel> </StackPanel>
</Button.Content> </Button.Content>
</Button> </Button>
<!-- 操作菜单 --> <!-- 操作菜单 -->
<controls:CommandBar Grid.Column="1" <controls:CommandBar
x:Name="PrimaryCommandBar" x:Name="PrimaryCommandBar"
Grid.Column="1"
DefaultLabelPosition="Right" DefaultLabelPosition="Right"
IsOpen="False"> IsOpen="False">
<ui:AppBarButton Command="{Binding AddVariableCommand}" Label="添加变量"> <ui:AppBarButton Command="{Binding AddVariableCommand}" Label="添加变量">
@@ -206,9 +207,7 @@
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Edit}" /> <ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Edit}" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem <MenuItem Command="{Binding ModifyConversionFormulaCommand}" Header="修改数值转换公式">
Command="{Binding ModifyConversionFormulaCommand}"
Header="修改数值转换公式">
<MenuItem.Icon> <MenuItem.Icon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Edit}" /> <ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Edit}" />
</MenuItem.Icon> </MenuItem.Icon>
@@ -245,17 +244,13 @@
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem <MenuItem Command="{Binding ChangeAlarmSettingsCommand}" Header="修改报警设置">
Command="{Binding ChangeAlarmSettingsCommand}"
Header="修改报警设置">
<MenuItem.Icon> <MenuItem.Icon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Edit}" /> <ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Edit}" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem <MenuItem Command="{Binding ToHistoryCommand}" Header="查看历史记录">
Command="{Binding ToHistoryCommand}"
Header="查看历史记录">
<MenuItem.Icon> <MenuItem.Icon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.History}" /> <ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.History}" />
</MenuItem.Icon> </MenuItem.Icon>