A:集成数据库功能

This commit is contained in:
jimmy committed 2025-12-24 21:16:48 +08:00
1 parent 974d16833c
commit 99c2c59552
6 files changed
+793 -69

No files matched your search

+1 -1
View File
@@ -127,8 +127,8 @@ namespace Sharp.Demo
services.AddTransient<Sharp.Demo.Views.Windows.HttpServerWindow>();
services.AddTransient<Sharp.Demo.Views.Windows.HttpClientWindow>();
services.AddTransient<Sharp.Demo.Views.Windows.PlcDebugWindow>();
services.AddTransient<Sharp.Demo.Views.Windows.DatabaseWindow>();
// TODO: 添加其他窗口注册
// services.AddTransient<Sharp.Demo.Views.Windows.DatabaseWindow>();
// services.AddTransient<Sharp.Demo.Views.Windows.SerialPortWindow>();
// etc...
@@ -237,8 +237,12 @@ namespace Sharp.Demo.ViewModels
_windowManager.ShowWindow<Sharp.Demo.Views.Windows.PlcDebugWindow>(moduleKey);
break;
// 其他窗口待实现
case "Database":
_logger.LogInformation("正在打开数据库管理窗口...");
_windowManager.ShowWindow<Sharp.Demo.Views.Windows.DatabaseWindow>(moduleKey);
break;
// 其他窗口待实现
case "SerialPort":
case "ProcessTest":
case "LogDemo":
@@ -14,11 +14,20 @@ using Sharp.Wpf.Services;
using Sharp.Wpf.Navigation;
using Sharp.Demo.Data;
using Sharp.Demo.Views.Dialogs;
using Microsoft.EntityFrameworkCore;
using Common.ExpandFun;
namespace Sharp.Demo.ViewModels.Pages
{
/// <summary>
/// 数据库管理页面 ViewModel - EF Core + Repository 演示
/// 展示 Sharp.Data 模块的完整功能:
/// - 基本 CRUD 操作
/// - 分页查询
/// - 批量操作
/// - 软删除和审计追踪
/// - 统计和查询
/// - 事务处理
/// </summary>
public class DatabaseViewModel : ViewModelBase, INavigationAware
{
@@ -31,6 +40,21 @@ namespace Sharp.Demo.ViewModels.Pages
private string _statusText = string.Empty;
private string _recordCount = "记录数: 0";
// 分页相关
private int _currentPage = 1;
private int _pageSize = 10;
private int _totalPages = 1;
private int _totalCount = 0;
private string _pageInfo = "第 1/1 页";
// 统计信息
private int _totalUsers = 0;
private int _activeUsers = 0;
private int _inactiveUsers = 0;
// 当前窗口引用(用于设置对话框父窗口)
public Window? CurrentWindow { get; set; }
public DatabaseViewModel(
IDataContext dataContext,
ILogger<DatabaseViewModel> logger,
@@ -43,17 +67,31 @@ namespace Sharp.Demo.ViewModels.Pages
Users = new ObservableCollection<User>();
// 初始化命令
LoadDataCommand = new AsyncCommand(async () => await LoadDataAsync());
SearchCommand = new AsyncCommand(async () => await LoadDataAsync(SearchText.Trim()));
LoadDataCommand = new AsyncCommand(async () => await LoadPagedDataAsync());
SearchCommand = new AsyncCommand(async () => await LoadPagedDataAsync());
ClearSearchCommand = new AsyncCommand(async () =>
{
SearchText = string.Empty;
await LoadDataAsync();
CurrentPage = 1;
await LoadPagedDataAsync();
});
AddCommand = new RelayCommand(_ => Add());
EditCommand = new RelayCommand(_ => Edit(), _ => SelectedUser != null);
DeleteCommand = new AsyncCommand(DeleteAsync, () => SelectedUser != null);
RefreshCommand = new AsyncCommand(async () => await LoadDataAsync(SearchText.Trim()));
RefreshCommand = new AsyncCommand(async () => await LoadPagedDataAsync());
// 分页命令
FirstPageCommand = new AsyncCommand(async () => { CurrentPage = 1; await LoadPagedDataAsync(); }, () => CurrentPage > 1);
PreviousPageCommand = new AsyncCommand(async () => { CurrentPage--; await LoadPagedDataAsync(); }, () => CurrentPage > 1);
NextPageCommand = new AsyncCommand(async () => { CurrentPage++; await LoadPagedDataAsync(); }, () => CurrentPage < TotalPages);
LastPageCommand = new AsyncCommand(async () => { CurrentPage = TotalPages; await LoadPagedDataAsync(); }, () => CurrentPage < TotalPages);
// 批量操作命令
BatchAddCommand = new AsyncCommand(BatchAddAsync);
BatchDeleteCommand = new AsyncCommand(BatchDeleteAsync, () => Users.Any());
// 统计命令
RefreshStatsCommand = new AsyncCommand(RefreshStatsAsync);
}
#region 属性
@@ -74,8 +112,8 @@ namespace Sharp.Demo.ViewModels.Pages
if (SetProperty(ref _selectedUser, value))
{
// 触发命令的 CanExecute 更新
OnPropertyChanged(nameof(EditCommand));
OnPropertyChanged(nameof(DeleteCommand));
(EditCommand as RelayCommand)?.RaiseCanExecuteChanged();
(DeleteCommand as AsyncCommand)?.RaiseCanExecuteChanged();
}
}
}
@@ -107,6 +145,86 @@ namespace Sharp.Demo.ViewModels.Pages
set => SetProperty(ref _recordCount, value);
}
/// <summary>
/// 当前页码
/// </summary>
public int CurrentPage
{
get => _currentPage;
set => SetProperty(ref _currentPage, value);
}
/// <summary>
/// 每页大小
/// </summary>
public int PageSize
{
get => _pageSize;
set
{
if (SetProperty(ref _pageSize, value))
{
// 每页大小改变时,重置到第一页并重新加载
CurrentPage = 1;
_ = LoadPagedDataAsync();
}
}
}
/// <summary>
/// 总页数
/// </summary>
public int TotalPages
{
get => _totalPages;
set => SetProperty(ref _totalPages, value);
}
/// <summary>
/// 总记录数
/// </summary>
public int TotalCount
{
get => _totalCount;
set => SetProperty(ref _totalCount, value);
}
/// <summary>
/// 页码信息
/// </summary>
public string PageInfo
{
get => _pageInfo;
set => SetProperty(ref _pageInfo, value);
}
/// <summary>
/// 总用户数
/// </summary>
public int TotalUsers
{
get => _totalUsers;
set => SetProperty(ref _totalUsers, value);
}
/// <summary>
/// 活跃用户数
/// </summary>
public int ActiveUsers
{
get => _activeUsers;
set => SetProperty(ref _activeUsers, value);
}
/// <summary>
/// 非活跃用户数
/// </summary>
public int InactiveUsers
{
get => _inactiveUsers;
set => SetProperty(ref _inactiveUsers, value);
}
#endregion
#region 命令
@@ -119,14 +237,30 @@ namespace Sharp.Demo.ViewModels.Pages
public ICommand DeleteCommand { get; }
public ICommand RefreshCommand { get; }
// 分页命令
public ICommand FirstPageCommand { get; }
public ICommand PreviousPageCommand { get; }
public ICommand NextPageCommand { get; }
public ICommand LastPageCommand { get; }
// 批量操作命令
public ICommand BatchAddCommand { get; }
public ICommand BatchDeleteCommand { get; }
// 统计命令
public ICommand RefreshStatsCommand { get; }
#endregion
#region INavigationAware 实现
public async Task OnNavigatedToAsync(object? parameter)
{
// 进入页面时加载数据
await LoadDataAsync();
// 进入页面时加载数据和统计信息
await Task.WhenAll(
LoadPagedDataAsync(),
RefreshStatsAsync()
);
}
public Task OnNavigatedFromAsync()
@@ -139,40 +273,71 @@ namespace Sharp.Demo.ViewModels.Pages
#region 私有方法
private async Task LoadDataAsync(string? searchText = null)
/// <summary>
/// 加载分页数据 - 演示 PagedResult 功能
/// </summary>
private async Task LoadPagedDataAsync()
{
try
{
StatusText = "正在加载数据...";
_logger.LogInformation("开始加载用户数据,搜索条件: {SearchText}", searchText ?? "无");
_logger.LogInformation("开始加载用户数据,页码: {Page}, 每页: {PageSize}, 搜索: {SearchText}",
CurrentPage, PageSize, SearchText ?? "无");
var repo = _dataContext.Repository<User>();
List<User> users;
if (string.IsNullOrWhiteSpace(searchText))
// 使用分页查询 - Data 模块的重要功能
// 注意:软删除后需要手动过滤 IsDeleted=false 的记录
PagedResult<User> pagedResult;
if (string.IsNullOrWhiteSpace(SearchText))
{
users = await repo.GetAllAsync();
pagedResult = await repo.GetPagedAsync(
page: CurrentPage,
pageSize: PageSize,
predicate: u => !u.IsDeleted, // 过滤已删除的数据
orderBy: q => q.OrderByDescending(u => u.CreatedAt));
}
else
{
users = await repo.FindAsync(u =>
u.UserName.Contains(searchText) ||
u.Email.Contains(searchText) ||
(u.Remarks != null && u.Remarks.Contains(searchText)));
var searchTerm = SearchText.Trim();
pagedResult = await repo.GetPagedAsync(
page: CurrentPage,
pageSize: PageSize,
predicate: u =>
!u.IsDeleted && // 过滤已删除的数据
(u.UserName.Contains(searchTerm) ||
u.Email.Contains(searchTerm) ||
(u.Remarks != null && u.Remarks.Contains(searchTerm))),
orderBy: q => q.OrderByDescending(u => u.CreatedAt));
}
// 更新UI数据
Users.Clear();
foreach (var user in users.OrderByDescending(u => u.CreatedAt))
foreach (var user in pagedResult.Items)
{
Users.Add(user);
}
RecordCount = $"记录数: {users.Count}";
StatusText = string.IsNullOrWhiteSpace(searchText)
? $"加载完成,共 {users.Count} 条记录"
: $"搜索完成,找到 {users.Count} 条记录";
// 更新分页信息
TotalCount = pagedResult.TotalCount;
TotalPages = pagedResult.TotalPages;
CurrentPage = pagedResult.PageIndex;
PageSize = pagedResult.PageSize;
PageInfo = $"第 {CurrentPage}/{TotalPages} 页";
RecordCount = $"共 {TotalCount} 条记录,当前页 {Users.Count} 条";
_logger.LogInformation("加载完成,共 {Count} 条记录", users.Count);
StatusText = string.IsNullOrWhiteSpace(SearchText)
? $"加载完成 (第 {CurrentPage}/{TotalPages} 页)"
: $"搜索完成,找到 {TotalCount} 条记录 (第 {CurrentPage}/{TotalPages} 页)";
_logger.LogInformation("分页加载完成,总数: {Total}, 当前页: {Page}/{TotalPages}",
TotalCount, CurrentPage, TotalPages);
// 触发分页按钮的 CanExecute 更新
(FirstPageCommand as AsyncCommand)?.RaiseCanExecuteChanged();
(PreviousPageCommand as AsyncCommand)?.RaiseCanExecuteChanged();
(NextPageCommand as AsyncCommand)?.RaiseCanExecuteChanged();
(LastPageCommand as AsyncCommand)?.RaiseCanExecuteChanged();
}
catch (Exception ex)
{
@@ -182,11 +347,34 @@ namespace Sharp.Demo.ViewModels.Pages
}
}
/// <summary>
/// 刷新统计信息 - 演示 Count 和 Any 功能
/// </summary>
private async Task RefreshStatsAsync()
{
try
{
var repo = _dataContext.Repository<User>();
// 使用 Count 和条件查询(过滤已删除的数据)
TotalUsers = await repo.CountAsync(u => !u.IsDeleted);
ActiveUsers = await repo.CountAsync(u => !u.IsDeleted && u.IsActive);
InactiveUsers = await repo.CountAsync(u => !u.IsDeleted && !u.IsActive);
_logger.LogInformation("统计信息已刷新 - 总数: {Total}, 活跃: {Active}, 非活跃: {Inactive}",
TotalUsers, ActiveUsers, InactiveUsers);
}
catch (Exception ex)
{
_logger.LogError(ex, "刷新统计信息失败");
}
}
private void Add()
{
var dialog = new UserEditDialog
{
Owner = Application.Current.MainWindow
Owner = CurrentWindow ?? Application.Current.MainWindow
};
if (dialog.ShowDialog() == true)
@@ -205,7 +393,7 @@ namespace Sharp.Demo.ViewModels.Pages
var dialog = new UserEditDialog(SelectedUser)
{
Owner = Application.Current.MainWindow
Owner = CurrentWindow ?? Application.Current.MainWindow
};
if (dialog.ShowDialog() == true)
@@ -246,7 +434,7 @@ namespace Sharp.Demo.ViewModels.Pages
_logger.LogInformation("用户新增成功: {UserId}", user.Id);
await _dialogService.ShowInfoAsync("用户新增成功!", "成功");
await LoadDataAsync();
await Task.WhenAll(LoadPagedDataAsync(), RefreshStatsAsync());
}
catch (Exception ex)
{
@@ -270,7 +458,7 @@ namespace Sharp.Demo.ViewModels.Pages
_logger.LogInformation("用户更新成功: {UserId}", user.Id);
await _dialogService.ShowInfoAsync("用户更新成功!", "成功");
await LoadDataAsync();
await Task.WhenAll(LoadPagedDataAsync(), RefreshStatsAsync());
}
catch (Exception ex)
{
@@ -294,7 +482,7 @@ namespace Sharp.Demo.ViewModels.Pages
_logger.LogInformation("用户软删除成功: {UserId}", userId);
await _dialogService.ShowInfoAsync("用户已标记为删除(软删除)!", "成功");
await LoadDataAsync();
await Task.WhenAll(LoadPagedDataAsync(), RefreshStatsAsync());
}
catch (Exception ex)
{
@@ -304,6 +492,111 @@ namespace Sharp.Demo.ViewModels.Pages
}
}
/// <summary>
/// 批量添加用户 - 演示 AddRangeAsync 功能
/// </summary>
private async Task BatchAddAsync()
{
try
{
var confirmed = await _dialogService.ShowConfirmAsync(
"将批量添加 10 个测试用户,确定要继续吗?\n\n这将演示 Sharp.Data 的 AddRangeAsync 批量操作功能。",
"批量添加");
if (!confirmed) return;
StatusText = "正在批量添加...";
_logger.LogInformation("开始批量添加测试用户");
// 生成测试数据
var testUsers = new List<User>();
for (int i = 1; i <= 10; i++)
{
testUsers.Add(new User
{
UserName = $"testuser{DateTime.Now:yyyyMMddHHmmss}_{i}",
Email = $"test{DateTime.Now:yyyyMMddHHmmss}_{i}@example.com",
Age = 20 + (i % 30),
IsActive = i % 2 == 0,
Remarks = $"批量导入的测试用户 {i}"
});
}
// 使用批量添加 - Data 模块的批量操作功能
var repo = _dataContext.Repository<User>();
await repo.AddRangeAsync(testUsers);
await _dataContext.SaveChangesAsync();
_logger.LogInformation("批量添加成功,共添加 {Count} 个用户", testUsers.Count);
await _dialogService.ShowInfoAsync($"批量添加成功!共添加 {testUsers.Count} 个用户。", "成功");
await Task.WhenAll(LoadPagedDataAsync(), RefreshStatsAsync());
}
catch (Exception ex)
{
_logger.LogError(ex, "批量添加失败");
StatusText = "批量添加失败";
await _dialogService.ShowErrorAsync($"批量添加失败: {ex.Message}", "错误");
}
}
/// <summary>
/// 批量删除用户 - 演示事务处理和批量删除
/// </summary>
private async Task BatchDeleteAsync()
{
try
{
if (!Users.Any())
{
await _dialogService.ShowInfoAsync("当前页没有可删除的数据", "提示");
return;
}
var confirmed = await _dialogService.ShowConfirmAsync(
$"将删除当前页所有 {Users.Count} 个用户(软删除),确定要继续吗?\n\n这将演示 Sharp.Data 的事务处理功能。",
"批量删除");
if (!confirmed) return;
StatusText = "正在批量删除...";
_logger.LogInformation("开始批量删除,数量: {Count}", Users.Count);
var repo = _dataContext.Repository<User>();
// 使用事务确保原子性
using var transaction = await _dataContext.Context.Database.BeginTransactionAsync();
try
{
// 批量软删除
var userIds = Users.Select(u => u.Id).ToList();
foreach (var userId in userIds)
{
await repo.RemoveAsync(userId);
}
await _dataContext.SaveChangesAsync();
await transaction.CommitAsync();
_logger.LogInformation("批量删除成功,共删除 {Count} 个用户", userIds.Count);
await _dialogService.ShowInfoAsync($"批量删除成功!共删除 {userIds.Count} 个用户。", "成功");
}
catch
{
await transaction.RollbackAsync();
throw;
}
await Task.WhenAll(LoadPagedDataAsync(), RefreshStatsAsync());
}
catch (Exception ex)
{
_logger.LogError(ex, "批量删除失败");
StatusText = "批量删除失败";
await _dialogService.ShowErrorAsync($"批量删除失败: {ex.Message}", "错误");
}
}
#endregion
}
}
@@ -31,6 +31,37 @@
Text="Sharp.Data - EF Core + Repository + 审计追踪 + 软删除"
Margin="0,0,0,20" />
<!-- 统计信息栏 -->
<Border
Grid.Row="2"
Background="{DynamicResource InfoBrush}"
BorderBrush="{DynamicResource BorderBrush}"
BorderThickness="1"
CornerRadius="8"
Padding="15"
Margin="0,0,0,10">
<StackPanel Orientation="Horizontal">
<TextBlock FontWeight="Bold" FontSize="14" Margin="0,0,20,0">
<Run Text="📊 数据统计"/>
</TextBlock>
<TextBlock FontSize="13" Margin="0,0,15,0">
<Run Text="总用户数: "/>
<Run Text="{Binding TotalUsers}" FontWeight="Bold" Foreground="{DynamicResource PrimaryBrush}"/>
</TextBlock>
<TextBlock FontSize="13" Margin="0,0,15,0">
<Run Text="活跃: "/>
<Run Text="{Binding ActiveUsers}" FontWeight="Bold" Foreground="#27AE60"/>
</TextBlock>
<TextBlock FontSize="13" Margin="0,0,15,0">
<Run Text="非活跃: "/>
<Run Text="{Binding InactiveUsers}" FontWeight="Bold" Foreground="#95A5A6"/>
</TextBlock>
<Button Content="刷新统计" Width="90" Height="28" Margin="10,0,0,0"
Command="{Binding RefreshStatsCommand}"
Style="{StaticResource ButtonInfo}"/>
</StackPanel>
</Border>
<!-- 搜索和操作栏 -->
<Border
Grid.Row="2"
@@ -39,46 +70,67 @@
BorderThickness="1"
CornerRadius="8"
Padding="15"
Margin="0,0,0,15">
Margin="0,45,0,15">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 搜索框 -->
<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>
<!-- 第一行:搜索和单条操作 -->
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- 状态信息 -->
<TextBlock Grid.Column="1" Text="{Binding StatusText}" VerticalAlignment="Center"
Foreground="{DynamicResource SecondaryTextBrush}" Margin="15,0"/>
<!-- 搜索框 -->
<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>
<!-- 操作按钮 -->
<StackPanel Grid.Column="2" Orientation="Horizontal">
<Button Content="新增" Width="90" Height="32" Margin="0,0,5,0"
Command="{Binding AddCommand}"
<!-- 状态信息 -->
<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>
<!-- 第二行:批量操作 -->
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,10,0,0">
<TextBlock Text="批量操作:" VerticalAlignment="Center" FontWeight="Bold" Margin="0,0,10,0"/>
<Button Content="批量添加测试数据" Width="140" Height="32" Margin="0,0,5,0"
Command="{Binding BatchAddCommand}"
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}"
<Button Content="批量删除当前页" Width="140" Height="32" Margin="0,0,5,0"
Command="{Binding BatchDeleteCommand}"
Style="{StaticResource ButtonDanger}"/>
<Button Content="刷新" Width="90" Height="32" Margin="5,0,0,0"
Command="{Binding RefreshCommand}"
Style="{StaticResource ButtonInfo}"/>
<TextBlock Text="(演示 AddRangeAsync 和事务处理)" VerticalAlignment="Center"
FontSize="11" Foreground="{DynamicResource SecondaryTextBrush}" Margin="10,0,0,0"/>
</StackPanel>
</Grid>
</Border>
@@ -126,6 +178,67 @@
</DataGrid>
</Border>
<!-- 分页控件 -->
<Border
Grid.Row="4"
Background="{DynamicResource RegionBrush}"
BorderBrush="{DynamicResource BorderBrush}"
BorderThickness="1"
CornerRadius="8"
Padding="15"
Margin="0,0,0,10">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- 左侧:记录信息 -->
<TextBlock Grid.Column="0" VerticalAlignment="Center" FontSize="13">
<Run Text="{Binding RecordCount}"/>
<Run Text=" | "/>
<Run Text="{Binding PageInfo}"/>
</TextBlock>
<!-- 中间:页码信息 -->
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="每页显示: " VerticalAlignment="Center" Margin="0,0,5,0"/>
<ComboBox SelectedItem="{Binding PageSize}" Width="80" Height="28">
<sys:Int32 xmlns:sys="clr-namespace:System;assembly=mscorlib">5</sys:Int32>
<sys:Int32 xmlns:sys="clr-namespace:System;assembly=mscorlib">10</sys:Int32>
<sys:Int32 xmlns:sys="clr-namespace:System;assembly=mscorlib">20</sys:Int32>
<sys:Int32 xmlns:sys="clr-namespace:System;assembly=mscorlib">50</sys:Int32>
<sys:Int32 xmlns:sys="clr-namespace:System;assembly=mscorlib">100</sys:Int32>
</ComboBox>
<TextBlock Text=" 条" VerticalAlignment="Center" Margin="5,0,0,0"/>
</StackPanel>
<!-- 右侧:分页按钮 -->
<StackPanel Grid.Column="2" Orientation="Horizontal" VerticalAlignment="Center">
<Button Content="首页" Width="60" Height="28" Margin="0,0,5,0"
Command="{Binding FirstPageCommand}"
Style="{StaticResource ButtonDefault}"/>
<Button Content="上一页" Width="70" Height="28" Margin="0,0,5,0"
Command="{Binding PreviousPageCommand}"
Style="{StaticResource ButtonDefault}"/>
<TextBlock VerticalAlignment="Center" Margin="10,0">
<Run Text="第 "/>
<Run Text="{Binding CurrentPage}" FontWeight="Bold" Foreground="{DynamicResource PrimaryBrush}"/>
<Run Text=" / "/>
<Run Text="{Binding TotalPages}" FontWeight="Bold"/>
<Run Text=" 页"/>
</TextBlock>
<Button Content="下一页" Width="70" Height="28" Margin="5,0,5,0"
Command="{Binding NextPageCommand}"
Style="{StaticResource ButtonDefault}"/>
<Button Content="末页" Width="60" Height="28"
Command="{Binding LastPageCommand}"
Style="{StaticResource ButtonDefault}"/>
</StackPanel>
</Grid>
</Border>
<!-- 底部信息栏 -->
<Border
Grid.Row="4"
@@ -133,7 +246,8 @@
BorderBrush="{DynamicResource BorderBrush}"
BorderThickness="1"
CornerRadius="8"
Padding="15">
Padding="15"
Margin="0,55,0,0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
@@ -145,17 +259,24 @@
<Run Text="💡 功能说明"/>
</TextBlock>
<TextBlock FontSize="11" TextWrapping="Wrap">
<Run Text="• 本示例展示 Sharp.Data 模块的使用:SQLite 数据库、仓储模式、自动审计、软删除"/>
<Run Text="• 完整演示 Sharp.Data 模块功能:SQLite + Repository + 分页 + 批量操作 + 审计追踪 + 软删除 + 事务处理"/>
<LineBreak/>
<Run Text="• 删除操作使用软删除(IsDeleted=true),数据不会真正删除"/>
<Run Text="• 分页查询:使用 GetPagedAsync 方法,返回 PagedResult&lt;T&gt; 分页结果"/>
<LineBreak/>
<Run Text="• 创建/修改时间和操作人由 Data 模块自动记录"/>
<Run Text="• 批量操作:AddRangeAsync 批量添加,事务确保批量删除的原子性"/>
<LineBreak/>
<Run Text="• 审计追踪:CreatedAt/UpdatedAt/CreatedBy/UpdatedBy 由框架自动管理"/>
<LineBreak/>
<Run Text="• 软删除:RemoveAsync 标记 IsDeleted=true,数据不会物理删除"/>
</TextBlock>
</StackPanel>
<StackPanel Grid.Column="1" VerticalAlignment="Center">
<TextBlock Text="数据库: SQLite (demo.db)" FontSize="11" FontWeight="Bold"/>
<TextBlock Text="{Binding RecordCount}" FontSize="11"/>
<TextBlock FontSize="11">
<Run Text="总用户数: "/>
<Run Text="{Binding TotalUsers}" FontWeight="Bold"/>
</TextBlock>
</StackPanel>
</Grid>
</Border>
@@ -0,0 +1,279 @@
<Window x:Class="Sharp.Demo.Views.Windows.DatabaseWindow"
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="数据库管理 - Sharp.Data 演示"
Height="800"
Width="1200"
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">
<Grid>
<StackPanel Orientation="Horizontal">
<TextBlock Text="📊 数据库管理" FontSize="18" FontWeight="Bold" VerticalAlignment="Center" Margin="0,0,20,0"/>
<Separator Margin="0,0,10,0"/>
<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" Margin="0,0,5,0"
Command="{Binding DeleteCommand}"
Style="{StaticResource ButtonDanger}"/>
<Button Content="🔄 刷新" Width="90" Height="32"
Command="{Binding RefreshCommand}"
Style="{StaticResource ButtonInfo}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<TextBlock Text="{Binding StatusText}" VerticalAlignment="Center" Margin="0,0,15,0"
Foreground="{DynamicResource SecondaryTextBrush}"/>
<TextBlock Text="{Binding RecordCount}" FontWeight="Bold" VerticalAlignment="Center"/>
</StackPanel>
</Grid>
</Border>
<!-- 主内容区 -->
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 统计信息栏 -->
<Border Grid.Row="0"
Background="{DynamicResource InfoBrush}"
BorderBrush="{DynamicResource BorderBrush}"
BorderThickness="1"
CornerRadius="8"
Padding="15"
Margin="0,0,0,10">
<StackPanel Orientation="Horizontal">
<TextBlock FontWeight="Bold" FontSize="14" Margin="0,0,20,0">
<Run Text="📊 数据统计"/>
</TextBlock>
<TextBlock FontSize="13" Margin="0,0,15,0">
<Run Text="总用户数: "/>
<Run Text="{Binding TotalUsers}" FontWeight="Bold" Foreground="{DynamicResource PrimaryBrush}"/>
</TextBlock>
<TextBlock FontSize="13" Margin="0,0,15,0">
<Run Text="活跃: "/>
<Run Text="{Binding ActiveUsers}" FontWeight="Bold" Foreground="#27AE60"/>
</TextBlock>
<TextBlock FontSize="13" Margin="0,0,15,0">
<Run Text="非活跃: "/>
<Run Text="{Binding InactiveUsers}" FontWeight="Bold" Foreground="#95A5A6"/>
</TextBlock>
<Button Content="刷新统计" Width="90" Height="28" Margin="10,0,0,0"
Command="{Binding RefreshStatsCommand}"
Style="{StaticResource ButtonInfo}"/>
</StackPanel>
</Border>
<!-- 搜索和操作栏 -->
<Border Grid.Row="1"
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="Auto"/>
</Grid.RowDefinitions>
<!-- 搜索框 -->
<StackPanel Grid.Row="0" Orientation="Horizontal">
<TextBlock Text="搜索:" VerticalAlignment="Center" Margin="0,0,10,0"/>
<TextBox Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}"
Width="300" 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>
<!-- 批量操作 -->
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,10,0,0">
<TextBlock Text="批量操作:" VerticalAlignment="Center" FontWeight="Bold" Margin="0,0,10,0"/>
<Button Content="批量添加测试数据" Width="140" Height="32" Margin="0,0,5,0"
Command="{Binding BatchAddCommand}"
Style="{StaticResource ButtonSuccess}"/>
<Button Content="批量删除当前页" Width="140" Height="32" Margin="0,0,5,0"
Command="{Binding BatchDeleteCommand}"
Style="{StaticResource ButtonDanger}"/>
<TextBlock Text="(演示 AddRangeAsync 和事务处理)" VerticalAlignment="Center"
FontSize="11" Foreground="{DynamicResource SecondaryTextBrush}" Margin="10,0,0,0"/>
</StackPanel>
</Grid>
</Border>
<!-- 数据列表 -->
<Border Grid.Row="3"
Background="{DynamicResource RegionBrush}"
BorderBrush="{DynamicResource BorderBrush}"
BorderThickness="1"
CornerRadius="8"
Padding="10"
Margin="0,0,0,15"
Height="450">
<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"
VerticalScrollBarVisibility="Disabled"
HorizontalScrollBarVisibility="Auto">
<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="220"/>
<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 RegionBrush}"
BorderBrush="{DynamicResource BorderBrush}"
BorderThickness="1"
CornerRadius="8"
Padding="15"
Margin="0,0,0,10">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- 左侧:记录信息 -->
<TextBlock Grid.Column="0" VerticalAlignment="Center" FontSize="13">
<Run Text="{Binding RecordCount}"/>
<Run Text=" | "/>
<Run Text="{Binding PageInfo}"/>
</TextBlock>
<!-- 中间:页码信息 -->
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="每页显示: " VerticalAlignment="Center" Margin="0,0,5,0"/>
<ComboBox SelectedItem="{Binding PageSize}" Width="80" Height="28">
<sys:Int32 xmlns:sys="clr-namespace:System;assembly=mscorlib">5</sys:Int32>
<sys:Int32 xmlns:sys="clr-namespace:System;assembly=mscorlib">10</sys:Int32>
<sys:Int32 xmlns:sys="clr-namespace:System;assembly=mscorlib">20</sys:Int32>
<sys:Int32 xmlns:sys="clr-namespace:System;assembly=mscorlib">50</sys:Int32>
<sys:Int32 xmlns:sys="clr-namespace:System;assembly=mscorlib">100</sys:Int32>
</ComboBox>
<TextBlock Text=" 条" VerticalAlignment="Center" Margin="5,0,0,0"/>
</StackPanel>
<!-- 右侧:分页按钮 -->
<StackPanel Grid.Column="2" Orientation="Horizontal" VerticalAlignment="Center">
<Button Content="首页" Width="60" Height="28" Margin="0,0,5,0"
Command="{Binding FirstPageCommand}"
Style="{StaticResource ButtonDefault}"/>
<Button Content="上一页" Width="70" Height="28" Margin="0,0,5,0"
Command="{Binding PreviousPageCommand}"
Style="{StaticResource ButtonDefault}"/>
<TextBlock VerticalAlignment="Center" Margin="10,0">
<Run Text="第 "/>
<Run Text="{Binding CurrentPage}" FontWeight="Bold" Foreground="{DynamicResource PrimaryBrush}"/>
<Run Text=" / "/>
<Run Text="{Binding TotalPages}" FontWeight="Bold"/>
<Run Text=" 页"/>
</TextBlock>
<Button Content="下一页" Width="70" Height="28" Margin="5,0,5,0"
Command="{Binding NextPageCommand}"
Style="{StaticResource ButtonDefault}"/>
<Button Content="末页" Width="60" Height="28"
Command="{Binding LastPageCommand}"
Style="{StaticResource ButtonDefault}"/>
</StackPanel>
</Grid>
</Border>
<!-- 底部信息栏 -->
<Border Grid.Row="5"
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 + Repository + 分页 + 批量操作 + 审计追踪 + 软删除 + 事务处理"/>
<LineBreak/>
<Run Text="• 分页查询:使用 GetPagedAsync 方法,返回 PagedResult&lt;T&gt; 分页结果"/>
<LineBreak/>
<Run Text="• 批量操作:AddRangeAsync 批量添加,事务确保批量删除的原子性"/>
<LineBreak/>
<Run Text="• 审计追踪:CreatedAt/UpdatedAt/CreatedBy/UpdatedBy 由框架自动管理"/>
<LineBreak/>
<Run Text="• 软删除:RemoveAsync 标记 IsDeleted=true,数据不会物理删除"/>
</TextBlock>
</StackPanel>
<StackPanel Grid.Column="1" VerticalAlignment="Center">
<TextBlock Text="数据库: SQLite (demo.db)" FontSize="11" FontWeight="Bold"/>
<TextBlock FontSize="11">
<Run Text="总用户数: "/>
<Run Text="{Binding TotalUsers}" FontWeight="Bold"/>
</TextBlock>
</StackPanel>
</Grid>
</Border>
</Grid>
</ScrollViewer>
</Grid>
</Window>
@@ -0,0 +1,27 @@
using System.Windows;
using Sharp.Demo.ViewModels.Pages;
namespace Sharp.Demo.Views.Windows
{
/// <summary>
/// DatabaseWindow.xaml 的交互逻辑
/// 数据库管理独立窗口 - 演示 Sharp.Data 模块的完整功能
/// </summary>
public partial class DatabaseWindow : Window
{
public DatabaseWindow(DatabaseViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
// 设置当前窗口引用(用于对话框父窗口)
viewModel.CurrentWindow = this;
// 窗口加载完成后加载数据
Loaded += async (s, e) =>
{
await viewModel.OnNavigatedToAsync(null);
};
}
}
}