A:调整页面展示和引入方式
This commit is contained in:
1 parent
4ec76b7855
commit
dd0681ba90
10 files changed
+786
-517
No files matched your search
@@ -1,9 +1,13 @@
|
|||||||
<Application x:Class="Server.App"
|
<Application x:Class="Server.App"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:local="clr-namespace:Server"
|
xmlns:local="clr-namespace:Server">
|
||||||
StartupUri="MainWindow.xaml">
|
|
||||||
<Application.Resources>
|
<Application.Resources>
|
||||||
|
<ResourceDictionary>
|
||||||
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml" />
|
||||||
|
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml" />
|
||||||
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
</ResourceDictionary>
|
||||||
</Application.Resources>
|
</Application.Resources>
|
||||||
</Application>
|
</Application>
|
||||||
+120
-83
@@ -1,12 +1,19 @@
|
|||||||
using System.Windows;
|
using System;
|
||||||
|
using System.Windows;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
using Config;
|
using Config;
|
||||||
using Sharp.Log;
|
using Sharp.Log.Extensions;
|
||||||
using Sharp.Data;
|
using Sharp.Data;
|
||||||
using Sharp.Data.Context;
|
using Sharp.Data.Context;
|
||||||
|
using Sharp.Wpf.Services;
|
||||||
|
using Sharp.Wpf.Abstractions;
|
||||||
using Server.Config;
|
using Server.Config;
|
||||||
using Server.Services;
|
using Server.Services;
|
||||||
using Server.Data;
|
using Server.Data;
|
||||||
|
using Server.ViewModels;
|
||||||
|
using Server.Views;
|
||||||
|
|
||||||
namespace Server
|
namespace Server
|
||||||
{
|
{
|
||||||
@@ -15,32 +22,124 @@ namespace Server
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class App : Application
|
public partial class App : Application
|
||||||
{
|
{
|
||||||
public ConfigManager? ConfigManager { get; private set; }
|
private ServiceProvider? _serviceProvider;
|
||||||
public AppConfig? AppConfig { get; private set; }
|
|
||||||
public ILoggerFactory? LoggerFactory { get; private set; }
|
|
||||||
public ServerService? ServerService { get; private set; }
|
|
||||||
public IDataContext? DataContext { get; private set; }
|
|
||||||
|
|
||||||
protected override void OnStartup(StartupEventArgs e)
|
protected override void OnStartup(StartupEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnStartup(e);
|
base.OnStartup(e);
|
||||||
|
|
||||||
|
// 1. Global Exception Handling
|
||||||
|
DispatcherUnhandledException += (s, args) =>
|
||||||
|
{
|
||||||
|
// Safe logging: handle case where DI failed
|
||||||
|
if (_serviceProvider != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var logger = _serviceProvider.GetService<ILogger<App>>();
|
||||||
|
logger?.LogError(args.Exception, "UI线程未处理异常");
|
||||||
|
}
|
||||||
|
catch { /* Ignore logging failure during crash */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageBox.Show($"发生未处理异常: {args.Exception.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
args.Handled = true; // Prevent crash
|
||||||
|
};
|
||||||
|
|
||||||
|
TaskScheduler.UnobservedTaskException += (s, args) =>
|
||||||
|
{
|
||||||
|
if (_serviceProvider != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var logger = _serviceProvider.GetService<ILogger<App>>();
|
||||||
|
logger?.LogError(args.Exception, "任务未观察到的异常");
|
||||||
|
}
|
||||||
|
catch { /* Ignore */ }
|
||||||
|
}
|
||||||
|
args.SetObserved();
|
||||||
|
};
|
||||||
|
|
||||||
|
AppDomain.CurrentDomain.UnhandledException += (s, args) =>
|
||||||
|
{
|
||||||
|
var ex = args.ExceptionObject as Exception;
|
||||||
|
if (_serviceProvider != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var logger = _serviceProvider.GetService<ILogger<App>>();
|
||||||
|
logger?.LogCritical(ex, "AppDomain未处理异常 (即将终止: {IsTerminating})", args.IsTerminating);
|
||||||
|
}
|
||||||
|
catch { /* Ignore */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.IsTerminating)
|
||||||
|
{
|
||||||
|
MessageBox.Show($"发生严重错误,程序即将退出: {ex?.Message}", "严重错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 1. 初始化配置
|
// 2. 构建配置
|
||||||
InitializeConfiguration();
|
var cm = ConfigDefaults.BuildDefault(defaults =>
|
||||||
|
{
|
||||||
|
defaults.MainFile = "appsettings.json";
|
||||||
|
defaults.IncludeEnvironmentVariables = true;
|
||||||
|
}, configure =>
|
||||||
|
{
|
||||||
|
configure.ReloadOnChange = true;
|
||||||
|
});
|
||||||
|
|
||||||
// 2. 初始化日志系统
|
// 3. 构建依赖注入容器
|
||||||
InitializeLogging();
|
var services = new ServiceCollection();
|
||||||
|
|
||||||
// 3. 初始化数据库
|
// 配置管理
|
||||||
InitializeDatabase();
|
services.AddSingleton(cm);
|
||||||
|
services.AddSingleton<IConfiguration>(cm.Configuration);
|
||||||
|
var appConfig = cm.Get<AppConfig>(validate: true);
|
||||||
|
services.AddSingleton(appConfig);
|
||||||
|
services.AddSingleton(appConfig.Server);
|
||||||
|
|
||||||
// 4. 初始化服务器服务
|
// 日志
|
||||||
InitializeServerService();
|
services.AddLogging(b => b.AddSharpLog());
|
||||||
|
|
||||||
var logger = LoggerFactory?.CreateLogger<App>();
|
// 数据库
|
||||||
logger?.LogInformation("应用程序启动成功");
|
services.AddSingleton<IDataContext>(sp =>
|
||||||
|
{
|
||||||
|
var builder = new DataContextBuilder<AppDbContext>();
|
||||||
|
builder.UseSqlite("Data Source=server.db")
|
||||||
|
.UseAudit(true)
|
||||||
|
.UseSoftDelete(true)
|
||||||
|
.UseCurrentUser("system")
|
||||||
|
.EnableDetailedErrors(true);
|
||||||
|
return builder.Build();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 服务器服务
|
||||||
|
services.AddSingleton<ServerService>();
|
||||||
|
|
||||||
|
// WPF服务
|
||||||
|
services.AddSingleton<IDialogService, DialogService>();
|
||||||
|
services.AddSingleton<IDispatcher>(new DispatcherService(Dispatcher));
|
||||||
|
|
||||||
|
// ViewModels
|
||||||
|
services.AddTransient<MainViewModel>();
|
||||||
|
services.AddTransient<UserManagementViewModel>();
|
||||||
|
|
||||||
|
// Windows
|
||||||
|
services.AddTransient<MainWindow>();
|
||||||
|
services.AddTransient<UserManagementWindow>();
|
||||||
|
|
||||||
|
_serviceProvider = services.BuildServiceProvider();
|
||||||
|
|
||||||
|
// 4. 显示主窗口
|
||||||
|
var mainWindow = _serviceProvider.GetRequiredService<MainWindow>();
|
||||||
|
Current.MainWindow = mainWindow;
|
||||||
|
mainWindow.Show();
|
||||||
|
|
||||||
|
var logger = _serviceProvider.GetRequiredService<ILogger<App>>();
|
||||||
|
logger.LogInformation("应用程序启动成功");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -54,75 +153,14 @@ namespace Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeConfiguration()
|
|
||||||
{
|
|
||||||
ConfigManager = ConfigDefaults.BuildDefault();
|
|
||||||
AppConfig = ConfigManager.Get<AppConfig>(validate: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitializeLogging()
|
|
||||||
{
|
|
||||||
if (ConfigManager == null)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("配置未初始化");
|
|
||||||
}
|
|
||||||
|
|
||||||
LoggerFactory = SharpLogDefaults.CreateLoggerFactory(
|
|
||||||
configuration: ConfigManager.Configuration,
|
|
||||||
configure: options =>
|
|
||||||
{
|
|
||||||
options.Enrich.Application ??= "SharpServer";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitializeDatabase()
|
|
||||||
{
|
|
||||||
if (LoggerFactory == null)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("日志系统未初始化");
|
|
||||||
}
|
|
||||||
|
|
||||||
var logger = LoggerFactory.CreateLogger<App>();
|
|
||||||
logger.LogInformation("正在初始化数据库...");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var builder = new DataContextBuilder<AppDbContext>();
|
|
||||||
builder.UseSqlite("Data Source=server.db")
|
|
||||||
.UseAudit(true)
|
|
||||||
.UseSoftDelete(true)
|
|
||||||
.UseCurrentUser("system")
|
|
||||||
.EnableDetailedErrors(true);
|
|
||||||
|
|
||||||
DataContext = builder.Build();
|
|
||||||
|
|
||||||
logger.LogInformation("数据库初始化成功");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
logger.LogError(ex, "数据库初始化失败");
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitializeServerService()
|
|
||||||
{
|
|
||||||
if (AppConfig == null || LoggerFactory == null)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("配置或日志系统未初始化");
|
|
||||||
}
|
|
||||||
|
|
||||||
ServerService = new ServerService(AppConfig, LoggerFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnExit(ExitEventArgs e)
|
protected override void OnExit(ExitEventArgs e)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ServerService?.Dispose();
|
_serviceProvider?.GetService<ServerService>()?.Dispose();
|
||||||
DataContext?.Dispose();
|
_serviceProvider?.GetService<IDataContext>()?.Dispose();
|
||||||
|
|
||||||
var logger = LoggerFactory?.CreateLogger<App>();
|
var logger = _serviceProvider?.GetService<ILogger<App>>();
|
||||||
logger?.LogInformation("应用程序正常退出");
|
logger?.LogInformation("应用程序正常退出");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -132,11 +170,10 @@ namespace Server
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
LoggerFactory?.Dispose();
|
_serviceProvider?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
base.OnExit(e);
|
base.OnExit(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
<Window x:Class="Server.MainWindow"
|
<hc:GlowWindow x:Class="Server.MainWindow"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:local="clr-namespace:Server"
|
xmlns:local="clr-namespace:Server"
|
||||||
|
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Title="Sharp HTTP Server" Height="600" Width="900"
|
Title="Sharp HTTP Server" Height="600" Width="900"
|
||||||
WindowStartupLocation="CenterScreen">
|
WindowStartupLocation="CenterScreen">
|
||||||
@@ -38,25 +39,33 @@
|
|||||||
|
|
||||||
<!-- 服务器信息 -->
|
<!-- 服务器信息 -->
|
||||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="服务器地址:" VerticalAlignment="Center" Margin="0,0,10,5"/>
|
<TextBlock Grid.Row="0" Grid.Column="0" Text="服务器地址:" VerticalAlignment="Center" Margin="0,0,10,5"/>
|
||||||
<TextBlock Grid.Row="0" Grid.Column="1" x:Name="ServerUrlTextBlock" Text="http://localhost:8080"
|
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding ServerUrl}"
|
||||||
FontWeight="Bold" VerticalAlignment="Center" Margin="0,0,0,5"/>
|
FontWeight="Bold" VerticalAlignment="Center" Margin="0,0,0,5"/>
|
||||||
|
|
||||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="服务器状态:" VerticalAlignment="Center" Margin="0,0,10,5"/>
|
<TextBlock Grid.Row="1" Grid.Column="0" Text="服务器状态:" VerticalAlignment="Center" Margin="0,0,10,5"/>
|
||||||
<TextBlock Grid.Row="1" Grid.Column="1" x:Name="StatusTextBlock" Text="未启动"
|
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding StatusText}"
|
||||||
Foreground="Gray" FontWeight="Bold" VerticalAlignment="Center" Margin="0,0,0,5"/>
|
Foreground="{Binding StatusColor}" FontWeight="Bold" VerticalAlignment="Center" Margin="0,0,0,5"/>
|
||||||
|
|
||||||
<!-- 控制按钮 -->
|
<!-- 控制按钮 -->
|
||||||
<StackPanel Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Orientation="Horizontal" Margin="0,10,0,0">
|
<StackPanel Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Orientation="Horizontal" Margin="0,10,0,0">
|
||||||
<Button x:Name="StartButton" Content="启动服务器" Width="100" Height="32" Margin="0,0,10,0" Click="StartButton_Click"
|
<Button Content="➕ 启动服务器" Width="120" Height="32" Margin="0,0,10,0"
|
||||||
Background="#4CAF50" Foreground="White" BorderThickness="0" FontWeight="Bold" Cursor="Hand"/>
|
Command="{Binding StartCommand}"
|
||||||
<Button x:Name="StopButton" Content="停止服务器" Width="100" Height="32" Margin="0,0,10,0" Click="StopButton_Click"
|
IsEnabled="{Binding CanStart}"
|
||||||
Background="#F44336" Foreground="White" BorderThickness="0" FontWeight="Bold" Cursor="Hand" IsEnabled="False"/>
|
Style="{StaticResource ButtonSuccess}"/>
|
||||||
<Button x:Name="RestartButton" Content="重启服务器" Width="100" Height="32" Margin="0,0,10,0" Click="RestartButton_Click"
|
<Button Content="⏹ 停止服务器" Width="120" Height="32" Margin="0,0,10,0"
|
||||||
Background="#FF9800" Foreground="White" BorderThickness="0" FontWeight="Bold" Cursor="Hand" IsEnabled="False"/>
|
Command="{Binding StopCommand}"
|
||||||
<Button x:Name="DataManagementButton" Content="数据管理" Width="100" Height="32" Margin="0,0,10,0" Click="DataManagementButton_Click"
|
IsEnabled="{Binding IsRunning}"
|
||||||
Background="#673AB7" Foreground="White" BorderThickness="0" FontWeight="Bold" Cursor="Hand"/>
|
Style="{StaticResource ButtonDanger}"/>
|
||||||
<Button x:Name="ClearLogButton" Content="清空日志" Width="100" Height="32" Click="ClearLogButton_Click"
|
<Button Content="🔄 重启服务器" Width="120" Height="32" Margin="0,0,10,0"
|
||||||
Background="#9E9E9E" Foreground="White" BorderThickness="0" FontWeight="Bold" Cursor="Hand"/>
|
Command="{Binding RestartCommand}"
|
||||||
|
IsEnabled="{Binding IsRunning}"
|
||||||
|
Style="{StaticResource ButtonWarning}"/>
|
||||||
|
<Button Content="📊 数据管理" Width="120" Height="32" Margin="0,0,10,0"
|
||||||
|
Command="{Binding OpenDataManagementCommand}"
|
||||||
|
Style="{StaticResource ButtonPrimary}"/>
|
||||||
|
<Button Content="🗑 清空日志" Width="100" Height="32"
|
||||||
|
Command="{Binding ClearLogCommand}"
|
||||||
|
Style="{StaticResource ButtonDefault}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
@@ -72,7 +81,7 @@
|
|||||||
<TextBlock Grid.Row="0" Text="日志信息" FontSize="14" FontWeight="Bold" Margin="0,0,0,10"/>
|
<TextBlock Grid.Row="0" Text="日志信息" FontSize="14" FontWeight="Bold" Margin="0,0,0,10"/>
|
||||||
|
|
||||||
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
|
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
|
||||||
<TextBox x:Name="LogTextBox"
|
<TextBox Text="{Binding LogText, Mode=OneWay}"
|
||||||
IsReadOnly="True"
|
IsReadOnly="True"
|
||||||
TextWrapping="Wrap"
|
TextWrapping="Wrap"
|
||||||
Background="#F5F5F5"
|
Background="#F5F5F5"
|
||||||
@@ -110,5 +119,4 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Window>
|
</hc:GlowWindow>
|
||||||
|
|
||||||
@@ -1,203 +1,24 @@
|
|||||||
using System.Windows;
|
using HandyControl.Controls;
|
||||||
using Server.Services;
|
using Server.ViewModels;
|
||||||
using Server.Config;
|
|
||||||
using Server.Views;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace Server
|
namespace Server
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Interaction logic for MainWindow.xaml
|
/// Interaction logic for MainWindow.xaml
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class MainWindow : Window
|
public partial class MainWindow : GlowWindow
|
||||||
{
|
{
|
||||||
private ServerService? _serverService;
|
public MainWindow(MainViewModel viewModel)
|
||||||
private AppConfig? _config;
|
|
||||||
private ILogger<MainWindow>? _logger;
|
|
||||||
|
|
||||||
public MainWindow()
|
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
Loaded += MainWindow_Loaded;
|
DataContext = viewModel;
|
||||||
Closing += MainWindow_Closing;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
|
// Handle window closing event
|
||||||
{
|
Closing += (s, e) =>
|
||||||
try
|
|
||||||
{
|
{
|
||||||
// 从App获取依赖
|
// Check if we need to stop the server before closing
|
||||||
var app = (App)Application.Current;
|
e.Cancel = !viewModel.CheckBeforeClose();
|
||||||
_config = app.AppConfig;
|
};
|
||||||
_logger = app.LoggerFactory?.CreateLogger<MainWindow>();
|
|
||||||
_serverService = app.ServerService;
|
|
||||||
|
|
||||||
if (_config != null && _serverService != null)
|
|
||||||
{
|
|
||||||
// 更新UI显示服务器地址
|
|
||||||
ServerUrlTextBlock.Text = $"http://{_config.Server.Host}:{_config.Server.Port}";
|
|
||||||
|
|
||||||
// 订阅服务器状态变化事件
|
|
||||||
_serverService.StatusChanged += OnServerStatusChanged;
|
|
||||||
|
|
||||||
AppendLog("应用程序已启动");
|
|
||||||
AppendLog($"配置已加载: {_config.Server.Host}:{_config.Server.Port}");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AppendLog("错误: 无法初始化服务器服务");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger?.LogError(ex, "MainWindow初始化失败");
|
|
||||||
AppendLog($"初始化失败: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MainWindow_Closing(object? sender, System.ComponentModel.CancelEventArgs e)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_serverService?.IsRunning == true)
|
|
||||||
{
|
|
||||||
var result = MessageBox.Show(
|
|
||||||
"服务器正在运行,是否停止服务器并退出?",
|
|
||||||
"确认退出",
|
|
||||||
MessageBoxButton.YesNo,
|
|
||||||
MessageBoxImage.Question);
|
|
||||||
|
|
||||||
if (result == MessageBoxResult.Yes)
|
|
||||||
{
|
|
||||||
_serverService.Stop();
|
|
||||||
AppendLog("服务器已停止");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
e.Cancel = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger?.LogError(ex, "关闭窗口时出错");
|
|
||||||
AppendLog($"关闭出错: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StartButton_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_serverService?.Start();
|
|
||||||
UpdateButtonStates(isRunning: true);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger?.LogError(ex, "启动服务器失败");
|
|
||||||
AppendLog($"启动失败: {ex.Message}");
|
|
||||||
MessageBox.Show($"启动服务器失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StopButton_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_serverService?.Stop();
|
|
||||||
UpdateButtonStates(isRunning: false);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger?.LogError(ex, "停止服务器失败");
|
|
||||||
AppendLog($"停止失败: {ex.Message}");
|
|
||||||
MessageBox.Show($"停止服务器失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RestartButton_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_serverService?.Restart();
|
|
||||||
UpdateButtonStates(isRunning: true);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger?.LogError(ex, "重启服务器失败");
|
|
||||||
AppendLog($"重启失败: {ex.Message}");
|
|
||||||
MessageBox.Show($"重启服务器失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ClearLogButton_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
LogTextBox.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DataManagementButton_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_logger?.LogInformation("打开数据管理窗口");
|
|
||||||
var window = new UserManagementWindow
|
|
||||||
{
|
|
||||||
Owner = this
|
|
||||||
};
|
|
||||||
window.ShowDialog();
|
|
||||||
_logger?.LogInformation("数据管理窗口已关闭");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger?.LogError(ex, "打开数据管理窗口失败");
|
|
||||||
AppendLog($"打开数据管理窗口失败: {ex.Message}");
|
|
||||||
MessageBox.Show($"打开数据管理窗口失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnServerStatusChanged(object? sender, string status)
|
|
||||||
{
|
|
||||||
Dispatcher.Invoke(() =>
|
|
||||||
{
|
|
||||||
StatusTextBlock.Text = status;
|
|
||||||
|
|
||||||
// 根据状态更新颜色
|
|
||||||
if (status.Contains("已启动"))
|
|
||||||
{
|
|
||||||
StatusTextBlock.Foreground = System.Windows.Media.Brushes.Green;
|
|
||||||
}
|
|
||||||
else if (status.Contains("已停止"))
|
|
||||||
{
|
|
||||||
StatusTextBlock.Foreground = System.Windows.Media.Brushes.Gray;
|
|
||||||
}
|
|
||||||
else if (status.Contains("失败") || status.Contains("错误"))
|
|
||||||
{
|
|
||||||
StatusTextBlock.Foreground = System.Windows.Media.Brushes.Red;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
StatusTextBlock.Foreground = System.Windows.Media.Brushes.Orange;
|
|
||||||
}
|
|
||||||
|
|
||||||
AppendLog(status);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateButtonStates(bool isRunning)
|
|
||||||
{
|
|
||||||
StartButton.IsEnabled = !isRunning;
|
|
||||||
StopButton.IsEnabled = isRunning;
|
|
||||||
RestartButton.IsEnabled = isRunning;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AppendLog(string message)
|
|
||||||
{
|
|
||||||
Dispatcher.Invoke(() =>
|
|
||||||
{
|
|
||||||
var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
|
||||||
LogTextBox.AppendText($"[{timestamp}] {message}\n");
|
|
||||||
LogTextBox.ScrollToEnd();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28,6 +28,7 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
|
||||||
|
<PackageReference Include="HandyControl" Version="3.5.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -0,0 +1,278 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Sharp.Wpf.Mvvm;
|
||||||
|
using Sharp.Wpf.Commands;
|
||||||
|
using Server.Config;
|
||||||
|
using Server.Services;
|
||||||
|
using Server.Views;
|
||||||
|
|
||||||
|
namespace Server.ViewModels
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 主窗口ViewModel
|
||||||
|
/// </summary>
|
||||||
|
public class MainViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
private readonly ServerService _serverService;
|
||||||
|
private readonly ILogger<MainViewModel> _logger;
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
|
||||||
|
private string _statusText = "未启动";
|
||||||
|
private Brush _statusColor = Brushes.Gray;
|
||||||
|
private string _logText = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
public MainViewModel(
|
||||||
|
ServerService serverService,
|
||||||
|
AppConfig config,
|
||||||
|
ILogger<MainViewModel> logger,
|
||||||
|
IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
_serverService = serverService ?? throw new ArgumentNullException(nameof(serverService));
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
_serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
|
||||||
|
|
||||||
|
ServerUrl = $"http://{config.Server.Host}:{config.Server.Port}";
|
||||||
|
|
||||||
|
// 初始化命令
|
||||||
|
StartCommand = new RelayCommand(_ => Start(), _ => CanStart);
|
||||||
|
StopCommand = new RelayCommand(_ => Stop(), _ => IsRunning);
|
||||||
|
RestartCommand = new RelayCommand(_ => Restart(), _ => IsRunning);
|
||||||
|
ClearLogCommand = new RelayCommand(_ => ClearLog());
|
||||||
|
OpenDataManagementCommand = new RelayCommand(_ => OpenDataManagement());
|
||||||
|
|
||||||
|
// 订阅服务器状态变化事件
|
||||||
|
_serverService.StatusChanged += OnServerStatusChanged;
|
||||||
|
|
||||||
|
AppendLog("应用程序已启动");
|
||||||
|
AppendLog($"配置已加载: {config.Server.Host}:{config.Server.Port}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 服务器URL
|
||||||
|
/// </summary>
|
||||||
|
public string ServerUrl { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 状态文本
|
||||||
|
/// </summary>
|
||||||
|
public string StatusText
|
||||||
|
{
|
||||||
|
get => _statusText;
|
||||||
|
set => SetProperty(ref _statusText, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 状态颜色
|
||||||
|
/// </summary>
|
||||||
|
public Brush StatusColor
|
||||||
|
{
|
||||||
|
get => _statusColor;
|
||||||
|
set => SetProperty(ref _statusColor, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 日志文本
|
||||||
|
/// </summary>
|
||||||
|
public string LogText
|
||||||
|
{
|
||||||
|
get => _logText;
|
||||||
|
set => SetProperty(ref _logText, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 服务器是否正在运行
|
||||||
|
/// </summary>
|
||||||
|
public bool IsRunning => _serverService.IsRunning;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 能否启动服务器
|
||||||
|
/// </summary>
|
||||||
|
public bool CanStart => !IsRunning;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 启动命令
|
||||||
|
/// </summary>
|
||||||
|
public ICommand StartCommand { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 停止命令
|
||||||
|
/// </summary>
|
||||||
|
public ICommand StopCommand { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 重启命令
|
||||||
|
/// </summary>
|
||||||
|
public ICommand RestartCommand { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清除日志命令
|
||||||
|
/// </summary>
|
||||||
|
public ICommand ClearLogCommand { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 打开数据管理命令
|
||||||
|
/// </summary>
|
||||||
|
public ICommand OpenDataManagementCommand { get; }
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_serverService.Start();
|
||||||
|
UpdateButtonStates();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "启动服务器失败");
|
||||||
|
AppendLog($"启动失败: {ex.Message}");
|
||||||
|
MessageBox.Show($"启动服务器失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Stop()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_serverService.Stop();
|
||||||
|
UpdateButtonStates();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "停止服务器失败");
|
||||||
|
AppendLog($"停止失败: {ex.Message}");
|
||||||
|
MessageBox.Show($"停止服务器失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Restart()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_serverService.Restart();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "重启服务器失败");
|
||||||
|
AppendLog($"重启失败: {ex.Message}");
|
||||||
|
MessageBox.Show($"重启服务器失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearLog()
|
||||||
|
{
|
||||||
|
LogText = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenDataManagement()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.LogInformation("打开数据管理窗口");
|
||||||
|
var window = _serviceProvider.GetRequiredService<UserManagementWindow>();
|
||||||
|
window.Owner = Application.Current.MainWindow;
|
||||||
|
window.ShowDialog();
|
||||||
|
_logger.LogInformation("数据管理窗口已关闭");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "打开数据管理窗口失败");
|
||||||
|
AppendLog($"打开数据管理窗口失败: {ex.Message}");
|
||||||
|
MessageBox.Show($"打开数据管理窗口失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnServerStatusChanged(object? sender, string status)
|
||||||
|
{
|
||||||
|
Application.Current.Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
StatusText = status;
|
||||||
|
|
||||||
|
// 根据状态更新颜色
|
||||||
|
if (status.Contains("已启动"))
|
||||||
|
{
|
||||||
|
StatusColor = Brushes.Green;
|
||||||
|
}
|
||||||
|
else if (status.Contains("已停止"))
|
||||||
|
{
|
||||||
|
StatusColor = Brushes.Gray;
|
||||||
|
}
|
||||||
|
else if (status.Contains("失败") || status.Contains("错误"))
|
||||||
|
{
|
||||||
|
StatusColor = Brushes.Red;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StatusColor = Brushes.Orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateButtonStates();
|
||||||
|
AppendLog(status);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateButtonStates()
|
||||||
|
{
|
||||||
|
OnPropertyChanged(nameof(IsRunning));
|
||||||
|
OnPropertyChanged(nameof(CanStart));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AppendLog(string message)
|
||||||
|
{
|
||||||
|
var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
||||||
|
LogText += $"[{timestamp}] {message}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查是否需要停止服务器
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>true表示可以关闭,false表示用户取消</returns>
|
||||||
|
public bool CheckBeforeClose()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_serverService.IsRunning)
|
||||||
|
{
|
||||||
|
var result = MessageBox.Show(
|
||||||
|
"服务器正在运行,是否停止服务器并退出?",
|
||||||
|
"确认退出",
|
||||||
|
MessageBoxButton.YesNo,
|
||||||
|
MessageBoxImage.Question);
|
||||||
|
|
||||||
|
if (result == MessageBoxResult.Yes)
|
||||||
|
{
|
||||||
|
_serverService.Stop();
|
||||||
|
AppendLog("服务器已停止");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "关闭窗口时出错");
|
||||||
|
AppendLog($"关闭出错: {ex.Message}");
|
||||||
|
return true; // 即使出错也允许关闭
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 释放资源
|
||||||
|
/// </summary>
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
_serverService.StatusChanged -= OnServerStatusChanged;
|
||||||
|
base.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,308 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Sharp.Data.Context;
|
||||||
|
using Sharp.Wpf.Mvvm;
|
||||||
|
using Sharp.Wpf.Commands;
|
||||||
|
using Sharp.Wpf.Abstractions;
|
||||||
|
using Sharp.Wpf.Services;
|
||||||
|
using Server.Data;
|
||||||
|
using Server.Views;
|
||||||
|
|
||||||
|
namespace Server.ViewModels
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 用户管理ViewModel
|
||||||
|
/// </summary>
|
||||||
|
public class UserManagementViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
private readonly IDataContext _dataContext;
|
||||||
|
private readonly ILogger<UserManagementViewModel> _logger;
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
private readonly IDialogService _dialogService;
|
||||||
|
|
||||||
|
private User? _selectedUser;
|
||||||
|
private string _searchText = string.Empty;
|
||||||
|
private string _statusText = string.Empty;
|
||||||
|
private string _recordCount = "记录数: 0";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
public UserManagementViewModel(
|
||||||
|
IDataContext dataContext,
|
||||||
|
ILogger<UserManagementViewModel> logger,
|
||||||
|
IServiceProvider serviceProvider,
|
||||||
|
IDialogService dialogService)
|
||||||
|
{
|
||||||
|
_dataContext = dataContext ?? throw new ArgumentNullException(nameof(dataContext));
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
_serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
|
||||||
|
_dialogService = dialogService ?? throw new ArgumentNullException(nameof(dialogService));
|
||||||
|
|
||||||
|
Users = new ObservableCollection<User>();
|
||||||
|
|
||||||
|
// 初始化命令
|
||||||
|
LoadDataCommand = new AsyncCommand(async () => await LoadDataAsync());
|
||||||
|
SearchCommand = new AsyncCommand(async () => await LoadDataAsync(SearchText.Trim()));
|
||||||
|
ClearSearchCommand = new AsyncCommand(async () =>
|
||||||
|
{
|
||||||
|
SearchText = string.Empty;
|
||||||
|
await LoadDataAsync();
|
||||||
|
});
|
||||||
|
AddCommand = new RelayCommand(_ => Add());
|
||||||
|
EditCommand = new RelayCommand(_ => Edit(), _ => SelectedUser != null);
|
||||||
|
DeleteCommand = new AsyncCommand(async () => await DeleteAsync(), () => SelectedUser != null);
|
||||||
|
RefreshCommand = new AsyncCommand(async () => await LoadDataAsync(SearchText.Trim()));
|
||||||
|
|
||||||
|
// 加载初始数据
|
||||||
|
_ = LoadDataAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户列表
|
||||||
|
/// </summary>
|
||||||
|
public ObservableCollection<User> Users { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 选中的用户
|
||||||
|
/// </summary>
|
||||||
|
public User? SelectedUser
|
||||||
|
{
|
||||||
|
get => _selectedUser;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _selectedUser, value))
|
||||||
|
{
|
||||||
|
((RelayCommand)EditCommand).RaiseCanExecuteChanged();
|
||||||
|
((AsyncCommand)DeleteCommand).RaiseCanExecuteChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 搜索文本
|
||||||
|
/// </summary>
|
||||||
|
public string SearchText
|
||||||
|
{
|
||||||
|
get => _searchText;
|
||||||
|
set => SetProperty(ref _searchText, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 状态文本
|
||||||
|
/// </summary>
|
||||||
|
public string StatusText
|
||||||
|
{
|
||||||
|
get => _statusText;
|
||||||
|
set => SetProperty(ref _statusText, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 记录数
|
||||||
|
/// </summary>
|
||||||
|
public string RecordCount
|
||||||
|
{
|
||||||
|
get => _recordCount;
|
||||||
|
set => SetProperty(ref _recordCount, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 加载数据命令
|
||||||
|
/// </summary>
|
||||||
|
public ICommand LoadDataCommand { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 搜索命令
|
||||||
|
/// </summary>
|
||||||
|
public ICommand SearchCommand { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清除搜索命令
|
||||||
|
/// </summary>
|
||||||
|
public ICommand ClearSearchCommand { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加命令
|
||||||
|
/// </summary>
|
||||||
|
public ICommand AddCommand { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 编辑命令
|
||||||
|
/// </summary>
|
||||||
|
public ICommand EditCommand { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除命令
|
||||||
|
/// </summary>
|
||||||
|
public ICommand DeleteCommand { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 刷新命令
|
||||||
|
/// </summary>
|
||||||
|
public ICommand RefreshCommand { get; }
|
||||||
|
|
||||||
|
private async Task LoadDataAsync(string? searchText = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
StatusText = "正在加载数据...";
|
||||||
|
_logger.LogInformation("开始加载用户数据,搜索条件: {SearchText}", searchText ?? "无");
|
||||||
|
|
||||||
|
var repo = _dataContext.Repository<User>();
|
||||||
|
|
||||||
|
List<User> users;
|
||||||
|
if (string.IsNullOrWhiteSpace(searchText))
|
||||||
|
{
|
||||||
|
users = await repo.GetAllAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
users = await repo.FindAsync(u =>
|
||||||
|
u.UserName.Contains(searchText) ||
|
||||||
|
u.Email.Contains(searchText) ||
|
||||||
|
(u.Remarks != null && u.Remarks.Contains(searchText)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Users.Clear();
|
||||||
|
foreach (var user in users.OrderByDescending(u => u.CreatedAt))
|
||||||
|
{
|
||||||
|
Users.Add(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
RecordCount = $"记录数: {users.Count}";
|
||||||
|
StatusText = string.IsNullOrWhiteSpace(searchText)
|
||||||
|
? $"加载完成,共 {users.Count} 条记录"
|
||||||
|
: $"搜索完成,找到 {users.Count} 条记录";
|
||||||
|
|
||||||
|
_logger.LogInformation("加载完成,共 {Count} 条记录", users.Count);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "加载数据失败");
|
||||||
|
StatusText = "加载失败";
|
||||||
|
await _dialogService.ShowErrorAsync($"加载数据失败: {ex.Message}", "错误");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Add()
|
||||||
|
{
|
||||||
|
var dialog = new UserEditDialog();
|
||||||
|
if (dialog.ShowDialog() == true)
|
||||||
|
{
|
||||||
|
_ = SaveUserAsync(dialog.UserData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Edit()
|
||||||
|
{
|
||||||
|
if (SelectedUser == null)
|
||||||
|
{
|
||||||
|
_ = _dialogService.ShowInfoAsync("请选择要编辑的用户", "提示");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dialog = new UserEditDialog(SelectedUser);
|
||||||
|
if (dialog.ShowDialog() == true)
|
||||||
|
{
|
||||||
|
_ = UpdateUserAsync(dialog.UserData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DeleteAsync()
|
||||||
|
{
|
||||||
|
if (SelectedUser == null)
|
||||||
|
{
|
||||||
|
await _dialogService.ShowInfoAsync("请选择要删除的用户", "提示");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var confirmed = await _dialogService.ShowConfirmAsync(
|
||||||
|
$"确定要删除用户 '{SelectedUser.UserName}' 吗?\n\n(注意:这是软删除,数据会被标记为已删除但不会真正删除)",
|
||||||
|
"确认删除");
|
||||||
|
|
||||||
|
if (confirmed)
|
||||||
|
{
|
||||||
|
await DeleteUserAsync(SelectedUser.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SaveUserAsync(User user)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
StatusText = "正在保存...";
|
||||||
|
_logger.LogInformation("新增用户: {UserName}", user.UserName);
|
||||||
|
|
||||||
|
var repo = _dataContext.Repository<User>();
|
||||||
|
await repo.AddAsync(user);
|
||||||
|
await _dataContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
_logger.LogInformation("用户新增成功: {UserId}", user.Id);
|
||||||
|
await _dialogService.ShowInfoAsync("用户新增成功!", "成功");
|
||||||
|
|
||||||
|
await LoadDataAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "新增用户失败");
|
||||||
|
StatusText = "保存失败";
|
||||||
|
await _dialogService.ShowErrorAsync($"新增用户失败: {ex.Message}", "错误");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task UpdateUserAsync(User user)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
StatusText = "正在更新...";
|
||||||
|
_logger.LogInformation("更新用户: {UserId}", user.Id);
|
||||||
|
|
||||||
|
var repo = _dataContext.Repository<User>();
|
||||||
|
repo.Update(user);
|
||||||
|
await _dataContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
_logger.LogInformation("用户更新成功: {UserId}", user.Id);
|
||||||
|
await _dialogService.ShowInfoAsync("用户更新成功!", "成功");
|
||||||
|
|
||||||
|
await LoadDataAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "更新用户失败");
|
||||||
|
StatusText = "更新失败";
|
||||||
|
await _dialogService.ShowErrorAsync($"更新用户失败: {ex.Message}", "错误");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DeleteUserAsync(Guid userId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
StatusText = "正在删除...";
|
||||||
|
_logger.LogInformation("软删除用户: {UserId}", userId);
|
||||||
|
|
||||||
|
var repo = _dataContext.Repository<User>();
|
||||||
|
await repo.RemoveAsync(userId); // 软删除
|
||||||
|
await _dataContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
_logger.LogInformation("用户软删除成功: {UserId}", userId);
|
||||||
|
await _dialogService.ShowInfoAsync("用户已标记为删除(软删除)!", "成功");
|
||||||
|
|
||||||
|
await LoadDataAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "删除用户失败");
|
||||||
|
StatusText = "删除失败";
|
||||||
|
await _dialogService.ShowErrorAsync($"删除用户失败: {ex.Message}", "错误");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
<Window x:Class="Server.Views.UserManagementWindow"
|
<hc:GlowWindow x:Class="Server.Views.UserManagementWindow"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Title="用户数据管理 - Sharp.Data 示例" Height="700" Width="1200"
|
Title="用户数据管理 - Sharp.Data 示例" Height="700" Width="1200"
|
||||||
WindowStartupLocation="CenterScreen">
|
WindowStartupLocation="CenterScreen">
|
||||||
@@ -34,35 +35,43 @@
|
|||||||
<!-- 搜索框 -->
|
<!-- 搜索框 -->
|
||||||
<StackPanel Grid.Column="0" Orientation="Horizontal" Margin="0,0,15,0">
|
<StackPanel Grid.Column="0" Orientation="Horizontal" Margin="0,0,15,0">
|
||||||
<TextBlock Text="搜索:" VerticalAlignment="Center" Margin="0,0,10,0"/>
|
<TextBlock Text="搜索:" VerticalAlignment="Center" Margin="0,0,10,0"/>
|
||||||
<TextBox x:Name="SearchTextBox" Width="250" Height="32" VerticalContentAlignment="Center"
|
<TextBox Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}"
|
||||||
|
Width="250" Height="32" VerticalContentAlignment="Center"
|
||||||
Padding="8,0" BorderBrush="#BDBDBD" BorderThickness="1"/>
|
Padding="8,0" BorderBrush="#BDBDBD" BorderThickness="1"/>
|
||||||
<Button Content="搜索" Width="80" Height="32" Margin="10,0,0,0" Click="SearchButton_Click"
|
<Button Content="搜索" Width="80" Height="32" Margin="10,0,0,0"
|
||||||
Background="#2196F3" Foreground="White" BorderThickness="0" FontWeight="Bold" Cursor="Hand"/>
|
Command="{Binding SearchCommand}"
|
||||||
<Button Content="清空" Width="80" Height="32" Margin="5,0,0,0" Click="ClearSearchButton_Click"
|
Style="{StaticResource ButtonPrimary}"/>
|
||||||
Background="#9E9E9E" Foreground="White" BorderThickness="0" FontWeight="Bold" Cursor="Hand"/>
|
<Button Content="清空" Width="80" Height="32" Margin="5,0,0,0"
|
||||||
|
Command="{Binding ClearSearchCommand}"
|
||||||
|
Style="{StaticResource ButtonDefault}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<!-- 状态信息 -->
|
<!-- 状态信息 -->
|
||||||
<TextBlock Grid.Column="1" x:Name="StatusTextBlock" Text="就绪" VerticalAlignment="Center"
|
<TextBlock Grid.Column="1" Text="{Binding StatusText}" VerticalAlignment="Center"
|
||||||
Foreground="#666" Margin="15,0"/>
|
Foreground="#666" Margin="15,0"/>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 操作按钮 -->
|
||||||
<StackPanel Grid.Column="2" Orientation="Horizontal">
|
<StackPanel Grid.Column="2" Orientation="Horizontal">
|
||||||
<Button Content="➕ 新增" Width="90" Height="32" Margin="0,0,5,0" Click="AddButton_Click"
|
<Button Content="➕ 新增" Width="90" Height="32" Margin="0,0,5,0"
|
||||||
Background="#4CAF50" Foreground="White" BorderThickness="0" FontWeight="Bold" Cursor="Hand"/>
|
Command="{Binding AddCommand}"
|
||||||
<Button Content="✏️ 编辑" Width="90" Height="32" Margin="0,0,5,0" Click="EditButton_Click"
|
Style="{StaticResource ButtonSuccess}"/>
|
||||||
Background="#FF9800" Foreground="White" BorderThickness="0" FontWeight="Bold" Cursor="Hand"/>
|
<Button Content="✏️ 编辑" Width="90" Height="32" Margin="0,0,5,0"
|
||||||
<Button Content="🗑️ 删除" Width="90" Height="32" Click="DeleteButton_Click"
|
Command="{Binding EditCommand}"
|
||||||
Background="#F44336" Foreground="White" BorderThickness="0" FontWeight="Bold" Cursor="Hand"/>
|
Style="{StaticResource ButtonWarning}"/>
|
||||||
<Button Content="🔄 刷新" Width="90" Height="32" Margin="5,0,0,0" Click="RefreshButton_Click"
|
<Button Content="🗑️ 删除" Width="90" Height="32"
|
||||||
Background="#607D8B" Foreground="White" BorderThickness="0" FontWeight="Bold" Cursor="Hand"/>
|
Command="{Binding DeleteCommand}"
|
||||||
|
Style="{StaticResource ButtonDanger}"/>
|
||||||
|
<Button Content="🔄 刷新" Width="90" Height="32" Margin="5,0,0,0"
|
||||||
|
Command="{Binding RefreshCommand}"
|
||||||
|
Style="{StaticResource ButtonInfo}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<!-- 数据列表 -->
|
<!-- 数据列表 -->
|
||||||
<Border Grid.Row="2" BorderBrush="#E0E0E0" BorderThickness="1" CornerRadius="5" Padding="10">
|
<Border Grid.Row="2" BorderBrush="#E0E0E0" BorderThickness="1" CornerRadius="5" Padding="10">
|
||||||
<DataGrid x:Name="UsersDataGrid"
|
<DataGrid ItemsSource="{Binding Users}"
|
||||||
|
SelectedItem="{Binding SelectedUser}"
|
||||||
AutoGenerateColumns="False"
|
AutoGenerateColumns="False"
|
||||||
IsReadOnly="True"
|
IsReadOnly="True"
|
||||||
SelectionMode="Single"
|
SelectionMode="Single"
|
||||||
@@ -109,9 +118,9 @@
|
|||||||
|
|
||||||
<StackPanel Grid.Column="1" VerticalAlignment="Center">
|
<StackPanel Grid.Column="1" VerticalAlignment="Center">
|
||||||
<TextBlock Text="数据库: SQLite (server.db)" FontSize="11" FontWeight="Bold"/>
|
<TextBlock Text="数据库: SQLite (server.db)" FontSize="11" FontWeight="Bold"/>
|
||||||
<TextBlock x:Name="RecordCountTextBlock" Text="记录数: 0" FontSize="11"/>
|
<TextBlock Text="{Binding RecordCount}" FontSize="11"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Window>
|
</hc:GlowWindow>
|
||||||
@@ -1,202 +1,17 @@
|
|||||||
using System.Windows;
|
using HandyControl.Controls;
|
||||||
using System.Windows.Controls;
|
using Server.ViewModels;
|
||||||
using Sharp.Data.Context;
|
|
||||||
using Server.Data;
|
|
||||||
using Common.ExpandFun;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace Server.Views;
|
namespace Server.Views
|
||||||
|
|
||||||
public partial class UserManagementWindow : Window
|
|
||||||
{
|
{
|
||||||
private readonly IDataContext _dataContext;
|
/// <summary>
|
||||||
private readonly ILogger<UserManagementWindow> _logger;
|
/// Interaction logic for UserManagementWindow.xaml
|
||||||
|
/// </summary>
|
||||||
public UserManagementWindow()
|
public partial class UserManagementWindow : GlowWindow
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
public UserManagementWindow(UserManagementViewModel viewModel)
|
||||||
|
|
||||||
var app = (App)Application.Current;
|
|
||||||
_dataContext = app.DataContext ?? throw new InvalidOperationException("数据库未初始化");
|
|
||||||
_logger = app.LoggerFactory?.CreateLogger<UserManagementWindow>()
|
|
||||||
?? throw new InvalidOperationException("日志系统未初始化");
|
|
||||||
|
|
||||||
Loaded += UserManagementWindow_Loaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void UserManagementWindow_Loaded(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
await LoadDataAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task LoadDataAsync(string? searchText = null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
StatusTextBlock.Text = "正在加载数据...";
|
InitializeComponent();
|
||||||
_logger.LogInformation("开始加载用户数据,搜索条件: {SearchText}", searchText ?? "无");
|
DataContext = viewModel;
|
||||||
|
|
||||||
var repo = _dataContext.Repository<User>();
|
|
||||||
|
|
||||||
List<User> users;
|
|
||||||
if (string.IsNullOrWhiteSpace(searchText))
|
|
||||||
{
|
|
||||||
users = await repo.GetAllAsync();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
users = await repo.FindAsync(u =>
|
|
||||||
u.UserName.Contains(searchText) ||
|
|
||||||
u.Email.Contains(searchText) ||
|
|
||||||
(u.Remarks != null && u.Remarks.Contains(searchText)));
|
|
||||||
}
|
|
||||||
|
|
||||||
UsersDataGrid.ItemsSource = users.OrderByDescending(u => u.CreatedAt).ToList();
|
|
||||||
RecordCountTextBlock.Text = $"记录数: {users.Count}";
|
|
||||||
StatusTextBlock.Text = string.IsNullOrWhiteSpace(searchText)
|
|
||||||
? $"加载完成,共 {users.Count} 条记录"
|
|
||||||
: $"搜索完成,找到 {users.Count} 条记录";
|
|
||||||
|
|
||||||
_logger.LogInformation("加载完成,共 {Count} 条记录", users.Count);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "加载数据失败");
|
|
||||||
StatusTextBlock.Text = "加载失败";
|
|
||||||
MessageBox.Show($"加载数据失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void SearchButton_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
var searchText = SearchTextBox.Text.Trim();
|
|
||||||
await LoadDataAsync(searchText);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void ClearSearchButton_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
SearchTextBox.Clear();
|
|
||||||
await LoadDataAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddButton_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
var dialog = new UserEditDialog();
|
|
||||||
if (dialog.ShowDialog() == true)
|
|
||||||
{
|
|
||||||
_ = SaveUserAsync(dialog.UserData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EditButton_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
if (UsersDataGrid.SelectedItem is not User selectedUser)
|
|
||||||
{
|
|
||||||
MessageBox.Show("请选择要编辑的用户", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var dialog = new UserEditDialog(selectedUser);
|
|
||||||
if (dialog.ShowDialog() == true)
|
|
||||||
{
|
|
||||||
_ = UpdateUserAsync(dialog.UserData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void DeleteButton_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
if (UsersDataGrid.SelectedItem is not User selectedUser)
|
|
||||||
{
|
|
||||||
MessageBox.Show("请选择要删除的用户", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = MessageBox.Show(
|
|
||||||
$"确定要删除用户 '{selectedUser.UserName}' 吗?\n\n(注意:这是软删除,数据会被标记为已删除但不会真正删除)",
|
|
||||||
"确认删除",
|
|
||||||
MessageBoxButton.YesNo,
|
|
||||||
MessageBoxImage.Question);
|
|
||||||
|
|
||||||
if (result == MessageBoxResult.Yes)
|
|
||||||
{
|
|
||||||
await DeleteUserAsync(selectedUser.Id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void RefreshButton_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
await LoadDataAsync(SearchTextBox.Text.Trim());
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SaveUserAsync(User user)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
StatusTextBlock.Text = "正在保存...";
|
|
||||||
_logger.LogInformation("新增用户: {UserName}", user.UserName);
|
|
||||||
|
|
||||||
var repo = _dataContext.Repository<User>();
|
|
||||||
await repo.AddAsync(user);
|
|
||||||
await _dataContext.SaveChangesAsync();
|
|
||||||
|
|
||||||
_logger.LogInformation("用户新增成功: {UserId}", user.Id);
|
|
||||||
MessageBox.Show("用户新增成功!", "成功", MessageBoxButton.OK, MessageBoxImage.Information);
|
|
||||||
|
|
||||||
await LoadDataAsync();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "新增用户失败");
|
|
||||||
StatusTextBlock.Text = "保存失败";
|
|
||||||
MessageBox.Show($"新增用户失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task UpdateUserAsync(User user)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
StatusTextBlock.Text = "正在更新...";
|
|
||||||
_logger.LogInformation("更新用户: {UserId}", user.Id);
|
|
||||||
|
|
||||||
var repo = _dataContext.Repository<User>();
|
|
||||||
repo.Update(user);
|
|
||||||
await _dataContext.SaveChangesAsync();
|
|
||||||
|
|
||||||
_logger.LogInformation("用户更新成功: {UserId}", user.Id);
|
|
||||||
MessageBox.Show("用户更新成功!", "成功", MessageBoxButton.OK, MessageBoxImage.Information);
|
|
||||||
|
|
||||||
await LoadDataAsync();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "更新用户失败");
|
|
||||||
StatusTextBlock.Text = "更新失败";
|
|
||||||
MessageBox.Show($"更新用户失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task DeleteUserAsync(Guid userId)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
StatusTextBlock.Text = "正在删除...";
|
|
||||||
_logger.LogInformation("软删除用户: {UserId}", userId);
|
|
||||||
|
|
||||||
var repo = _dataContext.Repository<User>();
|
|
||||||
await repo.RemoveAsync(userId); // 软删除
|
|
||||||
await _dataContext.SaveChangesAsync();
|
|
||||||
|
|
||||||
_logger.LogInformation("用户软删除成功: {UserId}", userId);
|
|
||||||
MessageBox.Show("用户已标记为删除(软删除)!", "成功", MessageBoxButton.OK, MessageBoxImage.Information);
|
|
||||||
|
|
||||||
await LoadDataAsync();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "删除用户失败");
|
|
||||||
StatusTextBlock.Text = "删除失败";
|
|
||||||
MessageBox.Show($"删除用户失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -111,15 +111,3 @@ var folder = await new FileDialogService().SelectFolderAsync();
|
|||||||
- 若要禁用全局异常弹窗,可在 `ExceptionHandlingOptions` 中设置 `ShowDialog = false` 并仅使用 `Log` 钩子记录。
|
- 若要禁用全局异常弹窗,可在 `ExceptionHandlingOptions` 中设置 `ShowDialog = false` 并仅使用 `Log` 钩子记录。
|
||||||
- `EventToCommand` 通过反射订阅事件,适用于大多数 Routed 事件;如需传递事件参数,可设置 `PassEventArgsToCommand="True"`。
|
- `EventToCommand` 通过反射订阅事件,适用于大多数 Routed 事件;如需传递事件参数,可设置 `PassEventArgsToCommand="True"`。
|
||||||
|
|
||||||
我们主要使用Data模块,Repository模块不是非常常用。增加你提到的功能,并在Client模块提供对应功能测试、使用案例,使用Sqlite数据库作为测试数据库。
|
|
||||||
|
|
||||||
对于NetServer增加JWT验证选项(可选使用),增加对于WebSocket的支持、API版本支持、健康端点检查、模型验证等,并且Server模块增加对应功能的测试、使用案例,在Client进行连接通信
|
|
||||||
|
|
||||||
对于PLC模块增加Modbus的支持,并在Server中提供使用示例。
|
|
||||||
|
|
||||||
Ports 模块增加ModbusRTU功能的支持、帧同步器、虚拟串口,并且在Client中增加对应功能的演示
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
这么多模块和功能,他们分别是客户端和服务端的功能,能不能让Client和Server集成他们需要集成的内容,尽可能的内容复用,各个模块之间也进行引用减少重复代码。从这个角度审视目前的所有的代码,并重新设计并在Client和Server中进行集成。并且
|
|
||||||
Client和Server页面也使用HandyControl
|
|
||||||
Reference in new issue
Block a user