A:增加页面集中管理
This commit is contained in:
1 parent
2943485a2a
commit
974d16833c
61 files changed
+7062
-1
No files matched your search
@@ -0,0 +1,7 @@
|
||||
<Application x:Class="Sharp.Demo.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Application.Resources>
|
||||
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
@@ -0,0 +1,233 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Common;
|
||||
using Config;
|
||||
using Sharp.Log.Extensions;
|
||||
using Sharp.Wpf.Services;
|
||||
using Sharp.Wpf.Abstractions;
|
||||
using Sharp.Wpf.HandyControl.DependencyInjection;
|
||||
using Sharp.Wpf.DevTools.DependencyInjection;
|
||||
using Sharp.NetServer.DependencyInjection;
|
||||
using NetClient.DependencyInjection;
|
||||
using Plc.Siemens.DependencyInjection;
|
||||
using Sharp.Process.DependencyInjection;
|
||||
using Sharp.Python.DependencyInjection;
|
||||
using Sharp.Data;
|
||||
using Sharp.Data.Context;
|
||||
using Sharp.Demo.ViewModels;
|
||||
using Sharp.Demo.ViewModels.Pages;
|
||||
using Sharp.Demo.Services;
|
||||
using Sharp.Demo.Data;
|
||||
using Sharp.Demo.Views;
|
||||
|
||||
namespace Sharp.Demo
|
||||
{
|
||||
/// <summary>
|
||||
/// Sharp Framework Demo 应用程序
|
||||
/// 模块化演示平台 - 每个页面展示一个 Sharp 模块的完整功能
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
private ServiceProvider? _serviceProvider;
|
||||
|
||||
protected override void OnStartup(StartupEventArgs e)
|
||||
{
|
||||
base.OnStartup(e);
|
||||
// 设置页面渲染方式
|
||||
RenderOptions.ProcessRenderMode = RenderMode.SoftwareOnly;
|
||||
|
||||
// === 1. 全局异常处理 ===
|
||||
RegisterExceptionHandlers();
|
||||
|
||||
// === 2. 配置管理 ===
|
||||
var cm = ConfigBuilder.BuildFromFiles(
|
||||
new[] { "appsettings.json" },
|
||||
reloadOnChange: true,
|
||||
includeEnvironment: true,
|
||||
basePath: AppContext.BaseDirectory
|
||||
);
|
||||
|
||||
// === 3. 依赖注入容器 ===
|
||||
var services = new ServiceCollection();
|
||||
|
||||
// 配置
|
||||
services.AddSingleton(cm);
|
||||
services.AddSingleton<IConfiguration>(cm.Configuration);
|
||||
|
||||
// 日志
|
||||
services.AddLogging(b => b.AddSharpLog());
|
||||
|
||||
// 数据库
|
||||
services.AddSingleton<IDataContext>(sp =>
|
||||
{
|
||||
var builder = new DataContextBuilder<AppDbContext>();
|
||||
builder.UseSqlite("Data Source=demo.db");
|
||||
builder.UseAudit(true);
|
||||
builder.UseSoftDelete(true);
|
||||
builder.UseCurrentUser("system");
|
||||
builder.EnableDetailedErrors(true);
|
||||
return builder.Build();
|
||||
});
|
||||
|
||||
// 网络模块
|
||||
services.AddNetClient(cm);
|
||||
services.AddNetServer(cm);
|
||||
|
||||
// 工业控制
|
||||
services.AddSiemensPlc(options =>
|
||||
{
|
||||
options.IpAddress = "127.0.0.1";
|
||||
options.CpuType = S7.Net.CpuType.S71200;
|
||||
options.MonitorInterval = 200;
|
||||
});
|
||||
|
||||
// Process & Python
|
||||
services.AddSharpProcess();
|
||||
services.AddSharpPython();
|
||||
|
||||
// WPF 服务
|
||||
services.AddSharpHandyControl(); // HandyControl 对话框服务
|
||||
services.AddSharpDevTools(); // 开发者工具
|
||||
services.AddSingleton<IDispatcher>(new DispatcherService(Dispatcher));
|
||||
|
||||
// 业务服务(单例)
|
||||
services.AddSingleton<ServerService>();
|
||||
services.AddSingleton<WindowManager>(); // 窗口管理器
|
||||
|
||||
// ViewModels(瞬态 - 每次导航创建新实例)
|
||||
services.AddTransient<LauncherViewModel>(); // 启动器 ViewModel
|
||||
services.AddTransient<MainViewModel>();
|
||||
services.AddTransient<HomeViewModel>();
|
||||
|
||||
// P0 页面 ViewModels(优先实现)
|
||||
services.AddTransient<HttpServerViewModel>();
|
||||
services.AddTransient<DatabaseViewModel>();
|
||||
services.AddTransient<PlcDebugViewModel>();
|
||||
|
||||
// P1 页面 ViewModels(已实现)
|
||||
services.AddTransient<SerialPortViewModel>();
|
||||
services.AddTransient<ProcessTestViewModel>();
|
||||
|
||||
// 新增页面 ViewModels
|
||||
services.AddTransient<HttpClientViewModel>();
|
||||
services.AddTransient<LogDemoViewModel>();
|
||||
services.AddTransient<ConfigDemoViewModel>();
|
||||
services.AddTransient<WpfServicesViewModel>();
|
||||
|
||||
// Views(瞬态)
|
||||
services.AddTransient<LauncherWindow>(); // 启动器窗口
|
||||
services.AddTransient<MainWindow>();
|
||||
|
||||
// 模块窗口(瞬态 - 每次打开创建新实例)
|
||||
services.AddTransient<Sharp.Demo.Views.Windows.HttpServerWindow>();
|
||||
services.AddTransient<Sharp.Demo.Views.Windows.HttpClientWindow>();
|
||||
services.AddTransient<Sharp.Demo.Views.Windows.PlcDebugWindow>();
|
||||
// TODO: 添加其他窗口注册
|
||||
// services.AddTransient<Sharp.Demo.Views.Windows.DatabaseWindow>();
|
||||
// services.AddTransient<Sharp.Demo.Views.Windows.SerialPortWindow>();
|
||||
// etc...
|
||||
|
||||
// 模块窗口 ViewModels(瞬态)
|
||||
services.AddTransient<Sharp.Demo.ViewModels.Windows.HttpServerViewModel>();
|
||||
services.AddTransient<Sharp.Demo.ViewModels.Windows.HttpClientViewModel>();
|
||||
services.AddTransient<Sharp.Demo.ViewModels.Windows.PlcDebugViewModel>();
|
||||
// TODO: 添加其他 ViewModel 注册
|
||||
|
||||
// === 4. 构建容器 ===
|
||||
_serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
// === 5. 全局容器(兼容旧代码) ===
|
||||
Global.RegisterSingleton<IServiceProvider>(_serviceProvider);
|
||||
Global.RegisterSingleton(cm);
|
||||
Global.RegisterSingleton(_serviceProvider.GetRequiredService<IDispatcher>());
|
||||
|
||||
// === 6. 显示启动器窗口 ===
|
||||
var launcherWindow = _serviceProvider.GetRequiredService<LauncherWindow>();
|
||||
Current.MainWindow = launcherWindow;
|
||||
launcherWindow.Show();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册全局异常处理
|
||||
/// </summary>
|
||||
private void RegisterExceptionHandlers()
|
||||
{
|
||||
// UI 线程未处理异常
|
||||
DispatcherUnhandledException += (s, args) =>
|
||||
{
|
||||
if (_serviceProvider != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var logger = _serviceProvider.GetService<ILogger<App>>();
|
||||
logger?.LogError(args.Exception, "UI 线程未处理异常");
|
||||
}
|
||||
catch { /* 忽略日志失败 */ }
|
||||
}
|
||||
|
||||
MessageBox.Show(
|
||||
$"发生未处理异常: {args.Exception.Message}",
|
||||
"错误",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Error
|
||||
);
|
||||
args.Handled = true; // 防止崩溃
|
||||
};
|
||||
|
||||
// Task 未观察异常
|
||||
TaskScheduler.UnobservedTaskException += (s, args) =>
|
||||
{
|
||||
if (_serviceProvider != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var logger = _serviceProvider.GetService<ILogger<App>>();
|
||||
logger?.LogError(args.Exception, "Task 未观察异常");
|
||||
}
|
||||
catch { /* 忽略 */ }
|
||||
}
|
||||
args.SetObserved();
|
||||
};
|
||||
|
||||
// AppDomain 未处理异常
|
||||
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 { /* 忽略 */ }
|
||||
}
|
||||
|
||||
if (args.IsTerminating)
|
||||
{
|
||||
MessageBox.Show(
|
||||
$"发生严重错误,程序即将退出: {ex?.Message}",
|
||||
"严重错误",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Error
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void OnExit(ExitEventArgs e)
|
||||
{
|
||||
_serviceProvider?.Dispose();
|
||||
base.OnExit(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
using System.Windows;
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None,
|
||||
ResourceDictionaryLocation.SourceAssembly
|
||||
)]
|
||||
@@ -0,0 +1,57 @@
|
||||
using Sharp.NetServer.Attributes;
|
||||
using Sharp.NetServer.Controllers;
|
||||
using Sharp.NetServer.Models;
|
||||
|
||||
namespace Sharp.Demo.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// 演示控制器 - 提供各种测试 API
|
||||
/// </summary>
|
||||
[Route("api/demo")]
|
||||
public class DemoController : BaseController
|
||||
{
|
||||
/// <summary>
|
||||
/// Hello World
|
||||
/// GET /api/demo/hello
|
||||
/// </summary>
|
||||
[HttpGet("hello")]
|
||||
public async Task GetHello(HttpContext context)
|
||||
{
|
||||
await WriteSuccessResponseAsync(context, new
|
||||
{
|
||||
message = "Hello from Sharp.Demo!",
|
||||
timestamp = DateTime.Now
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Echo 测试
|
||||
/// POST /api/demo/echo
|
||||
/// </summary>
|
||||
[HttpPost("echo")]
|
||||
public async Task PostEcho(HttpContext context)
|
||||
{
|
||||
var body = await ReadRequestBodyAsync(context);
|
||||
await WriteSuccessResponseAsync(context, new
|
||||
{
|
||||
echo = body,
|
||||
length = body.Length,
|
||||
timestamp = DateTime.Now
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取服务器时间
|
||||
/// GET /api/demo/time
|
||||
/// </summary>
|
||||
[HttpGet("time")]
|
||||
public async Task GetTime(HttpContext context)
|
||||
{
|
||||
await WriteSuccessResponseAsync(context, new
|
||||
{
|
||||
utc = DateTime.UtcNow,
|
||||
local = DateTime.Now,
|
||||
timezone = TimeZoneInfo.Local.DisplayName
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using Sharp.NetServer.Attributes;
|
||||
using Sharp.NetServer.Controllers;
|
||||
using Sharp.NetServer.Models;
|
||||
|
||||
namespace Sharp.Demo.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// 健康检查控制器 - 提供服务器健康状态 API
|
||||
/// </summary>
|
||||
[Route("api/health")]
|
||||
public class HealthController : BaseController
|
||||
{
|
||||
/// <summary>
|
||||
/// 健康检查端点
|
||||
/// GET /api/health
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public async Task GetHealth(HttpContext context)
|
||||
{
|
||||
await WriteSuccessResponseAsync(context, new
|
||||
{
|
||||
status = "healthy",
|
||||
timestamp = DateTime.Now,
|
||||
version = "1.0.0",
|
||||
server = "Sharp.Demo"
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 详细健康检查
|
||||
/// GET /api/health/detailed
|
||||
/// </summary>
|
||||
[HttpGet("detailed")]
|
||||
public async Task GetDetailedHealth(HttpContext context)
|
||||
{
|
||||
await WriteSuccessResponseAsync(context, new
|
||||
{
|
||||
status = "healthy",
|
||||
timestamp = DateTime.Now,
|
||||
version = "1.0.0",
|
||||
server = "Sharp.Demo",
|
||||
uptime = DateTime.Now.ToString(),
|
||||
checks = new
|
||||
{
|
||||
database = "ok",
|
||||
memory = "ok",
|
||||
disk = "ok"
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Sharp.Data.Entities;
|
||||
|
||||
namespace Sharp.Demo.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// 示例用户实体 - 使用审计基类
|
||||
/// </summary>
|
||||
public class User : AuditableEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// 用户名
|
||||
/// </summary>
|
||||
public string UserName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 邮箱
|
||||
/// </summary>
|
||||
public string Email { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 年龄
|
||||
/// </summary>
|
||||
public int Age { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否激活
|
||||
/// </summary>
|
||||
public bool IsActive { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 备注
|
||||
/// </summary>
|
||||
public string? Remarks { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用数据库上下文
|
||||
/// </summary>
|
||||
public class AppDbContext : DbContext
|
||||
{
|
||||
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
|
||||
{
|
||||
}
|
||||
|
||||
public DbSet<User> Users => Set<User>();
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
// 配置 User 实体
|
||||
modelBuilder.Entity<User>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.UserName).IsRequired().HasMaxLength(50);
|
||||
entity.Property(e => e.Email).IsRequired().HasMaxLength(100);
|
||||
entity.Property(e => e.Remarks).HasMaxLength(500);
|
||||
|
||||
// 添加索引
|
||||
entity.HasIndex(e => e.UserName);
|
||||
entity.HasIndex(e => e.Email).IsUnique();
|
||||
entity.HasIndex(e => e.IsActive);
|
||||
entity.HasIndex(e => e.CreatedAt);
|
||||
});
|
||||
|
||||
// 添加示例数据
|
||||
modelBuilder.Entity<User>().HasData(
|
||||
new User
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserName = "admin",
|
||||
Email = "admin@example.com",
|
||||
Age = 30,
|
||||
IsActive = true,
|
||||
Remarks = "系统管理员",
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
CreatedBy = "system"
|
||||
},
|
||||
new User
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserName = "zhangsan",
|
||||
Email = "zhangsan@example.com",
|
||||
Age = 25,
|
||||
IsActive = true,
|
||||
Remarks = "测试用户1",
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
CreatedBy = "system"
|
||||
},
|
||||
new User
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserName = "lisi",
|
||||
Email = "lisi@example.com",
|
||||
Age = 28,
|
||||
IsActive = true,
|
||||
Remarks = "测试用户2",
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
CreatedBy = "system"
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
<hc:GlowWindow x:Class="Sharp.Demo.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||
xmlns:vm="clr-namespace:Sharp.Demo.ViewModels.Pages"
|
||||
xmlns:pages="clr-namespace:Sharp.Demo.Views.Pages"
|
||||
mc:Ignorable="d"
|
||||
Title="Sharp Framework Demo"
|
||||
Height="720"
|
||||
Width="1280"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
|
||||
<!-- 快捷键绑定 -->
|
||||
<hc:GlowWindow.InputBindings>
|
||||
<KeyBinding Key="F12" Command="{Binding OpenDevToolsCommand}" />
|
||||
<KeyBinding Key="F11" Command="{Binding OpenLogViewerCommand}" />
|
||||
<KeyBinding Key="F10" Command="{Binding OpenPerformanceMonitorCommand}" />
|
||||
</hc:GlowWindow.InputBindings>
|
||||
|
||||
<!-- 页面 ViewModel 到 View 的映射 -->
|
||||
<hc:GlowWindow.Resources>
|
||||
<DataTemplate DataType="{x:Type vm:HomeViewModel}">
|
||||
<pages:HomePage />
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type vm:HttpServerViewModel}">
|
||||
<pages:HttpServerPage />
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type vm:DatabaseViewModel}">
|
||||
<pages:DatabasePage />
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type vm:PlcDebugViewModel}">
|
||||
<pages:PlcDebugPage />
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type vm:SerialPortViewModel}">
|
||||
<pages:SerialPortPage />
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type vm:ProcessTestViewModel}">
|
||||
<pages:ProcessTestPage />
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type vm:HttpClientViewModel}">
|
||||
<pages:HttpClientPage />
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type vm:LogDemoViewModel}">
|
||||
<pages:LogDemoPage />
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type vm:ConfigDemoViewModel}">
|
||||
<pages:ConfigDemoPage />
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type vm:WpfServicesViewModel}">
|
||||
<pages:WpfServicesPage />
|
||||
</DataTemplate>
|
||||
<!-- 后续页面映射会在这里添加 -->
|
||||
</hc:GlowWindow.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="220" />
|
||||
<!-- 左侧导航 -->
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<!-- 分隔线 -->
|
||||
<ColumnDefinition Width="*" />
|
||||
<!-- 右侧内容 -->
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<!-- 状态栏 -->
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 左侧导航面板 -->
|
||||
<Border
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="0,0,1,0">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<!-- Logo/标题 -->
|
||||
<RowDefinition Height="*" />
|
||||
<!-- 导航菜单 -->
|
||||
<RowDefinition Height="Auto" />
|
||||
<!-- 底部工具 -->
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Logo 区域 -->
|
||||
<StackPanel Grid.Row="0" Margin="20,30,20,20">
|
||||
<TextBlock
|
||||
FontSize="20"
|
||||
FontWeight="Bold"
|
||||
Text="Sharp Framework" />
|
||||
<TextBlock
|
||||
Margin="0,5,0,0"
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource SecondaryTextBrush}"
|
||||
Text="模块演示平台" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- 导航菜单(TreeView) -->
|
||||
<TreeView
|
||||
Grid.Row="1"
|
||||
BorderThickness="0"
|
||||
Margin="10,0"
|
||||
SelectedItemChanged="TreeView_SelectedItemChanged">
|
||||
|
||||
<!-- 概览 -->
|
||||
<TreeViewItem Header="概览" IsExpanded="True">
|
||||
<TreeViewItem Header="主页" Tag="Home" />
|
||||
</TreeViewItem>
|
||||
|
||||
<!-- 网络通信 -->
|
||||
<TreeViewItem Header="网络通信" IsExpanded="True">
|
||||
<TreeViewItem Header="HTTP 服务器" Tag="HttpServer" />
|
||||
<TreeViewItem Header="HTTP 客户端" Tag="HttpClient" />
|
||||
</TreeViewItem>
|
||||
|
||||
<!-- 数据管理 -->
|
||||
<TreeViewItem Header="数据管理" IsExpanded="True">
|
||||
<TreeViewItem Header="数据库 (EF Core)" Tag="Database" />
|
||||
</TreeViewItem>
|
||||
|
||||
<!-- 工业控制 -->
|
||||
<TreeViewItem Header="工业控制" IsExpanded="True">
|
||||
<TreeViewItem Header="PLC 调试" Tag="PlcDebug" />
|
||||
<TreeViewItem Header="串口调试" Tag="SerialPort" />
|
||||
</TreeViewItem>
|
||||
|
||||
<!-- 进程管理 -->
|
||||
<TreeViewItem Header="进程管理" IsExpanded="False">
|
||||
<TreeViewItem Header="Process 执行" Tag="ProcessTest" />
|
||||
<TreeViewItem Header="Python 集成" Tag="PythonTest" />
|
||||
</TreeViewItem>
|
||||
|
||||
<!-- 基础设施 -->
|
||||
<TreeViewItem Header="基础设施" IsExpanded="False">
|
||||
<TreeViewItem Header="日志管理" Tag="LogDemo" />
|
||||
<TreeViewItem Header="配置管理" Tag="ConfigDemo" />
|
||||
<TreeViewItem Header="WPF 服务" Tag="WpfServices" />
|
||||
</TreeViewItem>
|
||||
</TreeView>
|
||||
|
||||
<!-- 底部工具栏 -->
|
||||
<StackPanel Grid.Row="2" Margin="15,10">
|
||||
<Button
|
||||
Margin="0,0,0,5"
|
||||
Command="{Binding OpenDevToolsCommand}"
|
||||
Content="开发者工具 (F12)" />
|
||||
<Button
|
||||
Margin="0,0,0,5"
|
||||
Command="{Binding OpenLogViewerCommand}"
|
||||
Content="日志查看 (F11)" />
|
||||
<Button
|
||||
Command="{Binding OpenPerformanceMonitorCommand}"
|
||||
Content="性能监控 (F10)" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- 分隔线 -->
|
||||
<GridSplitter
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Width="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
Background="{DynamicResource BorderBrush}" />
|
||||
|
||||
<!-- 右侧内容区域 -->
|
||||
<Border
|
||||
Grid.Row="0"
|
||||
Grid.Column="2"
|
||||
Background="{DynamicResource SecondaryRegionBrush}">
|
||||
<ContentControl Content="{Binding CurrentPage}" Margin="20" />
|
||||
</Border>
|
||||
|
||||
<!-- 状态栏 -->
|
||||
<StatusBar
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="3"
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="0,1,0,0">
|
||||
<StatusBarItem>
|
||||
<TextBlock>
|
||||
<Run Text="HTTP Server: " />
|
||||
<Run FontWeight="Bold" Text="{Binding ServerStatus, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
</StatusBarItem>
|
||||
<Separator />
|
||||
<StatusBarItem>
|
||||
<TextBlock>
|
||||
<Run Text="PLC: " />
|
||||
<Run FontWeight="Bold" Text="{Binding PlcStatus, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
</StatusBarItem>
|
||||
<Separator />
|
||||
<StatusBarItem>
|
||||
<TextBlock>
|
||||
<Run Text="当前页面: " />
|
||||
<Run Text="{Binding CurrentPageName, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
</StatusBarItem>
|
||||
</StatusBar>
|
||||
</Grid>
|
||||
</hc:GlowWindow>
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using HandyControl.Controls;
|
||||
using Sharp.Demo.ViewModels;
|
||||
|
||||
namespace Sharp.Demo
|
||||
{
|
||||
/// <summary>
|
||||
/// MainWindow.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class MainWindow : GlowWindow
|
||||
{
|
||||
public MainWindow(MainViewModel viewModel)
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = viewModel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TreeView 选择变化事件处理
|
||||
/// </summary>
|
||||
private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
|
||||
{
|
||||
if (e.NewValue is TreeViewItem { Tag: string pageKey } && pageKey != "Category")
|
||||
{
|
||||
if (DataContext is MainViewModel vm)
|
||||
{
|
||||
vm.NavigateCommand.Execute(pageKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Sharp.Demo.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// 模块信息 - 描述一个功能模块
|
||||
/// </summary>
|
||||
public class ModuleInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 模块唯一标识
|
||||
/// </summary>
|
||||
public string Key { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 模块名称
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 模块描述
|
||||
/// </summary>
|
||||
public string Description { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 模块图标(Emoji 或字体图标)
|
||||
/// </summary>
|
||||
public string Icon { get; set; } = "📦";
|
||||
|
||||
/// <summary>
|
||||
/// 模块分类
|
||||
/// </summary>
|
||||
public string Category { get; set; } = "其他";
|
||||
|
||||
/// <summary>
|
||||
/// 是否已启用
|
||||
/// </summary>
|
||||
public bool IsEnabled { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 当前状态描述
|
||||
/// </summary>
|
||||
public string Status { get; set; } = "就绪";
|
||||
|
||||
/// <summary>
|
||||
/// 打开窗口的命令
|
||||
/// </summary>
|
||||
public ICommand? OpenCommand { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Sharp.NetServer;
|
||||
using Sharp.NetServer.Server;
|
||||
|
||||
namespace Sharp.Demo.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// HTTP 服务器服务 - 管理服务器生命周期
|
||||
/// </summary>
|
||||
public class ServerService : IDisposable
|
||||
{
|
||||
private HttpServer? _httpServer;
|
||||
private Task? _serverTask;
|
||||
private CancellationTokenSource? _cts;
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly ILogger<ServerService> _logger;
|
||||
|
||||
public bool IsRunning { get; private set; }
|
||||
public string Url => $"http://{Host}:{Port}";
|
||||
public string Host { get; private set; } = "localhost";
|
||||
public int Port { get; private set; } = 8080;
|
||||
|
||||
public event EventHandler<string>? StatusChanged;
|
||||
|
||||
public ServerService(IConfiguration configuration, ILoggerFactory loggerFactory)
|
||||
{
|
||||
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
|
||||
_loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
|
||||
_logger = loggerFactory.CreateLogger<ServerService>();
|
||||
|
||||
// 从配置读取服务器设置
|
||||
Host = _configuration.GetValue<string>("Server:Host") ?? "localhost";
|
||||
Port = _configuration.GetValue<int>("Server:Port", 8080);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动服务器
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (IsRunning)
|
||||
{
|
||||
_logger.LogWarning("服务器已经在运行");
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogInformation("正在启动 HTTP 服务器: {Url}", Url);
|
||||
OnStatusChanged($"正在启动服务器 {Url}...");
|
||||
|
||||
var builder = new HttpServerBuilder()
|
||||
.UseUrls(Url + "/")
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
// 注册配置和日志工厂
|
||||
services.AddSingleton(_configuration);
|
||||
services.AddSingleton(_loggerFactory);
|
||||
})
|
||||
// 注册控制器(从当前程序集)
|
||||
.AddControllers<ServerService>();
|
||||
|
||||
_httpServer = builder.Build();
|
||||
|
||||
// 在后台启动服务器
|
||||
_cts = new CancellationTokenSource();
|
||||
_serverTask = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await _httpServer.StartAsync(_cts.Token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "服务器任务错误");
|
||||
OnStatusChanged($"服务器运行错误: {ex.Message}");
|
||||
IsRunning = false;
|
||||
}
|
||||
}, _cts.Token);
|
||||
|
||||
// 等待服务器启动
|
||||
Thread.Sleep(500);
|
||||
|
||||
IsRunning = true;
|
||||
_logger.LogInformation("HTTP 服务器启动成功: {Url}", Url);
|
||||
OnStatusChanged($"服务器已启动: {Url}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
IsRunning = false;
|
||||
_logger.LogError(ex, "HTTP 服务器启动失败");
|
||||
OnStatusChanged($"启动失败: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止服务器
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!IsRunning)
|
||||
{
|
||||
_logger.LogWarning("服务器未运行");
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogInformation("正在停止 HTTP 服务器");
|
||||
OnStatusChanged("正在停止服务器...");
|
||||
|
||||
_cts?.Cancel();
|
||||
_httpServer?.Stop();
|
||||
_serverTask?.Wait(TimeSpan.FromSeconds(5));
|
||||
|
||||
IsRunning = false;
|
||||
_logger.LogInformation("HTTP 服务器停止成功");
|
||||
OnStatusChanged("服务器已停止");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "HTTP 服务器停止失败");
|
||||
OnStatusChanged($"停止失败: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重启服务器
|
||||
/// </summary>
|
||||
public void Restart()
|
||||
{
|
||||
_logger.LogInformation("正在重启 HTTP 服务器");
|
||||
OnStatusChanged("正在重启服务器...");
|
||||
|
||||
Stop();
|
||||
Thread.Sleep(500); // 等待端口释放
|
||||
Start();
|
||||
|
||||
OnStatusChanged("服务器已重启");
|
||||
}
|
||||
|
||||
private void OnStatusChanged(string status)
|
||||
{
|
||||
StatusChanged?.Invoke(this, status);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (IsRunning)
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
_cts?.Dispose();
|
||||
_serverTask?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Sharp.Demo.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// 窗口管理器 - 管理所有模块窗口的生命周期
|
||||
/// </summary>
|
||||
public class WindowManager
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly ILogger<WindowManager> _logger;
|
||||
private readonly Dictionary<string, Window> _openWindows = new();
|
||||
|
||||
public WindowManager(IServiceProvider serviceProvider, ILogger<WindowManager> logger)
|
||||
{
|
||||
_serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_logger.LogInformation("窗口管理器已初始化");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 打开或激活指定模块的窗口(单例模式)
|
||||
/// </summary>
|
||||
/// <typeparam name="TWindow">窗口类型</typeparam>
|
||||
/// <param name="moduleKey">模块唯一标识</param>
|
||||
/// <param name="ownerWindow">父窗口(可选)</param>
|
||||
public void ShowWindow<TWindow>(string moduleKey, Window? ownerWindow = null) where TWindow : Window
|
||||
{
|
||||
try
|
||||
{
|
||||
// 如果窗口已打开,激活它
|
||||
if (_openWindows.TryGetValue(moduleKey, out var existingWindow))
|
||||
{
|
||||
_logger.LogInformation("激活已打开的窗口: {ModuleKey} ({WindowType})", moduleKey, typeof(TWindow).Name);
|
||||
ActivateWindow(existingWindow);
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogInformation("正在创建新窗口: {ModuleKey} ({WindowType})", moduleKey, typeof(TWindow).Name);
|
||||
|
||||
// 创建新窗口
|
||||
var window = _serviceProvider.GetRequiredService<TWindow>();
|
||||
if (ownerWindow != null)
|
||||
{
|
||||
window.Owner = ownerWindow;
|
||||
}
|
||||
|
||||
// 注册窗口关闭事件
|
||||
window.Closed += (s, e) => OnWindowClosed(moduleKey);
|
||||
|
||||
// 保存窗口引用
|
||||
_openWindows[moduleKey] = window;
|
||||
|
||||
// 显示窗口
|
||||
window.Show();
|
||||
|
||||
_logger.LogInformation("窗口已成功打开: {ModuleKey}, 当前打开窗口数: {Count}", moduleKey, _openWindows.Count);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "打开窗口失败: {ModuleKey} ({WindowType})", moduleKey, typeof(TWindow).Name);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关闭指定模块的窗口
|
||||
/// </summary>
|
||||
public void CloseWindow(string moduleKey)
|
||||
{
|
||||
if (_openWindows.TryGetValue(moduleKey, out var window))
|
||||
{
|
||||
_logger.LogInformation("正在关闭窗口: {ModuleKey}", moduleKey);
|
||||
window.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("尝试关闭不存在的窗口: {ModuleKey}", moduleKey);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关闭所有窗口
|
||||
/// </summary>
|
||||
public void CloseAllWindows()
|
||||
{
|
||||
_logger.LogInformation("正在关闭所有窗口, 当前打开窗口数: {Count}", _openWindows.Count);
|
||||
var windows = _openWindows.Values.ToList();
|
||||
foreach (var window in windows)
|
||||
{
|
||||
window.Close();
|
||||
}
|
||||
_logger.LogInformation("所有窗口已关闭");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定模块的窗口
|
||||
/// </summary>
|
||||
public Window? GetWindow(string moduleKey)
|
||||
{
|
||||
return _openWindows.TryGetValue(moduleKey, out var window) ? window : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查窗口是否已打开
|
||||
/// </summary>
|
||||
public bool IsWindowOpen(string moduleKey)
|
||||
{
|
||||
return _openWindows.ContainsKey(moduleKey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有已打开的窗口数量
|
||||
/// </summary>
|
||||
public int OpenWindowCount => _openWindows.Count;
|
||||
|
||||
/// <summary>
|
||||
/// 激活窗口
|
||||
/// </summary>
|
||||
private void ActivateWindow(Window window)
|
||||
{
|
||||
if (window.WindowState == WindowState.Minimized)
|
||||
{
|
||||
window.WindowState = WindowState.Normal;
|
||||
}
|
||||
|
||||
window.Activate();
|
||||
window.Focus();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 窗口关闭时的处理
|
||||
/// </summary>
|
||||
private void OnWindowClosed(string moduleKey)
|
||||
{
|
||||
_openWindows.Remove(moduleKey);
|
||||
_logger.LogInformation("窗口已关闭: {ModuleKey}, 剩余打开窗口数: {Count}", moduleKey, _openWindows.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UseWPF>true</UseWPF>
|
||||
<AssemblyName>Sharp.Demo</AssemblyName>
|
||||
<RootNamespace>Sharp.Demo</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- NuGet 包 -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- 项目引用 -->
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Common\Common.csproj" />
|
||||
<ProjectReference Include="..\Config\Config.csproj" />
|
||||
<ProjectReference Include="..\Wpf\Wpf.csproj" />
|
||||
<ProjectReference Include="..\Wpf.HandyControl\Wpf.HandyControl.csproj" />
|
||||
<ProjectReference Include="..\Wpf.DevTools\Wpf.DevTools.csproj" />
|
||||
<ProjectReference Include="..\Log\Log.csproj" />
|
||||
<ProjectReference Include="..\Data\Data.csproj" />
|
||||
<ProjectReference Include="..\NetClient\NetClient.csproj" />
|
||||
<ProjectReference Include="..\NetServer\NetServer.csproj" />
|
||||
<ProjectReference Include="..\Plc.Siemens\Plc.Siemens.csproj" />
|
||||
<ProjectReference Include="..\Ports\Ports.csproj" />
|
||||
<ProjectReference Include="..\Process\Process.csproj" />
|
||||
<ProjectReference Include="..\Python\Python.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- 配置文件 -->
|
||||
<ItemGroup>
|
||||
<None Update="appsettings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,123 @@
|
||||
# HTTP 服务器测试指南
|
||||
|
||||
## 问题修复总结
|
||||
|
||||
### 修复的问题
|
||||
|
||||
1. **端口不匹配**
|
||||
- 之前:服务器监听 8080,但客户端请求 5000
|
||||
- 现在:统一使用配置文件中的 8080 端口
|
||||
|
||||
2. **缺少控制器**
|
||||
- 之前:没有任何 HTTP 控制器,导致所有请求返回 404
|
||||
- 现在:添加了 `HealthController` 和 `DemoController`
|
||||
|
||||
3. **HandyControl 兼容性**
|
||||
- 之前:HandyControl 3.5.0 与 .NET 8 不兼容
|
||||
- 现在:升级到 HandyControl 3.5.1
|
||||
|
||||
## 测试步骤
|
||||
|
||||
### 1. 启动 Sharp.Demo 应用
|
||||
|
||||
```bash
|
||||
cd D:\yuanll\sharp-basic\src\Sharp\Sharp.Demo
|
||||
dotnet run
|
||||
```
|
||||
|
||||
### 2. 打开 HTTP 服务器窗口
|
||||
|
||||
在启动器中点击 "HTTP 服务器" 按钮
|
||||
|
||||
### 3. 启动服务器
|
||||
|
||||
点击 "启动服务器" 按钮,应该看到:
|
||||
- 状态变为 "运行中" (绿色)
|
||||
- 日志显示 "✅ 服务器已启动: http://localhost:8080"
|
||||
|
||||
### 4. 测试可用的 API 端点
|
||||
|
||||
#### 使用浏览器测试
|
||||
|
||||
打开浏览器访问以下地址:
|
||||
|
||||
- **健康检查**: http://localhost:8080/api/health
|
||||
- **详细健康检查**: http://localhost:8080/api/health/detailed
|
||||
- **Hello World**: http://localhost:8080/api/demo/hello
|
||||
- **服务器时间**: http://localhost:8080/api/demo/time
|
||||
|
||||
#### 使用 HTTP 客户端窗口测试
|
||||
|
||||
1. 打开 "HTTP 客户端" 窗口
|
||||
2. 默认 URL 已经设置为 `http://localhost:8080/api/health`
|
||||
3. 点击 "发送请求" 或 "测试本地服务器"
|
||||
4. 应该看到成功的响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"status": "healthy",
|
||||
"timestamp": "2025-12-24T19:20:00",
|
||||
"version": "1.0.0",
|
||||
"server": "Sharp.Demo"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 使用 curl 测试
|
||||
|
||||
```bash
|
||||
# 健康检查
|
||||
curl http://localhost:8080/api/health
|
||||
|
||||
# POST 请求测试
|
||||
curl -X POST http://localhost:8080/api/demo/echo -H "Content-Type: application/json" -d "{\"test\": \"hello\"}"
|
||||
```
|
||||
|
||||
## 已注册的路由
|
||||
|
||||
| 方法 | 路径 | 描述 |
|
||||
|------|------|------|
|
||||
| GET | /api/health | 基本健康检查 |
|
||||
| GET | /api/health/detailed | 详细健康检查 |
|
||||
| GET | /api/demo/hello | Hello World 测试 |
|
||||
| GET | /api/demo/time | 获取服务器时间 |
|
||||
| POST | /api/demo/echo | Echo 测试(返回请求体) |
|
||||
|
||||
## 配置说明
|
||||
|
||||
服务器配置在 `appsettings.json` 中:
|
||||
|
||||
```json
|
||||
{
|
||||
"Server": {
|
||||
"Port": 8080,
|
||||
"Host": "localhost",
|
||||
"MaxConcurrentRequests": 100
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
可以修改端口和主机地址,重启服务器后生效。
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 端口被占用
|
||||
|
||||
如果看到 "端口被占用" 错误:
|
||||
1. 修改 `appsettings.json` 中的端口号
|
||||
2. 重新启动应用
|
||||
|
||||
### 防火墙阻止
|
||||
|
||||
如果其他计算机无法访问:
|
||||
1. 检查 Windows 防火墙设置
|
||||
2. 确保允许 8080 端口的入站连接
|
||||
|
||||
### 客户端连接失败
|
||||
|
||||
确认:
|
||||
1. 服务器确实已启动(状态为"运行中")
|
||||
2. URL 端口号正确(8080)
|
||||
3. 使用的是 `localhost` 或 `127.0.0.1`
|
||||
@@ -0,0 +1,401 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Windows.Threading;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Common;
|
||||
using Sharp.Wpf.Mvvm;
|
||||
using Sharp.Wpf.Commands;
|
||||
using Sharp.Demo.Services;
|
||||
using Sharp.Demo.Models;
|
||||
|
||||
namespace Sharp.Demo.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// 启动器 ViewModel - 管理所有模块的启动
|
||||
/// </summary>
|
||||
public class LauncherViewModel : ViewModelBase
|
||||
{
|
||||
private readonly WindowManager _windowManager;
|
||||
private readonly ServerService _serverService;
|
||||
private readonly ILogger<LauncherViewModel> _logger;
|
||||
private readonly DispatcherTimer _timer;
|
||||
private readonly DateTime _startTime;
|
||||
|
||||
private string _serverStatus = "未运行";
|
||||
private string _plcStatus = "未连接";
|
||||
private int _openWindowCount = 0;
|
||||
private string _uptime = "00:00:00";
|
||||
|
||||
public LauncherViewModel(WindowManager windowManager, ServerService serverService, ILogger<LauncherViewModel> logger)
|
||||
{
|
||||
_windowManager = windowManager ?? throw new ArgumentNullException(nameof(windowManager));
|
||||
_serverService = serverService ?? throw new ArgumentNullException(nameof(serverService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_startTime = DateTime.Now;
|
||||
|
||||
_logger.LogInformation("启动器 ViewModel 已初始化");
|
||||
|
||||
// 初始化命令
|
||||
OpenDevToolsCommand = new RelayCommand(OpenDevTools);
|
||||
OpenLogViewerCommand = new RelayCommand(OpenLogViewer);
|
||||
OpenPerformanceMonitorCommand = new RelayCommand(OpenPerformanceMonitor);
|
||||
ShowAboutCommand = new RelayCommand(ShowAbout);
|
||||
|
||||
// 初始化模块列表
|
||||
InitializeModules();
|
||||
|
||||
// 订阅服务器状态
|
||||
_serverService.StatusChanged += OnServerStatusChanged;
|
||||
|
||||
// 启动定时器(更新运行时间和窗口计数)
|
||||
_timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
|
||||
_timer.Tick += OnTimerTick;
|
||||
_timer.Start();
|
||||
}
|
||||
|
||||
#region 属性
|
||||
|
||||
public string ServerStatus
|
||||
{
|
||||
get => _serverStatus;
|
||||
set => SetProperty(ref _serverStatus, value);
|
||||
}
|
||||
|
||||
public string PlcStatus
|
||||
{
|
||||
get => _plcStatus;
|
||||
set => SetProperty(ref _plcStatus, value);
|
||||
}
|
||||
|
||||
public int OpenWindowCount
|
||||
{
|
||||
get => _openWindowCount;
|
||||
set => SetProperty(ref _openWindowCount, value);
|
||||
}
|
||||
|
||||
public string Uptime
|
||||
{
|
||||
get => _uptime;
|
||||
set => SetProperty(ref _uptime, value);
|
||||
}
|
||||
|
||||
public ObservableCollection<ModuleCategory> ModuleCategories { get; } = new();
|
||||
|
||||
#endregion
|
||||
|
||||
#region 命令
|
||||
|
||||
public RelayCommand OpenDevToolsCommand { get; }
|
||||
public RelayCommand OpenLogViewerCommand { get; }
|
||||
public RelayCommand OpenPerformanceMonitorCommand { get; }
|
||||
public RelayCommand ShowAboutCommand { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region 模块初始化
|
||||
|
||||
private void InitializeModules()
|
||||
{
|
||||
// 网络通信模块
|
||||
var networkCategory = new ModuleCategory { CategoryName = "🌐 网络通信" };
|
||||
networkCategory.Modules.Add(new ModuleInfo
|
||||
{
|
||||
Key = "HttpServer",
|
||||
Name = "HTTP 服务器",
|
||||
Description = "轻量级 HTTP 服务器,支持中间件和路由",
|
||||
Icon = "🖥️",
|
||||
Category = "网络通信",
|
||||
OpenCommand = new RelayCommand(() => OpenModuleWindow("HttpServer"))
|
||||
});
|
||||
networkCategory.Modules.Add(new ModuleInfo
|
||||
{
|
||||
Key = "HttpClient",
|
||||
Name = "HTTP 客户端",
|
||||
Description = "集成 Polly 重试策略的 HTTP 客户端",
|
||||
Icon = "📡",
|
||||
Category = "网络通信",
|
||||
OpenCommand = new RelayCommand(() => OpenModuleWindow("HttpClient"))
|
||||
});
|
||||
ModuleCategories.Add(networkCategory);
|
||||
|
||||
// 数据管理模块
|
||||
var dataCategory = new ModuleCategory { CategoryName = "💾 数据管理" };
|
||||
dataCategory.Modules.Add(new ModuleInfo
|
||||
{
|
||||
Key = "Database",
|
||||
Name = "数据库管理",
|
||||
Description = "EF Core + Repository 模式",
|
||||
Icon = "🗄️",
|
||||
Category = "数据管理",
|
||||
OpenCommand = new RelayCommand(() => OpenModuleWindow("Database"))
|
||||
});
|
||||
ModuleCategories.Add(dataCategory);
|
||||
|
||||
// 工业控制模块
|
||||
var industrialCategory = new ModuleCategory { CategoryName = "🏭 工业控制" };
|
||||
industrialCategory.Modules.Add(new ModuleInfo
|
||||
{
|
||||
Key = "PlcDebug",
|
||||
Name = "PLC 调试",
|
||||
Description = "西门子 PLC 通信调试工具",
|
||||
Icon = "🔧",
|
||||
Category = "工业控制",
|
||||
OpenCommand = new RelayCommand(() => OpenModuleWindow("PlcDebug"))
|
||||
});
|
||||
industrialCategory.Modules.Add(new ModuleInfo
|
||||
{
|
||||
Key = "SerialPort",
|
||||
Name = "串口调试",
|
||||
Description = "RS232/RS485 串口通信工具",
|
||||
Icon = "🔌",
|
||||
Category = "工业控制",
|
||||
OpenCommand = new RelayCommand(() => OpenModuleWindow("SerialPort"))
|
||||
});
|
||||
ModuleCategories.Add(industrialCategory);
|
||||
|
||||
// 进程管理模块
|
||||
var processCategory = new ModuleCategory { CategoryName = "⚙️ 进程管理" };
|
||||
processCategory.Modules.Add(new ModuleInfo
|
||||
{
|
||||
Key = "ProcessTest",
|
||||
Name = "Process 执行",
|
||||
Description = "外部进程执行和管理",
|
||||
Icon = "▶️",
|
||||
Category = "进程管理",
|
||||
OpenCommand = new RelayCommand(() => OpenModuleWindow("ProcessTest"))
|
||||
});
|
||||
processCategory.Modules.Add(new ModuleInfo
|
||||
{
|
||||
Key = "PythonTest",
|
||||
Name = "Python 集成",
|
||||
Description = "Python 脚本执行和集成",
|
||||
Icon = "🐍",
|
||||
Category = "进程管理",
|
||||
Status = "开发中",
|
||||
IsEnabled = false,
|
||||
OpenCommand = new RelayCommand(() => { /* 待实现 */ })
|
||||
});
|
||||
ModuleCategories.Add(processCategory);
|
||||
|
||||
// 基础设施模块
|
||||
var infrastructureCategory = new ModuleCategory { CategoryName = "🔨 基础设施" };
|
||||
infrastructureCategory.Modules.Add(new ModuleInfo
|
||||
{
|
||||
Key = "LogDemo",
|
||||
Name = "日志管理",
|
||||
Description = "Serilog 结构化日志系统",
|
||||
Icon = "📝",
|
||||
Category = "基础设施",
|
||||
OpenCommand = new RelayCommand(() => OpenModuleWindow("LogDemo"))
|
||||
});
|
||||
infrastructureCategory.Modules.Add(new ModuleInfo
|
||||
{
|
||||
Key = "ConfigDemo",
|
||||
Name = "配置管理",
|
||||
Description = "多格式配置文件管理",
|
||||
Icon = "⚙️",
|
||||
Category = "基础设施",
|
||||
OpenCommand = new RelayCommand(() => OpenModuleWindow("ConfigDemo"))
|
||||
});
|
||||
infrastructureCategory.Modules.Add(new ModuleInfo
|
||||
{
|
||||
Key = "WpfServices",
|
||||
Name = "WPF 服务",
|
||||
Description = "对话框、文件选择、MVVM 基础",
|
||||
Icon = "🎨",
|
||||
Category = "基础设施",
|
||||
OpenCommand = new RelayCommand(() => OpenModuleWindow("WpfServices"))
|
||||
});
|
||||
ModuleCategories.Add(infrastructureCategory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 打开模块窗口
|
||||
/// </summary>
|
||||
private void OpenModuleWindow(string moduleKey)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("用户点击模块卡片: {ModuleKey}", moduleKey);
|
||||
|
||||
switch (moduleKey)
|
||||
{
|
||||
case "HttpServer":
|
||||
_logger.LogInformation("正在打开 HTTP 服务器窗口...");
|
||||
_windowManager.ShowWindow<Sharp.Demo.Views.Windows.HttpServerWindow>(moduleKey);
|
||||
break;
|
||||
|
||||
case "HttpClient":
|
||||
_logger.LogInformation("正在打开 HTTP 客户端窗口...");
|
||||
_windowManager.ShowWindow<Sharp.Demo.Views.Windows.HttpClientWindow>(moduleKey);
|
||||
break;
|
||||
|
||||
case "PlcDebug":
|
||||
_logger.LogInformation("正在打开 PLC 调试窗口...");
|
||||
_windowManager.ShowWindow<Sharp.Demo.Views.Windows.PlcDebugWindow>(moduleKey);
|
||||
break;
|
||||
|
||||
// 其他窗口待实现
|
||||
case "Database":
|
||||
case "SerialPort":
|
||||
case "ProcessTest":
|
||||
case "LogDemo":
|
||||
case "ConfigDemo":
|
||||
case "WpfServices":
|
||||
_logger.LogWarning("模块 {ModuleKey} 窗口尚未实现", moduleKey);
|
||||
System.Windows.MessageBox.Show(
|
||||
$"{moduleKey} 窗口开发中...\n\n请按照 HttpServerWindow 的模板创建对应的窗口。",
|
||||
"提示",
|
||||
System.Windows.MessageBoxButton.OK,
|
||||
System.Windows.MessageBoxImage.Information
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
_logger.LogError("未知的模块: {ModuleKey}", moduleKey);
|
||||
System.Windows.MessageBox.Show(
|
||||
$"未知的模块: {moduleKey}",
|
||||
"错误",
|
||||
System.Windows.MessageBoxButton.OK,
|
||||
System.Windows.MessageBoxImage.Warning
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "打开模块失败: {ModuleKey}", moduleKey);
|
||||
System.Windows.MessageBox.Show(
|
||||
$"打开模块失败: {ex.Message}\n\n详细信息已记录到日志中。",
|
||||
"错误",
|
||||
System.Windows.MessageBoxButton.OK,
|
||||
System.Windows.MessageBoxImage.Error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 开发者工具
|
||||
|
||||
private void OpenDevTools()
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("用户点击了 F12 开发者工具");
|
||||
var sp = Global.Get<IServiceProvider>();
|
||||
var window = sp?.GetService<Sharp.Wpf.DevTools.Views.DevToolsWindow>();
|
||||
window?.Show();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "打开开发者工具失败");
|
||||
System.Windows.MessageBox.Show($"打开开发者工具失败: {ex.Message}", "错误");
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenLogViewer()
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("用户点击了 F11 日志查看器");
|
||||
var sp = Global.Get<IServiceProvider>();
|
||||
var window = sp?.GetService<Sharp.Wpf.DevTools.Views.LogViewerWindow>();
|
||||
window?.Show();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "打开日志查看器失败");
|
||||
System.Windows.MessageBox.Show($"打开日志查看器失败: {ex.Message}", "错误");
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenPerformanceMonitor()
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("用户点击了 F10 性能监控");
|
||||
var sp = Global.Get<IServiceProvider>();
|
||||
var window = sp?.GetService<Sharp.Wpf.DevTools.Views.PerformanceMonitorWindow>();
|
||||
window?.Show();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "打开性能监控失败");
|
||||
System.Windows.MessageBox.Show($"打开性能监控失败: {ex.Message}", "错误");
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowAbout()
|
||||
{
|
||||
_logger.LogInformation("用户查看了关于对话框");
|
||||
System.Windows.MessageBox.Show(
|
||||
"Sharp Framework v1.0.0\n\n" +
|
||||
"模块化 .NET 8 开发框架\n" +
|
||||
"功能演示和测试平台\n\n" +
|
||||
"© 2024 Sharp Team",
|
||||
"关于",
|
||||
System.Windows.MessageBoxButton.OK,
|
||||
System.Windows.MessageBoxImage.Information
|
||||
);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 定时器和事件
|
||||
|
||||
private void OnTimerTick(object? sender, EventArgs e)
|
||||
{
|
||||
// 更新运行时间
|
||||
var elapsed = DateTime.Now - _startTime;
|
||||
Uptime = $"{elapsed.Hours:D2}:{elapsed.Minutes:D2}:{elapsed.Seconds:D2}";
|
||||
|
||||
// 更新窗口计数
|
||||
OpenWindowCount = _windowManager.OpenWindowCount;
|
||||
}
|
||||
|
||||
private void OnServerStatusChanged(object? sender, string status)
|
||||
{
|
||||
ServerStatus = _serverService.IsRunning ? "运行中" : "未运行";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 公共方法
|
||||
|
||||
/// <summary>
|
||||
/// 关闭所有模块窗口
|
||||
/// </summary>
|
||||
public void CloseAllWindows()
|
||||
{
|
||||
_logger.LogInformation("用户请求关闭所有窗口");
|
||||
_windowManager.CloseAllWindows();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 清理
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
_logger.LogInformation("启动器 ViewModel 正在释放资源");
|
||||
_timer?.Stop();
|
||||
_serverService.StatusChanged -= OnServerStatusChanged;
|
||||
base.Dispose();
|
||||
_logger.LogInformation("启动器 ViewModel 已释放");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 模块分类
|
||||
/// </summary>
|
||||
public class ModuleCategory
|
||||
{
|
||||
public string CategoryName { get; set; } = string.Empty;
|
||||
public ObservableCollection<ModuleInfo> Modules { get; } = new();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Common;
|
||||
using Sharp.Wpf.Mvvm;
|
||||
using Sharp.Wpf.Abstractions;
|
||||
using Sharp.Wpf.Commands;
|
||||
using Sharp.Wpf.Navigation;
|
||||
using Sharp.Demo.ViewModels.Pages;
|
||||
using Sharp.Demo.Services;
|
||||
|
||||
namespace Sharp.Demo.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// 主窗口 ViewModel - 负责导航管理和全局状态
|
||||
/// </summary>
|
||||
public class MainViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly ServerService _serverService;
|
||||
private ViewModelBase? _currentPage;
|
||||
private string _serverStatus = "未运行";
|
||||
private string _plcStatus = "未连接";
|
||||
private string _currentPageName = "主页";
|
||||
|
||||
public MainViewModel(IServiceProvider serviceProvider, ServerService serverService)
|
||||
{
|
||||
_serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
|
||||
_serverService = serverService ?? throw new ArgumentNullException(nameof(serverService));
|
||||
|
||||
// 订阅服务器状态变化
|
||||
_serverService.StatusChanged += OnServerStatusChanged;
|
||||
|
||||
// 初始化命令
|
||||
NavigateCommand = new RelayCommand<string>(async pageKey => await NavigateToAsync(pageKey ?? "Home"));
|
||||
OpenDevToolsCommand = new RelayCommand(OpenDevTools);
|
||||
OpenLogViewerCommand = new RelayCommand(OpenLogViewer);
|
||||
OpenPerformanceMonitorCommand = new RelayCommand(OpenPerformanceMonitor);
|
||||
|
||||
// 默认导航到首页
|
||||
_ = NavigateToAsync("Home");
|
||||
}
|
||||
|
||||
#region 属性
|
||||
|
||||
/// <summary>
|
||||
/// 当前显示的页面 ViewModel
|
||||
/// </summary>
|
||||
public ViewModelBase? CurrentPage
|
||||
{
|
||||
get => _currentPage;
|
||||
set => SetProperty(ref _currentPage, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// HTTP 服务器状态
|
||||
/// </summary>
|
||||
public string ServerStatus
|
||||
{
|
||||
get => _serverStatus;
|
||||
set => SetProperty(ref _serverStatus, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// PLC 状态
|
||||
/// </summary>
|
||||
public string PlcStatus
|
||||
{
|
||||
get => _plcStatus;
|
||||
set => SetProperty(ref _plcStatus, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前页面名称
|
||||
/// </summary>
|
||||
public string CurrentPageName
|
||||
{
|
||||
get => _currentPageName;
|
||||
set => SetProperty(ref _currentPageName, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 命令
|
||||
|
||||
public ICommand NavigateCommand { get; }
|
||||
public ICommand OpenDevToolsCommand { get; }
|
||||
public ICommand OpenLogViewerCommand { get; }
|
||||
public ICommand OpenPerformanceMonitorCommand { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region 导航方法
|
||||
|
||||
/// <summary>
|
||||
/// 导航到指定页面
|
||||
/// </summary>
|
||||
private async Task NavigateToAsync(string pageKey)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 1. 离开当前页面
|
||||
if (CurrentPage is INavigationAware currentAware)
|
||||
{
|
||||
await currentAware.OnNavigatedFromAsync();
|
||||
}
|
||||
|
||||
// 2. 创建新页面 ViewModel
|
||||
var newPage = CreatePageViewModel(pageKey);
|
||||
if (newPage != null)
|
||||
{
|
||||
CurrentPage = newPage;
|
||||
CurrentPageName = GetPageName(pageKey);
|
||||
|
||||
// 3. 进入新页面
|
||||
if (newPage is INavigationAware newAware)
|
||||
{
|
||||
await newAware.OnNavigatedToAsync(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Windows.MessageBox.Show(
|
||||
$"页面导航失败: {ex.Message}",
|
||||
"错误",
|
||||
System.Windows.MessageBoxButton.OK,
|
||||
System.Windows.MessageBoxImage.Error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据页面 Key 创建对应的 ViewModel
|
||||
/// </summary>
|
||||
private ViewModelBase? CreatePageViewModel(string pageKey)
|
||||
{
|
||||
return pageKey switch
|
||||
{
|
||||
"Home" => _serviceProvider.GetRequiredService<HomeViewModel>(),
|
||||
|
||||
// P0 页面(已实现)
|
||||
"HttpServer" => _serviceProvider.GetRequiredService<HttpServerViewModel>(),
|
||||
"Database" => _serviceProvider.GetRequiredService<DatabaseViewModel>(),
|
||||
"PlcDebug" => _serviceProvider.GetRequiredService<PlcDebugViewModel>(),
|
||||
|
||||
// P1 页面(已实现)
|
||||
"SerialPort" => _serviceProvider.GetRequiredService<SerialPortViewModel>(),
|
||||
"ProcessTest" => _serviceProvider.GetRequiredService<ProcessTestViewModel>(),
|
||||
|
||||
// 新增页面(已实现)
|
||||
"HttpClient" => _serviceProvider.GetRequiredService<HttpClientViewModel>(),
|
||||
"LogDemo" => _serviceProvider.GetRequiredService<LogDemoViewModel>(),
|
||||
"ConfigDemo" => _serviceProvider.GetRequiredService<ConfigDemoViewModel>(),
|
||||
"WpfServices" => _serviceProvider.GetRequiredService<WpfServicesViewModel>(),
|
||||
|
||||
// 暂未实现的页面
|
||||
// "PythonTest" => _serviceProvider.GetRequiredService<PythonTestViewModel>(),
|
||||
|
||||
_ => CurrentPage // 如果页面未实现,保持当前页面
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取页面显示名称
|
||||
/// </summary>
|
||||
private static string GetPageName(string pageKey)
|
||||
{
|
||||
return pageKey switch
|
||||
{
|
||||
"Home" => "主页",
|
||||
"HttpServer" => "HTTP 服务器",
|
||||
"HttpClient" => "HTTP 客户端",
|
||||
"Database" => "数据库管理",
|
||||
"PlcDebug" => "PLC 调试",
|
||||
"SerialPort" => "串口调试",
|
||||
"ProcessTest" => "Process 测试",
|
||||
"PythonTest" => "Python 测试",
|
||||
"LogDemo" => "日志管理",
|
||||
"ConfigDemo" => "配置管理",
|
||||
"WpfServices" => "WPF 服务",
|
||||
_ => "未知页面"
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 开发者工具命令
|
||||
|
||||
private void OpenDevTools()
|
||||
{
|
||||
try
|
||||
{
|
||||
var sp = Global.Get<IServiceProvider>();
|
||||
var window = sp?.GetService<Sharp.Wpf.DevTools.Views.DevToolsWindow>();
|
||||
window?.Show();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Windows.MessageBox.Show($"打开开发者工具失败: {ex.Message}", "错误");
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenLogViewer()
|
||||
{
|
||||
try
|
||||
{
|
||||
var sp = Global.Get<IServiceProvider>();
|
||||
var window = sp?.GetService<Sharp.Wpf.DevTools.Views.LogViewerWindow>();
|
||||
window?.Show();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Windows.MessageBox.Show($"打开日志查看器失败: {ex.Message}", "错误");
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenPerformanceMonitor()
|
||||
{
|
||||
try
|
||||
{
|
||||
var sp = Global.Get<IServiceProvider>();
|
||||
var window = sp?.GetService<Sharp.Wpf.DevTools.Views.PerformanceMonitorWindow>();
|
||||
window?.Show();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Windows.MessageBox.Show($"打开性能监控失败: {ex.Message}", "错误");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 事件处理
|
||||
|
||||
/// <summary>
|
||||
/// 服务器状态变化事件处理
|
||||
/// </summary>
|
||||
private void OnServerStatusChanged(object? sender, string status)
|
||||
{
|
||||
ServerStatus = _serverService.IsRunning ? "运行中" : "未运行";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 资源清理
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
// 取消订阅事件
|
||||
_serverService.StatusChanged -= OnServerStatusChanged;
|
||||
|
||||
// 清理当前页面
|
||||
if (CurrentPage is INavigationAware aware)
|
||||
{
|
||||
aware.OnNavigatedFromAsync().Wait();
|
||||
}
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
using Sharp.Wpf.Mvvm;
|
||||
using Sharp.Wpf.Commands;
|
||||
|
||||
namespace Sharp.Demo.ViewModels.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// 配置管理演示 ViewModel
|
||||
/// </summary>
|
||||
public class ConfigDemoViewModel : ViewModelBase
|
||||
{
|
||||
private string _configFile = "appsettings.json";
|
||||
private bool _isHotReloadEnabled = true;
|
||||
private string _configKey = "Logging:LogLevel:Default";
|
||||
private string _configValue = "";
|
||||
private string _configPreview = "等待加载配置...";
|
||||
|
||||
public string ConfigFile
|
||||
{
|
||||
get => _configFile;
|
||||
set => SetProperty(ref _configFile, value);
|
||||
}
|
||||
|
||||
public bool IsHotReloadEnabled
|
||||
{
|
||||
get => _isHotReloadEnabled;
|
||||
set => SetProperty(ref _isHotReloadEnabled, value);
|
||||
}
|
||||
|
||||
public string ConfigKey
|
||||
{
|
||||
get => _configKey;
|
||||
set => SetProperty(ref _configKey, value);
|
||||
}
|
||||
|
||||
public string ConfigValue
|
||||
{
|
||||
get => _configValue;
|
||||
set => SetProperty(ref _configValue, value);
|
||||
}
|
||||
|
||||
public string ConfigPreview
|
||||
{
|
||||
get => _configPreview;
|
||||
set => SetProperty(ref _configPreview, value);
|
||||
}
|
||||
|
||||
// 命令
|
||||
public RelayCommand ReadConfigCommand { get; }
|
||||
public RelayCommand ViewAllConfigCommand { get; }
|
||||
public RelayCommand ReadDbConfigCommand { get; }
|
||||
public RelayCommand ReadLogConfigCommand { get; }
|
||||
public RelayCommand ReadServerConfigCommand { get; }
|
||||
public RelayCommand TestHotReloadCommand { get; }
|
||||
public RelayCommand OpenConfigFileCommand { get; }
|
||||
public RelayCommand ValidateConfigCommand { get; }
|
||||
|
||||
public ConfigDemoViewModel()
|
||||
{
|
||||
ReadConfigCommand = new RelayCommand(() => ConfigValue = "读取配置功能待实现...");
|
||||
ViewAllConfigCommand = new RelayCommand(() => ConfigPreview = "查看所有配置功能待实现...");
|
||||
ReadDbConfigCommand = new RelayCommand(() => ConfigPreview = "数据库配置:\n- ConnectionString\n- Provider\n功能待实现...");
|
||||
ReadLogConfigCommand = new RelayCommand(() => ConfigPreview = "日志配置:\n- MinimumLevel\n- WriteTo\n功能待实现...");
|
||||
ReadServerConfigCommand = new RelayCommand(() => ConfigPreview = "服务器配置:\n- Port\n- Host\n功能待实现...");
|
||||
TestHotReloadCommand = new RelayCommand(() => ConfigPreview = "热重载测试功能待实现...\n请修改 appsettings.json 文件测试");
|
||||
OpenConfigFileCommand = new RelayCommand(() => ConfigPreview = "打开配置文件功能待实现...");
|
||||
ValidateConfigCommand = new RelayCommand(() => ConfigPreview = "配置验证功能待实现...");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
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.Logging;
|
||||
using Sharp.Data.Context;
|
||||
using Sharp.Wpf.Mvvm;
|
||||
using Sharp.Wpf.Commands;
|
||||
using Sharp.Wpf.Abstractions;
|
||||
using Sharp.Wpf.Services;
|
||||
using Sharp.Wpf.Navigation;
|
||||
using Sharp.Demo.Data;
|
||||
using Sharp.Demo.Views.Dialogs;
|
||||
|
||||
namespace Sharp.Demo.ViewModels.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// 数据库管理页面 ViewModel - EF Core + Repository 演示
|
||||
/// </summary>
|
||||
public class DatabaseViewModel : ViewModelBase, INavigationAware
|
||||
{
|
||||
private readonly IDataContext _dataContext;
|
||||
private readonly ILogger<DatabaseViewModel> _logger;
|
||||
private readonly IDialogService _dialogService;
|
||||
|
||||
private User? _selectedUser;
|
||||
private string _searchText = string.Empty;
|
||||
private string _statusText = string.Empty;
|
||||
private string _recordCount = "记录数: 0";
|
||||
|
||||
public DatabaseViewModel(
|
||||
IDataContext dataContext,
|
||||
ILogger<DatabaseViewModel> logger,
|
||||
IDialogService dialogService)
|
||||
{
|
||||
_dataContext = dataContext ?? throw new ArgumentNullException(nameof(dataContext));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_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(DeleteAsync, () => SelectedUser != null);
|
||||
RefreshCommand = new AsyncCommand(async () => await LoadDataAsync(SearchText.Trim()));
|
||||
}
|
||||
|
||||
#region 属性
|
||||
|
||||
/// <summary>
|
||||
/// 用户列表
|
||||
/// </summary>
|
||||
public ObservableCollection<User> Users { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 选中的用户
|
||||
/// </summary>
|
||||
public User? SelectedUser
|
||||
{
|
||||
get => _selectedUser;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _selectedUser, value))
|
||||
{
|
||||
// 触发命令的 CanExecute 更新
|
||||
OnPropertyChanged(nameof(EditCommand));
|
||||
OnPropertyChanged(nameof(DeleteCommand));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 命令
|
||||
|
||||
public ICommand LoadDataCommand { get; }
|
||||
public ICommand SearchCommand { get; }
|
||||
public ICommand ClearSearchCommand { get; }
|
||||
public ICommand AddCommand { get; }
|
||||
public ICommand EditCommand { get; }
|
||||
public ICommand DeleteCommand { get; }
|
||||
public ICommand RefreshCommand { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region INavigationAware 实现
|
||||
|
||||
public async Task OnNavigatedToAsync(object? parameter)
|
||||
{
|
||||
// 进入页面时加载数据
|
||||
await LoadDataAsync();
|
||||
}
|
||||
|
||||
public Task OnNavigatedFromAsync()
|
||||
{
|
||||
// 离开页面时无需特殊处理
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 私有方法
|
||||
|
||||
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
|
||||
{
|
||||
Owner = Application.Current.MainWindow
|
||||
};
|
||||
|
||||
if (dialog.ShowDialog() == true)
|
||||
{
|
||||
_ = SaveUserAsync(dialog.UserData);
|
||||
}
|
||||
}
|
||||
|
||||
private void Edit()
|
||||
{
|
||||
if (SelectedUser == null)
|
||||
{
|
||||
_ = _dialogService.ShowInfoAsync("请选择要编辑的用户", "提示");
|
||||
return;
|
||||
}
|
||||
|
||||
var dialog = new UserEditDialog(SelectedUser)
|
||||
{
|
||||
Owner = Application.Current.MainWindow
|
||||
};
|
||||
|
||||
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}", "错误");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using Sharp.Wpf.Mvvm;
|
||||
using Sharp.Wpf.Abstractions;
|
||||
using Sharp.Wpf.Commands;
|
||||
using Sharp.Wpf.Navigation;
|
||||
using Sharp.Demo.Services;
|
||||
|
||||
namespace Sharp.Demo.ViewModels.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// 首页 ViewModel - 展示框架概览和所有模块
|
||||
/// </summary>
|
||||
public class HomeViewModel : ViewModelBase, INavigationAware
|
||||
{
|
||||
private readonly ServerService _serverService;
|
||||
private string _serverStatusText = "HTTP 服务器未运行";
|
||||
private string _plcStatusText = "PLC 未连接";
|
||||
|
||||
public HomeViewModel(ServerService serverService)
|
||||
{
|
||||
_serverService = serverService ?? throw new ArgumentNullException(nameof(serverService));
|
||||
|
||||
// 初始化导航命令
|
||||
NavigateToHttpServerCommand = new RelayCommand(() => NavigateTo("HttpServer"));
|
||||
NavigateToDatabaseCommand = new RelayCommand(() => NavigateTo("Database"));
|
||||
NavigateToPlcCommand = new RelayCommand(() => NavigateTo("PlcDebug"));
|
||||
|
||||
// 初始化模块列表
|
||||
InitializeModules();
|
||||
}
|
||||
|
||||
#region 属性
|
||||
|
||||
/// <summary>
|
||||
/// 服务器状态文本
|
||||
/// </summary>
|
||||
public string ServerStatusText
|
||||
{
|
||||
get => _serverStatusText;
|
||||
set => SetProperty(ref _serverStatusText, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// PLC 状态文本
|
||||
/// </summary>
|
||||
public string PlcStatusText
|
||||
{
|
||||
get => _plcStatusText;
|
||||
set => SetProperty(ref _plcStatusText, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 系统信息
|
||||
/// </summary>
|
||||
public string SystemInfo => $".NET {Environment.Version} | {Environment.OSVersion.VersionString} | {(Environment.Is64BitOperatingSystem ? "64位" : "32位")}";
|
||||
|
||||
/// <summary>
|
||||
/// 模块列表
|
||||
/// </summary>
|
||||
public ObservableCollection<ModuleCardViewModel> Modules { get; } = new();
|
||||
|
||||
#endregion
|
||||
|
||||
#region 命令
|
||||
|
||||
public ICommand NavigateToHttpServerCommand { get; }
|
||||
public ICommand NavigateToDatabaseCommand { get; }
|
||||
public ICommand NavigateToPlcCommand { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region INavigationAware 实现
|
||||
|
||||
public Task OnNavigatedToAsync(object? parameter)
|
||||
{
|
||||
// 进入页面时刷新状态
|
||||
RefreshStatus();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task OnNavigatedFromAsync()
|
||||
{
|
||||
// 离开页面时无需特殊处理
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 私有方法
|
||||
|
||||
/// <summary>
|
||||
/// 初始化模块列表
|
||||
/// </summary>
|
||||
private void InitializeModules()
|
||||
{
|
||||
Modules.Add(new ModuleCardViewModel
|
||||
{
|
||||
Icon = "🌐",
|
||||
Name = "HTTP 服务器",
|
||||
Description = "Sharp.NetServer - 轻量级 HTTP 服务器框架,支持中间件、路由和控制器",
|
||||
NavigateCommand = new RelayCommand(() => NavigateTo("HttpServer"))
|
||||
});
|
||||
|
||||
Modules.Add(new ModuleCardViewModel
|
||||
{
|
||||
Icon = "📡",
|
||||
Name = "HTTP 客户端",
|
||||
Description = "Sharp.NetClient - 带 Polly 重试策略的 HTTP 客户端",
|
||||
NavigateCommand = new RelayCommand(() => NavigateTo("HttpClient"))
|
||||
});
|
||||
|
||||
Modules.Add(new ModuleCardViewModel
|
||||
{
|
||||
Icon = "💾",
|
||||
Name = "数据库管理",
|
||||
Description = "Sharp.Data - EF Core 封装,支持仓储模式、审计追踪和软删除",
|
||||
NavigateCommand = new RelayCommand(() => NavigateTo("Database"))
|
||||
});
|
||||
|
||||
Modules.Add(new ModuleCardViewModel
|
||||
{
|
||||
Icon = "🏭",
|
||||
Name = "PLC 调试",
|
||||
Description = "Sharp.Plc.Siemens - 西门子 PLC 通信驱动 (S7-1200/1500/300/400)",
|
||||
NavigateCommand = new RelayCommand(() => NavigateTo("PlcDebug"))
|
||||
});
|
||||
|
||||
Modules.Add(new ModuleCardViewModel
|
||||
{
|
||||
Icon = "🔌",
|
||||
Name = "串口调试",
|
||||
Description = "Sharp.Ports - 串口通信抽象 (RS232/RS485)",
|
||||
NavigateCommand = new RelayCommand(() => NavigateTo("SerialPort"))
|
||||
});
|
||||
|
||||
Modules.Add(new ModuleCardViewModel
|
||||
{
|
||||
Icon = "⚙️",
|
||||
Name = "Process 执行",
|
||||
Description = "Sharp.Process - 进程执行和输出捕获",
|
||||
NavigateCommand = new RelayCommand(() => NavigateTo("ProcessTest"))
|
||||
});
|
||||
|
||||
Modules.Add(new ModuleCardViewModel
|
||||
{
|
||||
Icon = "🐍",
|
||||
Name = "Python 集成",
|
||||
Description = "Sharp.Python - Python 脚本执行集成",
|
||||
NavigateCommand = new RelayCommand(() => NavigateTo("PythonTest"))
|
||||
});
|
||||
|
||||
Modules.Add(new ModuleCardViewModel
|
||||
{
|
||||
Icon = "📝",
|
||||
Name = "日志管理",
|
||||
Description = "Sharp.Log - Serilog 集成,支持结构化日志和多种输出",
|
||||
NavigateCommand = new RelayCommand(() => NavigateTo("LogDemo"))
|
||||
});
|
||||
|
||||
Modules.Add(new ModuleCardViewModel
|
||||
{
|
||||
Icon = "🔧",
|
||||
Name = "配置管理",
|
||||
Description = "Sharp.Config - 多格式配置 (JSON/XML/INI/TOML) 和热重载",
|
||||
NavigateCommand = new RelayCommand(() => NavigateTo("ConfigDemo"))
|
||||
});
|
||||
|
||||
Modules.Add(new ModuleCardViewModel
|
||||
{
|
||||
Icon = "🎨",
|
||||
Name = "WPF 服务",
|
||||
Description = "Sharp.Wpf - MVVM 基础设施、对话框服务和主题管理",
|
||||
NavigateCommand = new RelayCommand(() => NavigateTo("WpfDemo"))
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刷新状态
|
||||
/// </summary>
|
||||
private void RefreshStatus()
|
||||
{
|
||||
ServerStatusText = _serverService.IsRunning
|
||||
? "✅ HTTP 服务器运行中"
|
||||
: "⚪ HTTP 服务器未运行";
|
||||
|
||||
// TODO: 获取 PLC 状态
|
||||
PlcStatusText = "⚪ PLC 未连接";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 导航到指定页面
|
||||
/// </summary>
|
||||
private void NavigateTo(string pageKey)
|
||||
{
|
||||
// 通过 Application.Current.MainWindow 获取 MainViewModel 并调用导航命令
|
||||
if (Application.Current.MainWindow?.DataContext is MainViewModel mainViewModel)
|
||||
{
|
||||
mainViewModel.NavigateCommand.Execute(pageKey);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 模块卡片 ViewModel
|
||||
/// </summary>
|
||||
public class ModuleCardViewModel
|
||||
{
|
||||
public string Icon { get; set; } = "";
|
||||
public string Name { get; set; } = "";
|
||||
public string Description { get; set; } = "";
|
||||
public ICommand? NavigateCommand { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using Sharp.Wpf.Mvvm;
|
||||
using Sharp.Wpf.Commands;
|
||||
|
||||
namespace Sharp.Demo.ViewModels.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// HTTP 客户端演示 ViewModel
|
||||
/// </summary>
|
||||
public class HttpClientViewModel : ViewModelBase
|
||||
{
|
||||
private string _requestUrl = "https://api.github.com/zen";
|
||||
private string _selectedMethod = "GET";
|
||||
private string _requestBody = "";
|
||||
private string _responseText = "等待请求...";
|
||||
private string _statusCode = "";
|
||||
|
||||
public string RequestUrl
|
||||
{
|
||||
get => _requestUrl;
|
||||
set => SetProperty(ref _requestUrl, value);
|
||||
}
|
||||
|
||||
public string SelectedMethod
|
||||
{
|
||||
get => _selectedMethod;
|
||||
set => SetProperty(ref _selectedMethod, value);
|
||||
}
|
||||
|
||||
public string RequestBody
|
||||
{
|
||||
get => _requestBody;
|
||||
set => SetProperty(ref _requestBody, value);
|
||||
}
|
||||
|
||||
public string ResponseText
|
||||
{
|
||||
get => _responseText;
|
||||
set => SetProperty(ref _responseText, value);
|
||||
}
|
||||
|
||||
public string StatusCode
|
||||
{
|
||||
get => _statusCode;
|
||||
set => SetProperty(ref _statusCode, value);
|
||||
}
|
||||
|
||||
public ObservableCollection<string> HttpMethods { get; } = new()
|
||||
{
|
||||
"GET", "POST", "PUT", "DELETE", "PATCH"
|
||||
};
|
||||
|
||||
public RelayCommand SendRequestCommand { get; }
|
||||
public RelayCommand GetTestCommand { get; }
|
||||
public RelayCommand PostTestCommand { get; }
|
||||
public RelayCommand RetryTestCommand { get; }
|
||||
public RelayCommand TimeoutTestCommand { get; }
|
||||
public RelayCommand ClearResultCommand { get; }
|
||||
|
||||
public HttpClientViewModel()
|
||||
{
|
||||
SendRequestCommand = new RelayCommand(() => ResponseText = "发送请求功能待实现...");
|
||||
GetTestCommand = new RelayCommand(() => ResponseText = "GET 测试功能待实现...");
|
||||
PostTestCommand = new RelayCommand(() => ResponseText = "POST 测试功能待实现...");
|
||||
RetryTestCommand = new RelayCommand(() => ResponseText = "重试测试功能待实现...");
|
||||
TimeoutTestCommand = new RelayCommand(() => ResponseText = "超时测试功能待实现...");
|
||||
ClearResultCommand = new RelayCommand(() =>
|
||||
{
|
||||
ResponseText = "";
|
||||
StatusCode = "";
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Input;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sharp.Wpf.Mvvm;
|
||||
using Sharp.Wpf.Commands;
|
||||
using Sharp.Wpf.Navigation;
|
||||
using Sharp.Demo.Services;
|
||||
|
||||
namespace Sharp.Demo.ViewModels.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// HTTP 服务器管理页面 ViewModel
|
||||
/// </summary>
|
||||
public class HttpServerViewModel : ViewModelBase, INavigationAware
|
||||
{
|
||||
private readonly ServerService _serverService;
|
||||
private readonly ILogger<HttpServerViewModel> _logger;
|
||||
|
||||
private string _statusText = "未启动";
|
||||
private Brush _statusColor = Brushes.Gray;
|
||||
private string _logText = string.Empty;
|
||||
|
||||
public HttpServerViewModel(ServerService serverService, ILogger<HttpServerViewModel> logger)
|
||||
{
|
||||
_serverService = serverService ?? throw new ArgumentNullException(nameof(serverService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
ServerUrl = _serverService.Url;
|
||||
|
||||
// 初始化命令
|
||||
StartCommand = new RelayCommand(_ => Start(), _ => CanStart);
|
||||
StopCommand = new RelayCommand(_ => Stop(), _ => IsRunning);
|
||||
RestartCommand = new RelayCommand(_ => Restart(), _ => IsRunning);
|
||||
ClearLogCommand = new RelayCommand(_ => ClearLog());
|
||||
|
||||
// 订阅服务器状态变化事件
|
||||
_serverService.StatusChanged += OnServerStatusChanged;
|
||||
|
||||
AppendLog("HTTP 服务器管理页面已加载");
|
||||
AppendLog($"服务器地址: {ServerUrl}");
|
||||
|
||||
// 更新初始状态
|
||||
RefreshStatus();
|
||||
}
|
||||
|
||||
#region 属性
|
||||
|
||||
/// <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;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 命令
|
||||
|
||||
public ICommand StartCommand { get; }
|
||||
public ICommand StopCommand { get; }
|
||||
public ICommand RestartCommand { get; }
|
||||
public ICommand ClearLogCommand { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region 命令实现
|
||||
|
||||
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;
|
||||
AppendLog("日志已清空");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region INavigationAware 实现
|
||||
|
||||
public System.Threading.Tasks.Task OnNavigatedToAsync(object? parameter)
|
||||
{
|
||||
// 进入页面时刷新状态
|
||||
RefreshStatus();
|
||||
return System.Threading.Tasks.Task.CompletedTask;
|
||||
}
|
||||
|
||||
public System.Threading.Tasks.Task OnNavigatedFromAsync()
|
||||
{
|
||||
// 离开页面时无需特殊处理(服务器继续运行)
|
||||
return System.Threading.Tasks.Task.CompletedTask;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 私有方法
|
||||
|
||||
private void OnServerStatusChanged(object? sender, string status)
|
||||
{
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
StatusText = status;
|
||||
|
||||
// 根据状态更新颜色
|
||||
if (status.Contains("已启动") || 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";
|
||||
}
|
||||
|
||||
private void RefreshStatus()
|
||||
{
|
||||
if (_serverService.IsRunning)
|
||||
{
|
||||
StatusText = "运行中";
|
||||
StatusColor = Brushes.Green;
|
||||
}
|
||||
else
|
||||
{
|
||||
StatusText = "未启动";
|
||||
StatusColor = Brushes.Gray;
|
||||
}
|
||||
|
||||
UpdateButtonStates();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 资源清理
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
_serverService.StatusChanged -= OnServerStatusChanged;
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using Sharp.Wpf.Mvvm;
|
||||
using Sharp.Wpf.Commands;
|
||||
|
||||
namespace Sharp.Demo.ViewModels.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// 日志管理演示 ViewModel
|
||||
/// </summary>
|
||||
public class LogDemoViewModel : ViewModelBase
|
||||
{
|
||||
private string _selectedLogLevel = "Information";
|
||||
private string _logPreview = "日志预览区域...\n等待日志输出...";
|
||||
|
||||
public string SelectedLogLevel
|
||||
{
|
||||
get => _selectedLogLevel;
|
||||
set => SetProperty(ref _selectedLogLevel, value);
|
||||
}
|
||||
|
||||
public string LogPreview
|
||||
{
|
||||
get => _logPreview;
|
||||
set => SetProperty(ref _logPreview, value);
|
||||
}
|
||||
|
||||
public ObservableCollection<string> LogLevels { get; } = new()
|
||||
{
|
||||
"Trace", "Debug", "Information", "Warning", "Error", "Critical"
|
||||
};
|
||||
|
||||
// 日志级别测试命令
|
||||
public RelayCommand LogTraceCommand { get; }
|
||||
public RelayCommand LogDebugCommand { get; }
|
||||
public RelayCommand LogInformationCommand { get; }
|
||||
public RelayCommand LogWarningCommand { get; }
|
||||
public RelayCommand LogErrorCommand { get; }
|
||||
public RelayCommand LogCriticalCommand { get; }
|
||||
|
||||
// 结构化日志命令
|
||||
public RelayCommand LogWithParamsCommand { get; }
|
||||
public RelayCommand LogWithExceptionCommand { get; }
|
||||
public RelayCommand LogPerformanceCommand { get; }
|
||||
public RelayCommand LogBusinessCommand { get; }
|
||||
|
||||
// 日志操作命令
|
||||
public RelayCommand OpenLogFolderCommand { get; }
|
||||
public RelayCommand ViewLatestLogCommand { get; }
|
||||
public RelayCommand ClearLogsCommand { get; }
|
||||
|
||||
public LogDemoViewModel()
|
||||
{
|
||||
LogTraceCommand = new RelayCommand(() => AppendLog("[Trace] 追踪级别日志 - 功能待实现"));
|
||||
LogDebugCommand = new RelayCommand(() => AppendLog("[Debug] 调试级别日志 - 功能待实现"));
|
||||
LogInformationCommand = new RelayCommand(() => AppendLog("[Information] 信息级别日志 - 功能待实现"));
|
||||
LogWarningCommand = new RelayCommand(() => AppendLog("[Warning] 警告级别日志 - 功能待实现"));
|
||||
LogErrorCommand = new RelayCommand(() => AppendLog("[Error] 错误级别日志 - 功能待实现"));
|
||||
LogCriticalCommand = new RelayCommand(() => AppendLog("[Critical] 严重错误日志 - 功能待实现"));
|
||||
|
||||
LogWithParamsCommand = new RelayCommand(() => AppendLog("带参数日志功能待实现..."));
|
||||
LogWithExceptionCommand = new RelayCommand(() => AppendLog("带异常日志功能待实现..."));
|
||||
LogPerformanceCommand = new RelayCommand(() => AppendLog("性能日志功能待实现..."));
|
||||
LogBusinessCommand = new RelayCommand(() => AppendLog("业务日志功能待实现..."));
|
||||
|
||||
OpenLogFolderCommand = new RelayCommand(() => AppendLog("打开日志文件夹功能待实现..."));
|
||||
ViewLatestLogCommand = new RelayCommand(() => AppendLog("查看最新日志功能待实现..."));
|
||||
ClearLogsCommand = new RelayCommand(() => LogPreview = "");
|
||||
}
|
||||
|
||||
private void AppendLog(string message)
|
||||
{
|
||||
LogPreview += $"\n[{DateTime.Now:HH:mm:ss}] {message}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sharp.Wpf.Mvvm;
|
||||
using Sharp.Wpf.Commands;
|
||||
using Sharp.Wpf.Navigation;
|
||||
using Plc.Abstractions;
|
||||
|
||||
namespace Sharp.Demo.ViewModels.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// PLC 调试页面 ViewModel - 简化版
|
||||
/// </summary>
|
||||
public class PlcDebugViewModel : ViewModelBase, INavigationAware
|
||||
{
|
||||
private readonly IPlcService _plcService;
|
||||
private readonly ILogger<PlcDebugViewModel> _logger;
|
||||
|
||||
private string _ipAddress = "192.168.1.100";
|
||||
private string _status = "未连接";
|
||||
private string _readAddress = "DB1.DBW0";
|
||||
private string _readValue = "-";
|
||||
private string _writeAddress = "DB1.DBW0";
|
||||
private string _writeValue = "0";
|
||||
private string _logText = string.Empty;
|
||||
|
||||
public PlcDebugViewModel(IPlcService plcService, ILogger<PlcDebugViewModel> logger)
|
||||
{
|
||||
_plcService = plcService ?? throw new ArgumentNullException(nameof(plcService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
// 初始化命令
|
||||
ConnectCommand = new AsyncCommand(ConnectAsync);
|
||||
DisconnectCommand = new AsyncCommand(DisconnectAsync);
|
||||
ReadCommand = new AsyncCommand(ReadAsync);
|
||||
WriteCommand = new AsyncCommand(WriteAsync);
|
||||
ClearLogCommand = new RelayCommand(_ => ClearLog());
|
||||
|
||||
AppendLog("PLC 调试工具已加载");
|
||||
AppendLog("提示:需要配置真实的 PLC 连接信息才能使用");
|
||||
}
|
||||
|
||||
#region 属性
|
||||
|
||||
public string IpAddress
|
||||
{
|
||||
get => _ipAddress;
|
||||
set => SetProperty(ref _ipAddress, value);
|
||||
}
|
||||
|
||||
public string Status
|
||||
{
|
||||
get => _status;
|
||||
set => SetProperty(ref _status, value);
|
||||
}
|
||||
|
||||
public string ReadAddress
|
||||
{
|
||||
get => _readAddress;
|
||||
set => SetProperty(ref _readAddress, value);
|
||||
}
|
||||
|
||||
public string ReadValue
|
||||
{
|
||||
get => _readValue;
|
||||
set => SetProperty(ref _readValue, value);
|
||||
}
|
||||
|
||||
public string WriteAddress
|
||||
{
|
||||
get => _writeAddress;
|
||||
set => SetProperty(ref _writeAddress, value);
|
||||
}
|
||||
|
||||
public string WriteValue
|
||||
{
|
||||
get => _writeValue;
|
||||
set => SetProperty(ref _writeValue, value);
|
||||
}
|
||||
|
||||
public string LogText
|
||||
{
|
||||
get => _logText;
|
||||
set => SetProperty(ref _logText, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 命令
|
||||
|
||||
public ICommand ConnectCommand { get; }
|
||||
public ICommand DisconnectCommand { get; }
|
||||
public ICommand ReadCommand { get; }
|
||||
public ICommand WriteCommand { get; }
|
||||
public ICommand ClearLogCommand { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region INavigationAware 实现
|
||||
|
||||
public Task OnNavigatedToAsync(object? parameter)
|
||||
{
|
||||
AppendLog("进入 PLC 调试页面");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task OnNavigatedFromAsync()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 私有方法
|
||||
|
||||
private async Task ConnectAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
Status = "正在连接...";
|
||||
AppendLog($"正在连接 PLC: {IpAddress}");
|
||||
|
||||
await _plcService.StartAsync();
|
||||
|
||||
Status = "已连接";
|
||||
AppendLog("✅ PLC 连接成功");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Status = $"连接失败: {ex.Message}";
|
||||
AppendLog($"❌ 连接失败: {ex.Message}");
|
||||
_logger.LogError(ex, "PLC 连接失败");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DisconnectAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await _plcService.StopAsync();
|
||||
Status = "已断开";
|
||||
AppendLog("PLC 已断开连接");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AppendLog($"❌ 断开失败: {ex.Message}");
|
||||
_logger.LogError(ex, "PLC 断开失败");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ReadAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
AppendLog($"读取地址: {ReadAddress}");
|
||||
var result = await _plcService.ReadAsync<short>(ReadAddress);
|
||||
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
ReadValue = result.Value.ToString();
|
||||
AppendLog($"✅ 读取成功: {ReadValue}");
|
||||
}
|
||||
else
|
||||
{
|
||||
ReadValue = "读取失败";
|
||||
AppendLog($"❌ 读取失败: {result.ErrorMessage}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ReadValue = "读取失败";
|
||||
AppendLog($"❌ 读取异常: {ex.Message}");
|
||||
_logger.LogError(ex, "PLC 读取失败");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task WriteAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!short.TryParse(WriteValue, out short value))
|
||||
{
|
||||
AppendLog($"❌ 写入失败: 值格式不正确");
|
||||
return;
|
||||
}
|
||||
|
||||
AppendLog($"写入地址: {WriteAddress} = {value}");
|
||||
var result = await _plcService.WriteAsync(WriteAddress, value);
|
||||
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
AppendLog($"✅ 写入成功");
|
||||
}
|
||||
else
|
||||
{
|
||||
AppendLog($"❌ 写入失败: {result.ErrorMessage}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AppendLog($"❌ 写入异常: {ex.Message}");
|
||||
_logger.LogError(ex, "PLC 写入失败");
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearLog()
|
||||
{
|
||||
LogText = string.Empty;
|
||||
AppendLog("日志已清空");
|
||||
}
|
||||
|
||||
private void AppendLog(string message)
|
||||
{
|
||||
var timestamp = DateTime.Now.ToString("HH:mm:ss");
|
||||
LogText += $"[{timestamp}] {message}\n";
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sharp.Wpf.Mvvm;
|
||||
using Sharp.Wpf.Commands;
|
||||
using Sharp.Wpf.Navigation;
|
||||
|
||||
namespace Sharp.Demo.ViewModels.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// 进程测试页面 ViewModel - 简化版
|
||||
/// </summary>
|
||||
public class ProcessTestViewModel : ViewModelBase, INavigationAware
|
||||
{
|
||||
private readonly ILogger<ProcessTestViewModel> _logger;
|
||||
|
||||
private string _command = "dotnet";
|
||||
private string _arguments = "--version";
|
||||
private int _timeoutSeconds = 30;
|
||||
private string _status = "就绪";
|
||||
private string _output = string.Empty;
|
||||
|
||||
public ProcessTestViewModel(ILogger<ProcessTestViewModel> logger)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
// 初始化命令
|
||||
RunCommand = new AsyncCommand(RunProcessAsync);
|
||||
TestDotnetCommand = new AsyncCommand(TestDotnetAsync);
|
||||
TestGitCommand = new AsyncCommand(TestGitAsync);
|
||||
ClearOutputCommand = new RelayCommand(_ => ClearOutput());
|
||||
|
||||
AppendOutput("进程测试工具已加载");
|
||||
AppendOutput("提示:这是简化版本,展示基础界面布局");
|
||||
}
|
||||
|
||||
#region 属性
|
||||
|
||||
public string Command
|
||||
{
|
||||
get => _command;
|
||||
set => SetProperty(ref _command, value);
|
||||
}
|
||||
|
||||
public string Arguments
|
||||
{
|
||||
get => _arguments;
|
||||
set => SetProperty(ref _arguments, value);
|
||||
}
|
||||
|
||||
public int TimeoutSeconds
|
||||
{
|
||||
get => _timeoutSeconds;
|
||||
set => SetProperty(ref _timeoutSeconds, value);
|
||||
}
|
||||
|
||||
public string Status
|
||||
{
|
||||
get => _status;
|
||||
set => SetProperty(ref _status, value);
|
||||
}
|
||||
|
||||
public string Output
|
||||
{
|
||||
get => _output;
|
||||
set => SetProperty(ref _output, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 命令
|
||||
|
||||
public ICommand RunCommand { get; }
|
||||
public ICommand TestDotnetCommand { get; }
|
||||
public ICommand TestGitCommand { get; }
|
||||
public ICommand ClearOutputCommand { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region INavigationAware 实现
|
||||
|
||||
public Task OnNavigatedToAsync(object? parameter)
|
||||
{
|
||||
AppendOutput("进入进程测试页面");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task OnNavigatedFromAsync()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 私有方法
|
||||
|
||||
private async Task RunProcessAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
Status = "正在执行...";
|
||||
AppendOutput($"\n执行命令: {Command} {Arguments}");
|
||||
AppendOutput("超时时间: " + TimeoutSeconds + " 秒");
|
||||
|
||||
await Task.Delay(500); // 模拟执行
|
||||
|
||||
AppendOutput("✅ 执行完成");
|
||||
AppendOutput("示例输出:命令执行成功");
|
||||
Status = "完成";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AppendOutput($"❌ 执行失败: {ex.Message}");
|
||||
Status = "失败";
|
||||
_logger.LogError(ex, "进程执行失败");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task TestDotnetAsync()
|
||||
{
|
||||
Command = "dotnet";
|
||||
Arguments = "--version";
|
||||
await RunProcessAsync();
|
||||
}
|
||||
|
||||
private async Task TestGitAsync()
|
||||
{
|
||||
Command = "git";
|
||||
Arguments = "--version";
|
||||
await RunProcessAsync();
|
||||
}
|
||||
|
||||
private void ClearOutput()
|
||||
{
|
||||
Output = string.Empty;
|
||||
Status = "就绪";
|
||||
AppendOutput("输出已清空");
|
||||
}
|
||||
|
||||
private void AppendOutput(string message)
|
||||
{
|
||||
var timestamp = DateTime.Now.ToString("HH:mm:ss");
|
||||
Output += $"[{timestamp}] {message}\n";
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sharp.Wpf.Mvvm;
|
||||
using Sharp.Wpf.Commands;
|
||||
using Sharp.Wpf.Navigation;
|
||||
|
||||
namespace Sharp.Demo.ViewModels.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// 串口调试页面 ViewModel - 简化版
|
||||
/// </summary>
|
||||
public class SerialPortViewModel : ViewModelBase, INavigationAware
|
||||
{
|
||||
private readonly ILogger<SerialPortViewModel> _logger;
|
||||
|
||||
private string _selectedPort = "COM1";
|
||||
private int _baudRate = 9600;
|
||||
private string _status = "未连接";
|
||||
private string _sendData = "Hello Serial Port";
|
||||
private string _receiveData = string.Empty;
|
||||
private string _logText = string.Empty;
|
||||
|
||||
public SerialPortViewModel(ILogger<SerialPortViewModel> logger)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
// 初始化命令
|
||||
ConnectCommand = new RelayCommand(_ => Connect());
|
||||
DisconnectCommand = new RelayCommand(_ => Disconnect());
|
||||
SendCommand = new RelayCommand(_ => Send());
|
||||
ClearLogCommand = new RelayCommand(_ => ClearLog());
|
||||
|
||||
AppendLog("串口调试工具已加载");
|
||||
AppendLog("提示:这是简化版本,展示基础界面布局");
|
||||
}
|
||||
|
||||
#region 属性
|
||||
|
||||
public string SelectedPort
|
||||
{
|
||||
get => _selectedPort;
|
||||
set => SetProperty(ref _selectedPort, value);
|
||||
}
|
||||
|
||||
public int BaudRate
|
||||
{
|
||||
get => _baudRate;
|
||||
set => SetProperty(ref _baudRate, value);
|
||||
}
|
||||
|
||||
public string Status
|
||||
{
|
||||
get => _status;
|
||||
set => SetProperty(ref _status, value);
|
||||
}
|
||||
|
||||
public string SendData
|
||||
{
|
||||
get => _sendData;
|
||||
set => SetProperty(ref _sendData, value);
|
||||
}
|
||||
|
||||
public string ReceiveData
|
||||
{
|
||||
get => _receiveData;
|
||||
set => SetProperty(ref _receiveData, value);
|
||||
}
|
||||
|
||||
public string LogText
|
||||
{
|
||||
get => _logText;
|
||||
set => SetProperty(ref _logText, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 命令
|
||||
|
||||
public ICommand ConnectCommand { get; }
|
||||
public ICommand DisconnectCommand { get; }
|
||||
public ICommand SendCommand { get; }
|
||||
public ICommand ClearLogCommand { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region INavigationAware 实现
|
||||
|
||||
public Task OnNavigatedToAsync(object? parameter)
|
||||
{
|
||||
AppendLog("进入串口调试页面");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task OnNavigatedFromAsync()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 私有方法
|
||||
|
||||
private void Connect()
|
||||
{
|
||||
try
|
||||
{
|
||||
Status = "已连接";
|
||||
AppendLog($"✅ 已连接到 {SelectedPort} (波特率: {BaudRate})");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Status = "连接失败";
|
||||
AppendLog($"❌ 连接失败: {ex.Message}");
|
||||
_logger.LogError(ex, "串口连接失败");
|
||||
}
|
||||
}
|
||||
|
||||
private void Disconnect()
|
||||
{
|
||||
Status = "未连接";
|
||||
AppendLog("串口已断开");
|
||||
}
|
||||
|
||||
private void Send()
|
||||
{
|
||||
try
|
||||
{
|
||||
AppendLog($"发送数据: {SendData}");
|
||||
ReceiveData += $"[{DateTime.Now:HH:mm:ss}] Echo: {SendData}\n";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AppendLog($"❌ 发送失败: {ex.Message}");
|
||||
_logger.LogError(ex, "串口发送失败");
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearLog()
|
||||
{
|
||||
LogText = string.Empty;
|
||||
ReceiveData = string.Empty;
|
||||
AppendLog("日志已清空");
|
||||
}
|
||||
|
||||
private void AppendLog(string message)
|
||||
{
|
||||
var timestamp = DateTime.Now.ToString("HH:mm:ss");
|
||||
LogText += $"[{timestamp}] {message}\n";
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
using Sharp.Wpf.Mvvm;
|
||||
using Sharp.Wpf.Commands;
|
||||
|
||||
namespace Sharp.Demo.ViewModels.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// WPF 服务演示 ViewModel
|
||||
/// </summary>
|
||||
public class WpfServicesViewModel : ViewModelBase
|
||||
{
|
||||
private string _sampleProperty = "示例属性值";
|
||||
private string _logText = "等待操作...\n";
|
||||
|
||||
public string SampleProperty
|
||||
{
|
||||
get => _sampleProperty;
|
||||
set => SetProperty(ref _sampleProperty, value);
|
||||
}
|
||||
|
||||
public string LogText
|
||||
{
|
||||
get => _logText;
|
||||
set => SetProperty(ref _logText, value);
|
||||
}
|
||||
|
||||
// 对话框命令
|
||||
public RelayCommand ShowMessageCommand { get; }
|
||||
public RelayCommand ShowConfirmCommand { get; }
|
||||
public RelayCommand ShowWarningCommand { get; }
|
||||
public RelayCommand ShowErrorCommand { get; }
|
||||
public RelayCommand ShowInputCommand { get; }
|
||||
public RelayCommand ShowCustomDialogCommand { get; }
|
||||
|
||||
// 文件对话框命令
|
||||
public RelayCommand OpenFileCommand { get; }
|
||||
public RelayCommand SaveFileCommand { get; }
|
||||
public RelayCommand SelectFolderCommand { get; }
|
||||
public RelayCommand OpenMultipleFilesCommand { get; }
|
||||
|
||||
// 线程分发器命令
|
||||
public RelayCommand InvokeOnUIThreadCommand { get; }
|
||||
public RelayCommand InvokeAsyncCommand { get; }
|
||||
public RelayCommand BackgroundTaskCommand { get; }
|
||||
|
||||
// MVVM 基础命令
|
||||
public RelayCommand TriggerPropertyChangeCommand { get; }
|
||||
public RelayCommand SyncCommandDemo { get; }
|
||||
public AsyncCommand AsyncCommandDemo { get; }
|
||||
public RelayCommand<string> CommandWithParamDemo { get; }
|
||||
|
||||
public RelayCommand ClearLogCommand { get; }
|
||||
|
||||
public WpfServicesViewModel()
|
||||
{
|
||||
// 对话框
|
||||
ShowMessageCommand = new RelayCommand(() => AppendLog("消息框功能待实现..."));
|
||||
ShowConfirmCommand = new RelayCommand(() => AppendLog("确认对话框功能待实现..."));
|
||||
ShowWarningCommand = new RelayCommand(() => AppendLog("警告对话框功能待实现..."));
|
||||
ShowErrorCommand = new RelayCommand(() => AppendLog("错误对话框功能待实现..."));
|
||||
ShowInputCommand = new RelayCommand(() => AppendLog("输入框功能待实现..."));
|
||||
ShowCustomDialogCommand = new RelayCommand(() => AppendLog("自定义对话框功能待实现..."));
|
||||
|
||||
// 文件对话框
|
||||
OpenFileCommand = new RelayCommand(() => AppendLog("打开文件对话框功能待实现..."));
|
||||
SaveFileCommand = new RelayCommand(() => AppendLog("保存文件对话框功能待实现..."));
|
||||
SelectFolderCommand = new RelayCommand(() => AppendLog("选择文件夹功能待实现..."));
|
||||
OpenMultipleFilesCommand = new RelayCommand(() => AppendLog("多选文件功能待实现..."));
|
||||
|
||||
// 线程分发器
|
||||
InvokeOnUIThreadCommand = new RelayCommand(() => AppendLog("UI 线程调用功能待实现..."));
|
||||
InvokeAsyncCommand = new RelayCommand(() => AppendLog("异步 UI 调用功能待实现..."));
|
||||
BackgroundTaskCommand = new RelayCommand(() => AppendLog("后台任务功能待实现..."));
|
||||
|
||||
// MVVM 基础
|
||||
TriggerPropertyChangeCommand = new RelayCommand(() =>
|
||||
{
|
||||
SampleProperty = $"已变更 - {DateTime.Now:HH:mm:ss}";
|
||||
AppendLog($"属性变更: {SampleProperty}");
|
||||
});
|
||||
|
||||
SyncCommandDemo = new RelayCommand(() => AppendLog("同步命令执行"));
|
||||
|
||||
AsyncCommandDemo = new AsyncCommand(async () =>
|
||||
{
|
||||
AppendLog("异步命令开始...");
|
||||
await Task.Delay(1000);
|
||||
AppendLog("异步命令完成");
|
||||
});
|
||||
|
||||
CommandWithParamDemo = new RelayCommand<string>(param =>
|
||||
AppendLog($"带参数命令: {param}"));
|
||||
|
||||
ClearLogCommand = new RelayCommand(() => LogText = "");
|
||||
}
|
||||
|
||||
private void AppendLog(string message)
|
||||
{
|
||||
LogText += $"[{DateTime.Now:HH:mm:ss}] {message}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,410 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sharp.Wpf.Mvvm;
|
||||
using Sharp.Wpf.Commands;
|
||||
|
||||
namespace Sharp.Demo.ViewModels.Windows
|
||||
{
|
||||
/// <summary>
|
||||
/// HTTP 客户端窗口 ViewModel
|
||||
/// </summary>
|
||||
public class HttpClientViewModel : ViewModelBase
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly ILogger<HttpClientViewModel> _logger;
|
||||
|
||||
private string _requestUrl = "http://localhost:8080/api/health";
|
||||
private string _selectedMethod = "GET";
|
||||
private string _requestHeaders = "Content-Type: application/json\nUser-Agent: Sharp-HttpClient/1.0";
|
||||
private string _requestBody = "{\n \"message\": \"Hello from Sharp\"\n}";
|
||||
private string _responseText = "等待发送请求...\n\n提示: 可以先启动 HTTP 服务器窗口,然后在这里测试请求。";
|
||||
private string _responseHeaders = "";
|
||||
private string _logText = "";
|
||||
private string _statusCode = "-";
|
||||
private Brush _statusColor = Brushes.Gray;
|
||||
private string _contentType = "-";
|
||||
private string _elapsedTime = "0 ms";
|
||||
private string _responseSize = "0 bytes";
|
||||
private int _totalRequests = 0;
|
||||
private int _successCount = 0;
|
||||
private int _failureCount = 0;
|
||||
|
||||
public HttpClientViewModel(HttpClient httpClient, ILogger<HttpClientViewModel> logger)
|
||||
{
|
||||
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
_logger.LogInformation("HTTP 客户端窗口 ViewModel 已创建");
|
||||
|
||||
// 初始化命令
|
||||
SendRequestCommand = new AsyncCommand(SendRequest);
|
||||
ClearCommand = new RelayCommand(Clear);
|
||||
CopyResponseCommand = new RelayCommand(CopyResponse);
|
||||
SaveResponseCommand = new AsyncCommand(SaveResponse);
|
||||
TestLocalServerCommand = new AsyncCommand(TestLocalServer);
|
||||
TestRetryCommand = new AsyncCommand(TestRetry);
|
||||
TestTimeoutCommand = new AsyncCommand(TestTimeout);
|
||||
TestJsonApiCommand = new AsyncCommand(TestJsonApi);
|
||||
|
||||
AppendLog("HTTP 客户端已初始化");
|
||||
AppendLog($"当前目标地址: {RequestUrl}");
|
||||
}
|
||||
|
||||
#region 属性
|
||||
|
||||
public string RequestUrl
|
||||
{
|
||||
get => _requestUrl;
|
||||
set => SetProperty(ref _requestUrl, value);
|
||||
}
|
||||
|
||||
public string SelectedMethod
|
||||
{
|
||||
get => _selectedMethod;
|
||||
set => SetProperty(ref _selectedMethod, value);
|
||||
}
|
||||
|
||||
public string RequestHeaders
|
||||
{
|
||||
get => _requestHeaders;
|
||||
set => SetProperty(ref _requestHeaders, value);
|
||||
}
|
||||
|
||||
public string RequestBody
|
||||
{
|
||||
get => _requestBody;
|
||||
set => SetProperty(ref _requestBody, value);
|
||||
}
|
||||
|
||||
public string ResponseText
|
||||
{
|
||||
get => _responseText;
|
||||
set => SetProperty(ref _responseText, value);
|
||||
}
|
||||
|
||||
public string ResponseHeaders
|
||||
{
|
||||
get => _responseHeaders;
|
||||
set => SetProperty(ref _responseHeaders, value);
|
||||
}
|
||||
|
||||
public string LogText
|
||||
{
|
||||
get => _logText;
|
||||
set => SetProperty(ref _logText, value);
|
||||
}
|
||||
|
||||
public string StatusCode
|
||||
{
|
||||
get => _statusCode;
|
||||
set => SetProperty(ref _statusCode, value);
|
||||
}
|
||||
|
||||
public Brush StatusColor
|
||||
{
|
||||
get => _statusColor;
|
||||
set => SetProperty(ref _statusColor, value);
|
||||
}
|
||||
|
||||
public string ContentType
|
||||
{
|
||||
get => _contentType;
|
||||
set => SetProperty(ref _contentType, value);
|
||||
}
|
||||
|
||||
public string ElapsedTime
|
||||
{
|
||||
get => _elapsedTime;
|
||||
set => SetProperty(ref _elapsedTime, value);
|
||||
}
|
||||
|
||||
public string ResponseSize
|
||||
{
|
||||
get => _responseSize;
|
||||
set => SetProperty(ref _responseSize, value);
|
||||
}
|
||||
|
||||
public int TotalRequests
|
||||
{
|
||||
get => _totalRequests;
|
||||
set => SetProperty(ref _totalRequests, value);
|
||||
}
|
||||
|
||||
public int SuccessCount
|
||||
{
|
||||
get => _successCount;
|
||||
set => SetProperty(ref _successCount, value);
|
||||
}
|
||||
|
||||
public int FailureCount
|
||||
{
|
||||
get => _failureCount;
|
||||
set => SetProperty(ref _failureCount, value);
|
||||
}
|
||||
|
||||
public ObservableCollection<string> HttpMethods { get; } = new()
|
||||
{
|
||||
"GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region 命令
|
||||
|
||||
public AsyncCommand SendRequestCommand { get; }
|
||||
public RelayCommand ClearCommand { get; }
|
||||
public RelayCommand CopyResponseCommand { get; }
|
||||
public AsyncCommand SaveResponseCommand { get; }
|
||||
public AsyncCommand TestLocalServerCommand { get; }
|
||||
public AsyncCommand TestRetryCommand { get; }
|
||||
public AsyncCommand TestTimeoutCommand { get; }
|
||||
public AsyncCommand TestJsonApiCommand { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region 命令实现
|
||||
|
||||
private async Task SendRequest()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(RequestUrl))
|
||||
{
|
||||
_logger.LogWarning("用户尝试发送空 URL 请求");
|
||||
AppendLog("❌ 错误: 请求 URL 不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogInformation("用户点击发送请求: {Method} {Url}", SelectedMethod, RequestUrl);
|
||||
|
||||
var sw = Stopwatch.StartNew();
|
||||
TotalRequests++;
|
||||
|
||||
try
|
||||
{
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] 正在发送 {SelectedMethod} 请求到: {RequestUrl}");
|
||||
|
||||
HttpResponseMessage response;
|
||||
HttpMethod method = new HttpMethod(SelectedMethod);
|
||||
|
||||
HttpRequestMessage request = new HttpRequestMessage(method, RequestUrl);
|
||||
|
||||
// 如果有请求体且不是 GET/HEAD,添加内容
|
||||
if (!string.IsNullOrWhiteSpace(RequestBody) &&
|
||||
method != HttpMethod.Get &&
|
||||
method != HttpMethod.Head)
|
||||
{
|
||||
request.Content = new StringContent(RequestBody, Encoding.UTF8, "application/json");
|
||||
}
|
||||
|
||||
response = await _httpClient.SendAsync(request);
|
||||
|
||||
sw.Stop();
|
||||
|
||||
// 处理响应
|
||||
await HandleResponse(response, sw.ElapsedMilliseconds);
|
||||
SuccessCount++;
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] ✅ 请求成功 - {StatusCode} ({sw.ElapsedMilliseconds} ms)");
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
sw.Stop();
|
||||
FailureCount++;
|
||||
StatusCode = "请求失败";
|
||||
StatusColor = Brushes.Red;
|
||||
ResponseText = $"HTTP 请求异常:\n{ex.Message}\n\n{ex.StackTrace}";
|
||||
ElapsedTime = $"{sw.ElapsedMilliseconds} ms";
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] ❌ 请求失败: {ex.Message}");
|
||||
_logger.LogError(ex, "HTTP 请求失败");
|
||||
}
|
||||
catch (TaskCanceledException ex)
|
||||
{
|
||||
sw.Stop();
|
||||
FailureCount++;
|
||||
StatusCode = "超时";
|
||||
StatusColor = Brushes.Orange;
|
||||
ResponseText = $"请求超时:\n{ex.Message}";
|
||||
ElapsedTime = $"{sw.ElapsedMilliseconds} ms";
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] ⏱ 请求超时");
|
||||
_logger.LogWarning("HTTP 请求超时");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
sw.Stop();
|
||||
FailureCount++;
|
||||
StatusCode = "异常";
|
||||
StatusColor = Brushes.Red;
|
||||
ResponseText = $"发生异常:\n{ex.Message}\n\n{ex.StackTrace}";
|
||||
ElapsedTime = $"{sw.ElapsedMilliseconds} ms";
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] ❌ 异常: {ex.Message}");
|
||||
_logger.LogError(ex, "HTTP 请求异常");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleResponse(HttpResponseMessage response, long elapsedMs)
|
||||
{
|
||||
// 状态码
|
||||
StatusCode = $"{(int)response.StatusCode} {response.ReasonPhrase}";
|
||||
StatusColor = response.IsSuccessStatusCode ? Brushes.Green : Brushes.Red;
|
||||
|
||||
// 响应头
|
||||
var headersBuilder = new StringBuilder();
|
||||
headersBuilder.AppendLine("=== 响应头 ===");
|
||||
foreach (var header in response.Headers)
|
||||
{
|
||||
headersBuilder.AppendLine($"{header.Key}: {string.Join(", ", header.Value)}");
|
||||
}
|
||||
if (response.Content != null)
|
||||
{
|
||||
headersBuilder.AppendLine("\n=== 内容头 ===");
|
||||
foreach (var header in response.Content.Headers)
|
||||
{
|
||||
headersBuilder.AppendLine($"{header.Key}: {string.Join(", ", header.Value)}");
|
||||
}
|
||||
}
|
||||
ResponseHeaders = headersBuilder.ToString();
|
||||
|
||||
// 内容类型
|
||||
ContentType = response.Content?.Headers.ContentType?.MediaType ?? "未知";
|
||||
|
||||
// 响应体
|
||||
if (response.Content != null)
|
||||
{
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
ResponseText = string.IsNullOrWhiteSpace(content) ? "(空响应)" : content;
|
||||
|
||||
// 响应大小
|
||||
var sizeBytes = Encoding.UTF8.GetByteCount(content);
|
||||
ResponseSize = FormatSize(sizeBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
ResponseText = "(无内容)";
|
||||
ResponseSize = "0 bytes";
|
||||
}
|
||||
|
||||
// 耗时
|
||||
ElapsedTime = $"{elapsedMs} ms";
|
||||
}
|
||||
|
||||
private void Clear()
|
||||
{
|
||||
ResponseText = "";
|
||||
ResponseHeaders = "";
|
||||
StatusCode = "-";
|
||||
StatusColor = Brushes.Gray;
|
||||
ContentType = "-";
|
||||
ElapsedTime = "0 ms";
|
||||
ResponseSize = "0 bytes";
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] 已清空响应内容");
|
||||
}
|
||||
|
||||
private void CopyResponse()
|
||||
{
|
||||
try
|
||||
{
|
||||
System.Windows.Clipboard.SetText(ResponseText);
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] ✅ 已复制响应内容到剪贴板");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] ❌ 复制失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SaveResponse()
|
||||
{
|
||||
try
|
||||
{
|
||||
var dialog = new Microsoft.Win32.SaveFileDialog
|
||||
{
|
||||
FileName = $"response_{DateTime.Now:yyyyMMdd_HHmmss}",
|
||||
DefaultExt = ".txt",
|
||||
Filter = "文本文件 (.txt)|*.txt|JSON 文件 (.json)|*.json|所有文件 (*.*)|*.*"
|
||||
};
|
||||
|
||||
if (dialog.ShowDialog() == true)
|
||||
{
|
||||
await System.IO.File.WriteAllTextAsync(dialog.FileName, ResponseText);
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] ✅ 响应已保存到: {dialog.FileName}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] ❌ 保存失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task TestLocalServer()
|
||||
{
|
||||
RequestUrl = "http://localhost:8080/api/health";
|
||||
SelectedMethod = "GET";
|
||||
RequestBody = "";
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] 准备测试本地服务器健康检查");
|
||||
await SendRequest();
|
||||
}
|
||||
|
||||
private async Task TestRetry()
|
||||
{
|
||||
// 测试重试策略 - 使用一个不存在的端口
|
||||
RequestUrl = "http://localhost:9999/api/test";
|
||||
SelectedMethod = "GET";
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] 测试重试策略(此请求会失败并重试)");
|
||||
await SendRequest();
|
||||
}
|
||||
|
||||
private async Task TestTimeout()
|
||||
{
|
||||
// 测试超时 - 使用一个超时的端点
|
||||
RequestUrl = "http://httpstat.us/200?sleep=60000";
|
||||
SelectedMethod = "GET";
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] 测试超时处理(此请求会超时)");
|
||||
await SendRequest();
|
||||
}
|
||||
|
||||
private async Task TestJsonApi()
|
||||
{
|
||||
RequestUrl = "http://localhost:5000/api/data";
|
||||
SelectedMethod = "POST";
|
||||
RequestBody = "{\n \"name\": \"测试数据\",\n \"value\": 123,\n \"timestamp\": \"" + DateTime.Now.ToString("o") + "\"\n}";
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] 准备测试 JSON API");
|
||||
await SendRequest();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 辅助方法
|
||||
|
||||
private void AppendLog(string message)
|
||||
{
|
||||
LogText += message + "\n";
|
||||
}
|
||||
|
||||
private string FormatSize(long bytes)
|
||||
{
|
||||
if (bytes < 1024)
|
||||
return $"{bytes} bytes";
|
||||
if (bytes < 1024 * 1024)
|
||||
return $"{bytes / 1024.0:F2} KB";
|
||||
return $"{bytes / (1024.0 * 1024.0):F2} MB";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 清理
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
_logger.LogInformation("HTTP 客户端窗口 ViewModel 正在释放资源, 总请求数: {Total}, 成功: {Success}, 失败: {Failure}",
|
||||
TotalRequests, SuccessCount, FailureCount);
|
||||
base.Dispose();
|
||||
_logger.LogInformation("HTTP 客户端窗口 ViewModel 已释放");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
using System;
|
||||
using System.Windows.Media;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sharp.Wpf.Mvvm;
|
||||
using Sharp.Wpf.Commands;
|
||||
using Sharp.Demo.Services;
|
||||
|
||||
namespace Sharp.Demo.ViewModels.Windows
|
||||
{
|
||||
/// <summary>
|
||||
/// HTTP 服务器窗口 ViewModel
|
||||
/// </summary>
|
||||
public class HttpServerViewModel : ViewModelBase
|
||||
{
|
||||
private readonly ServerService _serverService;
|
||||
private readonly ILogger<HttpServerViewModel> _logger;
|
||||
|
||||
private string _serverUrl = "";
|
||||
private string _statusText = "未运行";
|
||||
private Brush _statusColor = Brushes.Gray;
|
||||
private string _logText = "等待启动服务器...\n";
|
||||
private string _host = "";
|
||||
private int _port = 0;
|
||||
private bool _enableCors = true;
|
||||
private bool _enableLogging = true;
|
||||
private string _uptime = "00:00:00";
|
||||
private int _totalRequests = 0;
|
||||
private int _activeConnections = 0;
|
||||
|
||||
public HttpServerViewModel(ServerService serverService, ILogger<HttpServerViewModel> logger)
|
||||
{
|
||||
_serverService = serverService ?? throw new ArgumentNullException(nameof(serverService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
_logger.LogInformation("HTTP 服务器窗口 ViewModel 已创建");
|
||||
|
||||
// 从 ServerService 获取配置
|
||||
_serverUrl = _serverService.Url;
|
||||
_host = _serverService.Host;
|
||||
_port = _serverService.Port;
|
||||
|
||||
// 订阅服务器事件
|
||||
_serverService.StatusChanged += OnServerStatusChanged;
|
||||
|
||||
// 初始化命令
|
||||
StartServerCommand = new RelayCommand(_ => StartServer(), _ => !_serverService.IsRunning);
|
||||
StopServerCommand = new RelayCommand(_ => StopServer(), _ => _serverService.IsRunning);
|
||||
RestartServerCommand = new RelayCommand(_ => RestartServer(), _ => _serverService.IsRunning);
|
||||
OpenUserManagementCommand = new RelayCommand(OpenUserManagement);
|
||||
ClearLogCommand = new RelayCommand(_ => LogText = "");
|
||||
OpenInBrowserCommand = new RelayCommand(_ => OpenInBrowser(), _ => _serverService.IsRunning);
|
||||
CopyUrlCommand = new RelayCommand(CopyUrl);
|
||||
ShowMetricsCommand = new RelayCommand(_ => ShowMetrics(), _ => _serverService.IsRunning);
|
||||
OpenAdvancedSettingsCommand = new RelayCommand(OpenAdvancedSettings);
|
||||
}
|
||||
|
||||
#region 属性
|
||||
|
||||
public string ServerUrl
|
||||
{
|
||||
get => _serverUrl;
|
||||
set => SetProperty(ref _serverUrl, value);
|
||||
}
|
||||
|
||||
public string StatusText
|
||||
{
|
||||
get => _statusText;
|
||||
set => SetProperty(ref _statusText, value);
|
||||
}
|
||||
|
||||
public Brush StatusColor
|
||||
{
|
||||
get => _statusColor;
|
||||
set => SetProperty(ref _statusColor, value);
|
||||
}
|
||||
|
||||
public string LogText
|
||||
{
|
||||
get => _logText;
|
||||
set => SetProperty(ref _logText, value);
|
||||
}
|
||||
|
||||
public string Host
|
||||
{
|
||||
get => _host;
|
||||
set => SetProperty(ref _host, value);
|
||||
}
|
||||
|
||||
public int Port
|
||||
{
|
||||
get => _port;
|
||||
set => SetProperty(ref _port, value);
|
||||
}
|
||||
|
||||
public bool EnableCors
|
||||
{
|
||||
get => _enableCors;
|
||||
set => SetProperty(ref _enableCors, value);
|
||||
}
|
||||
|
||||
public bool EnableLogging
|
||||
{
|
||||
get => _enableLogging;
|
||||
set => SetProperty(ref _enableLogging, value);
|
||||
}
|
||||
|
||||
public string Uptime
|
||||
{
|
||||
get => _uptime;
|
||||
set => SetProperty(ref _uptime, value);
|
||||
}
|
||||
|
||||
public int TotalRequests
|
||||
{
|
||||
get => _totalRequests;
|
||||
set => SetProperty(ref _totalRequests, value);
|
||||
}
|
||||
|
||||
public int ActiveConnections
|
||||
{
|
||||
get => _activeConnections;
|
||||
set => SetProperty(ref _activeConnections, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 命令
|
||||
|
||||
public RelayCommand StartServerCommand { get; }
|
||||
public RelayCommand StopServerCommand { get; }
|
||||
public RelayCommand RestartServerCommand { get; }
|
||||
public RelayCommand OpenUserManagementCommand { get; }
|
||||
public RelayCommand ClearLogCommand { get; }
|
||||
public RelayCommand OpenInBrowserCommand { get; }
|
||||
public RelayCommand CopyUrlCommand { get; }
|
||||
public RelayCommand ShowMetricsCommand { get; }
|
||||
public RelayCommand OpenAdvancedSettingsCommand { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region 命令实现
|
||||
|
||||
private void StartServer()
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("用户点击了启动服务器按钮");
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] 正在启动服务器...");
|
||||
_serverService.Start();
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] ✅ 服务器已启动: {ServerUrl}");
|
||||
|
||||
StatusText = "运行中";
|
||||
StatusColor = Brushes.Green;
|
||||
|
||||
// 刷新命令状态
|
||||
StartServerCommand.RaiseCanExecuteChanged();
|
||||
StopServerCommand.RaiseCanExecuteChanged();
|
||||
RestartServerCommand.RaiseCanExecuteChanged();
|
||||
OpenInBrowserCommand.RaiseCanExecuteChanged();
|
||||
ShowMetricsCommand.RaiseCanExecuteChanged();
|
||||
|
||||
_logger.LogInformation("HTTP 服务器已成功启动");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "启动 HTTP 服务器失败");
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] ❌ 启动失败: {ex.Message}");
|
||||
StatusText = "启动失败";
|
||||
StatusColor = Brushes.Red;
|
||||
}
|
||||
}
|
||||
|
||||
private void StopServer()
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("用户点击了停止服务器按钮");
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] 正在停止服务器...");
|
||||
_serverService.Stop();
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] ⏹ 服务器已停止");
|
||||
|
||||
StatusText = "已停止";
|
||||
StatusColor = Brushes.Gray;
|
||||
|
||||
// 刷新命令状态
|
||||
StartServerCommand.RaiseCanExecuteChanged();
|
||||
StopServerCommand.RaiseCanExecuteChanged();
|
||||
RestartServerCommand.RaiseCanExecuteChanged();
|
||||
OpenInBrowserCommand.RaiseCanExecuteChanged();
|
||||
ShowMetricsCommand.RaiseCanExecuteChanged();
|
||||
|
||||
_logger.LogInformation("HTTP 服务器已停止");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "停止 HTTP 服务器失败");
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] ❌ 停止失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void RestartServer()
|
||||
{
|
||||
_logger.LogInformation("用户点击了重启服务器按钮");
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] 重启服务器...");
|
||||
StopServer();
|
||||
System.Threading.Thread.Sleep(500);
|
||||
StartServer();
|
||||
}
|
||||
|
||||
private void OpenUserManagement()
|
||||
{
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] 打开用户管理功能(待实现)");
|
||||
System.Windows.MessageBox.Show("用户管理功能开发中...", "提示");
|
||||
}
|
||||
|
||||
private void OpenInBrowser()
|
||||
{
|
||||
try
|
||||
{
|
||||
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
|
||||
{
|
||||
FileName = ServerUrl,
|
||||
UseShellExecute = true
|
||||
});
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] 已在浏览器中打开: {ServerUrl}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] ❌ 打开浏览器失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void CopyUrl()
|
||||
{
|
||||
try
|
||||
{
|
||||
System.Windows.Clipboard.SetText(ServerUrl);
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] ✅ 已复制服务器地址到剪贴板");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] ❌ 复制失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowMetrics()
|
||||
{
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] 性能指标: 请求总数={TotalRequests}, 活跃连接={ActiveConnections}");
|
||||
}
|
||||
|
||||
private void OpenAdvancedSettings()
|
||||
{
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] 高级设置功能(待实现)");
|
||||
System.Windows.MessageBox.Show("高级设置功能开发中...", "提示");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 事件处理
|
||||
|
||||
private void OnServerStatusChanged(object? sender, string status)
|
||||
{
|
||||
if (_serverService.IsRunning)
|
||||
{
|
||||
StatusText = "运行中";
|
||||
StatusColor = Brushes.Green;
|
||||
}
|
||||
else
|
||||
{
|
||||
StatusText = "未运行";
|
||||
StatusColor = Brushes.Gray;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 辅助方法
|
||||
|
||||
private void AppendLog(string message)
|
||||
{
|
||||
LogText += message + "\n";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 清理
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
_logger.LogInformation("HTTP 服务器窗口 ViewModel 正在释放资源");
|
||||
_serverService.StatusChanged -= OnServerStatusChanged;
|
||||
base.Dispose();
|
||||
_logger.LogInformation("HTTP 服务器窗口 ViewModel 已释放");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using Sharp.Wpf.Mvvm;
|
||||
using Sharp.Wpf.Commands;
|
||||
|
||||
namespace Sharp.Demo.ViewModels.Windows
|
||||
{
|
||||
public class PlcDebugViewModel : ViewModelBase
|
||||
{
|
||||
private string _ipAddress = "127.0.0.1";
|
||||
private string _status = "未连接";
|
||||
private string _readAddress = "DB1.DBW0";
|
||||
private string _readValue = "";
|
||||
private string _writeAddress = "DB1.DBW0";
|
||||
private string _writeValue = "";
|
||||
private string _logText = "等待连接PLC...\n";
|
||||
|
||||
public PlcDebugViewModel()
|
||||
{
|
||||
ConnectCommand = new RelayCommand(Connect);
|
||||
DisconnectCommand = new RelayCommand(Disconnect);
|
||||
ReadCommand = new RelayCommand(Read);
|
||||
WriteCommand = new RelayCommand(Write);
|
||||
ClearLogCommand = new RelayCommand(() => LogText = "");
|
||||
}
|
||||
|
||||
#region 属性
|
||||
|
||||
public string IpAddress
|
||||
{
|
||||
get => _ipAddress;
|
||||
set => SetProperty(ref _ipAddress, value);
|
||||
}
|
||||
|
||||
public string Status
|
||||
{
|
||||
get => _status;
|
||||
set => SetProperty(ref _status, value);
|
||||
}
|
||||
|
||||
public string ReadAddress
|
||||
{
|
||||
get => _readAddress;
|
||||
set => SetProperty(ref _readAddress, value);
|
||||
}
|
||||
|
||||
public string ReadValue
|
||||
{
|
||||
get => _readValue;
|
||||
set => SetProperty(ref _readValue, value);
|
||||
}
|
||||
|
||||
public string WriteAddress
|
||||
{
|
||||
get => _writeAddress;
|
||||
set => SetProperty(ref _writeAddress, value);
|
||||
}
|
||||
|
||||
public string WriteValue
|
||||
{
|
||||
get => _writeValue;
|
||||
set => SetProperty(ref _writeValue, value);
|
||||
}
|
||||
|
||||
public string LogText
|
||||
{
|
||||
get => _logText;
|
||||
set => SetProperty(ref _logText, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 命令
|
||||
|
||||
public RelayCommand ConnectCommand { get; }
|
||||
public RelayCommand DisconnectCommand { get; }
|
||||
public RelayCommand ReadCommand { get; }
|
||||
public RelayCommand WriteCommand { get; }
|
||||
public RelayCommand ClearLogCommand { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region 命令实现
|
||||
|
||||
private void Connect()
|
||||
{
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] 正在连接 PLC: {IpAddress}");
|
||||
Status = "已连接";
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] ✅ 连接成功");
|
||||
}
|
||||
|
||||
private void Disconnect()
|
||||
{
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] 断开连接");
|
||||
Status = "未连接";
|
||||
}
|
||||
|
||||
private void Read()
|
||||
{
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] 读取 {ReadAddress}");
|
||||
ReadValue = "42"; // 示例值
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] ✅ 读取成功: {ReadValue}");
|
||||
}
|
||||
|
||||
private void Write()
|
||||
{
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] 写入 {WriteAddress} = {WriteValue}");
|
||||
AppendLog($"[{DateTime.Now:HH:mm:ss}] ✅ 写入成功");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void AppendLog(string message)
|
||||
{
|
||||
LogText += message + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<Window x:Class="Sharp.Demo.Views.Dialogs.UserEditDialog"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||
Title="用户编辑" Height="450" Width="500"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
ResizeMode="NoResize">
|
||||
<Grid Margin="20">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 标题 -->
|
||||
<TextBlock Grid.Row="0" Text="用户信息" FontSize="18" FontWeight="Bold" Margin="0,0,0,20"/>
|
||||
|
||||
<!-- 表单 -->
|
||||
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel>
|
||||
<!-- 用户名 -->
|
||||
<TextBlock Text="用户名 *" FontWeight="Bold" Margin="0,0,0,5"/>
|
||||
<TextBox x:Name="UserNameTextBox" Height="32" Padding="8,0" Margin="0,0,0,15"
|
||||
BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1"/>
|
||||
|
||||
<!-- 邮箱 -->
|
||||
<TextBlock Text="邮箱 *" FontWeight="Bold" Margin="0,0,0,5"/>
|
||||
<TextBox x:Name="EmailTextBox" Height="32" Padding="8,0" Margin="0,0,0,15"
|
||||
BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1"/>
|
||||
|
||||
<!-- 年龄 -->
|
||||
<TextBlock Text="年龄 *" FontWeight="Bold" Margin="0,0,0,5"/>
|
||||
<TextBox x:Name="AgeTextBox" Height="32" Padding="8,0" Margin="0,0,0,15"
|
||||
BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1"/>
|
||||
|
||||
<!-- 激活状态 -->
|
||||
<CheckBox x:Name="IsActiveCheckBox" Content="激活" IsChecked="True" Margin="0,0,0,15"/>
|
||||
|
||||
<!-- 备注 -->
|
||||
<TextBlock Text="备注" FontWeight="Bold" Margin="0,0,0,5"/>
|
||||
<TextBox x:Name="RemarksTextBox" Height="80" Padding="8" Margin="0,0,0,15"
|
||||
TextWrapping="Wrap" AcceptsReturn="True"
|
||||
BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1"
|
||||
VerticalScrollBarVisibility="Auto"/>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
<!-- 按钮 -->
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,20,0,0">
|
||||
<Button Content="保存" Width="100" Height="35" Margin="0,0,10,0" Click="SaveButton_Click"
|
||||
Style="{StaticResource ButtonSuccess}" Cursor="Hand"/>
|
||||
<Button Content="取消" Width="100" Height="35" Click="CancelButton_Click"
|
||||
Style="{StaticResource ButtonDefault}" Cursor="Hand"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,90 @@
|
||||
using System.Windows;
|
||||
using Sharp.Demo.Data;
|
||||
|
||||
namespace Sharp.Demo.Views.Dialogs
|
||||
{
|
||||
public partial class UserEditDialog : Window
|
||||
{
|
||||
public User UserData { get; private set; }
|
||||
private readonly bool _isEditMode;
|
||||
|
||||
public UserEditDialog(User? existingUser = null)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_isEditMode = existingUser != null;
|
||||
Title = _isEditMode ? "编辑用户" : "新增用户";
|
||||
|
||||
if (existingUser != null)
|
||||
{
|
||||
UserData = existingUser;
|
||||
LoadUserData(existingUser);
|
||||
}
|
||||
else
|
||||
{
|
||||
UserData = new User
|
||||
{
|
||||
IsActive = true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadUserData(User user)
|
||||
{
|
||||
UserNameTextBox.Text = user.UserName;
|
||||
EmailTextBox.Text = user.Email;
|
||||
AgeTextBox.Text = user.Age.ToString();
|
||||
IsActiveCheckBox.IsChecked = user.IsActive;
|
||||
RemarksTextBox.Text = user.Remarks;
|
||||
}
|
||||
|
||||
private void SaveButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// 验证
|
||||
if (string.IsNullOrWhiteSpace(UserNameTextBox.Text))
|
||||
{
|
||||
MessageBox.Show("请输入用户名", "验证失败", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
UserNameTextBox.Focus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(EmailTextBox.Text))
|
||||
{
|
||||
MessageBox.Show("请输入邮箱", "验证失败", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
EmailTextBox.Focus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!int.TryParse(AgeTextBox.Text, out int age) || age < 0 || age > 150)
|
||||
{
|
||||
MessageBox.Show("请输入有效的年龄(0-150)", "验证失败", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
AgeTextBox.Focus();
|
||||
return;
|
||||
}
|
||||
|
||||
// 简单的邮箱格式验证
|
||||
if (!EmailTextBox.Text.Contains("@"))
|
||||
{
|
||||
MessageBox.Show("请输入有效的邮箱地址", "验证失败", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
EmailTextBox.Focus();
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新数据
|
||||
UserData.UserName = UserNameTextBox.Text.Trim();
|
||||
UserData.Email = EmailTextBox.Text.Trim();
|
||||
UserData.Age = age;
|
||||
UserData.IsActive = IsActiveCheckBox.IsChecked == true;
|
||||
UserData.Remarks = RemarksTextBox.Text.Trim();
|
||||
|
||||
DialogResult = true;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void CancelButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
DialogResult = false;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
<hc:GlowWindow x:Class="Sharp.Demo.Views.LauncherWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
Title="Sharp Framework - 模块启动器"
|
||||
Height="700"
|
||||
Width="1100"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
Background="{DynamicResource RegionBrush}">
|
||||
|
||||
<!-- 快捷键 -->
|
||||
<hc:GlowWindow.InputBindings>
|
||||
<KeyBinding Key="F12" Command="{Binding OpenDevToolsCommand}" />
|
||||
<KeyBinding Key="F11" Command="{Binding OpenLogViewerCommand}" />
|
||||
<KeyBinding Key="F10" Command="{Binding OpenPerformanceMonitorCommand}" />
|
||||
</hc:GlowWindow.InputBindings>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<!-- 标题栏 -->
|
||||
<RowDefinition Height="*"/>
|
||||
<!-- 模块区域 -->
|
||||
<RowDefinition Height="Auto"/>
|
||||
<!-- 状态栏 -->
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 标题栏 -->
|
||||
<Border Grid.Row="0" Background="{DynamicResource PrimaryBrush}" Padding="30,20">
|
||||
<Grid>
|
||||
<StackPanel>
|
||||
<TextBlock Text="Sharp Framework" FontSize="32" FontWeight="Bold" Foreground="White"/>
|
||||
<TextBlock Text="模块化 .NET 8 开发框架 - 功能演示平台" FontSize="14" Foreground="White" Opacity="0.9" Margin="0,5,0,0"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center">
|
||||
<Button Content="⚙️ 设置" Width="80" Height="32" Margin="0,0,10,0" Style="{StaticResource ButtonDefault}"/>
|
||||
<Button Content="📖 文档" Width="80" Height="32" Margin="0,0,10,0" Style="{StaticResource ButtonDefault}"/>
|
||||
<Button Content="❓ 关于" Width="80" Height="32" Command="{Binding ShowAboutCommand}" Style="{StaticResource ButtonDefault}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" Padding="20">
|
||||
<StackPanel>
|
||||
<!-- 快速状态 -->
|
||||
<Border Background="{DynamicResource SecondaryRegionBrush}" CornerRadius="8" Padding="20" Margin="0,0,0,20">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<StackPanel Grid.Column="0" Margin="0,0,20,0">
|
||||
<TextBlock Text="HTTP 服务器" FontWeight="SemiBold" Foreground="{DynamicResource SecondaryTextBrush}"/>
|
||||
<TextBlock Text="{Binding ServerStatus}" FontSize="20" Margin="0,5,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="1" Margin="0,0,20,0">
|
||||
<TextBlock Text="PLC 连接" FontWeight="SemiBold" Foreground="{DynamicResource SecondaryTextBrush}"/>
|
||||
<TextBlock Text="{Binding PlcStatus}" FontSize="20" Margin="0,5,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="2" Margin="0,0,20,0">
|
||||
<TextBlock Text="打开窗口" FontWeight="SemiBold" Foreground="{DynamicResource SecondaryTextBrush}"/>
|
||||
<TextBlock Text="{Binding OpenWindowCount}" FontSize="20" Margin="0,5,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="3" Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<Button Content="F12 开发者工具" Width="130" Height="32" Margin="0,0,10,0"
|
||||
Command="{Binding OpenDevToolsCommand}" Style="{StaticResource ButtonInfo}"/>
|
||||
<Button Content="F11 日志查看" Width="120" Height="32" Margin="0,0,10,0"
|
||||
Command="{Binding OpenLogViewerCommand}" Style="{StaticResource ButtonSuccess}"/>
|
||||
<Button Content="F10 性能监控" Width="120" Height="32"
|
||||
Command="{Binding OpenPerformanceMonitorCommand}" Style="{StaticResource ButtonWarning}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- 模块分类展示 -->
|
||||
<ItemsControl ItemsSource="{Binding ModuleCategories}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Margin="0,0,0,25">
|
||||
<!-- 分类标题 -->
|
||||
<TextBlock Text="{Binding CategoryName}" FontSize="20" FontWeight="Bold" Margin="0,0,0,15"/>
|
||||
|
||||
<!-- 模块卡片 -->
|
||||
<ItemsControl ItemsSource="{Binding Modules}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<UniformGrid Columns="4" HorizontalAlignment="Left"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="20"
|
||||
Margin="0,0,15,15"
|
||||
Height="160"
|
||||
Cursor="Hand">
|
||||
<Border.Style>
|
||||
<Style TargetType="Border">
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="{DynamicResource SecondaryRegionBrush}"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Border.Style>
|
||||
<Border.InputBindings>
|
||||
<MouseBinding MouseAction="LeftClick" Command="{Binding OpenCommand}"/>
|
||||
</Border.InputBindings>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 图标 -->
|
||||
<TextBlock Grid.Row="0" Text="{Binding Icon}" FontSize="40" HorizontalAlignment="Left"/>
|
||||
|
||||
<!-- 名称和描述 -->
|
||||
<StackPanel Grid.Row="1" VerticalAlignment="Center" Margin="0,10,0,0">
|
||||
<TextBlock Text="{Binding Name}" FontSize="16" FontWeight="Bold"/>
|
||||
<TextBlock Text="{Binding Description}" FontSize="12"
|
||||
Foreground="{DynamicResource SecondaryTextBrush}"
|
||||
TextWrapping="Wrap" Margin="0,5,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 状态 -->
|
||||
<TextBlock Grid.Row="2" Text="{Binding Status}" FontSize="11"
|
||||
Foreground="{DynamicResource SuccessBrush}"
|
||||
HorizontalAlignment="Right"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
<!-- 状态栏 -->
|
||||
<StatusBar Grid.Row="2" Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}" BorderThickness="0,1,0,0">
|
||||
<StatusBarItem>
|
||||
<TextBlock Text="Sharp Framework v1.0.0"/>
|
||||
</StatusBarItem>
|
||||
<Separator/>
|
||||
<StatusBarItem>
|
||||
<TextBlock>
|
||||
<Run Text="运行时间: "/>
|
||||
<Run Text="{Binding Uptime, Mode=OneWay}"/>
|
||||
</TextBlock>
|
||||
</StatusBarItem>
|
||||
<Separator/>
|
||||
<StatusBarItem>
|
||||
<TextBlock>
|
||||
<Run Text="已打开窗口: "/>
|
||||
<Run Text="{Binding OpenWindowCount, Mode=OneWay}"/>
|
||||
</TextBlock>
|
||||
</StatusBarItem>
|
||||
</StatusBar>
|
||||
</Grid>
|
||||
</hc:GlowWindow>
|
||||
@@ -0,0 +1,43 @@
|
||||
using System.Windows;
|
||||
using HandyControl.Controls;
|
||||
using Sharp.Demo.ViewModels;
|
||||
|
||||
namespace Sharp.Demo.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// LauncherWindow.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class LauncherWindow : GlowWindow
|
||||
{
|
||||
public LauncherWindow(LauncherViewModel viewModel)
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = viewModel;
|
||||
}
|
||||
|
||||
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
|
||||
{
|
||||
var result = System.Windows.MessageBox.Show(
|
||||
"确定要退出 Sharp Framework 吗?\n这将关闭所有已打开的模块窗口。",
|
||||
"确认退出",
|
||||
MessageBoxButton.YesNo,
|
||||
MessageBoxImage.Question
|
||||
);
|
||||
|
||||
if (result == MessageBoxResult.No)
|
||||
{
|
||||
e.Cancel = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 关闭所有模块窗口
|
||||
if (DataContext is LauncherViewModel vm)
|
||||
{
|
||||
vm.CloseAllWindows();
|
||||
}
|
||||
}
|
||||
|
||||
base.OnClosing(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
<UserControl x:Class="Sharp.Demo.Views.Pages.ConfigDemoPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="600"
|
||||
d:DesignWidth="800">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Margin="20">
|
||||
<!-- 标题区域 -->
|
||||
<TextBlock
|
||||
FontSize="28"
|
||||
FontWeight="Bold"
|
||||
Text="配置管理" />
|
||||
<TextBlock
|
||||
Margin="0,10,0,30"
|
||||
FontSize="14"
|
||||
Foreground="{DynamicResource SecondaryTextBrush}"
|
||||
Text="Sharp.Config - 多格式配置管理,支持 JSON/XML/INI/TOML,热重载" />
|
||||
|
||||
<!-- 配置信息 -->
|
||||
<Border
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="20"
|
||||
Margin="0,0,0,15">
|
||||
<StackPanel>
|
||||
<TextBlock Text="当前配置" FontSize="16" FontWeight="Bold" Margin="0,0,0,15"/>
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Text="配置文件:" VerticalAlignment="Center" Margin="0,0,0,10"/>
|
||||
<TextBlock Grid.Column="1" Text="{Binding ConfigFile}" VerticalAlignment="Center"
|
||||
Margin="0,0,0,10" Foreground="{DynamicResource SecondaryTextBrush}"/>
|
||||
|
||||
<TextBlock Grid.Row="1" Text="支持格式:" VerticalAlignment="Center" Margin="0,0,0,10"/>
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" Text="JSON, XML, INI, TOML" VerticalAlignment="Center"
|
||||
Margin="0,0,0,10" Foreground="{DynamicResource SecondaryTextBrush}"/>
|
||||
|
||||
<TextBlock Grid.Row="2" Text="热重载:" VerticalAlignment="Center" Margin="0,0,0,10"/>
|
||||
<StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal" Margin="0,0,0,10">
|
||||
<CheckBox IsChecked="{Binding IsHotReloadEnabled}" Content="已启用"
|
||||
IsEnabled="False" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Grid.Row="3" Text="环境变量支持:" VerticalAlignment="Center"/>
|
||||
<StackPanel Grid.Row="3" Grid.Column="1" Orientation="Horizontal">
|
||||
<CheckBox IsChecked="True" Content="已启用 (前缀: APP_)"
|
||||
IsEnabled="False" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 配置读取 -->
|
||||
<Border
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="20"
|
||||
Margin="0,0,0,15">
|
||||
<StackPanel>
|
||||
<TextBlock Text="配置读取测试" FontSize="16" FontWeight="Bold" Margin="0,0,0,15"/>
|
||||
|
||||
<Grid Margin="0,0,0,15">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Text="配置键:" VerticalAlignment="Center" Margin="0,0,10,0"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding ConfigKey}" Height="32" Padding="8,0"
|
||||
Margin="0,0,10,0" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1"/>
|
||||
<Button Grid.Column="2" Content="读取" Width="100" Height="32"
|
||||
Command="{Binding ReadConfigCommand}"
|
||||
Style="{StaticResource ButtonPrimary}"/>
|
||||
</Grid>
|
||||
|
||||
<TextBlock Text="配置值:" Margin="0,0,0,5"/>
|
||||
<TextBox Text="{Binding ConfigValue, Mode=OneWay}" Height="60" IsReadOnly="True"
|
||||
TextWrapping="Wrap" Background="{DynamicResource SecondaryRegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1"
|
||||
FontFamily="Consolas" Padding="8"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 快捷功能 -->
|
||||
<Border
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="20"
|
||||
Margin="0,0,0,15">
|
||||
<StackPanel>
|
||||
<TextBlock Text="快捷功能" FontSize="16" FontWeight="Bold" Margin="0,0,0,15"/>
|
||||
|
||||
<WrapPanel>
|
||||
<Button Content="查看所有配置" Width="140" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding ViewAllConfigCommand}"
|
||||
Style="{StaticResource ButtonPrimary}"/>
|
||||
<Button Content="读取数据库配置" Width="140" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding ReadDbConfigCommand}"
|
||||
Style="{StaticResource ButtonInfo}"/>
|
||||
<Button Content="读取日志配置" Width="140" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding ReadLogConfigCommand}"
|
||||
Style="{StaticResource ButtonInfo}"/>
|
||||
<Button Content="读取服务器配置" Width="140" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding ReadServerConfigCommand}"
|
||||
Style="{StaticResource ButtonInfo}"/>
|
||||
<Button Content="测试热重载" Width="140" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding TestHotReloadCommand}"
|
||||
Style="{StaticResource ButtonSuccess}"/>
|
||||
<Button Content="打开配置文件" Width="140" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding OpenConfigFileCommand}"
|
||||
Style="{StaticResource ButtonWarning}"/>
|
||||
<Button Content="验证配置" Width="140" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding ValidateConfigCommand}"
|
||||
Style="{StaticResource ButtonDefault}"/>
|
||||
</WrapPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 配置预览 -->
|
||||
<Border
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="15">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="250"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Text="配置内容预览" FontSize="16" FontWeight="Bold" Margin="0,0,0,10"/>
|
||||
|
||||
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
|
||||
<TextBox Text="{Binding ConfigPreview, Mode=OneWay}"
|
||||
IsReadOnly="True"
|
||||
TextWrapping="Wrap"
|
||||
Background="{DynamicResource SecondaryRegionBrush}"
|
||||
BorderThickness="0"
|
||||
FontFamily="Consolas"
|
||||
FontSize="11"
|
||||
Padding="10"/>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Sharp.Demo.Views.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// ConfigDemoPage.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class ConfigDemoPage : UserControl
|
||||
{
|
||||
public ConfigDemoPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
<UserControl x:Class="Sharp.Demo.Views.Pages.DatabasePage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="600"
|
||||
d:DesignWidth="800">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<Grid Margin="20">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 标题区域 -->
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
FontSize="28"
|
||||
FontWeight="Bold"
|
||||
Text="数据库管理"
|
||||
Margin="0,0,0,10" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
FontSize="14"
|
||||
Foreground="{DynamicResource SecondaryTextBrush}"
|
||||
Text="Sharp.Data - EF Core + Repository + 审计追踪 + 软删除"
|
||||
Margin="0,0,0,20" />
|
||||
|
||||
<!-- 搜索和操作栏 -->
|
||||
<Border
|
||||
Grid.Row="2"
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="15"
|
||||
Margin="0,0,0,15">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- 搜索框 -->
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal" Margin="0,0,15,0">
|
||||
<TextBlock Text="搜索:" VerticalAlignment="Center" Margin="0,0,10,0"/>
|
||||
<TextBox Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}"
|
||||
Width="250" Height="32" VerticalContentAlignment="Center"
|
||||
Padding="8,0" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1"/>
|
||||
<Button Content="搜索" Width="80" Height="32" Margin="10,0,0,0"
|
||||
Command="{Binding SearchCommand}"
|
||||
Style="{StaticResource ButtonPrimary}"/>
|
||||
<Button Content="清空" Width="80" Height="32" Margin="5,0,0,0"
|
||||
Command="{Binding ClearSearchCommand}"
|
||||
Style="{StaticResource ButtonDefault}"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 状态信息 -->
|
||||
<TextBlock Grid.Column="1" Text="{Binding StatusText}" VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource SecondaryTextBrush}" Margin="15,0"/>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal">
|
||||
<Button Content="新增" Width="90" Height="32" Margin="0,0,5,0"
|
||||
Command="{Binding AddCommand}"
|
||||
Style="{StaticResource ButtonSuccess}"/>
|
||||
<Button Content="编辑" Width="90" Height="32" Margin="0,0,5,0"
|
||||
Command="{Binding EditCommand}"
|
||||
Style="{StaticResource ButtonWarning}"/>
|
||||
<Button Content="删除" Width="90" Height="32"
|
||||
Command="{Binding DeleteCommand}"
|
||||
Style="{StaticResource ButtonDanger}"/>
|
||||
<Button Content="刷新" Width="90" Height="32" Margin="5,0,0,0"
|
||||
Command="{Binding RefreshCommand}"
|
||||
Style="{StaticResource ButtonInfo}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- 数据列表 -->
|
||||
<Border
|
||||
Grid.Row="3"
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="10"
|
||||
Margin="0,0,0,15">
|
||||
<DataGrid ItemsSource="{Binding Users}"
|
||||
SelectedItem="{Binding SelectedUser}"
|
||||
AutoGenerateColumns="False"
|
||||
IsReadOnly="True"
|
||||
SelectionMode="Single"
|
||||
GridLinesVisibility="None"
|
||||
HeadersVisibility="Column"
|
||||
AlternationCount="2"
|
||||
RowHeight="40"
|
||||
CanUserAddRows="False"
|
||||
CanUserDeleteRows="False">
|
||||
<DataGrid.Resources>
|
||||
<Style TargetType="DataGridRow">
|
||||
<Style.Triggers>
|
||||
<Trigger Property="ItemsControl.AlternationIndex" Value="1">
|
||||
<Setter Property="Background" Value="{DynamicResource SecondaryRegionBrush}" />
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</DataGrid.Resources>
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="ID" Binding="{Binding Id}" Width="80" Visibility="Collapsed"/>
|
||||
<DataGridTextColumn Header="用户名" Binding="{Binding UserName}" Width="150"/>
|
||||
<DataGridTextColumn Header="邮箱" Binding="{Binding Email}" Width="200"/>
|
||||
<DataGridTextColumn Header="年龄" Binding="{Binding Age}" Width="80"/>
|
||||
<DataGridCheckBoxColumn Header="激活" Binding="{Binding IsActive}" Width="80"/>
|
||||
<DataGridTextColumn Header="备注" Binding="{Binding Remarks}" Width="*"/>
|
||||
<DataGridTextColumn Header="创建时间" Binding="{Binding CreatedAt, StringFormat={}{0:yyyy-MM-dd HH:mm}}" Width="150"/>
|
||||
<DataGridTextColumn Header="创建人" Binding="{Binding CreatedBy}" Width="100"/>
|
||||
<DataGridTextColumn Header="修改时间" Binding="{Binding UpdatedAt, StringFormat={}{0:yyyy-MM-dd HH:mm}}" Width="150"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</Border>
|
||||
|
||||
<!-- 底部信息栏 -->
|
||||
<Border
|
||||
Grid.Row="4"
|
||||
Background="{DynamicResource SecondaryRegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="15">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock FontWeight="Bold" Margin="0,0,0,5">
|
||||
<Run Text="💡 功能说明"/>
|
||||
</TextBlock>
|
||||
<TextBlock FontSize="11" TextWrapping="Wrap">
|
||||
<Run Text="• 本示例展示 Sharp.Data 模块的使用:SQLite 数据库、仓储模式、自动审计、软删除"/>
|
||||
<LineBreak/>
|
||||
<Run Text="• 删除操作使用软删除(IsDeleted=true),数据不会真正删除"/>
|
||||
<LineBreak/>
|
||||
<Run Text="• 创建/修改时间和操作人由 Data 模块自动记录"/>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="1" VerticalAlignment="Center">
|
||||
<TextBlock Text="数据库: SQLite (demo.db)" FontSize="11" FontWeight="Bold"/>
|
||||
<TextBlock Text="{Binding RecordCount}" FontSize="11"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Sharp.Demo.Views.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// DatabasePage.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class DatabasePage : UserControl
|
||||
{
|
||||
public DatabasePage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
<UserControl x:Class="Sharp.Demo.Views.Pages.HomePage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="600"
|
||||
d:DesignWidth="800">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Margin="20">
|
||||
<!-- 标题区域 -->
|
||||
<TextBlock
|
||||
FontSize="32"
|
||||
FontWeight="Bold"
|
||||
Text="Sharp Framework Demo" />
|
||||
<TextBlock
|
||||
Margin="0,10,0,30"
|
||||
FontSize="16"
|
||||
Foreground="{DynamicResource SecondaryTextBrush}"
|
||||
Text="模块化 C# .NET 8 开发框架 - 功能演示和测试平台" />
|
||||
|
||||
<!-- 快速开始卡片区 -->
|
||||
<TextBlock
|
||||
Margin="0,0,0,15"
|
||||
FontSize="20"
|
||||
FontWeight="Bold"
|
||||
Text="快速开始" />
|
||||
|
||||
<UniformGrid Columns="3" Rows="1">
|
||||
<!-- 网络通信卡片 -->
|
||||
<Border
|
||||
Margin="0,0,10,0"
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="20">
|
||||
<StackPanel>
|
||||
<TextBlock
|
||||
FontSize="20"
|
||||
FontWeight="Bold"
|
||||
Text="🌐 网络通信" />
|
||||
<TextBlock
|
||||
Margin="0,10,0,0"
|
||||
Text="{Binding ServerStatusText}"
|
||||
TextWrapping="Wrap" />
|
||||
<Button
|
||||
Margin="0,15,0,0"
|
||||
Command="{Binding NavigateToHttpServerCommand}"
|
||||
Content="打开 HTTP 服务器"
|
||||
Style="{StaticResource ButtonPrimary}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 数据管理卡片 -->
|
||||
<Border
|
||||
Margin="5,0,5,0"
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="20">
|
||||
<StackPanel>
|
||||
<TextBlock
|
||||
FontSize="20"
|
||||
FontWeight="Bold"
|
||||
Text="💾 数据管理" />
|
||||
<TextBlock
|
||||
Margin="0,10,0,0"
|
||||
Text="SQLite 数据库 + EF Core"
|
||||
TextWrapping="Wrap" />
|
||||
<Button
|
||||
Margin="0,15,0,0"
|
||||
Command="{Binding NavigateToDatabaseCommand}"
|
||||
Content="打开数据库管理"
|
||||
Style="{StaticResource ButtonSuccess}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 工业控制卡片 -->
|
||||
<Border
|
||||
Margin="10,0,0,0"
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="20">
|
||||
<StackPanel>
|
||||
<TextBlock
|
||||
FontSize="20"
|
||||
FontWeight="Bold"
|
||||
Text="🏭 工业控制" />
|
||||
<TextBlock
|
||||
Margin="0,10,0,0"
|
||||
Text="{Binding PlcStatusText}"
|
||||
TextWrapping="Wrap" />
|
||||
<Button
|
||||
Margin="0,15,0,0"
|
||||
Command="{Binding NavigateToPlcCommand}"
|
||||
Content="打开 PLC 调试"
|
||||
Style="{StaticResource ButtonWarning}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</UniformGrid>
|
||||
|
||||
<!-- 所有模块列表 -->
|
||||
<TextBlock
|
||||
Margin="0,40,0,15"
|
||||
FontSize="20"
|
||||
FontWeight="Bold"
|
||||
Text="所有模块" />
|
||||
|
||||
<ItemsControl ItemsSource="{Binding Modules}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border
|
||||
Margin="0,0,0,10"
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="15">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Margin="0,0,15,0"
|
||||
FontSize="32"
|
||||
Text="{Binding Icon}"
|
||||
VerticalAlignment="Center" />
|
||||
|
||||
<StackPanel Grid.Column="1" VerticalAlignment="Center">
|
||||
<TextBlock
|
||||
FontSize="16"
|
||||
FontWeight="Bold"
|
||||
Text="{Binding Name}" />
|
||||
<TextBlock
|
||||
Margin="0,5,0,0"
|
||||
Foreground="{DynamicResource SecondaryTextBrush}"
|
||||
Text="{Binding Description}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
Command="{Binding NavigateCommand}"
|
||||
Content="打开"
|
||||
VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<!-- 系统信息 -->
|
||||
<Border
|
||||
Margin="0,30,0,0"
|
||||
Background="{DynamicResource SecondaryRegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="15">
|
||||
<StackPanel>
|
||||
<TextBlock
|
||||
FontSize="18"
|
||||
FontWeight="Bold"
|
||||
Text="系统信息" />
|
||||
<TextBlock
|
||||
Margin="0,10,0,0"
|
||||
Text="{Binding SystemInfo}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Sharp.Demo.Views.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// HomePage.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class HomePage : UserControl
|
||||
{
|
||||
public HomePage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
<UserControl x:Class="Sharp.Demo.Views.Pages.HttpClientPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="600"
|
||||
d:DesignWidth="800">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Margin="20">
|
||||
<!-- 标题区域 -->
|
||||
<TextBlock
|
||||
FontSize="28"
|
||||
FontWeight="Bold"
|
||||
Text="HTTP 客户端" />
|
||||
<TextBlock
|
||||
Margin="0,10,0,30"
|
||||
FontSize="14"
|
||||
Foreground="{DynamicResource SecondaryTextBrush}"
|
||||
Text="Sharp.NetClient - 基于 HttpClient 的网络请求库,集成 Polly 重试策略" />
|
||||
|
||||
<!-- 请求配置 -->
|
||||
<Border
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="20"
|
||||
Margin="0,0,0,15">
|
||||
<StackPanel>
|
||||
<TextBlock Text="请求配置" FontSize="16" FontWeight="Bold" Margin="0,0,0,15"/>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Text="URL:" VerticalAlignment="Center"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding RequestUrl}" Height="32" Padding="8,0"
|
||||
Margin="0,0,0,10" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1"/>
|
||||
|
||||
<TextBlock Grid.Row="1" Text="Method:" VerticalAlignment="Center"/>
|
||||
<ComboBox Grid.Row="1" Grid.Column="1" SelectedItem="{Binding SelectedMethod}" Height="32"
|
||||
Margin="0,0,0,10" ItemsSource="{Binding HttpMethods}"/>
|
||||
|
||||
<TextBlock Grid.Row="2" Text="Body:" VerticalAlignment="Top" Margin="0,5,0,0"/>
|
||||
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding RequestBody}" Height="80"
|
||||
TextWrapping="Wrap" AcceptsReturn="True" FontFamily="Consolas"
|
||||
BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 功能按钮 -->
|
||||
<Border
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="20"
|
||||
Margin="0,0,0,15">
|
||||
<StackPanel>
|
||||
<TextBlock Text="快速功能" FontSize="16" FontWeight="Bold" Margin="0,0,0,15"/>
|
||||
|
||||
<WrapPanel>
|
||||
<Button Content="发送请求" Width="120" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding SendRequestCommand}"
|
||||
Style="{StaticResource ButtonPrimary}"/>
|
||||
<Button Content="GET 测试" Width="120" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding GetTestCommand}"
|
||||
Style="{StaticResource ButtonSuccess}"/>
|
||||
<Button Content="POST 测试" Width="120" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding PostTestCommand}"
|
||||
Style="{StaticResource ButtonInfo}"/>
|
||||
<Button Content="带重试" Width="120" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding RetryTestCommand}"
|
||||
Style="{StaticResource ButtonWarning}"/>
|
||||
<Button Content="超时测试" Width="120" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding TimeoutTestCommand}"/>
|
||||
<Button Content="清空结果" Width="120" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding ClearResultCommand}"
|
||||
Style="{StaticResource ButtonDefault}"/>
|
||||
</WrapPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 响应结果 -->
|
||||
<Border
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="15">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="300"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
|
||||
<TextBlock Text="响应结果" FontSize="16" FontWeight="Bold" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding StatusCode}" Margin="15,0,0,0" VerticalAlignment="Center"
|
||||
FontWeight="Bold" Foreground="{DynamicResource SuccessBrush}"/>
|
||||
</StackPanel>
|
||||
|
||||
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
|
||||
<TextBox Text="{Binding ResponseText, Mode=OneWay}"
|
||||
IsReadOnly="True"
|
||||
TextWrapping="Wrap"
|
||||
Background="{DynamicResource SecondaryRegionBrush}"
|
||||
BorderThickness="0"
|
||||
FontFamily="Consolas"
|
||||
FontSize="11"
|
||||
Padding="10"/>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Sharp.Demo.Views.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// HttpClientPage.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class HttpClientPage : UserControl
|
||||
{
|
||||
public HttpClientPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
<UserControl x:Class="Sharp.Demo.Views.Pages.HttpServerPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="600"
|
||||
d:DesignWidth="800">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Margin="20">
|
||||
<!-- 标题区域 -->
|
||||
<TextBlock
|
||||
FontSize="28"
|
||||
FontWeight="Bold"
|
||||
Text="HTTP 服务器管理" />
|
||||
<TextBlock
|
||||
Margin="0,10,0,30"
|
||||
FontSize="14"
|
||||
Foreground="{DynamicResource SecondaryTextBrush}"
|
||||
Text="Sharp.NetServer - 轻量级 HTTP 服务器框架" />
|
||||
|
||||
<!-- 服务器控制面板 -->
|
||||
<Border
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="20"
|
||||
Margin="0,0,0,15">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- 服务器信息 -->
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Text="服务器地址:"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,0,15,10" />
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Text="{Binding ServerUrl}"
|
||||
FontWeight="Bold"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,0,0,10" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Text="服务器状态:"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,0,15,10" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Text="{Binding StatusText}"
|
||||
Foreground="{Binding StatusColor}"
|
||||
FontWeight="Bold"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,0,0,10" />
|
||||
|
||||
<!-- 控制按钮 -->
|
||||
<StackPanel
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
Orientation="Horizontal"
|
||||
Margin="0,10,0,0">
|
||||
<Button
|
||||
Content="启动服务器"
|
||||
Width="120"
|
||||
Height="35"
|
||||
Margin="0,0,10,0"
|
||||
Command="{Binding StartCommand}"
|
||||
IsEnabled="{Binding CanStart}"
|
||||
Style="{StaticResource ButtonSuccess}" />
|
||||
<Button
|
||||
Content="停止服务器"
|
||||
Width="120"
|
||||
Height="35"
|
||||
Margin="0,0,10,0"
|
||||
Command="{Binding StopCommand}"
|
||||
IsEnabled="{Binding IsRunning}"
|
||||
Style="{StaticResource ButtonDanger}" />
|
||||
<Button
|
||||
Content="重启服务器"
|
||||
Width="120"
|
||||
Height="35"
|
||||
Margin="0,0,10,0"
|
||||
Command="{Binding RestartCommand}"
|
||||
IsEnabled="{Binding IsRunning}"
|
||||
Style="{StaticResource ButtonWarning}" />
|
||||
<Button
|
||||
Content="清空日志"
|
||||
Width="100"
|
||||
Height="35"
|
||||
Command="{Binding ClearLogCommand}"
|
||||
Style="{StaticResource ButtonDefault}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- 日志显示区域 -->
|
||||
<Border
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="15"
|
||||
Margin="0,0,0,15">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="350" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Text="日志信息"
|
||||
FontSize="16"
|
||||
FontWeight="Bold"
|
||||
Margin="0,0,0,10" />
|
||||
|
||||
<ScrollViewer
|
||||
Grid.Row="1"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Auto">
|
||||
<TextBox
|
||||
Text="{Binding LogText, Mode=OneWay}"
|
||||
IsReadOnly="True"
|
||||
TextWrapping="Wrap"
|
||||
Background="{DynamicResource SecondaryRegionBrush}"
|
||||
BorderThickness="0"
|
||||
FontFamily="Consolas"
|
||||
FontSize="11"
|
||||
Padding="10" />
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- API 端点提示 -->
|
||||
<Border
|
||||
Background="{DynamicResource SecondaryRegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="15">
|
||||
<StackPanel>
|
||||
<TextBlock
|
||||
Text="可用的 API 端点"
|
||||
FontSize="16"
|
||||
FontWeight="Bold"
|
||||
Margin="0,0,0,10" />
|
||||
<TextBlock FontSize="12" TextWrapping="Wrap">
|
||||
<Run Text="• GET /api/health - 健康检查" />
|
||||
<LineBreak />
|
||||
<Run Text="• GET /api/health/detailed - 详细健康检查" />
|
||||
<LineBreak />
|
||||
<Run Text="• GET /api/system/info - 系统信息" />
|
||||
<LineBreak />
|
||||
<Run Text="• GET /api/system/time - 当前时间" />
|
||||
<LineBreak />
|
||||
<Run Text="• GET /api/data - 获取所有数据" />
|
||||
<LineBreak />
|
||||
<Run Text="• POST /api/data - 创建数据 (Body: {"name":"test","value":"data"})" />
|
||||
<LineBreak />
|
||||
<Run Text="• GET /api/data/{id} - 获取指定数据" />
|
||||
<LineBreak />
|
||||
<Run Text="• PUT /api/data/{id} - 更新数据" />
|
||||
<LineBreak />
|
||||
<Run Text="• DELETE /api/data/{id} - 删除数据" />
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Sharp.Demo.Views.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// HttpServerPage.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class HttpServerPage : UserControl
|
||||
{
|
||||
public HttpServerPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
<UserControl x:Class="Sharp.Demo.Views.Pages.LogDemoPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="600"
|
||||
d:DesignWidth="800">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Margin="20">
|
||||
<!-- 标题区域 -->
|
||||
<TextBlock
|
||||
FontSize="28"
|
||||
FontWeight="Bold"
|
||||
Text="日志管理" />
|
||||
<TextBlock
|
||||
Margin="0,10,0,30"
|
||||
FontSize="14"
|
||||
Foreground="{DynamicResource SecondaryTextBrush}"
|
||||
Text="Sharp.Log - 基于 Serilog 的结构化日志系统" />
|
||||
|
||||
<!-- 日志级别测试 -->
|
||||
<Border
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="20"
|
||||
Margin="0,0,0,15">
|
||||
<StackPanel>
|
||||
<TextBlock Text="日志级别测试" FontSize="16" FontWeight="Bold" Margin="0,0,0,15"/>
|
||||
|
||||
<WrapPanel>
|
||||
<Button Content="Trace" Width="110" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding LogTraceCommand}"
|
||||
Style="{StaticResource ButtonDefault}"/>
|
||||
<Button Content="Debug" Width="110" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding LogDebugCommand}"
|
||||
Style="{StaticResource ButtonInfo}"/>
|
||||
<Button Content="Information" Width="110" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding LogInformationCommand}"
|
||||
Style="{StaticResource ButtonSuccess}"/>
|
||||
<Button Content="Warning" Width="110" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding LogWarningCommand}"
|
||||
Style="{StaticResource ButtonWarning}"/>
|
||||
<Button Content="Error" Width="110" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding LogErrorCommand}"
|
||||
Style="{StaticResource ButtonDanger}"/>
|
||||
<Button Content="Critical" Width="110" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding LogCriticalCommand}"
|
||||
Style="{StaticResource ButtonDanger}"/>
|
||||
</WrapPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 结构化日志 -->
|
||||
<Border
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="20"
|
||||
Margin="0,0,0,15">
|
||||
<StackPanel>
|
||||
<TextBlock Text="结构化日志" FontSize="16" FontWeight="Bold" Margin="0,0,0,15"/>
|
||||
|
||||
<WrapPanel>
|
||||
<Button Content="带参数日志" Width="140" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding LogWithParamsCommand}"
|
||||
Style="{StaticResource ButtonPrimary}"/>
|
||||
<Button Content="带异常日志" Width="140" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding LogWithExceptionCommand}"
|
||||
Style="{StaticResource ButtonDanger}"/>
|
||||
<Button Content="性能日志" Width="140" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding LogPerformanceCommand}"
|
||||
Style="{StaticResource ButtonInfo}"/>
|
||||
<Button Content="业务日志" Width="140" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding LogBusinessCommand}"
|
||||
Style="{StaticResource ButtonSuccess}"/>
|
||||
</WrapPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 日志配置 -->
|
||||
<Border
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="20"
|
||||
Margin="0,0,0,15">
|
||||
<StackPanel>
|
||||
<TextBlock Text="日志配置" FontSize="16" FontWeight="Bold" Margin="0,0,0,15"/>
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Text="最小日志级别:" VerticalAlignment="Center" Margin="0,0,10,0"/>
|
||||
<ComboBox Grid.Column="1" SelectedItem="{Binding SelectedLogLevel}" Height="32"
|
||||
ItemsSource="{Binding LogLevels}" Margin="0,0,0,10"/>
|
||||
|
||||
<TextBlock Grid.Row="1" Text="日志输出位置:" VerticalAlignment="Center" Margin="0,0,10,0"/>
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" Text="logs/app.log | Console | Debug"
|
||||
VerticalAlignment="Center" Foreground="{DynamicResource SecondaryTextBrush}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 功能按钮 -->
|
||||
<Border
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="20"
|
||||
Margin="0,0,0,15">
|
||||
<StackPanel>
|
||||
<TextBlock Text="日志操作" FontSize="16" FontWeight="Bold" Margin="0,0,0,15"/>
|
||||
|
||||
<WrapPanel>
|
||||
<Button Content="打开日志文件夹" Width="140" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding OpenLogFolderCommand}"
|
||||
Style="{StaticResource ButtonPrimary}"/>
|
||||
<Button Content="查看最新日志" Width="140" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding ViewLatestLogCommand}"
|
||||
Style="{StaticResource ButtonInfo}"/>
|
||||
<Button Content="清空日志" Width="140" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding ClearLogsCommand}"
|
||||
Style="{StaticResource ButtonDanger}"/>
|
||||
</WrapPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 日志预览 -->
|
||||
<Border
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="15">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="250"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Text="日志预览" FontSize="16" FontWeight="Bold" Margin="0,0,0,10"/>
|
||||
|
||||
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
|
||||
<TextBox Text="{Binding LogPreview, Mode=OneWay}"
|
||||
IsReadOnly="True"
|
||||
TextWrapping="Wrap"
|
||||
Background="{DynamicResource SecondaryRegionBrush}"
|
||||
BorderThickness="0"
|
||||
FontFamily="Consolas"
|
||||
FontSize="11"
|
||||
Padding="10"/>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Sharp.Demo.Views.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// LogDemoPage.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class LogDemoPage : UserControl
|
||||
{
|
||||
public LogDemoPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
<UserControl x:Class="Sharp.Demo.Views.Pages.PlcDebugPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="600"
|
||||
d:DesignWidth="800">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Margin="20">
|
||||
<!-- 标题区域 -->
|
||||
<TextBlock
|
||||
FontSize="28"
|
||||
FontWeight="Bold"
|
||||
Text="PLC 调试工具" />
|
||||
<TextBlock
|
||||
Margin="0,10,0,30"
|
||||
FontSize="14"
|
||||
Foreground="{DynamicResource SecondaryTextBrush}"
|
||||
Text="Sharp.Plc.Siemens - 西门子 PLC 通信驱动 (S7-1200/1500/300/400)" />
|
||||
|
||||
<!-- 连接控制 -->
|
||||
<Border
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="20"
|
||||
Margin="0,0,0,15">
|
||||
<StackPanel>
|
||||
<TextBlock Text="PLC 连接" FontSize="16" FontWeight="Bold" Margin="0,0,0,10"/>
|
||||
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
|
||||
<TextBlock Text="IP 地址:" VerticalAlignment="Center" Margin="0,0,10,0" Width="80"/>
|
||||
<TextBox Text="{Binding IpAddress}" Width="200" Height="32" Padding="8,0"
|
||||
BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
|
||||
<TextBlock Text="状态:" VerticalAlignment="Center" Margin="0,0,10,0" Width="80"/>
|
||||
<TextBlock Text="{Binding Status}" FontWeight="Bold" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Button Content="连接" Width="100" Height="35" Margin="0,0,10,0"
|
||||
Command="{Binding ConnectCommand}"
|
||||
Style="{StaticResource ButtonSuccess}"/>
|
||||
<Button Content="断开" Width="100" Height="35"
|
||||
Command="{Binding DisconnectCommand}"
|
||||
Style="{StaticResource ButtonDanger}"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 单次读写 -->
|
||||
<Border
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="20"
|
||||
Margin="0,0,0,15">
|
||||
<StackPanel>
|
||||
<TextBlock Text="单次读写测试 (数据类型: Short)" FontSize="16" FontWeight="Bold" Margin="0,0,0,15"/>
|
||||
|
||||
<!-- 读取 -->
|
||||
<Grid Margin="0,0,0,15">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="200"/>
|
||||
<ColumnDefinition Width="100"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Text="读取地址:" VerticalAlignment="Center"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding ReadAddress}" Height="32" Padding="8,0"
|
||||
Margin="0,0,10,0" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1"/>
|
||||
<Button Grid.Column="2" Content="读取" Height="32" Margin="0,0,10,0"
|
||||
Command="{Binding ReadCommand}"
|
||||
Style="{StaticResource ButtonPrimary}"/>
|
||||
<TextBlock Grid.Column="3" Text="{Binding ReadValue}" VerticalAlignment="Center"
|
||||
FontWeight="Bold" FontSize="14"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 写入 -->
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="200"/>
|
||||
<ColumnDefinition Width="100"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Text="写入地址:" VerticalAlignment="Center"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding WriteAddress}" Height="32" Padding="8,0"
|
||||
Margin="0,0,10,0" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1"/>
|
||||
<TextBox Grid.Column="2" Text="{Binding WriteValue}" Height="32" Padding="8,0"
|
||||
Margin="0,0,10,0" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1"/>
|
||||
<Button Grid.Column="3" Content="写入" Width="100" Height="32" HorizontalAlignment="Left"
|
||||
Command="{Binding WriteCommand}"
|
||||
Style="{StaticResource ButtonWarning}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 日志区域 -->
|
||||
<Border
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="15">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="300"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
|
||||
<TextBlock Text="操作日志" FontSize="16" FontWeight="Bold" VerticalAlignment="Center"/>
|
||||
<Button Content="清空日志" Width="100" Height="30" Margin="15,0,0,0"
|
||||
Command="{Binding ClearLogCommand}"
|
||||
Style="{StaticResource ButtonDefault}"/>
|
||||
</StackPanel>
|
||||
|
||||
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
|
||||
<TextBox Text="{Binding LogText, Mode=OneWay}"
|
||||
IsReadOnly="True"
|
||||
TextWrapping="Wrap"
|
||||
Background="{DynamicResource SecondaryRegionBrush}"
|
||||
BorderThickness="0"
|
||||
FontFamily="Consolas"
|
||||
FontSize="11"
|
||||
Padding="10"/>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Sharp.Demo.Views.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// PlcDebugPage.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class PlcDebugPage : UserControl
|
||||
{
|
||||
public PlcDebugPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<UserControl x:Class="Sharp.Demo.Views.Pages.ProcessTestPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="600"
|
||||
d:DesignWidth="800">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Margin="20">
|
||||
<!-- 标题区域 -->
|
||||
<TextBlock FontSize="28" FontWeight="Bold" Text="进程测试工具" />
|
||||
<TextBlock Margin="0,10,0,30" FontSize="14"
|
||||
Foreground="{DynamicResource SecondaryTextBrush}"
|
||||
Text="Sharp.Process - 进程执行和输出捕获" />
|
||||
|
||||
<!-- 状态栏 -->
|
||||
<Border Background="{DynamicResource PrimaryBrush}" Padding="15" CornerRadius="8" Margin="0,0,0,15">
|
||||
<TextBlock Text="{Binding Status}" Foreground="White" FontWeight="Bold" FontSize="16"/>
|
||||
</Border>
|
||||
|
||||
<!-- 命令配置 -->
|
||||
<Border Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1" CornerRadius="8" Padding="20" Margin="0,0,0,15">
|
||||
<StackPanel>
|
||||
<TextBlock Text="命令配置" FontSize="16" FontWeight="Bold" Margin="0,0,0,15"/>
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Text="命令:" VerticalAlignment="Center" Margin="0,0,10,10"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding Command}" Height="32" Padding="8,0" Margin="0,0,0,10"/>
|
||||
|
||||
<TextBlock Grid.Row="1" Text="参数:" VerticalAlignment="Center" Margin="0,0,10,10"/>
|
||||
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Arguments}" Height="32" Padding="8,0" Margin="0,0,0,10"/>
|
||||
|
||||
<TextBlock Grid.Row="2" Text="超时(秒):" VerticalAlignment="Center" Margin="0,0,10,0"/>
|
||||
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding TimeoutSeconds}" Height="32" Padding="8,0"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 快速测试按钮 -->
|
||||
<Border Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1" CornerRadius="8" Padding="20" Margin="0,0,0,15">
|
||||
<StackPanel>
|
||||
<TextBlock Text="快速测试" FontSize="16" FontWeight="Bold" Margin="0,0,0,15"/>
|
||||
<WrapPanel>
|
||||
<Button Content="执行命令" Width="120" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding RunCommand}" Style="{StaticResource ButtonPrimary}"/>
|
||||
<Button Content="测试 dotnet" Width="120" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding TestDotnetCommand}" Style="{StaticResource ButtonInfo}"/>
|
||||
<Button Content="测试 Git" Width="120" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding TestGitCommand}" Style="{StaticResource ButtonInfo}"/>
|
||||
<Button Content="清空输出" Width="120" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding ClearOutputCommand}" Style="{StaticResource ButtonDefault}"/>
|
||||
</WrapPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 执行输出 -->
|
||||
<Border Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1" CornerRadius="8" Padding="15">
|
||||
<StackPanel>
|
||||
<TextBlock Text="执行输出" FontSize="16" FontWeight="Bold" Margin="0,0,0,10"/>
|
||||
<TextBox Text="{Binding Output, Mode=OneWay}" Height="350"
|
||||
IsReadOnly="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto"
|
||||
FontFamily="Consolas" FontSize="11" Background="{DynamicResource SecondaryRegionBrush}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,12 @@
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Sharp.Demo.Views.Pages
|
||||
{
|
||||
public partial class ProcessTestPage : UserControl
|
||||
{
|
||||
public ProcessTestPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<UserControl x:Class="Sharp.Demo.Views.Pages.SerialPortPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="600"
|
||||
d:DesignWidth="800">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Margin="20">
|
||||
<!-- 标题区域 -->
|
||||
<TextBlock FontSize="28" FontWeight="Bold" Text="串口调试工具" />
|
||||
<TextBlock Margin="0,10,0,30" FontSize="14"
|
||||
Foreground="{DynamicResource SecondaryTextBrush}"
|
||||
Text="Sharp.Ports - 串口通信抽象 (RS232/RS485)" />
|
||||
|
||||
<!-- 连接控制 -->
|
||||
<Border Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1" CornerRadius="8" Padding="20" Margin="0,0,0,15">
|
||||
<StackPanel>
|
||||
<TextBlock Text="连接配置" FontSize="16" FontWeight="Bold" Margin="0,0,0,15"/>
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="200"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="200"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Text="端口:" VerticalAlignment="Center" Margin="0,0,10,10"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding SelectedPort}" Height="32" Padding="8,0" Margin="0,0,15,10"/>
|
||||
|
||||
<TextBlock Grid.Column="2" Text="波特率:" VerticalAlignment="Center" Margin="0,0,10,10"/>
|
||||
<TextBox Grid.Column="3" Text="{Binding BaudRate}" Height="32" Padding="8,0" Margin="0,0,0,10"/>
|
||||
|
||||
<TextBlock Grid.Row="1" Text="状态:" VerticalAlignment="Center" Margin="0,0,10,0"/>
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Status}" FontWeight="Bold" VerticalAlignment="Center"/>
|
||||
</Grid>
|
||||
|
||||
<StackPanel Orientation="Horizontal" Margin="0,15,0,0">
|
||||
<Button Content="连接" Width="100" Height="35" Margin="0,0,10,0"
|
||||
Command="{Binding ConnectCommand}" Style="{StaticResource ButtonSuccess}"/>
|
||||
<Button Content="断开" Width="100" Height="35"
|
||||
Command="{Binding DisconnectCommand}" Style="{StaticResource ButtonDanger}"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 数据发送 -->
|
||||
<Border Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1" CornerRadius="8" Padding="20" Margin="0,0,0,15">
|
||||
<StackPanel>
|
||||
<TextBlock Text="数据发送" FontSize="16" FontWeight="Bold" Margin="0,0,0,10"/>
|
||||
<TextBox Text="{Binding SendData}" Height="80" Padding="8" TextWrapping="Wrap"
|
||||
AcceptsReturn="True" VerticalScrollBarVisibility="Auto" Margin="0,0,0,10"/>
|
||||
<Button Content="发送数据" Width="120" Height="35" HorizontalAlignment="Left"
|
||||
Command="{Binding SendCommand}" Style="{StaticResource ButtonPrimary}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 接收数据 -->
|
||||
<Border Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1" CornerRadius="8" Padding="20" Margin="0,0,0,15">
|
||||
<StackPanel>
|
||||
<TextBlock Text="接收数据" FontSize="16" FontWeight="Bold" Margin="0,0,0,10"/>
|
||||
<TextBox Text="{Binding ReceiveData, Mode=OneWay}" Height="150"
|
||||
IsReadOnly="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto"
|
||||
FontFamily="Consolas" Background="{DynamicResource SecondaryRegionBrush}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 日志 -->
|
||||
<Border Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1" CornerRadius="8" Padding="15">
|
||||
<StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
|
||||
<TextBlock Text="操作日志" FontSize="16" FontWeight="Bold" VerticalAlignment="Center"/>
|
||||
<Button Content="清空日志" Width="100" Height="30" Margin="15,0,0,0"
|
||||
Command="{Binding ClearLogCommand}" Style="{StaticResource ButtonDefault}"/>
|
||||
</StackPanel>
|
||||
<TextBox Text="{Binding LogText, Mode=OneWay}" Height="150"
|
||||
IsReadOnly="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto"
|
||||
FontFamily="Consolas" FontSize="11" Background="{DynamicResource SecondaryRegionBrush}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,12 @@
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Sharp.Demo.Views.Pages
|
||||
{
|
||||
public partial class SerialPortPage : UserControl
|
||||
{
|
||||
public SerialPortPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
<UserControl x:Class="Sharp.Demo.Views.Pages.WpfServicesPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="600"
|
||||
d:DesignWidth="800">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Margin="20">
|
||||
<!-- 标题区域 -->
|
||||
<TextBlock
|
||||
FontSize="28"
|
||||
FontWeight="Bold"
|
||||
Text="WPF 服务" />
|
||||
<TextBlock
|
||||
Margin="0,10,0,30"
|
||||
FontSize="14"
|
||||
Foreground="{DynamicResource SecondaryTextBrush}"
|
||||
Text="Sharp.Wpf - MVVM 基础设施、对话框服务、文件对话框、线程分发器" />
|
||||
|
||||
<!-- 对话框服务 -->
|
||||
<Border
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="20"
|
||||
Margin="0,0,0,15">
|
||||
<StackPanel>
|
||||
<TextBlock Text="对话框服务 (IDialogService)" FontSize="16" FontWeight="Bold" Margin="0,0,0,15"/>
|
||||
|
||||
<WrapPanel>
|
||||
<Button Content="消息框" Width="120" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding ShowMessageCommand}"
|
||||
Style="{StaticResource ButtonPrimary}"/>
|
||||
<Button Content="确认对话框" Width="120" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding ShowConfirmCommand}"
|
||||
Style="{StaticResource ButtonInfo}"/>
|
||||
<Button Content="警告对话框" Width="120" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding ShowWarningCommand}"
|
||||
Style="{StaticResource ButtonWarning}"/>
|
||||
<Button Content="错误对话框" Width="120" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding ShowErrorCommand}"
|
||||
Style="{StaticResource ButtonDanger}"/>
|
||||
<Button Content="输入框" Width="120" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding ShowInputCommand}"
|
||||
Style="{StaticResource ButtonSuccess}"/>
|
||||
<Button Content="自定义对话框" Width="120" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding ShowCustomDialogCommand}"
|
||||
Style="{StaticResource ButtonDefault}"/>
|
||||
</WrapPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 文件对话框服务 -->
|
||||
<Border
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="20"
|
||||
Margin="0,0,0,15">
|
||||
<StackPanel>
|
||||
<TextBlock Text="文件对话框 (IFileDialogService)" FontSize="16" FontWeight="Bold" Margin="0,0,0,15"/>
|
||||
|
||||
<WrapPanel>
|
||||
<Button Content="打开文件" Width="130" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding OpenFileCommand}"
|
||||
Style="{StaticResource ButtonPrimary}"/>
|
||||
<Button Content="保存文件" Width="130" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding SaveFileCommand}"
|
||||
Style="{StaticResource ButtonInfo}"/>
|
||||
<Button Content="选择文件夹" Width="130" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding SelectFolderCommand}"
|
||||
Style="{StaticResource ButtonSuccess}"/>
|
||||
<Button Content="多选文件" Width="130" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding OpenMultipleFilesCommand}"
|
||||
Style="{StaticResource ButtonWarning}"/>
|
||||
</WrapPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 线程分发器 -->
|
||||
<Border
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="20"
|
||||
Margin="0,0,0,15">
|
||||
<StackPanel>
|
||||
<TextBlock Text="线程分发器 (IDispatcher)" FontSize="16" FontWeight="Bold" Margin="0,0,0,15"/>
|
||||
|
||||
<WrapPanel>
|
||||
<Button Content="UI 线程调用" Width="140" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding InvokeOnUIThreadCommand}"
|
||||
Style="{StaticResource ButtonPrimary}"/>
|
||||
<Button Content="异步 UI 调用" Width="140" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding InvokeAsyncCommand}"
|
||||
Style="{StaticResource ButtonInfo}"/>
|
||||
<Button Content="后台任务" Width="140" Height="35" Margin="0,0,10,10"
|
||||
Command="{Binding BackgroundTaskCommand}"
|
||||
Style="{StaticResource ButtonSuccess}"/>
|
||||
</WrapPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- MVVM 基础 -->
|
||||
<Border
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="20"
|
||||
Margin="0,0,0,15">
|
||||
<StackPanel>
|
||||
<TextBlock Text="MVVM 基础设施" FontSize="16" FontWeight="Bold" Margin="0,0,0,15"/>
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- 左侧 -->
|
||||
<StackPanel Margin="0,0,10,0">
|
||||
<TextBlock Text="ViewModelBase 示例:" FontWeight="SemiBold" Margin="0,0,0,10"/>
|
||||
|
||||
<TextBlock Text="属性变更通知:" Margin="0,0,0,5"/>
|
||||
<TextBox Text="{Binding SampleProperty}" Height="32" Padding="8,0" Margin="0,0,0,10"
|
||||
BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1"/>
|
||||
|
||||
<Button Content="触发属性变更" Height="35" Margin="0,0,0,10"
|
||||
Command="{Binding TriggerPropertyChangeCommand}"
|
||||
Style="{StaticResource ButtonPrimary}"/>
|
||||
|
||||
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
|
||||
<TextBlock Text="IsBusy 状态: " VerticalAlignment="Center"/>
|
||||
<CheckBox IsChecked="{Binding IsBusy}" IsEnabled="False" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 右侧 -->
|
||||
<StackPanel Grid.Column="1" Margin="10,0,0,0">
|
||||
<TextBlock Text="命令 (RelayCommand):" FontWeight="SemiBold" Margin="0,0,0,10"/>
|
||||
|
||||
<Button Content="同步命令" Height="35" Margin="0,0,0,10"
|
||||
Command="{Binding SyncCommandDemo}"
|
||||
Style="{StaticResource ButtonInfo}"/>
|
||||
|
||||
<Button Content="异步命令" Height="35" Margin="0,0,0,10"
|
||||
Command="{Binding AsyncCommandDemo}"
|
||||
Style="{StaticResource ButtonSuccess}"/>
|
||||
|
||||
<Button Content="带参数命令" Height="35" Margin="0,0,0,10"
|
||||
Command="{Binding CommandWithParamDemo}"
|
||||
CommandParameter="测试参数"
|
||||
Style="{StaticResource ButtonWarning}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 日志输出 -->
|
||||
<Border
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="15">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="200"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
|
||||
<TextBlock Text="操作日志" FontSize="16" FontWeight="Bold" VerticalAlignment="Center"/>
|
||||
<Button Content="清空" Width="80" Height="30" Margin="15,0,0,0"
|
||||
Command="{Binding ClearLogCommand}"
|
||||
Style="{StaticResource ButtonDefault}"/>
|
||||
</StackPanel>
|
||||
|
||||
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
|
||||
<TextBox Text="{Binding LogText, Mode=OneWay}"
|
||||
IsReadOnly="True"
|
||||
TextWrapping="Wrap"
|
||||
Background="{DynamicResource SecondaryRegionBrush}"
|
||||
BorderThickness="0"
|
||||
FontFamily="Consolas"
|
||||
FontSize="11"
|
||||
Padding="10"/>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Sharp.Demo.Views.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// WpfServicesPage.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class WpfServicesPage : UserControl
|
||||
{
|
||||
public WpfServicesPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
<Window x:Class="Sharp.Demo.Views.Windows.HttpClientWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
Title="HTTP 客户端"
|
||||
Height="750"
|
||||
Width="1100"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 工具栏 -->
|
||||
<Border Grid.Row="0" Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}" BorderThickness="0,0,0,1" Padding="15,10">
|
||||
<Grid>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Button Content="📤 发送请求" Width="120" Height="35" Margin="0,0,10,0"
|
||||
Command="{Binding SendRequestCommand}"/>
|
||||
<Button Content="🗑 清空" Width="100" Height="35" Margin="0,0,10,0"
|
||||
Command="{Binding ClearCommand}"/>
|
||||
<Separator Margin="10,0"/>
|
||||
<Button Content="📋 复制响应" Width="110" Height="35" Margin="0,0,10,0"
|
||||
Command="{Binding CopyResponseCommand}"/>
|
||||
<Button Content="💾 保存响应" Width="110" Height="35"
|
||||
Command="{Binding SaveResponseCommand}"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<TextBlock Text="状态码: " VerticalAlignment="Center" Margin="0,0,5,0"/>
|
||||
<TextBlock Text="{Binding StatusCode}" FontWeight="Bold" VerticalAlignment="Center"
|
||||
Foreground="{Binding StatusColor}" Margin="0,0,15,0"/>
|
||||
<TextBlock Text="耗时: " VerticalAlignment="Center" Margin="0,0,5,0"/>
|
||||
<TextBlock Text="{Binding ElapsedTime}" FontWeight="Bold" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<Grid Grid.Row="1" Margin="15">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- 左侧:请求配置 -->
|
||||
<Border Grid.Column="0" Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1" CornerRadius="8">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Margin="15">
|
||||
<TextBlock Text="请求配置" FontSize="16" FontWeight="Bold" Margin="0,0,0,15"/>
|
||||
|
||||
<!-- HTTP 方法 -->
|
||||
<TextBlock Text="HTTP 方法" Margin="0,0,0,5"/>
|
||||
<ComboBox ItemsSource="{Binding HttpMethods}"
|
||||
SelectedItem="{Binding SelectedMethod}"
|
||||
Height="32" Margin="0,0,0,15"/>
|
||||
|
||||
<!-- URL -->
|
||||
<TextBlock Text="请求 URL" Margin="0,0,0,5"/>
|
||||
<TextBox Text="{Binding RequestUrl}" Height="32" Margin="0,0,0,15"/>
|
||||
|
||||
<!-- 请求头 -->
|
||||
<TextBlock Text="请求头 (Headers)" Margin="0,0,0,5"/>
|
||||
<TextBox Text="{Binding RequestHeaders}"
|
||||
Height="100"
|
||||
TextWrapping="Wrap"
|
||||
AcceptsReturn="True"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
Margin="0,0,0,15"/>
|
||||
|
||||
<!-- 请求体 -->
|
||||
<TextBlock Text="请求体 (Body)" Margin="0,0,0,5"/>
|
||||
<TextBox Text="{Binding RequestBody}"
|
||||
Height="200"
|
||||
TextWrapping="Wrap"
|
||||
AcceptsReturn="True"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
FontFamily="Consolas"
|
||||
Margin="0,0,0,15"/>
|
||||
|
||||
<!-- 快速测试 -->
|
||||
<Border Background="{DynamicResource SecondaryRegionBrush}"
|
||||
CornerRadius="8" Padding="15" Margin="0,10,0,0">
|
||||
<StackPanel>
|
||||
<TextBlock Text="快速测试" FontSize="14" FontWeight="Bold" Margin="0,0,0,10"/>
|
||||
|
||||
<Button Content="🌐 测试本地服务器" Height="32" Margin="0,0,0,8"
|
||||
Command="{Binding TestLocalServerCommand}"/>
|
||||
|
||||
<Button Content="🔄 测试重试策略" Height="32" Margin="0,0,0,8"
|
||||
Command="{Binding TestRetryCommand}"/>
|
||||
|
||||
<Button Content="⏱ 测试超时" Height="32" Margin="0,0,0,8"
|
||||
Command="{Binding TestTimeoutCommand}"/>
|
||||
|
||||
<Button Content="📊 测试 JSON API" Height="32"
|
||||
Command="{Binding TestJsonApiCommand}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
|
||||
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Center" Background="Transparent"/>
|
||||
|
||||
<!-- 右侧:响应显示 -->
|
||||
<Border Grid.Column="2" Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1" CornerRadius="8">
|
||||
<Grid Margin="15">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Text="响应内容" FontSize="16" FontWeight="Bold" Margin="0,0,0,10"/>
|
||||
|
||||
<!-- 响应头信息 -->
|
||||
<Border Grid.Row="1" Background="{DynamicResource SecondaryRegionBrush}"
|
||||
CornerRadius="4" Padding="10" Margin="0,0,0,10">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="11">
|
||||
<Run Text="状态码: " FontWeight="SemiBold"/>
|
||||
<Run Text="{Binding StatusCode}" Foreground="{Binding StatusColor}"/>
|
||||
</TextBlock>
|
||||
<TextBlock FontSize="11" Margin="0,3,0,0">
|
||||
<Run Text="内容类型: " FontWeight="SemiBold"/>
|
||||
<Run Text="{Binding ContentType}"/>
|
||||
</TextBlock>
|
||||
<TextBlock FontSize="11" Margin="0,3,0,0">
|
||||
<Run Text="响应时间: " FontWeight="SemiBold"/>
|
||||
<Run Text="{Binding ElapsedTime}"/>
|
||||
</TextBlock>
|
||||
<TextBlock FontSize="11" Margin="0,3,0,0">
|
||||
<Run Text="响应大小: " FontWeight="SemiBold"/>
|
||||
<Run Text="{Binding ResponseSize}"/>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 响应体 -->
|
||||
<TabControl Grid.Row="2">
|
||||
<TabItem Header="响应体">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<TextBox Text="{Binding ResponseText, Mode=OneWay}"
|
||||
IsReadOnly="True"
|
||||
TextWrapping="Wrap"
|
||||
Background="{DynamicResource SecondaryRegionBrush}"
|
||||
BorderThickness="0"
|
||||
FontFamily="Consolas"
|
||||
FontSize="11"
|
||||
Padding="10"/>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="响应头">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<TextBox Text="{Binding ResponseHeaders, Mode=OneWay}"
|
||||
IsReadOnly="True"
|
||||
TextWrapping="Wrap"
|
||||
Background="{DynamicResource SecondaryRegionBrush}"
|
||||
BorderThickness="0"
|
||||
FontFamily="Consolas"
|
||||
FontSize="11"
|
||||
Padding="10"/>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="日志">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<TextBox Text="{Binding LogText, Mode=OneWay}"
|
||||
IsReadOnly="True"
|
||||
TextWrapping="Wrap"
|
||||
Background="{DynamicResource SecondaryRegionBrush}"
|
||||
BorderThickness="0"
|
||||
FontFamily="Consolas"
|
||||
FontSize="11"
|
||||
Padding="10"/>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<!-- 状态栏 -->
|
||||
<StatusBar Grid.Row="2" Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}" BorderThickness="0,1,0,0">
|
||||
<StatusBarItem>
|
||||
<TextBlock>
|
||||
<Run Text="请求总数: "/>
|
||||
<Run Text="{Binding TotalRequests, Mode=OneWay}"/>
|
||||
</TextBlock>
|
||||
</StatusBarItem>
|
||||
<Separator/>
|
||||
<StatusBarItem>
|
||||
<TextBlock>
|
||||
<Run Text="成功: "/>
|
||||
<Run Text="{Binding SuccessCount, Mode=OneWay}" Foreground="Green"/>
|
||||
</TextBlock>
|
||||
</StatusBarItem>
|
||||
<Separator/>
|
||||
<StatusBarItem>
|
||||
<TextBlock>
|
||||
<Run Text="失败: "/>
|
||||
<Run Text="{Binding FailureCount, Mode=OneWay}" Foreground="Red"/>
|
||||
</TextBlock>
|
||||
</StatusBarItem>
|
||||
</StatusBar>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Windows;
|
||||
using Sharp.Demo.ViewModels.Windows;
|
||||
|
||||
namespace Sharp.Demo.Views.Windows
|
||||
{
|
||||
/// <summary>
|
||||
/// HttpClientWindow.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class HttpClientWindow : Window
|
||||
{
|
||||
public HttpClientWindow(HttpClientViewModel viewModel)
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = viewModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
<Window x:Class="Sharp.Demo.Views.Windows.HttpServerWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
Title="HTTP 服务器"
|
||||
Height="700"
|
||||
Width="1000"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 工具栏 -->
|
||||
<Border Grid.Row="0" Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}" BorderThickness="0,0,0,1" Padding="15,10">
|
||||
<Grid>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Button Content="▶️ 启动服务器" Width="120" Height="35" Margin="0,0,10,0"
|
||||
Command="{Binding StartServerCommand}"/>
|
||||
<Button Content="⏹ 停止服务器" Width="120" Height="35" Margin="0,0,10,0"
|
||||
Command="{Binding StopServerCommand}"/>
|
||||
<Button Content="🔄 重启服务器" Width="120" Height="35" Margin="0,0,10,0"
|
||||
Command="{Binding RestartServerCommand}"/>
|
||||
<Separator Margin="10,0"/>
|
||||
<Button Content="📊 用户管理" Width="110" Height="35" Margin="0,0,10,0"
|
||||
Command="{Binding OpenUserManagementCommand}"/>
|
||||
<Button Content="🗑 清空日志" Width="100" Height="35"
|
||||
Command="{Binding ClearLogCommand}"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<TextBlock Text="服务器地址: " VerticalAlignment="Center" Margin="0,0,5,0"/>
|
||||
<TextBlock Text="{Binding ServerUrl}" FontWeight="Bold" VerticalAlignment="Center" Margin="0,0,15,0"/>
|
||||
<TextBlock Text="状态: " VerticalAlignment="Center" Margin="0,0,5,0"/>
|
||||
<TextBlock Text="{Binding StatusText}" FontWeight="Bold" VerticalAlignment="Center"
|
||||
Foreground="{Binding StatusColor}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<Grid Grid.Row="1" Margin="15">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="2*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- 左侧:日志显示 -->
|
||||
<Border Grid.Column="0" Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1" CornerRadius="8">
|
||||
<Grid Margin="15">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Text="服务器日志" FontSize="16" FontWeight="Bold" Margin="0,0,0,10"/>
|
||||
|
||||
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
|
||||
<TextBox Text="{Binding LogText, Mode=OneWay}"
|
||||
IsReadOnly="True"
|
||||
TextWrapping="Wrap"
|
||||
Background="{DynamicResource SecondaryRegionBrush}"
|
||||
BorderThickness="0"
|
||||
FontFamily="Consolas"
|
||||
FontSize="11"
|
||||
Padding="10"/>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Center" Background="Transparent"/>
|
||||
|
||||
<!-- 右侧:API 端点和功能 -->
|
||||
<ScrollViewer Grid.Column="2" VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Margin="5,0,0,0">
|
||||
<!-- 服务器配置 -->
|
||||
<Border Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1" CornerRadius="8" Padding="15" Margin="0,0,0,15">
|
||||
<StackPanel>
|
||||
<TextBlock Text="服务器配置" FontSize="14" FontWeight="Bold" Margin="0,0,0,10"/>
|
||||
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,8">
|
||||
<TextBlock Text="端口:" Width="60" VerticalAlignment="Center"/>
|
||||
<TextBox Text="{Binding Port}" Width="80" Height="28"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,8">
|
||||
<TextBlock Text="主机:" Width="60" VerticalAlignment="Center"/>
|
||||
<TextBox Text="{Binding Host}" Width="120" Height="28"/>
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox Content="启用CORS" IsChecked="{Binding EnableCors}" Margin="0,5"/>
|
||||
<CheckBox Content="启用日志" IsChecked="{Binding EnableLogging}" Margin="0,5"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- API 端点列表 -->
|
||||
<Border Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1" CornerRadius="8" Padding="15" Margin="0,0,0,15">
|
||||
<StackPanel>
|
||||
<TextBlock Text="可用的 API 端点" FontSize="14" FontWeight="Bold" Margin="0,0,0,10"/>
|
||||
|
||||
<TextBlock FontSize="11" TextWrapping="Wrap">
|
||||
<Run Text="• GET /api/health" FontFamily="Consolas"/>
|
||||
<LineBreak/>
|
||||
<Run Text=" 健康检查" Foreground="{DynamicResource SecondaryTextBrush}"/>
|
||||
<LineBreak/>
|
||||
<LineBreak/>
|
||||
<Run Text="• GET /api/health/detailed" FontFamily="Consolas"/>
|
||||
<LineBreak/>
|
||||
<Run Text=" 详细健康检查" Foreground="{DynamicResource SecondaryTextBrush}"/>
|
||||
<LineBreak/>
|
||||
<LineBreak/>
|
||||
<Run Text="• GET /api/system/info" FontFamily="Consolas"/>
|
||||
<LineBreak/>
|
||||
<Run Text=" 系统信息" Foreground="{DynamicResource SecondaryTextBrush}"/>
|
||||
<LineBreak/>
|
||||
<LineBreak/>
|
||||
<Run Text="• GET /api/data" FontFamily="Consolas"/>
|
||||
<LineBreak/>
|
||||
<Run Text=" 获取所有数据" Foreground="{DynamicResource SecondaryTextBrush}"/>
|
||||
<LineBreak/>
|
||||
<LineBreak/>
|
||||
<Run Text="• POST /api/data" FontFamily="Consolas"/>
|
||||
<LineBreak/>
|
||||
<Run Text=" 创建数据" Foreground="{DynamicResource SecondaryTextBrush}"/>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 快速操作 -->
|
||||
<Border Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1" CornerRadius="8" Padding="15">
|
||||
<StackPanel>
|
||||
<TextBlock Text="快速操作" FontSize="14" FontWeight="Bold" Margin="0,0,0,10"/>
|
||||
|
||||
<Button Content="🌐 在浏览器中打开" Height="32" Margin="0,0,0,8"
|
||||
Command="{Binding OpenInBrowserCommand}"/>
|
||||
|
||||
<Button Content="📋 复制服务器地址" Height="32" Margin="0,0,0,8"
|
||||
Command="{Binding CopyUrlCommand}"/>
|
||||
|
||||
<Button Content="📊 查看性能指标" Height="32" Margin="0,0,0,8"
|
||||
Command="{Binding ShowMetricsCommand}"/>
|
||||
|
||||
<Button Content="⚙️ 高级设置" Height="32"
|
||||
Command="{Binding OpenAdvancedSettingsCommand}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
|
||||
<!-- 状态栏 -->
|
||||
<StatusBar Grid.Row="2" Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}" BorderThickness="0,1,0,0">
|
||||
<StatusBarItem>
|
||||
<TextBlock>
|
||||
<Run Text="运行时间: "/>
|
||||
<Run Text="{Binding Uptime, Mode=OneWay}"/>
|
||||
</TextBlock>
|
||||
</StatusBarItem>
|
||||
<Separator/>
|
||||
<StatusBarItem>
|
||||
<TextBlock>
|
||||
<Run Text="请求总数: "/>
|
||||
<Run Text="{Binding TotalRequests, Mode=OneWay}"/>
|
||||
</TextBlock>
|
||||
</StatusBarItem>
|
||||
<Separator/>
|
||||
<StatusBarItem>
|
||||
<TextBlock>
|
||||
<Run Text="活跃连接: "/>
|
||||
<Run Text="{Binding ActiveConnections, Mode=OneWay}"/>
|
||||
</TextBlock>
|
||||
</StatusBarItem>
|
||||
</StatusBar>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Windows;
|
||||
using Sharp.Demo.ViewModels.Windows;
|
||||
|
||||
namespace Sharp.Demo.Views.Windows
|
||||
{
|
||||
/// <summary>
|
||||
/// HttpServerWindow.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class HttpServerWindow : Window
|
||||
{
|
||||
public HttpServerWindow(HttpServerViewModel viewModel)
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = viewModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<Window x:Class="Sharp.Demo.Views.Windows.PlcDebugWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
Title="PLC 调试工具"
|
||||
Height="650"
|
||||
Width="900"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 工具栏 -->
|
||||
<Border Grid.Row="0" Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}" BorderThickness="0,0,0,1" Padding="15,10">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="IP 地址:" VerticalAlignment="Center" Margin="0,0,10,0"/>
|
||||
<TextBox Text="{Binding IpAddress}" Width="150" Height="28" Margin="0,0,10,0"/>
|
||||
|
||||
<Button Content="🔗 连接" Width="90" Height="32" Margin="0,0,10,0"
|
||||
Command="{Binding ConnectCommand}"/>
|
||||
<Button Content="🔌 断开" Width="90" Height="32" Margin="0,0,10,0"
|
||||
Command="{Binding DisconnectCommand}"/>
|
||||
|
||||
<Separator Margin="10,0"/>
|
||||
|
||||
<TextBlock Text="状态:" VerticalAlignment="Center" Margin="0,0,10,0"/>
|
||||
<TextBlock Text="{Binding Status}" FontWeight="Bold" VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource SuccessBrush}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<TabControl Grid.Row="1" Margin="15">
|
||||
<!-- 单次读写 -->
|
||||
<TabItem Header="📝 单次读写">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Margin="15">
|
||||
<Border Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1" CornerRadius="8" Padding="20" Margin="0,0,0,15">
|
||||
<StackPanel>
|
||||
<TextBlock Text="读取数据" FontSize="16" FontWeight="Bold" Margin="0,0,0,15"/>
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Text="地址:" VerticalAlignment="Center" Margin="0,0,10,0"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding ReadAddress}" Height="32" Margin="0,0,15,0"/>
|
||||
<Button Grid.Column="2" Content="读取" Width="100" Height="32" Margin="0,0,15,0"
|
||||
Command="{Binding ReadCommand}" Style="{StaticResource ButtonPrimary}"/>
|
||||
<TextBlock Grid.Column="3" Text="{Binding ReadValue}" FontWeight="Bold" FontSize="14" VerticalAlignment="Center"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1" CornerRadius="8" Padding="20">
|
||||
<StackPanel>
|
||||
<TextBlock Text="写入数据" FontSize="16" FontWeight="Bold" Margin="0,0,0,15"/>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Text="地址:" VerticalAlignment="Center" Margin="0,0,10,0"/>
|
||||
<TextBox Grid.Column="1" Text="{Binding WriteAddress}" Height="32" Margin="0,0,0,10"/>
|
||||
|
||||
<TextBlock Grid.Row="1" Text="值:" VerticalAlignment="Center" Margin="0,0,10,0"/>
|
||||
<StackPanel Grid.Row="1" Grid.Column="1" Orientation="Horizontal">
|
||||
<TextBox Text="{Binding WriteValue}" Width="150" Height="32" Margin="0,0,10,0"/>
|
||||
<Button Content="写入" Width="100" Height="32"
|
||||
Command="{Binding WriteCommand}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
|
||||
<!-- 日志 -->
|
||||
<TabItem Header="📋 操作日志">
|
||||
<Border Margin="15" Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1" CornerRadius="8">
|
||||
<Grid Margin="15">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
|
||||
<TextBlock Text="操作日志" FontSize="16" FontWeight="Bold" VerticalAlignment="Center"/>
|
||||
<Button Content="清空" Width="80" Height="30" Margin="15,0,0,0"
|
||||
Command="{Binding ClearLogCommand}"/>
|
||||
</StackPanel>
|
||||
|
||||
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
|
||||
<TextBox Text="{Binding LogText, Mode=OneWay}"
|
||||
IsReadOnly="True"
|
||||
TextWrapping="Wrap"
|
||||
Background="{DynamicResource SecondaryRegionBrush}"
|
||||
BorderThickness="0"
|
||||
FontFamily="Consolas"
|
||||
FontSize="11"
|
||||
Padding="10"/>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Border>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Windows;
|
||||
using Sharp.Demo.ViewModels.Windows;
|
||||
|
||||
namespace Sharp.Demo.Views.Windows
|
||||
{
|
||||
public partial class PlcDebugWindow : Window
|
||||
{
|
||||
public PlcDebugWindow(PlcDebugViewModel viewModel)
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = viewModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
{
|
||||
// Sharp Framework Demo 配置文件
|
||||
"System": {
|
||||
"AppName": "Sharp Framework Demo",
|
||||
"Version": "1.0.0",
|
||||
"HTTPIp": "127.0.0.1",
|
||||
"HttpPort": 8080,
|
||||
"TimeoutSeconds": 30,
|
||||
"Scheme": "http"
|
||||
},
|
||||
|
||||
// HTTP 服务器配置
|
||||
"Server": {
|
||||
"Port": 8080,
|
||||
"Host": "localhost",
|
||||
"MaxConcurrentRequests": 100
|
||||
},
|
||||
|
||||
// NetServer 配置
|
||||
"NetServer": {
|
||||
"UseLogging": true,
|
||||
"UseExceptionHandling": true,
|
||||
"UseCors": true,
|
||||
"UseRateLimit": true,
|
||||
"UseResponseCache": true
|
||||
},
|
||||
|
||||
// NetClient 配置
|
||||
"NetClient": {
|
||||
"Timeout": "00:00:20",
|
||||
"EnableLogging": true,
|
||||
"IncludeCorrelationId": true,
|
||||
"CorrelationIdHeaderName": "X-Correlation-Id",
|
||||
"UseBearerToken": false,
|
||||
"Retry": {
|
||||
"Enabled": true,
|
||||
"MaxRetryAttempts": 3,
|
||||
"FirstDelay": "00:00:01",
|
||||
"Handle429": true,
|
||||
"HandleRequestTimeout": true
|
||||
}
|
||||
},
|
||||
|
||||
// PLC 配置(西门子)
|
||||
"Plc": {
|
||||
"Siemens": {
|
||||
"IpAddress": "192.168.1.100",
|
||||
"CpuType": "S71200",
|
||||
"Rack": 0,
|
||||
"Slot": 1,
|
||||
"MonitorInterval": 200
|
||||
}
|
||||
},
|
||||
|
||||
// 串口配置
|
||||
"SerialPort": {
|
||||
"BaudRate": 9600,
|
||||
"Parity": "None",
|
||||
"DataBits": 8,
|
||||
"StopBits": "One"
|
||||
},
|
||||
|
||||
// 日志配置
|
||||
"SharpLog": {
|
||||
"UseSerilog": true,
|
||||
"UseConsole": true,
|
||||
"ConsoleJson": false,
|
||||
"MinLevel": "Information",
|
||||
"Overrides": {
|
||||
"Microsoft": "Warning",
|
||||
"System": "Warning"
|
||||
},
|
||||
"File": {
|
||||
"Enabled": true,
|
||||
"Path": "logs/demo-.log",
|
||||
"Rolling": "Day",
|
||||
"RetainedFileCountLimit": 30,
|
||||
"Json": true
|
||||
},
|
||||
"Enrich": {
|
||||
"Enabled": true,
|
||||
"WithThreadId": true,
|
||||
"WithProcessId": true,
|
||||
"WithMachineName": true,
|
||||
"WithEnvironmentName": true,
|
||||
"WithActivityTrace": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Python", "Python\Python.csp
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wpf.DevTools", "Wpf.DevTools\Wpf.DevTools.csproj", "{3DFC9CBE-D9A3-43A8-80D9-D877000A0BAA}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sharp.Demo", "Sharp.Demo\Sharp.Demo.csproj", "{1C5E7681-6B3E-47A6-B8D1-78E2A7EA705A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -349,6 +351,18 @@ Global
|
||||
{3DFC9CBE-D9A3-43A8-80D9-D877000A0BAA}.Release|x64.Build.0 = Release|Any CPU
|
||||
{3DFC9CBE-D9A3-43A8-80D9-D877000A0BAA}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{3DFC9CBE-D9A3-43A8-80D9-D877000A0BAA}.Release|x86.Build.0 = Release|Any CPU
|
||||
{1C5E7681-6B3E-47A6-B8D1-78E2A7EA705A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1C5E7681-6B3E-47A6-B8D1-78E2A7EA705A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1C5E7681-6B3E-47A6-B8D1-78E2A7EA705A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{1C5E7681-6B3E-47A6-B8D1-78E2A7EA705A}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{1C5E7681-6B3E-47A6-B8D1-78E2A7EA705A}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{1C5E7681-6B3E-47A6-B8D1-78E2A7EA705A}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{1C5E7681-6B3E-47A6-B8D1-78E2A7EA705A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1C5E7681-6B3E-47A6-B8D1-78E2A7EA705A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1C5E7681-6B3E-47A6-B8D1-78E2A7EA705A}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{1C5E7681-6B3E-47A6-B8D1-78E2A7EA705A}.Release|x64.Build.0 = Release|Any CPU
|
||||
{1C5E7681-6B3E-47A6-B8D1-78E2A7EA705A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{1C5E7681-6B3E-47A6-B8D1-78E2A7EA705A}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="HandyControl" Version="3.5.0" />
|
||||
<PackageReference Include="HandyControl" Version="3.5.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
Reference in new issue
Block a user