diff --git a/DMS.Application/Interfaces/INlogAppService.cs b/DMS.Application/Interfaces/INlogAppService.cs
index bad8743..b8ee522 100644
--- a/DMS.Application/Interfaces/INlogAppService.cs
+++ b/DMS.Application/Interfaces/INlogAppService.cs
@@ -23,6 +23,11 @@ public interface INlogAppService
/// 要获取的日志条目数量。
Task> GetLatestLogsAsync(int count);
+ ///
+ /// 异步清空所有Nlog日志。
+ ///
+ Task ClearAllLogsAsync();
+
// 可以在这里添加更多针对日志的查询服务方法,例如:
// Task> GetLogsByLevelAsync(string level);
// Task> GetLogsByDateRangeAsync(DateTime startDate, DateTime endDate);
diff --git a/DMS.Application/Profiles/MappingProfile.cs b/DMS.Application/Profiles/MappingProfile.cs
index f14f236..55ef82f 100644
--- a/DMS.Application/Profiles/MappingProfile.cs
+++ b/DMS.Application/Profiles/MappingProfile.cs
@@ -47,5 +47,6 @@ public class MappingProfile : Profile
// User 映射
CreateMap().ReverseMap();
+ CreateMap().ReverseMap();
}
}
diff --git a/DMS.Application/Services/NlogAppService.cs b/DMS.Application/Services/NlogAppService.cs
index 8f89696..2b5244b 100644
--- a/DMS.Application/Services/NlogAppService.cs
+++ b/DMS.Application/Services/NlogAppService.cs
@@ -65,6 +65,17 @@ public class NlogAppService : INlogAppService
return allLogs.OrderByDescending(l => l.LogTime).Take(count).ToList();
}
+ ///
+ /// 异步清空所有Nlog日志。
+ ///
+ public async Task ClearAllLogsAsync()
+ {
+ // 这里需要实现清空日志的逻辑
+ // 暂时只清空UI上的日志列表
+ // 实际实现可能需要在 INlogRepository 中添加相应的方法
+ await _repoManager.Nlogs.DeleteAllAsync();
+ }
+
// 可以在这里实现 INlogAppService 接口中定义的其他方法
// 例如:
/*
diff --git a/DMS.Core/Interfaces/Repositories/INlogRepository.cs b/DMS.Core/Interfaces/Repositories/INlogRepository.cs
index 567f13d..d1c5328 100644
--- a/DMS.Core/Interfaces/Repositories/INlogRepository.cs
+++ b/DMS.Core/Interfaces/Repositories/INlogRepository.cs
@@ -8,6 +8,11 @@ namespace DMS.Core.Interfaces.Repositories;
///
public interface INlogRepository : IBaseRepository
{
+ ///
+ /// 异步删除所有Nlog日志。
+ ///
+ Task DeleteAllAsync();
+
// 可以在此处添加 Nlog 特定的查询方法,例如:
// Task> GetLogsByLevelAsync(string level);
// Task> GetLogsByDateRangeAsync(DateTime startDate, DateTime endDate);
diff --git a/DMS.Infrastructure/Profiles/MappingProfile.cs b/DMS.Infrastructure/Profiles/MappingProfile.cs
index c983c12..260bfe1 100644
--- a/DMS.Infrastructure/Profiles/MappingProfile.cs
+++ b/DMS.Infrastructure/Profiles/MappingProfile.cs
@@ -34,5 +34,6 @@ public class MappingProfile : Profile
CreateMap().ReverseMap();
+ CreateMap().ReverseMap();
}
}
diff --git a/DMS.Infrastructure/Repositories/InitializeRepository.cs b/DMS.Infrastructure/Repositories/InitializeRepository.cs
index 06d051c..fa88cf3 100644
--- a/DMS.Infrastructure/Repositories/InitializeRepository.cs
+++ b/DMS.Infrastructure/Repositories/InitializeRepository.cs
@@ -134,14 +134,20 @@ public class InitializeRepository : IInitializeRepository
},
new DbMenu
{
- Id = 5, Header = "设置", Icon = "\uE713", ParentId = 0,
- MenuType = MenuType.MainMenu, TargetViewKey = "SettingView",
+ Id = 5, Header = "日志历史", Icon = "\uE7BA", ParentId = 0,
+ MenuType = MenuType.MainMenu, TargetViewKey = "LogHistoryView",
DisplayOrder = 5
},
new DbMenu
{
- Id = 6, Header = "关于", Icon = "\uE946", ParentId = 0,
- MenuType = MenuType.MainMenu, TargetViewKey = "", DisplayOrder = 6
+ Id = 6, Header = "设置", Icon = "\uE713", ParentId = 0,
+ MenuType = MenuType.MainMenu, TargetViewKey = "SettingView",
+ DisplayOrder = 6
+ },
+ new DbMenu
+ {
+ Id = 7, Header = "关于", Icon = "\uE946", ParentId = 0,
+ MenuType = MenuType.MainMenu, TargetViewKey = "", DisplayOrder = 7
} // 假设有一个AboutView
};
diff --git a/DMS.Infrastructure/Repositories/NlogRepository.cs b/DMS.Infrastructure/Repositories/NlogRepository.cs
index e420978..6ff34f2 100644
--- a/DMS.Infrastructure/Repositories/NlogRepository.cs
+++ b/DMS.Infrastructure/Repositories/NlogRepository.cs
@@ -27,6 +27,14 @@ public class NlogRepository : BaseRepository, INlogRepository
_mapper = mapper;
}
+ ///
+ /// 异步删除所有Nlog日志。
+ ///
+ public async Task DeleteAllAsync()
+ {
+ await Db.Deleteable().ExecuteCommandAsync();
+ }
+
// Nlog 通常是只读或追加的日志,因此像 AddAsync, UpdateAsync, DeleteAsync 这样的修改方法
// 可能不需要在仓储接口中暴露,或者可以省略具体实现或抛出 NotSupportedException。
// 但为了保持与基类一致性并满足接口要求,这里显式实现它们。
diff --git a/DMS.WPF/App.xaml.cs b/DMS.WPF/App.xaml.cs
index aaa0560..b3fc0b4 100644
--- a/DMS.WPF/App.xaml.cs
+++ b/DMS.WPF/App.xaml.cs
@@ -221,6 +221,7 @@ public partial class App : System.Windows.Application
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
+ services.AddSingleton();
// 注册MQTT服务管理器
services.AddSingleton();
@@ -248,6 +249,7 @@ public partial class App : System.Windows.Application
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
+ services.AddSingleton();
services.AddTransient(provider =>
new VariableTableViewModel(
provider.GetRequiredService(),
@@ -270,6 +272,14 @@ public partial class App : System.Windows.Application
provider.GetRequiredService()
)
);
+ services.AddSingleton(provider =>
+ new LogHistoryViewModel(
+ provider.GetRequiredService(),
+ provider.GetRequiredService(),
+ provider.GetRequiredService(),
+ provider.GetRequiredService()
+ )
+ );
services.AddScoped();
// 注册对话框视图模型
@@ -292,6 +302,7 @@ public partial class App : System.Windows.Application
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
+ services.AddSingleton();
services.AddScoped();
services.AddScoped();
}
diff --git a/DMS.WPF/Profiles/MappingProfile.cs b/DMS.WPF/Profiles/MappingProfile.cs
index 1c5eeb7..24ab14a 100644
--- a/DMS.WPF/Profiles/MappingProfile.cs
+++ b/DMS.WPF/Profiles/MappingProfile.cs
@@ -29,6 +29,7 @@ namespace DMS.WPF.Profiles
CreateMap().ReverseMap();
CreateMap().ReverseMap();
CreateMap().ReverseMap();
+ CreateMap().ReverseMap();
}
}
}
\ No newline at end of file
diff --git a/DMS.WPF/Services/NavigationService.cs b/DMS.WPF/Services/NavigationService.cs
index fc644f2..950fce3 100644
--- a/DMS.WPF/Services/NavigationService.cs
+++ b/DMS.WPF/Services/NavigationService.cs
@@ -11,13 +11,15 @@ namespace DMS.WPF.Services;
public class NavigationService : INavigationService
{
private readonly IServiceProvider _serviceProvider;
+ private readonly INotificationService _notificationService;
///
/// 构造函数。
///
- public NavigationService(IServiceProvider serviceProvider)
+ public NavigationService(IServiceProvider serviceProvider,INotificationService notificationService)
{
_serviceProvider = serviceProvider;
+ _notificationService = notificationService;
}
///
@@ -34,8 +36,7 @@ public class NavigationService : INavigationService
var viewModel = GetViewModelByKey(menu.TargetViewKey);
if (viewModel == null)
{
- var notificationService = App.Current.Services.GetRequiredService();
- notificationService.ShowError($"切换界面失败,没有找到界面:{menu.TargetViewKey}");
+ _notificationService.ShowError($"切换界面失败,没有找到界面:{menu.TargetViewKey}");
return;
}
@@ -51,26 +52,36 @@ public class NavigationService : INavigationService
private ViewModelBase GetViewModelByKey(string key)
{
- switch (key)
+ try
{
- case "HomeView":
- return App.Current.Services.GetRequiredService();
- case "DevicesView":
- return App.Current.Services.GetRequiredService();
- case "DeviceDetailView":
- return App.Current.Services.GetRequiredService();
- case "DataTransformView":
- return App.Current.Services.GetRequiredService();
- case "VariableTableView":
- return App.Current.Services.GetRequiredService();
- case "MqttsView":
- return App.Current.Services.GetRequiredService();
- case "MqttServerDetailView":
- return App.Current.Services.GetRequiredService();
- case "SettingView":
- return App.Current.Services.GetRequiredService();
- default:
- return null;
+ switch (key)
+ {
+ case "HomeView":
+ return App.Current.Services.GetRequiredService();
+ case "DevicesView":
+ return App.Current.Services.GetRequiredService();
+ case "DeviceDetailView":
+ return App.Current.Services.GetRequiredService();
+ case "DataTransformView":
+ return App.Current.Services.GetRequiredService();
+ case "VariableTableView":
+ return App.Current.Services.GetRequiredService();
+ case "LogHistoryView":
+ return App.Current.Services.GetRequiredService();
+ case "MqttsView":
+ return App.Current.Services.GetRequiredService();
+ case "MqttServerDetailView":
+ return App.Current.Services.GetRequiredService();
+ case "SettingView":
+ return App.Current.Services.GetRequiredService();
+ default:
+ return null;
+ }
+ }
+ catch (Exception e)
+ {
+ _notificationService.ShowError($"切换界面失败,获取:{key}对应的ViewModel时发生了错误:{e.Message}");
+ throw;
}
}
}
\ No newline at end of file
diff --git a/DMS.WPF/ViewModels/Items/NlogItemViewModel.cs b/DMS.WPF/ViewModels/Items/NlogItemViewModel.cs
index a86574e..32d34c4 100644
--- a/DMS.WPF/ViewModels/Items/NlogItemViewModel.cs
+++ b/DMS.WPF/ViewModels/Items/NlogItemViewModel.cs
@@ -1,50 +1,24 @@
-// 文件: DMS.WPF/ViewModels/Items/NlogItemViewModel.cs
-
using CommunityToolkit.Mvvm.ComponentModel;
+using DMS.Core.Models;
namespace DMS.WPF.ViewModels.Items;
-///
-/// 代表日志列表中的单个日志项的ViewModel。
-/// 实现了INotifyPropertyChanged,其任何属性变化都会自动通知UI。
-///
-public partial class NlogItemViewModel : ObservableObject
+public class NlogItemViewModel : ObservableObject
{
- public int Id { get; set; }
+ private Nlog _nlog;
- [ObservableProperty]
- private DateTime _logTime;
+ public NlogItemViewModel(Nlog nlog)
+ {
+ _nlog = nlog;
+ }
- [ObservableProperty]
- private string _level;
-
- [ObservableProperty]
- private int _threadId;
-
- [ObservableProperty]
- private string _threadName;
-
- [ObservableProperty]
- private string _callsite;
-
- [ObservableProperty]
- private int _callsiteLineNumber;
-
- [ObservableProperty]
- private string _message;
-
- [ObservableProperty]
- private string _logger;
-
- [ObservableProperty]
- private string _exception;
-
- [ObservableProperty]
- private string _callerFilePath;
-
- [ObservableProperty]
- private int _callerLineNumber;
-
- [ObservableProperty]
- private string _callerMember;
+ public int Id => _nlog.Id;
+ public string Level => _nlog.Level;
+ public string ThreadName => _nlog.ThreadName;
+ public string Callsite => _nlog.Callsite;
+ public string Message => _nlog.Message;
+ public string Logger => _nlog.Logger;
+ public string Exception => _nlog.Exception;
+ public string StackTrace => _nlog.Exception; // Using Exception as StackTrace since it's not in the Nlog model
+ public System.DateTime TimeStamp => _nlog.LogTime;
}
\ No newline at end of file
diff --git a/DMS.WPF/ViewModels/LogHistoryViewModel.cs b/DMS.WPF/ViewModels/LogHistoryViewModel.cs
new file mode 100644
index 0000000..e91d6e1
--- /dev/null
+++ b/DMS.WPF/ViewModels/LogHistoryViewModel.cs
@@ -0,0 +1,139 @@
+using AutoMapper;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using DMS.Application.DTOs;
+using DMS.Application.Interfaces;
+using DMS.Core.Models;
+using DMS.WPF.Interfaces;
+using DMS.WPF.ViewModels.Items;
+using ObservableCollections;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using DMS.WPF.ViewModels.Dialogs;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace DMS.WPF.ViewModels;
+
+partial class LogHistoryViewModel : ViewModelBase
+{
+ private readonly IMapper _mapper;
+ private readonly INlogAppService _nlogAppService;
+ private readonly IDialogService _dialogService;
+ private readonly INotificationService _notificationService;
+
+ [ObservableProperty]
+ private NlogItemViewModel _selectedLog;
+
+ [ObservableProperty]
+ private IList _selectedLogs = new ArrayList();
+
+ [ObservableProperty]
+ private string _searchText;
+
+ private readonly ObservableList _logItemList;
+ private readonly ISynchronizedView _synchronizedView;
+ public NotifyCollectionChangedSynchronizedViewList LogItemListView { get; }
+
+ public LogHistoryViewModel(IMapper mapper, INlogAppService nlogAppService, IDialogService dialogService, INotificationService notificationService)
+ {
+ _mapper = mapper;
+ _nlogAppService = nlogAppService;
+ _dialogService = dialogService;
+ _notificationService = notificationService;
+
+ _logItemList = new ObservableList();
+ _synchronizedView = _logItemList.CreateView(v => v);
+ LogItemListView = _synchronizedView.ToNotifyCollectionChanged();
+ }
+
+ private bool FilterLogs(NlogItemViewModel item)
+ {
+ var searchTextLower = SearchText.ToLower();
+ return item.Logger?.ToLower().Contains(searchTextLower) == true ||
+ item.Message?.ToLower().Contains(searchTextLower) == true ||
+ item.Exception?.ToLower().Contains(searchTextLower) == true ||
+ item.StackTrace?.ToLower().Contains(searchTextLower) == true;
+ }
+
+ partial void OnSearchTextChanged(string value)
+ {
+ if (string.IsNullOrWhiteSpace(SearchText))
+ {
+ _synchronizedView.ResetFilter();
+ }
+ else
+ {
+ _synchronizedView.AttachFilter(FilterLogs);
+ }
+ }
+
+ public override async void OnLoaded()
+ {
+ await LoadLogsAsync();
+ }
+
+ [RelayCommand]
+ private async Task RefreshLogsAsync()
+ {
+ await LoadLogsAsync();
+ }
+
+ [RelayCommand]
+ private async Task ClearLogsAsync()
+ {
+ var confirmDialogViewModel = new ConfirmDialogViewModel("确认", "确定要清空所有日志吗?", "确定");
+
+ var result = await _dialogService.ShowDialogAsync(confirmDialogViewModel);
+ if (result == true)
+ {
+ try
+ {
+ await _nlogAppService.ClearAllLogsAsync();
+ _logItemList.Clear();
+ _notificationService.ShowInfo("日志已清空");
+ }
+ catch (System.Exception ex)
+ {
+ _notificationService.ShowError($"清空日志时发生错误: {ex.Message}", ex);
+ }
+ }
+ }
+
+ private async Task LoadLogsAsync()
+ {
+ try
+ {
+ var logs = await _nlogAppService.GetAllLogsAsync();
+ var logItems = logs.Select(logDto =>
+ {
+ // Manually map NlogDto to Nlog
+ var nlog = new Nlog
+ {
+ Id = logDto.Id,
+ LogTime = logDto.LogTime,
+ Level = logDto.Level,
+ ThreadId = logDto.ThreadId,
+ ThreadName = logDto.ThreadName,
+ Callsite = logDto.Callsite,
+ CallsiteLineNumber = logDto.CallsiteLineNumber,
+ Message = logDto.Message,
+ Logger = logDto.Logger,
+ Exception = logDto.Exception,
+ CallerFilePath = logDto.CallerFilePath,
+ CallerLineNumber = logDto.CallerLineNumber,
+ CallerMember = logDto.CallerMember
+ };
+ return new NlogItemViewModel(nlog);
+ }).ToList();
+
+ _logItemList.Clear();
+ _logItemList.AddRange(logItems);
+ }
+ catch (System.Exception ex)
+ {
+ _notificationService.ShowError($"加载日志时发生错误: {ex.Message}", ex);
+ }
+ }
+}
\ No newline at end of file
diff --git a/DMS.WPF/Views/LogHistoryView.xaml b/DMS.WPF/Views/LogHistoryView.xaml
new file mode 100644
index 0000000..45cc055
--- /dev/null
+++ b/DMS.WPF/Views/LogHistoryView.xaml
@@ -0,0 +1,156 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DMS.WPF/Views/LogHistoryView.xaml.cs b/DMS.WPF/Views/LogHistoryView.xaml.cs
new file mode 100644
index 0000000..37112f1
--- /dev/null
+++ b/DMS.WPF/Views/LogHistoryView.xaml.cs
@@ -0,0 +1,24 @@
+using System.Windows;
+using System.Windows.Controls;
+using DMS.WPF.ViewModels;
+
+namespace DMS.WPF.Views;
+
+///
+/// LogHistoryView.xaml 的交互逻辑
+///
+public partial class LogHistoryView : UserControl
+{
+ public LogHistoryView()
+ {
+ InitializeComponent();
+ }
+
+ private void LogHistoryView_OnLoaded(object sender, RoutedEventArgs e)
+ {
+ if (DataContext is LogHistoryViewModel viewModel)
+ {
+ viewModel.OnLoaded();
+ }
+ }
+}
\ No newline at end of file
diff --git a/DMS.WPF/Views/MainView.xaml b/DMS.WPF/Views/MainView.xaml
index 326c3fd..d728ea3 100644
--- a/DMS.WPF/Views/MainView.xaml
+++ b/DMS.WPF/Views/MainView.xaml
@@ -104,6 +104,10 @@
+
+
+
+