修改代码
This commit is contained in:
1 parent
f03e2b08f4
commit
3dcb6a1c34
24 files changed
+882
-480
No files matched your search
@@ -3,7 +3,6 @@ namespace Store.Management.Api;
|
||||
public sealed class ApiClientOptions
|
||||
{
|
||||
public required Uri BaseUrl { get; init; }
|
||||
public string Version { get; init; } = "v1";
|
||||
public TimeSpan Timeout { get; init; } = TimeSpan.FromSeconds(30);
|
||||
|
||||
internal void Validate()
|
||||
@@ -12,8 +11,6 @@ public sealed class ApiClientOptions
|
||||
(BaseUrl.Scheme != "https" && BaseUrl.Scheme != "http") ||
|
||||
!string.IsNullOrEmpty(BaseUrl.UserInfo) || !string.IsNullOrEmpty(BaseUrl.Query) || !string.IsNullOrEmpty(BaseUrl.Fragment))
|
||||
throw new ArgumentException("BaseUrl 必须是无凭据、查询参数和片段的 HTTP(S) 绝对地址。");
|
||||
if (!System.Text.RegularExpressions.Regex.IsMatch(Version, @"^v\d+(\.\d+)?$"))
|
||||
throw new ArgumentException("Version 格式应为 v1 或 v1.0。");
|
||||
if (Timeout <= TimeSpan.Zero) throw new ArgumentOutOfRangeException(nameof(Timeout));
|
||||
}
|
||||
}
|
||||
@@ -7,23 +7,26 @@ namespace Store.Management.Api;
|
||||
/// <summary>一个服务地址与一个用户会话对应一个实例;在应用容器中注册为单例。</summary>
|
||||
public sealed class StoreApiClient : IDisposable
|
||||
{
|
||||
public TokenSession Session { get; } = new();
|
||||
public ApiTransport Transport { get; }
|
||||
public AuthApi Auth { get; }
|
||||
public UserApi Users { get; }
|
||||
public TokenSession Session { get; } = new();
|
||||
public ApiTransport Transport { get; }
|
||||
public AuthApi Auth { get; }
|
||||
public UserApi Users { get; }
|
||||
|
||||
/// <param name="httpClient">可选的外部 HTTP 客户端,由调用方管理其生命周期。</param>
|
||||
public StoreApiClient(ApiClientOptions options, HttpClient? httpClient = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(options);
|
||||
Transport = new ApiTransport(options, Session, httpClient);
|
||||
Auth = new AuthApi(Transport, Session);
|
||||
Users = new UserApi(Transport);
|
||||
}
|
||||
/// <param name="httpClient">可选的外部 HTTP 客户端,由调用方管理其生命周期。</param>
|
||||
public StoreApiClient(ApiClientOptions options, HttpClient? httpClient = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(options);
|
||||
#if DEBUG
|
||||
Session.Set("store-local-dev-7f2c9e4a6b814d38a5910e62c4f837bd");
|
||||
#endif
|
||||
Transport = new ApiTransport(options, Session, httpClient);
|
||||
Auth = new AuthApi(Transport, Session);
|
||||
Users = new UserApi(Transport);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Session.Clear();
|
||||
Transport.Dispose();
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
Session.Clear();
|
||||
Transport.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -12,14 +12,12 @@ public sealed class ApiTransport : IDisposable
|
||||
{
|
||||
private readonly RestClient _client;
|
||||
private readonly TokenSession _session;
|
||||
private readonly string _version;
|
||||
private static readonly JsonSerializerOptions Json = new(JsonSerializerDefaults.Web);
|
||||
|
||||
internal ApiTransport(ApiClientOptions options, TokenSession session, HttpClient? httpClient)
|
||||
{
|
||||
options.Validate();
|
||||
_session = session;
|
||||
_version = options.Version;
|
||||
RestClientOptions settings = new RestClientOptions(new Uri(options.BaseUrl.AbsoluteUri.TrimEnd('/') + "/"))
|
||||
{
|
||||
Timeout = options.Timeout,
|
||||
@@ -47,7 +45,7 @@ public sealed class ApiTransport : IDisposable
|
||||
{
|
||||
throw new ApiException(ApiErrorKind.Unauthorized, "请先登录。", code: 401);
|
||||
}
|
||||
RestRequest request = new RestRequest($"{_version}/{route}", method);
|
||||
RestRequest request = new RestRequest($"/{route}", method);
|
||||
if (token is not null)
|
||||
{
|
||||
request.AddHeader("Authorization", $"Bearer {token}");
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Store.Management.Api.Users;
|
||||
public sealed class UserSearchRequest : PageRequest
|
||||
{
|
||||
[StringLength(100)] public string? Keyword { get; set; }
|
||||
public int? GroupId { get; set; }
|
||||
public int? GroupId { get; set; }
|
||||
[StringLength(20)] public string? ParentUserName { get; set; }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,22 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:Store.Management.Framework.Controls"
|
||||
xmlns:ext="clr-namespace:Store.Management.Framework.Controls"
|
||||
xmlns:f="clr-namespace:Store.Management.Framework.Controls.Attach;assembly=Store.Management.Framework"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib">
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ext="clr-namespace:Store.Management.Framework.Controls"
|
||||
xmlns:f="clr-namespace:Store.Management.Framework.Controls.Attach;assembly=Store.Management.Framework"
|
||||
xmlns:local="clr-namespace:Store.Management.Framework.Controls"
|
||||
xmlns:wpf="MAX.WPF"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib">
|
||||
|
||||
|
||||
<sys:Double x:Key="DataGridExFontSize">12</sys:Double>
|
||||
<FontFamily x:Key="DataGridExFontFamily">Microsoft YaHei UI, Segoe UI</FontFamily>
|
||||
<BooleanToVisibilityConverter x:Key="DataGridExVisibility"/>
|
||||
<Style x:Key="DataGridExCheckBoxStyle" TargetType="CheckBox" BasedOn="{StaticResource {x:Type CheckBox}}"/>
|
||||
<Style x:Key="DataGridExDefaultCellHeaderStyle" TargetType="DataGridColumnHeader">
|
||||
<FontFamily x:Key="DataGridExFontFamily">Microsoft YaHei UI, Segoe UI</FontFamily>
|
||||
<wpf:ThicknessSplitConverter x:Key="DataGridExBorderThicknessSplit" />
|
||||
<BooleanToVisibilityConverter x:Key="DataGridExVisibility" />
|
||||
<Style
|
||||
x:Key="DataGridExCheckBoxStyle"
|
||||
BasedOn="{StaticResource {x:Type CheckBox}}"
|
||||
TargetType="CheckBox" />
|
||||
<Style x:Key="DataGridExDefaultCellHeaderStyle" TargetType="DataGridColumnHeader">
|
||||
<Setter Property="UseLayoutRounding" Value="True" />
|
||||
<!--<Setter Property="f:DataGridHelper.ShowBorder" Value="True" />-->
|
||||
|
||||
@@ -240,7 +245,8 @@
|
||||
Grid.Column="1"
|
||||
SelectiveScrollingGrid.SelectiveScrollingOrientation="{Binding AreRowDetailsFrozen, ConverterParameter={x:Static SelectiveScrollingOrientation.Vertical}, Converter={x:Static DataGrid.RowDetailsScrollingConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
|
||||
Visibility="{TemplateBinding DetailsVisibility}" />
|
||||
<DataGridRowHeader x:Name="PART_RowHeader"
|
||||
<DataGridRowHeader
|
||||
x:Name="PART_RowHeader"
|
||||
Grid.RowSpan="2"
|
||||
SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical"
|
||||
Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.Row}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
|
||||
@@ -355,84 +361,143 @@
|
||||
</Style>
|
||||
|
||||
<Style x:Key="DataGridExDefaultDataGridStyle" TargetType="local:DataGridEx">
|
||||
<Style.Resources>
|
||||
<Style x:Key="DataGridExColumnMenu" TargetType="ContextMenu">
|
||||
<Setter Property="Width" Value="240"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="Foreground" Value="#343A40"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ContextMenu">
|
||||
<Border Background="White" BorderBrush="#DDE0E5" BorderThickness="1" CornerRadius="6" Padding="0">
|
||||
<StackPanel KeyboardNavigation.DirectionalNavigation="Cycle">
|
||||
<StackPanel Margin="14,12,14,8">
|
||||
<TextBlock Text="显示的列" FontSize="14" FontWeight="SemiBold"/>
|
||||
<TextBlock Text="勾选后立即生效" Foreground="#858A94" Margin="0,4,0,0"/>
|
||||
</StackPanel>
|
||||
<ScrollViewer MaxHeight="340" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" Margin="6,0,6,8">
|
||||
<ItemsPresenter KeyboardNavigation.DirectionalNavigation="Cycle"/>
|
||||
</ScrollViewer>
|
||||
<Border BorderBrush="#ECEEF1" BorderThickness="0,1,0,0" Padding="12,9" Background="#FAFAFB">
|
||||
<Button x:Name="PART_DoneButton" Content="完成" HorizontalAlignment="Right" Width="64" Height="28"
|
||||
Command="{x:Static ApplicationCommands.Close}"
|
||||
Foreground="White" Background="#98638F" BorderThickness="0">
|
||||
<Button.Template>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border x:Name="ButtonBorder" Background="{TemplateBinding Background}" CornerRadius="4">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True"><Setter TargetName="ButtonBorder" Property="Opacity" Value="0.85"/></Trigger>
|
||||
<Trigger Property="IsKeyboardFocused" Value="True"><Setter TargetName="ButtonBorder" Property="BorderBrush" Value="#343A40"/><Setter TargetName="ButtonBorder" Property="BorderThickness" Value="1"/></Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Button.Template>
|
||||
</Button>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="ItemContainerStyle">
|
||||
<Setter.Value>
|
||||
<Style TargetType="MenuItem">
|
||||
<Setter Property="Height" Value="30"/>
|
||||
<Setter Property="Padding" Value="8,0"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="MenuItem">
|
||||
<Border x:Name="ItemBorder" Background="Transparent" CornerRadius="4" Padding="{TemplateBinding Padding}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions><ColumnDefinition Width="24"/><ColumnDefinition Width="*"/></Grid.ColumnDefinitions>
|
||||
<Border x:Name="CheckBorder" Width="14" Height="14" HorizontalAlignment="Left" VerticalAlignment="Center"
|
||||
BorderBrush="#C8CDD4" BorderThickness="1" CornerRadius="3" Background="White">
|
||||
<Path x:Name="CheckMark" Data="M 2,6 L 5,9 L 11,3" Stroke="White" StrokeThickness="1.5" Visibility="Collapsed"/>
|
||||
</Border>
|
||||
<ContentPresenter Grid.Column="1" ContentSource="Header" VerticalAlignment="Center"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsHighlighted" Value="True"><Setter TargetName="ItemBorder" Property="Background" Value="#F3F4F6"/></Trigger>
|
||||
<Trigger Property="IsChecked" Value="True">
|
||||
<Setter TargetName="CheckBorder" Property="Background" Value="#98638F"/>
|
||||
<Setter TargetName="CheckBorder" Property="BorderBrush" Value="#98638F"/>
|
||||
<Setter TargetName="CheckMark" Property="Visibility" Value="Visible"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Foreground" Value="#8A8F98"/>
|
||||
<Setter TargetName="CheckBorder" Property="Background" Value="#D5D8DE"/>
|
||||
<Setter TargetName="CheckBorder" Property="BorderBrush" Value="#D5D8DE"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Style.Resources>
|
||||
<Style.Resources>
|
||||
<Style x:Key="DataGridExColumnMenu" TargetType="ContextMenu">
|
||||
<Setter Property="Width" Value="240" />
|
||||
<Setter Property="FontSize" Value="12" />
|
||||
<Setter Property="Foreground" Value="#343A40" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ContextMenu">
|
||||
<Border
|
||||
Padding="0"
|
||||
Background="White"
|
||||
BorderBrush="#DDE0E5"
|
||||
BorderThickness="1"
|
||||
CornerRadius="6">
|
||||
<StackPanel KeyboardNavigation.DirectionalNavigation="Cycle">
|
||||
<StackPanel Margin="14,12,14,8">
|
||||
<TextBlock
|
||||
FontSize="14"
|
||||
FontWeight="SemiBold"
|
||||
Text="显示的列" />
|
||||
<TextBlock
|
||||
Margin="0,4,0,0"
|
||||
Foreground="#858A94"
|
||||
Text="勾选后立即生效" />
|
||||
</StackPanel>
|
||||
<ScrollViewer
|
||||
MaxHeight="340"
|
||||
Margin="6,0,6,8"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<ItemsPresenter KeyboardNavigation.DirectionalNavigation="Cycle" />
|
||||
</ScrollViewer>
|
||||
<Border
|
||||
Padding="12,9"
|
||||
Background="#FAFAFB"
|
||||
BorderBrush="#ECEEF1"
|
||||
BorderThickness="0,1,0,0">
|
||||
<Button
|
||||
x:Name="PART_DoneButton"
|
||||
Width="64"
|
||||
Height="28"
|
||||
HorizontalAlignment="Right"
|
||||
Background="#98638F"
|
||||
BorderThickness="0"
|
||||
Command="{x:Static ApplicationCommands.Close}"
|
||||
Content="完成"
|
||||
Foreground="White">
|
||||
<Button.Template>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border
|
||||
x:Name="ButtonBorder"
|
||||
Background="{TemplateBinding Background}"
|
||||
CornerRadius="4">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="ButtonBorder" Property="Opacity" Value="0.85" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsKeyboardFocused" Value="True">
|
||||
<Setter TargetName="ButtonBorder" Property="BorderBrush" Value="#343A40" />
|
||||
<Setter TargetName="ButtonBorder" Property="BorderThickness" Value="1" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Button.Template>
|
||||
</Button>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="ItemContainerStyle">
|
||||
<Setter.Value>
|
||||
<Style TargetType="MenuItem">
|
||||
<Setter Property="Height" Value="30" />
|
||||
<Setter Property="Padding" Value="8,0" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="MenuItem">
|
||||
<Border
|
||||
x:Name="ItemBorder"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
Background="Transparent"
|
||||
CornerRadius="4">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="24" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border
|
||||
x:Name="CheckBorder"
|
||||
Width="14"
|
||||
Height="14"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Background="White"
|
||||
BorderBrush="#C8CDD4"
|
||||
BorderThickness="1"
|
||||
CornerRadius="3">
|
||||
<Path
|
||||
x:Name="CheckMark"
|
||||
Data="M 2,6 L 5,9 L 11,3"
|
||||
Stroke="White"
|
||||
StrokeThickness="1.5"
|
||||
Visibility="Collapsed" />
|
||||
</Border>
|
||||
<ContentPresenter
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
ContentSource="Header" />
|
||||
</Grid>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsHighlighted" Value="True">
|
||||
<Setter TargetName="ItemBorder" Property="Background" Value="#F3F4F6" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsChecked" Value="True">
|
||||
<Setter TargetName="CheckBorder" Property="Background" Value="#98638F" />
|
||||
<Setter TargetName="CheckBorder" Property="BorderBrush" Value="#98638F" />
|
||||
<Setter TargetName="CheckMark" Property="Visibility" Value="Visible" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Foreground" Value="#8A8F98" />
|
||||
<Setter TargetName="CheckBorder" Property="Background" Value="#D5D8DE" />
|
||||
<Setter TargetName="CheckBorder" Property="BorderBrush" Value="#D5D8DE" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Style.Resources>
|
||||
<Setter Property="UseLayoutRounding" Value="True" />
|
||||
<Setter Property="SnapsToDevicePixels" Value="True" />
|
||||
<Setter Property="Background" Value="White" />
|
||||
@@ -449,7 +514,7 @@
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
<Setter Property="Foreground" Value="#343A40" />
|
||||
<Setter Property="BorderBrush" Value="#E1E4E8" />
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="VerticalGridLinesBrush" Value="#E1E4E8" />
|
||||
<Setter Property="HorizontalGridLinesBrush" Value="#E1E4E8" />
|
||||
<Setter Property="RowDetailsVisibilityMode" Value="VisibleWhenSelected" />
|
||||
@@ -466,194 +531,263 @@
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type local:DataGridEx}">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions><RowDefinition Height="Auto"/><RowDefinition Height="*"/><RowDefinition Height="Auto"/></Grid.RowDefinitions>
|
||||
<!-- 调用方可以提供批量操作内容,默认展示选择提示和列设置。 -->
|
||||
<Border x:Name="PART_ToolbarArea" Grid.Row="0" Background="#FAFAFA" BorderBrush="#E1E4E8" BorderThickness="1,1,1,0" Padding="8,5"
|
||||
Visibility="{Binding ShowToolbar, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay, Converter={StaticResource DataGridExVisibility}}">
|
||||
<DockPanel LastChildFill="True">
|
||||
<Button x:Name="PART_ColumnsButton" DockPanel.Dock="Right" Content="列设置" Padding="10,3" MinHeight="26"
|
||||
Visibility="{Binding ShowColumnSettings, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay, Converter={StaticResource DataGridExVisibility}}"/>
|
||||
<TextBlock DockPanel.Dock="Left" VerticalAlignment="Center" Margin="0,0,12,0"
|
||||
Text="{Binding SelectionSummary, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"/>
|
||||
<ContentPresenter Content="{TemplateBinding ToolbarContent}" VerticalAlignment="Center"/>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
<!-- 原生 DataGrid 滚动与虚拟化宿主。 -->
|
||||
<Border x:Name="PART_TableArea" Grid.Row="1"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
SnapsToDevicePixels="True"
|
||||
UseLayoutRounding="True">
|
||||
<ScrollViewer x:Name="DG_ScrollViewer" Focusable="false">
|
||||
<ScrollViewer.Template>
|
||||
<ControlTemplate TargetType="{x:Type ScrollViewer}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Border
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Width="{Binding CellsPanelHorizontalOffset, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
|
||||
Background="#eeeeee"
|
||||
BorderBrush="{Binding BorderBrush, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
|
||||
BorderThickness="0,0,1,1">
|
||||
<CheckBox
|
||||
Width="16"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalContentAlignment="Center"
|
||||
AutomationProperties.Name="全选行"
|
||||
Command="{x:Static ext:DataGridEx.ToggleSelectAllCommand}"
|
||||
CommandTarget="{Binding RelativeSource={RelativeSource AncestorType={x:Type ext:DataGridEx}}}"
|
||||
IsChecked="{Binding AreAllRowsSelected, RelativeSource={RelativeSource AncestorType={x:Type ext:DataGridEx}}, Mode=OneWay}">
|
||||
<CheckBox.Style>
|
||||
<Style TargetType="CheckBox" BasedOn="{StaticResource DataGridExCheckBoxStyle}">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
<Style.Triggers>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding ShowCheckBox, RelativeSource={RelativeSource AncestorType={x:Type ext:DataGridEx}}}" Value="True" />
|
||||
<Condition Binding="{Binding ShowSelectAll, RelativeSource={RelativeSource AncestorType={x:Type ext:DataGridEx}}}" Value="True" />
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</MultiDataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</CheckBox.Style>
|
||||
</CheckBox>
|
||||
</Border>
|
||||
|
||||
<DataGridColumnHeadersPresenter
|
||||
x:Name="PART_ColumnHeadersPresenter"
|
||||
Grid.Column="1"
|
||||
Grid.ColumnSpan="2"
|
||||
Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.Column}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
|
||||
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2">
|
||||
<ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanContentScroll="{TemplateBinding CanContentScroll}" />
|
||||
<Grid
|
||||
x:Name="NoDataTipsGrid"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
IsHitTestVisible="False"
|
||||
Visibility="Collapsed">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="60" />
|
||||
<RowDefinition Height="auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Path
|
||||
Width="100"
|
||||
Height="60"
|
||||
Data="M156.662278 699.758173h21.097186A10.444152 10.444152 0 0 1 187.994733 710.202325c0 5.765172-4.490985 10.444152-10.235269 10.444152H156.662278v21.097186A10.444152 10.444152 0 0 1 146.218126 751.978932a10.277045 10.277045 0 0 1-10.444152-10.235269V720.646477H114.676787A10.444152 10.444152 0 0 1 104.441518 710.202325c0-5.765172 4.490985-10.444152 10.235269-10.444152H135.773974v-21.097187A10.444152 10.444152 0 0 1 146.218126 668.425717c5.765172 0 10.444152 4.490985 10.444152 10.235269v21.097187z m1378.628042-83.553215v-21.097186A10.277045 10.277045 0 0 0 1524.846168 584.872503a10.444152 10.444152 0 0 0-10.444152 10.235269v21.097186h-21.097186a10.277045 10.277045 0 0 0-10.235269 10.444152c0 5.598065 4.595427 10.444152 10.235269 10.444152h21.097186v21.097187c0 5.744284 4.67898 10.235269 10.444152 10.235268a10.444152 10.444152 0 0 0 10.444152-10.235268V637.093262h21.097187c5.744284 0 10.235269-4.67898 10.235268-10.444152a10.444152 10.444152 0 0 0-10.235268-10.444152H1535.29032zM776.460024 960.861969H250.596979A20.80475 20.80475 0 0 1 229.77134 939.973665c0-11.530344 9.462402-20.888304 20.825639-20.888303h94.728457A83.010119 83.010119 0 0 1 334.212859 877.413196v-605.96969A83.49055 83.49055 0 0 1 417.849627 187.994733H480.430984V167.001988A83.49055 83.49055 0 0 1 564.067752 83.553215h501.152182A83.448773 83.448773 0 0 1 1148.856702 167.001988v605.969689c0 15.185797-4.052331 29.410732-11.133466 41.672166h115.554096c11.551232 0 20.909192 9.274407 20.909192 20.888304 0 11.530344-9.295295 20.888304-20.888304 20.888304H1002.638576v20.992745c0 15.185797-4.052331 29.410732-11.133466 41.672166h11.196131c11.488567 0 20.825639 9.274407 20.825639 20.888303 0 11.530344-9.462402 20.888304-20.825639 20.888304h-109.893365c9.545955 16.000441 7.478013 36.972297-6.41271 50.863019a41.672166 41.672166 0 0 1-59.072122 0L776.460024 960.861969z m76.367638-41.776607h66.424806c22.977134 0 41.609501-18.59059 41.609501-41.881049V270.461756c0-22.559368-18.047494-40.690416-40.314426-40.690416H416.303892c-22.266932 0-40.314426 18.214601-40.314426 40.690416v606.742557c0 23.123352 18.799473 41.881049 41.588613 41.881049h317.084449l-10.736588-10.757477a41.693054 41.693054 0 0 1-10.861918-40.377091l-19.718558-19.739447A146.259902 146.259902 0 0 1 502.363703 627.693525a146.218126 146.218126 0 0 1 220.517822 190.981761l19.739447 19.739447a41.630389 41.630389 0 0 1 40.377091 10.841029L852.827662 919.085362zM1002.638576 814.643843h62.852906A41.797496 41.797496 0 0 0 1107.080095 772.867236V167.106429c0-23.14424-18.632367-41.776607-41.588613-41.776607H563.775316A41.797496 41.797496 0 0 0 522.207592 167.106429v20.888304h396.794216A83.448773 83.448773 0 0 1 1002.638576 271.443506V814.643843zM266.325872 46.998683h31.123572c8.773088 0 15.875111 6.955805 15.875111 15.666228 0 8.647758-7.102023 15.666228-15.875111 15.666228h-31.123572v31.123572c0 8.773088-6.955805 15.875111-15.666228 15.875111a15.770669 15.770669 0 0 1-15.666228-15.875111V78.331139H203.869844A15.728893 15.728893 0 0 1 187.994733 62.664911c0-8.647758 7.102023-15.666228 15.875111-15.666228h31.123572V15.875111c0-8.773088 6.955805-15.875111 15.666228-15.875111 8.647758 0 15.666228 7.102023 15.666228 15.875111v31.123572zM20.888304 939.973665c0-11.530344 9.462402-20.888304 20.825638-20.888303h125.455152c11.488567 0 20.825639 9.274407 20.825639 20.888303 0 11.530344-9.462402 20.888304-20.825639 20.888304H41.713942A20.80475 20.80475 0 0 1 20.888304 939.973665z m658.733544-135.021995a104.441518 104.441518 0 1 0-147.722083-147.722083 104.441518 104.441518 0 0 0 147.722083 147.722083zM459.542681 313.324555a20.888304 20.888304 0 0 1 20.867415-20.888304H710.202325a20.888304 20.888304 0 1 1 0 41.776608H480.430984A20.825639 20.825639 0 0 1 459.542681 313.324555z m0 104.441518c0-11.530344 9.295295-20.888304 20.742085-20.888303h334.505295c11.44679 0 20.742086 9.274407 20.742086 20.888303 0 11.530344-9.295295 20.888304-20.742086 20.888304H480.284766A20.762974 20.762974 0 0 1 459.542681 417.766073z m0 104.441519c0-11.530344 9.316183-20.888304 20.846527-20.888304h146.301679c11.509455 0 20.846527 9.274407 20.846527 20.888304 0 11.530344-9.316183 20.888304-20.846527 20.888303h-146.301679A20.80475 20.80475 0 0 1 459.542681 522.207592zM62.664911 396.87777a62.664911 62.664911 0 1 1 0-125.329822 62.664911 62.664911 0 0 1 0 125.329822z m0-31.332456a31.332456 31.332456 0 1 0 0-62.664911 31.332456 31.332456 0 0 0 0 62.664911zM1357.739739 271.547948a62.664911 62.664911 0 1 1 0-125.329822 62.664911 62.664911 0 0 1 0 125.329822z m0-31.332456a31.332456 31.332456 0 1 0 0-62.664911 31.332456 31.332456 0 0 0 0 62.664911z"
|
||||
Fill="LightGray"
|
||||
Stretch="Fill" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="10"
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<!-- 调用方可以提供批量操作内容,默认展示选择提示和列设置。 -->
|
||||
<Border
|
||||
x:Name="PART_ToolbarArea"
|
||||
Grid.Row="0"
|
||||
Padding="8,5"
|
||||
Background="#FAFAFA"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{Binding BorderThickness, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource DataGridExBorderThicknessSplit}, ConverterParameter='1,1,1,0'}"
|
||||
Visibility="{Binding ShowToolbar, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay, Converter={StaticResource DataGridExVisibility}}">
|
||||
<DockPanel LastChildFill="True">
|
||||
<Button
|
||||
x:Name="PART_ColumnsButton"
|
||||
MinHeight="26"
|
||||
Padding="10,3"
|
||||
Content="列设置"
|
||||
DockPanel.Dock="Right"
|
||||
Visibility="{Binding ShowColumnSettings, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay, Converter={StaticResource DataGridExVisibility}}" />
|
||||
<TextBlock
|
||||
Margin="0,0,12,0"
|
||||
VerticalAlignment="Center"
|
||||
DockPanel.Dock="Left"
|
||||
Text="{Binding SelectionSummary, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}" />
|
||||
<ContentPresenter VerticalAlignment="Center" Content="{TemplateBinding ToolbarContent}" />
|
||||
</DockPanel>
|
||||
</Border>
|
||||
<!-- 原生 DataGrid 滚动与虚拟化宿主。 -->
|
||||
<Border
|
||||
x:Name="PART_TableArea"
|
||||
Grid.Row="1"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
SnapsToDevicePixels="True"
|
||||
UseLayoutRounding="True">
|
||||
<ScrollViewer x:Name="DG_ScrollViewer" Focusable="false">
|
||||
<ScrollViewer.Template>
|
||||
<ControlTemplate TargetType="{x:Type ScrollViewer}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Border
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Width="{Binding CellsPanelHorizontalOffset, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
|
||||
Background="#eeeeee"
|
||||
BorderBrush="{Binding BorderBrush, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
|
||||
BorderThickness="0,0,1,1">
|
||||
<CheckBox
|
||||
Width="16"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Bottom"
|
||||
FontSize="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=FontSize, Mode=OneWay}"
|
||||
Foreground="LightGray"
|
||||
Text="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=(f:DataGridHelper.NoDataTips), Mode=OneWay}" />
|
||||
VerticalAlignment="Center"
|
||||
HorizontalContentAlignment="Center"
|
||||
AutomationProperties.Name="全选行"
|
||||
Command="{x:Static ext:DataGridEx.ToggleSelectAllCommand}"
|
||||
CommandTarget="{Binding RelativeSource={RelativeSource AncestorType={x:Type ext:DataGridEx}}}"
|
||||
IsChecked="{Binding AreAllRowsSelected, RelativeSource={RelativeSource AncestorType={x:Type ext:DataGridEx}}, Mode=OneWay}">
|
||||
<CheckBox.Style>
|
||||
<Style BasedOn="{StaticResource DataGridExCheckBoxStyle}" TargetType="CheckBox">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
<Style.Triggers>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding ShowCheckBox, RelativeSource={RelativeSource AncestorType={x:Type ext:DataGridEx}}}" Value="True" />
|
||||
<Condition Binding="{Binding ShowSelectAll, RelativeSource={RelativeSource AncestorType={x:Type ext:DataGridEx}}}" Value="True" />
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</MultiDataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</CheckBox.Style>
|
||||
</CheckBox>
|
||||
</Border>
|
||||
|
||||
<DataGridColumnHeadersPresenter
|
||||
x:Name="PART_ColumnHeadersPresenter"
|
||||
Grid.Column="1"
|
||||
Grid.ColumnSpan="2"
|
||||
Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.Column}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
|
||||
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2">
|
||||
<ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanContentScroll="{TemplateBinding CanContentScroll}" />
|
||||
<Grid
|
||||
x:Name="NoDataTipsGrid"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
IsHitTestVisible="False"
|
||||
Visibility="Collapsed">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="60" />
|
||||
<RowDefinition Height="auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Path
|
||||
Width="100"
|
||||
Height="60"
|
||||
Data="M156.662278 699.758173h21.097186A10.444152 10.444152 0 0 1 187.994733 710.202325c0 5.765172-4.490985 10.444152-10.235269 10.444152H156.662278v21.097186A10.444152 10.444152 0 0 1 146.218126 751.978932a10.277045 10.277045 0 0 1-10.444152-10.235269V720.646477H114.676787A10.444152 10.444152 0 0 1 104.441518 710.202325c0-5.765172 4.490985-10.444152 10.235269-10.444152H135.773974v-21.097187A10.444152 10.444152 0 0 1 146.218126 668.425717c5.765172 0 10.444152 4.490985 10.444152 10.235269v21.097187z m1378.628042-83.553215v-21.097186A10.277045 10.277045 0 0 0 1524.846168 584.872503a10.444152 10.444152 0 0 0-10.444152 10.235269v21.097186h-21.097186a10.277045 10.277045 0 0 0-10.235269 10.444152c0 5.598065 4.595427 10.444152 10.235269 10.444152h21.097186v21.097187c0 5.744284 4.67898 10.235269 10.444152 10.235268a10.444152 10.444152 0 0 0 10.444152-10.235268V637.093262h21.097187c5.744284 0 10.235269-4.67898 10.235268-10.444152a10.444152 10.444152 0 0 0-10.235268-10.444152H1535.29032zM776.460024 960.861969H250.596979A20.80475 20.80475 0 0 1 229.77134 939.973665c0-11.530344 9.462402-20.888304 20.825639-20.888303h94.728457A83.010119 83.010119 0 0 1 334.212859 877.413196v-605.96969A83.49055 83.49055 0 0 1 417.849627 187.994733H480.430984V167.001988A83.49055 83.49055 0 0 1 564.067752 83.553215h501.152182A83.448773 83.448773 0 0 1 1148.856702 167.001988v605.969689c0 15.185797-4.052331 29.410732-11.133466 41.672166h115.554096c11.551232 0 20.909192 9.274407 20.909192 20.888304 0 11.530344-9.295295 20.888304-20.888304 20.888304H1002.638576v20.992745c0 15.185797-4.052331 29.410732-11.133466 41.672166h11.196131c11.488567 0 20.825639 9.274407 20.825639 20.888303 0 11.530344-9.462402 20.888304-20.825639 20.888304h-109.893365c9.545955 16.000441 7.478013 36.972297-6.41271 50.863019a41.672166 41.672166 0 0 1-59.072122 0L776.460024 960.861969z m76.367638-41.776607h66.424806c22.977134 0 41.609501-18.59059 41.609501-41.881049V270.461756c0-22.559368-18.047494-40.690416-40.314426-40.690416H416.303892c-22.266932 0-40.314426 18.214601-40.314426 40.690416v606.742557c0 23.123352 18.799473 41.881049 41.588613 41.881049h317.084449l-10.736588-10.757477a41.693054 41.693054 0 0 1-10.861918-40.377091l-19.718558-19.739447A146.259902 146.259902 0 0 1 502.363703 627.693525a146.218126 146.218126 0 0 1 220.517822 190.981761l19.739447 19.739447a41.630389 41.630389 0 0 1 40.377091 10.841029L852.827662 919.085362zM1002.638576 814.643843h62.852906A41.797496 41.797496 0 0 0 1107.080095 772.867236V167.106429c0-23.14424-18.632367-41.776607-41.588613-41.776607H563.775316A41.797496 41.797496 0 0 0 522.207592 167.106429v20.888304h396.794216A83.448773 83.448773 0 0 1 1002.638576 271.443506V814.643843zM266.325872 46.998683h31.123572c8.773088 0 15.875111 6.955805 15.875111 15.666228 0 8.647758-7.102023 15.666228-15.875111 15.666228h-31.123572v31.123572c0 8.773088-6.955805 15.875111-15.666228 15.875111a15.770669 15.770669 0 0 1-15.666228-15.875111V78.331139H203.869844A15.728893 15.728893 0 0 1 187.994733 62.664911c0-8.647758 7.102023-15.666228 15.875111-15.666228h31.123572V15.875111c0-8.773088 6.955805-15.875111 15.666228-15.875111 8.647758 0 15.666228 7.102023 15.666228 15.875111v31.123572zM20.888304 939.973665c0-11.530344 9.462402-20.888304 20.825638-20.888303h125.455152c11.488567 0 20.825639 9.274407 20.825639 20.888303 0 11.530344-9.462402 20.888304-20.825639 20.888304H41.713942A20.80475 20.80475 0 0 1 20.888304 939.973665z m658.733544-135.021995a104.441518 104.441518 0 1 0-147.722083-147.722083 104.441518 104.441518 0 0 0 147.722083 147.722083zM459.542681 313.324555a20.888304 20.888304 0 0 1 20.867415-20.888304H710.202325a20.888304 20.888304 0 1 1 0 41.776608H480.430984A20.825639 20.825639 0 0 1 459.542681 313.324555z m0 104.441518c0-11.530344 9.295295-20.888304 20.742085-20.888303h334.505295c11.44679 0 20.742086 9.274407 20.742086 20.888303 0 11.530344-9.295295 20.888304-20.742086 20.888304H480.284766A20.762974 20.762974 0 0 1 459.542681 417.766073z m0 104.441519c0-11.530344 9.316183-20.888304 20.846527-20.888304h146.301679c11.509455 0 20.846527 9.274407 20.846527 20.888304 0 11.530344-9.316183 20.888304-20.846527 20.888303h-146.301679A20.80475 20.80475 0 0 1 459.542681 522.207592zM62.664911 396.87777a62.664911 62.664911 0 1 1 0-125.329822 62.664911 62.664911 0 0 1 0 125.329822z m0-31.332456a31.332456 31.332456 0 1 0 0-62.664911 31.332456 31.332456 0 0 0 0 62.664911zM1357.739739 271.547948a62.664911 62.664911 0 1 1 0-125.329822 62.664911 62.664911 0 0 1 0 125.329822z m0-31.332456a31.332456 31.332456 0 1 0 0-62.664911 31.332456 31.332456 0 0 0 0 62.664911z"
|
||||
Fill="LightGray"
|
||||
Stretch="Fill" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="10"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Bottom"
|
||||
FontSize="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=FontSize, Mode=OneWay}"
|
||||
Foreground="LightGray"
|
||||
Text="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=(f:DataGridHelper.NoDataTips), Mode=OneWay}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
<ScrollBar
|
||||
x:Name="PART_VerticalScrollBar"
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Maximum="{TemplateBinding ScrollableHeight}"
|
||||
Orientation="Vertical"
|
||||
ViewportSize="{TemplateBinding ViewportHeight}"
|
||||
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"
|
||||
Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" />
|
||||
<Grid
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2">
|
||||
<ScrollBar
|
||||
x:Name="PART_HorizontalScrollBar"
|
||||
Maximum="{TemplateBinding ScrollableWidth}"
|
||||
Orientation="Horizontal"
|
||||
ViewportSize="{TemplateBinding ViewportWidth}"
|
||||
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"
|
||||
Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
<ScrollBar
|
||||
x:Name="PART_VerticalScrollBar"
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Maximum="{TemplateBinding ScrollableHeight}"
|
||||
Orientation="Vertical"
|
||||
|
||||
ViewportSize="{TemplateBinding ViewportHeight}"
|
||||
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"
|
||||
Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" />
|
||||
<Grid Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2">
|
||||
<ScrollBar
|
||||
x:Name="PART_HorizontalScrollBar"
|
||||
Maximum="{TemplateBinding ScrollableWidth}"
|
||||
Orientation="Horizontal"
|
||||
|
||||
ViewportSize="{TemplateBinding ViewportWidth}"
|
||||
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"
|
||||
Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=(f:DataGridHelper.IsActive)}" Value="false" />
|
||||
<Condition Binding="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=HasItems}" Value="false" />
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter TargetName="NoDataTipsGrid" Property="Visibility" Value="Visible" />
|
||||
</MultiDataTrigger>
|
||||
<!--<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=(f:DataGridHelper.NoDataTips)}" Value="{x:Null}">
|
||||
<ControlTemplate.Triggers>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=(f:DataGridHelper.IsActive)}" Value="false" />
|
||||
<Condition Binding="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=HasItems}" Value="false" />
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter TargetName="NoDataTipsGrid" Property="Visibility" Value="Visible" />
|
||||
</MultiDataTrigger>
|
||||
<!--<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=(f:DataGridHelper.NoDataTips)}" Value="{x:Null}">
|
||||
<Setter TargetName="NoDataTipsGrid" Property="Visibility" Value="Collapsed" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=(f:DataGridHelper.NoDataTips)}" Value="">
|
||||
<Setter TargetName="NoDataTipsGrid" Property="Visibility" Value="Collapsed" />
|
||||
</DataTrigger>-->
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</ScrollViewer.Template>
|
||||
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
<!-- 分页仅请求数据,不截断调用方的 ItemsSource。 -->
|
||||
<Border x:Name="PART_PaginationArea" Grid.Row="2" Background="#FAFAFA" BorderBrush="#E1E4E8" BorderThickness="1,0,1,1" Padding="8,4"
|
||||
Visibility="{Binding ShowPagination, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay, Converter={StaticResource DataGridExVisibility}}">
|
||||
<Grid>
|
||||
<ContentPresenter x:Name="PART_PaginationContent" Content="{TemplateBinding PaginationContent}"/>
|
||||
<DockPanel x:Name="PART_DefaultPagination" Visibility="Collapsed">
|
||||
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal">
|
||||
<TextBlock Text="每页" VerticalAlignment="Center" Margin="0,0,6,0"/>
|
||||
<ComboBox Width="70" MinHeight="26" IsEditable="False" Margin="0,0,8,0"
|
||||
ItemsSource="{Binding PageSizes, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
|
||||
SelectedItem="{Binding PageSize, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
<Button Content="首页" Command="{x:Static local:DataGridEx.FirstPageCommand}" Padding="8,2" Margin="0,0,4,0"/>
|
||||
<Button Content="上一页" Command="{x:Static local:DataGridEx.PreviousPageCommand}" Padding="8,2"/>
|
||||
<TextBlock Text="{Binding PageSummary, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}" VerticalAlignment="Center" Margin="10,0"/>
|
||||
<Button Content="下一页" Command="{x:Static local:DataGridEx.NextPageCommand}" Padding="8,2"/>
|
||||
<Button Content="末页" Command="{x:Static local:DataGridEx.LastPageCommand}" Padding="8,2" Margin="4,0,0,0"/>
|
||||
</StackPanel>
|
||||
<TextBlock Text="{Binding TotalSummary, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}" VerticalAlignment="Center"/>
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
<Border x:Name="PART_LoadingOverlay" Grid.RowSpan="3" Background="#DDFFFFFF" Visibility="Collapsed" Cursor="Wait">
|
||||
<StackPanel Width="180" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<ProgressBar x:Name="PART_LoadingProgress" Height="4" IsIndeterminate="False" Foreground="#98638F" Background="#EDE5EB" BorderThickness="0"/>
|
||||
<TextBlock Text="{TemplateBinding LoadingText}" HorizontalAlignment="Center" Margin="0,12,0,0" Foreground="#626873"
|
||||
AutomationProperties.LiveSetting="Polite"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsLoading" Value="True">
|
||||
<Setter TargetName="PART_LoadingOverlay" Property="Visibility" Value="Visible"/>
|
||||
<Setter TargetName="PART_LoadingProgress" Property="IsIndeterminate" Value="True"/>
|
||||
<Setter TargetName="PART_ToolbarArea" Property="IsEnabled" Value="False"/>
|
||||
<Setter TargetName="PART_TableArea" Property="IsEnabled" Value="False"/>
|
||||
<Setter TargetName="PART_PaginationArea" Property="IsEnabled" Value="False"/>
|
||||
</Trigger>
|
||||
<Trigger Property="PaginationContent" Value="{x:Null}">
|
||||
<Setter TargetName="PART_DefaultPagination" Property="Visibility" Value="Visible"/>
|
||||
<Setter TargetName="PART_PaginationContent" Property="Visibility" Value="Collapsed"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</ScrollViewer.Template>
|
||||
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
<!-- 分页仅请求数据,不截断调用方的 ItemsSource。 -->
|
||||
<Border
|
||||
x:Name="PART_PaginationArea"
|
||||
Grid.Row="2"
|
||||
Padding="8,4"
|
||||
Background="#FAFAFA"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{Binding BorderThickness, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource DataGridExBorderThicknessSplit}, ConverterParameter='1,0,1,1'}"
|
||||
Visibility="{Binding ShowPagination, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay, Converter={StaticResource DataGridExVisibility}}">
|
||||
<Grid>
|
||||
<ContentPresenter x:Name="PART_PaginationContent" Content="{TemplateBinding PaginationContent}" />
|
||||
<DockPanel x:Name="PART_DefaultPagination" Visibility="Collapsed">
|
||||
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,6,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="每页" />
|
||||
<ComboBox
|
||||
Width="70"
|
||||
MinHeight="26"
|
||||
Margin="0,0,8,0"
|
||||
IsEditable="False"
|
||||
ItemsSource="{Binding PageSizes, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
|
||||
SelectedItem="{Binding PageSize, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<Button
|
||||
Margin="0,0,4,0"
|
||||
Padding="8,2"
|
||||
Command="{x:Static local:DataGridEx.FirstPageCommand}"
|
||||
Content="首页" />
|
||||
<Button
|
||||
Padding="8,2"
|
||||
Command="{x:Static local:DataGridEx.PreviousPageCommand}"
|
||||
Content="上一页" />
|
||||
<TextBlock
|
||||
Margin="10,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding PageSummary, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}" />
|
||||
<Button
|
||||
Padding="8,2"
|
||||
Command="{x:Static local:DataGridEx.NextPageCommand}"
|
||||
Content="下一页" />
|
||||
<Button
|
||||
Margin="4,0,0,0"
|
||||
Padding="8,2"
|
||||
Command="{x:Static local:DataGridEx.LastPageCommand}"
|
||||
Content="末页" />
|
||||
</StackPanel>
|
||||
<TextBlock VerticalAlignment="Center" Text="{Binding TotalSummary, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}" />
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
<Border
|
||||
x:Name="PART_LoadingOverlay"
|
||||
Grid.RowSpan="3"
|
||||
Background="#DDFFFFFF"
|
||||
Cursor="Wait"
|
||||
Visibility="Collapsed">
|
||||
<StackPanel
|
||||
Width="180"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center">
|
||||
<ProgressBar
|
||||
x:Name="PART_LoadingProgress"
|
||||
Height="4"
|
||||
Background="#EDE5EB"
|
||||
BorderThickness="0"
|
||||
Foreground="#98638F"
|
||||
IsIndeterminate="False" />
|
||||
<TextBlock
|
||||
Margin="0,12,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
AutomationProperties.LiveSetting="Polite"
|
||||
Foreground="#626873"
|
||||
Text="{TemplateBinding LoadingText}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsLoading" Value="True">
|
||||
<Setter TargetName="PART_LoadingOverlay" Property="Visibility" Value="Visible" />
|
||||
<Setter TargetName="PART_LoadingProgress" Property="IsIndeterminate" Value="True" />
|
||||
<Setter TargetName="PART_ToolbarArea" Property="IsEnabled" Value="False" />
|
||||
<Setter TargetName="PART_TableArea" Property="IsEnabled" Value="False" />
|
||||
<Setter TargetName="PART_PaginationArea" Property="IsEnabled" Value="False" />
|
||||
</Trigger>
|
||||
<Trigger Property="PaginationContent" Value="{x:Null}">
|
||||
<Setter TargetName="PART_DefaultPagination" Property="Visibility" Value="Visible" />
|
||||
<Setter TargetName="PART_PaginationContent" Property="Visibility" Value="Collapsed" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
@@ -667,7 +801,7 @@
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
|
||||
|
||||
|
||||
<Style x:Key="DataGridExCellStyle" TargetType="DataGridCell">
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
@@ -704,8 +838,8 @@
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="DataGridExRowStyle" TargetType="{x:Type DataGridRow}">
|
||||
<Setter Property="ContextMenu" Value="{Binding RowContextMenu, RelativeSource={RelativeSource AncestorType=local:DataGridEx}}" />
|
||||
<Setter Property="Tag" Value="{Binding DataContext, RelativeSource={RelativeSource AncestorType=local:DataGridEx}}" />
|
||||
<Setter Property="ContextMenu" Value="{Binding RowContextMenu, RelativeSource={RelativeSource AncestorType=local:DataGridEx}}" />
|
||||
<Setter Property="Tag" Value="{Binding DataContext, RelativeSource={RelativeSource AncestorType=local:DataGridEx}}" />
|
||||
<Setter Property="BorderThickness" Value="0,0,1,0" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="FontSize" Value="{StaticResource DataGridExFontSize}" />
|
||||
@@ -739,7 +873,8 @@
|
||||
Grid.Column="1"
|
||||
SelectiveScrollingGrid.SelectiveScrollingOrientation="{Binding AreRowDetailsFrozen, ConverterParameter={x:Static SelectiveScrollingOrientation.Vertical}, Converter={x:Static DataGrid.RowDetailsScrollingConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
|
||||
Visibility="{TemplateBinding DetailsVisibility}" />
|
||||
<DataGridRowHeader x:Name="PART_RowHeader"
|
||||
<DataGridRowHeader
|
||||
x:Name="PART_RowHeader"
|
||||
Grid.RowSpan="2"
|
||||
SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical"
|
||||
Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.Row}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
|
||||
@@ -838,18 +973,28 @@
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="DataGridExRowHeaderStyle" BasedOn="{StaticResource DataGridExDefaultRowHeaderStyle}" TargetType="DataGridRowHeader">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="DataGridRowHeader">
|
||||
<Border Background="Transparent" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="0,0,1,1">
|
||||
<CheckBox Style="{StaticResource DataGridExCheckBoxStyle}" HorizontalAlignment="Center" VerticalAlignment="Center" AutomationProperties.Name="选择行"
|
||||
IsChecked="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=DataGridRow}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="DataGridExRowHeaderStyle"
|
||||
BasedOn="{StaticResource DataGridExDefaultRowHeaderStyle}"
|
||||
TargetType="DataGridRowHeader">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="DataGridRowHeader">
|
||||
<Border
|
||||
Background="Transparent"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="0,0,1,1">
|
||||
<CheckBox
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.Name="选择行"
|
||||
IsChecked="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=DataGridRow}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Style="{StaticResource DataGridExCheckBoxStyle}" />
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="DataGridExNumberCellStyle"
|
||||
@@ -859,11 +1004,11 @@
|
||||
</Style>
|
||||
|
||||
<Style BasedOn="{StaticResource DataGridExDefaultDataGridStyle}" TargetType="local:DataGridEx">
|
||||
<Setter Property="EnableRowVirtualization" Value="True" />
|
||||
<Setter Property="EnableColumnVirtualization" Value="True" />
|
||||
<Setter Property="VirtualizingPanel.IsVirtualizing" Value="True" />
|
||||
<Setter Property="VirtualizingPanel.VirtualizationMode" Value="Recycling" />
|
||||
<Setter Property="ScrollViewer.CanContentScroll" Value="True" />
|
||||
<Setter Property="EnableRowVirtualization" Value="True" />
|
||||
<Setter Property="EnableColumnVirtualization" Value="True" />
|
||||
<Setter Property="VirtualizingPanel.IsVirtualizing" Value="True" />
|
||||
<Setter Property="VirtualizingPanel.VirtualizationMode" Value="Recycling" />
|
||||
<Setter Property="ScrollViewer.CanContentScroll" Value="True" />
|
||||
|
||||
<Setter Property="SelectionMode" Value="Extended" />
|
||||
<Setter Property="SelectionUnit" Value="FullRow" />
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:f="clr-namespace:Store.Management.Framework.Controls">
|
||||
xmlns:f="clr-namespace:Store.Management.Framework.Controls"
|
||||
xmlns:wpf="MAX.WPF">
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Fonts/FontGeometry.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MAX.Common.WPF;component/Resources/Generic.xaml" />
|
||||
@@ -35,4 +36,14 @@
|
||||
</Style>
|
||||
<Style BasedOn="{StaticResource WindowBase}" TargetType="f:WindowBase" />
|
||||
<Style BasedOn="{StaticResource DialogWindowStyle}" TargetType="f:DialogWindow" />
|
||||
|
||||
<Style
|
||||
x:Key="VerticalAlignment"
|
||||
BasedOn="{StaticResource TitleContentBaseStyle}"
|
||||
TargetType="wpf:TitleContent">
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
<Setter Property="TitleVerticalAlignment" Value="Center" />
|
||||
<Setter Property="Placement" Value="Left" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
@@ -0,0 +1,44 @@
|
||||
using MAX.Common.WPF.Helpers;
|
||||
using MAX.Core.DependencyInjection;
|
||||
using Prism.Mvvm;
|
||||
|
||||
namespace Store.Management.Framework.Services;
|
||||
|
||||
public enum StatusLevel
|
||||
{
|
||||
Info,
|
||||
Success,
|
||||
Warning,
|
||||
Error
|
||||
}
|
||||
|
||||
public sealed record StatusBarMessage(string Text, StatusLevel Level, int? Progress);
|
||||
|
||||
/// <summary>各页面共享主窗口底部的最新状态。</summary>
|
||||
public interface IStatusBarService
|
||||
{
|
||||
StatusBarMessage Current { get; }
|
||||
|
||||
/// <summary>发布一条状态。进度范围为 0 到 100,省略时隐藏进度条。</summary>
|
||||
void Report(string text, StatusLevel level = StatusLevel.Info, int? progress = null);
|
||||
}
|
||||
|
||||
internal sealed class StatusBarService : BindableBase, IStatusBarService, ISingletonDependency
|
||||
{
|
||||
private StatusBarMessage _current = new("就绪", StatusLevel.Info, null);
|
||||
public StatusBarMessage Current
|
||||
{
|
||||
get => _current;
|
||||
private set => SetProperty(ref _current, value);
|
||||
}
|
||||
|
||||
public void Report(string text, StatusLevel level = StatusLevel.Info, int? progress = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(text);
|
||||
if (progress is < 0 or > 100)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(progress), "进度必须在 0 到 100 之间。");
|
||||
}
|
||||
DispatcherHelper.RunInDispatcher(() => Current = new StatusBarMessage(text, level, progress));
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,7 @@ public partial class App : FrameworkApplication
|
||||
|
||||
containerRegistry.RegisterDialog<DataGridTestView, DataGridTestViewModel>();
|
||||
|
||||
ViewModelLocationProvider.Register<MainWindow, MainWindowViewModel>();
|
||||
ViewModelLocationProvider.Register<MainWindow, MainViewModel>();
|
||||
containerRegistry.RegisterDialog<LoginWindow, LoginViewModel>();
|
||||
|
||||
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
<f:WindowBase
|
||||
x:Class="Store.Management.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:f="Store.Management.Framework"
|
||||
xmlns:local="clr-namespace:Store.Management.Views"
|
||||
xmlns:prism="http://prismlibrary.com/"
|
||||
Title="{Binding Title}"
|
||||
Width="1140"
|
||||
Height="720"
|
||||
MinWidth="900"
|
||||
MinHeight="560"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
<Border>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="20" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="180" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border
|
||||
Grid.Column="0"
|
||||
BorderBrush="{StaticResource SecondaryBorderBrush}"
|
||||
BorderThickness="0,0,1,0">
|
||||
<local:LeftNavView />
|
||||
</Border>
|
||||
<!-- 右侧页面标题与内容 -->
|
||||
<DockPanel Grid.Column="1">
|
||||
<TextBlock
|
||||
Margin="16,16,16,0"
|
||||
DockPanel.Dock="Top"
|
||||
Text="{Binding SelectedItem.Header, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}" />
|
||||
<ContentControl x:Name="PART_SelectedContentHost" prism:RegionManager.RegionName="{Binding Source={x:Static f:Navigation.MainContext}}" />
|
||||
</DockPanel>
|
||||
<Border
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="2"
|
||||
BorderBrush="{StaticResource SecondaryBorderBrush}"
|
||||
BorderThickness="0,1,0,0" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</f:WindowBase>
|
||||
@@ -1,29 +0,0 @@
|
||||
using Prism.Mvvm;
|
||||
using Prism.Commands;
|
||||
using Prism.Services.Dialogs;
|
||||
using Store.Management.Views;
|
||||
using Store.Management.Framework.Controls;
|
||||
using System.Windows;
|
||||
namespace Store.Management;
|
||||
|
||||
public partial class MainWindow : WindowBase
|
||||
{
|
||||
public MainWindow() => InitializeComponent();
|
||||
}
|
||||
|
||||
public class MainWindowViewModel(IDialogService dialogService) : BindableBase
|
||||
{
|
||||
public DelegateCommand OpenDataGridTestCommand => field ??= new(() =>
|
||||
dialogService.ShowDialog(nameof(DataGridTestView), new DialogParameters(), _ => { }));
|
||||
|
||||
|
||||
public DelegateCommand OpenLoginCommand => field ??= new(() =>
|
||||
dialogService.ShowDialog(nameof(LoginWindow), new DialogParameters(), _ => { }));
|
||||
|
||||
private string _title="TTTT";
|
||||
public string Title
|
||||
{
|
||||
get => _title;
|
||||
set => SetProperty(ref _title, value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Store.Management.ViewModels
|
||||
{
|
||||
partial class MainViewModel
|
||||
{
|
||||
partial void InitHeader()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-24
@@ -1,8 +1,6 @@
|
||||
|
||||
using MAX.Common.Helpers;
|
||||
using MAX.Common.Helpers;
|
||||
using PipeReviewReport.Resources.Fonts;
|
||||
using Prism.Commands;
|
||||
using Prism.Mvvm;
|
||||
using Prism.Regions;
|
||||
using Store.Management.Framework;
|
||||
using Store.Management.Views;
|
||||
@@ -16,19 +14,17 @@ using System.Windows.Input;
|
||||
|
||||
namespace Store.Management.ViewModels
|
||||
{
|
||||
internal class LeftNavViewModel : BindableBase
|
||||
partial class MainViewModel
|
||||
{
|
||||
private readonly IRegionManager _regionManager;
|
||||
|
||||
public ObservableCollection<NavItemViewModel> NavItems { get; } = [];
|
||||
public LeftNavViewModel(IRegionManager regionManager)
|
||||
partial void InitNav()
|
||||
{
|
||||
NavItems.Add(new NavItemViewModel
|
||||
{
|
||||
Code = "1",
|
||||
Title = "角色管理",
|
||||
Icon = IconNameEnum.Person,
|
||||
Url=nameof(DataGridTestView)
|
||||
Url = nameof(DataGridTestView)
|
||||
});
|
||||
NavItems.Add(new NavItemViewModel
|
||||
{
|
||||
@@ -36,9 +32,8 @@ namespace Store.Management.ViewModels
|
||||
Title = "商铺管理",
|
||||
Icon = IconNameEnum.BuildingShop,
|
||||
IsSelected = true,
|
||||
Url=nameof(UserManagementPage)
|
||||
Url = nameof(UserManagementPage)
|
||||
});
|
||||
_regionManager = regionManager;
|
||||
}
|
||||
|
||||
public ICommand NavItemCommand => new AsyncDelegateCommand<NavItemViewModel>(NavItem);
|
||||
@@ -51,18 +46,4 @@ namespace Store.Management.ViewModels
|
||||
_regionManager.RequestNavigate(Navigation.MainContext, model.Url);
|
||||
}
|
||||
}
|
||||
|
||||
public class NavItemViewModel : BindableBase
|
||||
{
|
||||
public string Code { get; set; }
|
||||
public string Title { get; set; }
|
||||
public IconNameEnum Icon { get; set; }
|
||||
public string Url { get; set; }
|
||||
private bool _isSelected;
|
||||
public bool IsSelected
|
||||
{
|
||||
get => _isSelected;
|
||||
set => SetProperty(ref _isSelected, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using Store.Management.Framework.Services;
|
||||
using Prism.Mvvm;
|
||||
using Prism.Regions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Store.Management.ViewModels
|
||||
{
|
||||
partial class MainViewModel : BindableBase
|
||||
{
|
||||
private readonly IRegionManager _regionManager;
|
||||
public IStatusBarService StatusBar { get; }
|
||||
public MainViewModel(IRegionManager regionManager, IStatusBarService statusBar)
|
||||
{
|
||||
_regionManager = regionManager;
|
||||
StatusBar = statusBar;
|
||||
InitNav();
|
||||
InitHeader();
|
||||
}
|
||||
partial void InitNav();
|
||||
partial void InitHeader();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using PipeReviewReport.Resources.Fonts;
|
||||
using Prism.Mvvm;
|
||||
|
||||
namespace Store.Management.ViewModels
|
||||
{
|
||||
public class NavItemViewModel : BindableBase
|
||||
{
|
||||
public string Code { get; set; }
|
||||
public string Title { get; set; }
|
||||
public IconNameEnum Icon { get; set; }
|
||||
public string Url { get; set; }
|
||||
private bool _isSelected;
|
||||
public bool IsSelected
|
||||
{
|
||||
get => _isSelected;
|
||||
set => SetProperty(ref _isSelected, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,20 +6,22 @@ using Store.Management.Api.Models;
|
||||
using Store.Management.Api.Transport;
|
||||
using Store.Management.Api.Users;
|
||||
using Store.Management.Framework.Controls;
|
||||
using Store.Management.Framework.ViewModels;
|
||||
using Store.Management.Framework.Services;
|
||||
using Store.Management.Views;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls.Primitives;
|
||||
|
||||
namespace Store.Management.ViewModels;
|
||||
|
||||
public sealed class UserManagementPageViewModel(StoreApiClient client, IDialogService dialogs) : BindableBase
|
||||
public sealed class UserManagementPageViewModel(StoreApiClient client, IDialogService dialogs, IStatusBarService statusBar) : BindableBase
|
||||
{
|
||||
public const int KeywordLimit = 100;
|
||||
private string appliedKeyword = "";
|
||||
private int pageSize = 50;
|
||||
|
||||
public string Keyword { get; set; } = "";
|
||||
public LoadingStatus Status { get; } = new();
|
||||
private bool _canInteract = true;
|
||||
public bool CanInteract { get => _canInteract; private set => SetProperty(ref _canInteract, value); }
|
||||
public int Page { get; private set => SetProperty(ref field, value); } = 1;
|
||||
public int Total { get; private set => SetProperty(ref field, value); }
|
||||
public IReadOnlyList<UserInfo> Users { get; private set => SetProperty(ref field, value); } = [];
|
||||
@@ -27,7 +29,7 @@ public sealed class UserManagementPageViewModel(StoreApiClient client, IDialogSe
|
||||
|
||||
public AsyncDelegateCommand SearchCommand => field ??= new(() => LoadAsync(Keyword.Trim(), 1, pageSize));
|
||||
public AsyncDelegateCommand<DataGridPageRequest> PageCommand => field ??= new(
|
||||
request => LoadAsync(appliedKeyword, request.PageIndex, request.PageSize), _ => !Status.IsLoading);
|
||||
request => LoadAsync(appliedKeyword, request.PageIndex, request.PageSize), _ => CanInteract);
|
||||
public AsyncDelegateCommand CreateCommand => field ??= new(() => OpenEditorAsync(null));
|
||||
public AsyncDelegateCommand EditCommand => field ??= new AsyncDelegateCommand(() => OpenEditorAsync(SelectedUser),
|
||||
() => SelectedUser != null).ObservesProperty(() => SelectedUser);
|
||||
@@ -48,13 +50,14 @@ public sealed class UserManagementPageViewModel(StoreApiClient client, IDialogSe
|
||||
|
||||
private async Task LoadAsync(string keyword, int page, int size)
|
||||
{
|
||||
if (Status.IsLoading)
|
||||
if (!CanInteract)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Status.Initialize("正在查询用户…");
|
||||
CanInteract = false;
|
||||
try
|
||||
{
|
||||
statusBar.Report("正在查询用户…");
|
||||
UserSearchRequest request = new() { Keyword = keyword, Page = page, PageSize = size };
|
||||
PageResult<UserInfo> result = await client.Users.SearchAsync(request);
|
||||
// 删除末页最后一条记录后,按服务端总数回到有效页。
|
||||
@@ -70,6 +73,8 @@ public sealed class UserManagementPageViewModel(StoreApiClient client, IDialogSe
|
||||
Users = result.Items;
|
||||
Total = (int)Math.Min(result.Total, int.MaxValue);
|
||||
Page = request.Page;
|
||||
|
||||
statusBar.Report($"用户查询完成,共 {Total} 条。", StatusLevel.Success,100);
|
||||
}
|
||||
catch (ApiException ex)
|
||||
{
|
||||
@@ -79,11 +84,11 @@ public sealed class UserManagementPageViewModel(StoreApiClient client, IDialogSe
|
||||
Page = 1;
|
||||
appliedKeyword = keyword;
|
||||
pageSize = size;
|
||||
Status.Error(ex.Message);
|
||||
statusBar.Report(ex.Message, StatusLevel.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Status.Done();
|
||||
CanInteract = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,17 +111,21 @@ public sealed class UserManagementPageViewModel(StoreApiClient client, IDialogSe
|
||||
{
|
||||
return;
|
||||
}
|
||||
Status.Initialize("正在删除用户…");
|
||||
CanInteract = false;
|
||||
try
|
||||
{
|
||||
statusBar.Report("正在删除用户…");
|
||||
await client.Users.DeleteAsync(user.Id);
|
||||
}
|
||||
catch (ApiException ex)
|
||||
{
|
||||
Status.Error(ex.Message);
|
||||
statusBar.Report(ex.Message, StatusLevel.Error);
|
||||
return;
|
||||
}
|
||||
Status.Done();
|
||||
finally
|
||||
{
|
||||
CanInteract = true;
|
||||
}
|
||||
await LoadAsync(appliedKeyword, Page, pageSize);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
xmlns:f="Store.Management.Framework"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="clr-namespace:Store.Management.ViewModels"
|
||||
d:Background="White"
|
||||
d:DataContext="{d:DesignInstance Type=vm:DataGridTestViewModel}"
|
||||
d:DesignHeight="760"
|
||||
d:DesignWidth="1280"
|
||||
@@ -109,7 +110,8 @@
|
||||
x:Name="StoreGrid"
|
||||
Grid.Row="1"
|
||||
Margin="10"
|
||||
d:IsLoading="True"
|
||||
d:IsLoading="false"
|
||||
BorderThickness="1"
|
||||
CanUserSortColumns="True"
|
||||
IsLoading="{Binding LoadingStatus.IsLoading, Mode=OneWay}"
|
||||
IsReadOnly="True"
|
||||
|
||||
@@ -9,11 +9,10 @@
|
||||
xmlns:vm="clr-namespace:Store.Management.ViewModels"
|
||||
xmlns:wpf="MAX.WPF"
|
||||
d:Background="White"
|
||||
d:DataContext="{d:DesignInstance Type=vm:LeftNavViewModel}"
|
||||
d:DataContext="{d:DesignInstance Type=vm:MainViewModel}"
|
||||
d:DesignHeight="450"
|
||||
d:Width="255"
|
||||
Background="White"
|
||||
DataContext="{f:DISource Type=vm:LeftNavViewModel}"
|
||||
mc:Ignorable="d">
|
||||
<UserControl.Resources>
|
||||
<wpf:CommandProxy x:Key="NavItemCommand" Command="{Binding NavItemCommand}" />
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<UserControl
|
||||
x:Class="Store.Management.Views.MainHeaderView"
|
||||
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:local="clr-namespace:Store.Management.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Height="50"
|
||||
d:Background="White"
|
||||
d:Width="800"
|
||||
mc:Ignorable="d">
|
||||
<Grid />
|
||||
</UserControl>
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace Store.Management.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// MainHeaderView.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class MainHeaderView : UserControl
|
||||
{
|
||||
public MainHeaderView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<f:WindowBase
|
||||
x:Class="Store.Management.Views.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:f="Store.Management.Framework"
|
||||
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||
xmlns:local="clr-namespace:Store.Management.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:prism="http://prismlibrary.com/"
|
||||
Width="1140"
|
||||
Height="720"
|
||||
MinWidth="900"
|
||||
MinHeight="560"
|
||||
Background="#f4f5f7"
|
||||
ShowIcon="True"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
mc:Ignorable="d">
|
||||
<Border>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="20" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="180" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border
|
||||
Grid.Column="0"
|
||||
BorderBrush="{StaticResource SecondaryBorderBrush}"
|
||||
BorderThickness="0,0,1,0">
|
||||
<local:LeftNavView />
|
||||
</Border>
|
||||
<!-- 右侧页面标题与内容 -->
|
||||
<Border Grid.Column="1" Padding="0">
|
||||
<ContentControl x:Name="PART_SelectedContentHost" prism:RegionManager.RegionName="{Binding Source={x:Static f:Navigation.MainContext}}" />
|
||||
</Border>
|
||||
<Border
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="2"
|
||||
Padding="0,0"
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
BorderBrush="{StaticResource SecondaryBorderBrush}"
|
||||
BorderThickness="0,1,0,0"
|
||||
DataContext="{Binding StatusBar.Current}">
|
||||
<Grid Width="auto" HorizontalAlignment="Stretch">
|
||||
<Grid.Style>
|
||||
<Style TargetType="Grid">
|
||||
<Setter Property="TextElement.Foreground" Value="{DynamicResource PrimaryTextBrush}" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Level}" Value="Success">
|
||||
<Setter Property="TextElement.Foreground" Value="{DynamicResource SuccessBrush}" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding Level}" Value="Warning">
|
||||
<Setter Property="TextElement.Foreground" Value="{DynamicResource WarningBrush}" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding Level}" Value="Error">
|
||||
<Setter Property="TextElement.Foreground" Value="{DynamicResource DangerBrush}" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Grid.Style>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ProgressBar
|
||||
x:Name="StatusProgress"
|
||||
Grid.Column="1"
|
||||
Width="80"
|
||||
Height="6"
|
||||
Margin="15,0,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
d:Visibility="Visible"
|
||||
hc:BorderElement.CornerRadius="2"
|
||||
hc:VisualElement.Text=""
|
||||
AutomationProperties.Name="任务进度"
|
||||
Foreground="{Binding Path=(TextElement.Foreground), RelativeSource={RelativeSource AncestorType=Grid}}"
|
||||
Value="{Binding Progress, Mode=OneWay, TargetNullValue=0}">
|
||||
<ProgressBar.Style>
|
||||
<Style BasedOn="{StaticResource {x:Type ProgressBar}}" TargetType="ProgressBar">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Progress}" Value="{x:Null}">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ProgressBar.Style>
|
||||
</ProgressBar>
|
||||
<TextBlock
|
||||
x:Name="StatusText"
|
||||
Grid.Column="0"
|
||||
Margin="15,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
d:Text="就绪"
|
||||
FontSize="12"
|
||||
Foreground="{Binding Path=(TextElement.Foreground), RelativeSource={RelativeSource AncestorType=Grid}}"
|
||||
Text="{Binding Text}"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
ToolTip="{Binding Text}" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
</f:WindowBase>
|
||||
@@ -0,0 +1,29 @@
|
||||
using Prism.Mvvm;
|
||||
using Prism.Commands;
|
||||
using Prism.Services.Dialogs;
|
||||
using Store.Management.Views;
|
||||
using Store.Management.Framework.Controls;
|
||||
using System.Windows;
|
||||
namespace Store.Management.Views;
|
||||
|
||||
public partial class MainWindow : WindowBase
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
NonClientAreaHeight = 50;
|
||||
NonClientAreaContent = new MainHeaderView();
|
||||
}
|
||||
}
|
||||
|
||||
//public class MainWindowViewModel(IDialogService dialogService) : BindableBase
|
||||
//{
|
||||
// public DelegateCommand OpenDataGridTestCommand => field ??= new(() =>
|
||||
// dialogService.ShowDialog(nameof(DataGridTestView), new DialogParameters(), _ => { }));
|
||||
|
||||
|
||||
// public DelegateCommand OpenLoginCommand => field ??= new(() =>
|
||||
// dialogService.ShowDialog(nameof(LoginWindow), new DialogParameters(), _ => { }));
|
||||
|
||||
|
||||
//}
|
||||
@@ -7,60 +7,54 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="clr-namespace:Store.Management.ViewModels"
|
||||
xmlns:wpf="MAX.WPF"
|
||||
Margin="5"
|
||||
d:Background="White"
|
||||
d:DataContext="{d:DesignInstance Type=vm:UserManagementPageViewModel}"
|
||||
d:DesignHeight="760"
|
||||
d:DesignWidth="1280"
|
||||
Background="White"
|
||||
Loaded="OnLoaded"
|
||||
mc:Ignorable="d">
|
||||
<DockPanel Margin="16">
|
||||
|
||||
<DockPanel IsEnabled="{Binding CanInteract}">
|
||||
|
||||
<!-- 查询及用户操作 -->
|
||||
<wpf:SpacingStackPanel
|
||||
Margin="0,0,0,12"
|
||||
DockPanel.Dock="Top"
|
||||
HorizontalSpacing="8"
|
||||
Orientation="Horizontal">
|
||||
<wpf:SpacingStackPanel.Style>
|
||||
<Style TargetType="wpf:SpacingStackPanel">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Status.IsLoading, Mode=OneWay}" Value="True">
|
||||
<Setter Property="IsEnabled" Value="False" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</wpf:SpacingStackPanel.Style>
|
||||
<Button Command="{Binding LoginCommand}" Content="登录" />
|
||||
<wpf:TitleContent
|
||||
Title="关键词"
|
||||
Placement="Left"
|
||||
TitleVerticalAlignment="Center">
|
||||
<TextBox
|
||||
Width="175"
|
||||
AutomationProperties.Name="用户查询关键词"
|
||||
MaxLength="{x:Static vm:UserManagementPageViewModel.KeywordLimit}"
|
||||
Text="{Binding Keyword, UpdateSourceTrigger=PropertyChanged}">
|
||||
<TextBox.InputBindings>
|
||||
<KeyBinding Key="Enter" Command="{Binding SearchCommand}" />
|
||||
</TextBox.InputBindings>
|
||||
</TextBox>
|
||||
</wpf:TitleContent>
|
||||
<Button Command="{Binding SearchCommand}" Content="查询" />
|
||||
<Button Command="{Binding CreateCommand}" Content="创建用户" />
|
||||
<Button Command="{Binding EditCommand}" Content="编辑" />
|
||||
<Button Command="{Binding DeleteCommand}" Content="删除" />
|
||||
</wpf:SpacingStackPanel>
|
||||
<TextBlock
|
||||
DockPanel.Dock="Bottom"
|
||||
Foreground="{DynamicResource DangerBrush}"
|
||||
Text="{Binding Status.ErrorMessage, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
<Border
|
||||
Padding="10,5"
|
||||
VerticalAlignment="Center"
|
||||
DockPanel.Dock="Top">
|
||||
<wpf:SpacingStackPanel
|
||||
Height="44"
|
||||
HorizontalSpacing="8"
|
||||
Orientation="Horizontal">
|
||||
<wpf:TitleContent Title="关键词" Style="{StaticResource VerticalAlignment}">
|
||||
<TextBox
|
||||
Width="175"
|
||||
AutomationProperties.Name="用户查询关键词"
|
||||
MaxLength="{x:Static vm:UserManagementPageViewModel.KeywordLimit}"
|
||||
Text="{Binding Keyword, UpdateSourceTrigger=PropertyChanged}">
|
||||
<TextBox.InputBindings>
|
||||
<KeyBinding Key="Enter" Command="{Binding SearchCommand}" />
|
||||
</TextBox.InputBindings>
|
||||
</TextBox>
|
||||
</wpf:TitleContent>
|
||||
<Button
|
||||
Command="{Binding SearchCommand}"
|
||||
Content="查询"
|
||||
Style="{StaticResource ButtonPrimary}" />
|
||||
<Button Command="{Binding CreateCommand}" Content="创建用户" />
|
||||
<Button Command="{Binding EditCommand}" Content="编辑" />
|
||||
<Button Command="{Binding DeleteCommand}" Content="删除" />
|
||||
</wpf:SpacingStackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 用户列表与服务端分页 -->
|
||||
<f:DataGridEx
|
||||
AutoGenerateColumns="False"
|
||||
BorderThickness="1"
|
||||
CanUserSortColumns="False"
|
||||
IsLoading="{Binding Status.IsLoading, Mode=OneWay}"
|
||||
IsReadOnly="True"
|
||||
ItemsSource="{Binding Users, Mode=OneWay}"
|
||||
LoadingText="{Binding Status.Message, Mode=OneWay}"
|
||||
NoDataTips="没有符合条件的用户"
|
||||
PageChangedCommand="{Binding PageCommand}"
|
||||
PageSize="50"
|
||||
@@ -95,4 +89,5 @@
|
||||
</f:DataGridEx.Columns>
|
||||
</f:DataGridEx>
|
||||
</DockPanel>
|
||||
|
||||
</UserControl>
|
||||
@@ -1,4 +1,6 @@
|
||||
using Autofac;
|
||||
using Store.Management.Framework.Services;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
using Store.Management;
|
||||
@@ -30,13 +32,10 @@ class Program
|
||||
Check(ReferenceEquals(productionClient, productionContainer.Resolve<StoreApiClient>()), "自动注册 API 单例");
|
||||
Check(productionContainer.Resolve<Store.Management.Framework.Services.ILoginService>() != null, "自动注册登录服务");
|
||||
}
|
||||
var builder = new ContainerBuilder();
|
||||
builder.RegisterInstance(client);
|
||||
new Store.Management.Framework.DependencyInjection.ApiClientRegistration().Register(builder);
|
||||
builder.RegisterAssemblyTypes(typeof(Store.Management.Framework.Services.ILoginService).Assembly)
|
||||
.Where(type => typeof(MAX.Core.DependencyInjection.ISingletonDependency).IsAssignableFrom(type))
|
||||
.AsImplementedInterfaces().SingleInstance();
|
||||
using var container = builder.Build();
|
||||
var testExtension = new Prism.Autofac.AutofacContainerExtension();
|
||||
testExtension.RegisterInstance(typeof(StoreApiClient), client);
|
||||
testExtension.FinalizeExtension();
|
||||
using var container = testExtension.Instance;
|
||||
Check(ReferenceEquals(client, container.Resolve<StoreApiClient>()), "保留测试客户端注册");
|
||||
var service = container.Resolve<Store.Management.Framework.Services.ILoginService>();
|
||||
using var vm = new LoginViewModel(service);
|
||||
@@ -59,13 +58,51 @@ class Program
|
||||
Console.WriteLine("PASS: LoginViewModel -> ILoginService -> AuthApi, retry, password contract and cancellation.");
|
||||
return 0;
|
||||
}
|
||||
var app = new TestApp(client);
|
||||
App app = args.Contains("--status-bar") ? new App() : new TestApp(client);
|
||||
app.InitializeComponent();
|
||||
int exitCode = 0;
|
||||
app.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (args.Contains("--status-bar"))
|
||||
{
|
||||
for (int attempt = 0; attempt < 200 && app.MainWindow is not MainWindow; attempt++)
|
||||
{
|
||||
await Task.Delay(50);
|
||||
}
|
||||
MainWindow shell = (MainWindow)app.MainWindow;
|
||||
IStatusBarService status = app.Container.Resolve<IStatusBarService>();
|
||||
TextBlock text = (TextBlock)shell.FindName("StatusText");
|
||||
ProgressBar progress = (ProgressBar)shell.FindName("StatusProgress");
|
||||
Check(text.Text == "就绪" && progress.Visibility == Visibility.Collapsed, "初始状态");
|
||||
foreach ((StatusLevel level, string brush, int value) in new[] {
|
||||
(StatusLevel.Info, "PrimaryTextBrush", 0), (StatusLevel.Warning, "WarningBrush", 50),
|
||||
(StatusLevel.Error, "DangerBrush", 75), (StatusLevel.Success, "SuccessBrush", 100) })
|
||||
{
|
||||
await Task.Run(() => status.Report(level.ToString(), level, value));
|
||||
await Dispatcher.Yield(DispatcherPriority.ApplicationIdle);
|
||||
Check(text.Text == level.ToString() && text.Foreground == shell.FindResource(brush), "状态文字和颜色");
|
||||
Check(progress.Visibility == Visibility.Visible && progress.Value == value, "任务进度");
|
||||
}
|
||||
status.Report("完成");
|
||||
await Dispatcher.Yield(DispatcherPriority.ApplicationIdle);
|
||||
Check(text.Text == "完成" && progress.Visibility == Visibility.Collapsed, "纯文本状态隐藏进度");
|
||||
using var usersClient = new StoreApiClient(new ApiClientOptions { BaseUrl = new Uri("http://localhost:14312/api/v1/") }, httpClient);
|
||||
usersClient.Session.Clear();
|
||||
var usersVm = new UserManagementPageViewModel(usersClient, app.Container.Resolve<IDialogService>(), status);
|
||||
await usersVm.SearchCommand.Execute();
|
||||
Check(status.Current.Level == StatusLevel.Error && usersVm.CanInteract, "查询失败后显示错误并允许重试");
|
||||
await usersClient.Auth.LoginAsync(new() { UserName = "admin", Password = "320BB1D43EC042096A83DBAC6DC9294A" });
|
||||
Task search = usersVm.SearchCommand.Execute();
|
||||
Check(!usersVm.CanInteract && status.Current.Level == StatusLevel.Info, "查询期间禁用操作");
|
||||
await search;
|
||||
Check(usersVm.CanInteract && usersVm.Total == 51 && status.Current.Level == StatusLevel.Success, "重试查询成功");
|
||||
await usersVm.PageCommand.Execute(new DataGridPageRequest(2, 50));
|
||||
Check(usersVm.Page == 2 && usersVm.CanInteract && status.Current.Level == StatusLevel.Success, "分页查询成功");
|
||||
Console.WriteLine("PASS: status bar bindings, colors and background progress reports.");
|
||||
return;
|
||||
}
|
||||
for (int attempt = 0; attempt < 100 && !app.Windows.OfType<DialogWindow>().Any(window => window.Content is LoginWindow && window.IsLoaded); attempt++)
|
||||
{
|
||||
await Task.Delay(20);
|
||||
@@ -185,6 +222,14 @@ sealed class LoginHandler : HttpMessageHandler
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
RequestCount++;
|
||||
if (request.RequestUri?.AbsolutePath == "/api/v1/user/GetAllUser")
|
||||
{
|
||||
await Task.Delay(30, cancellationToken);
|
||||
return new HttpResponseMessage(HttpStatusCode.OK)
|
||||
{
|
||||
Content = new StringContent("""{"code":200,"data":{"total":51,"items":[{"id":1,"userName":"admin"}]}}""", Encoding.UTF8, "application/json")
|
||||
};
|
||||
}
|
||||
if (request.RequestUri?.AbsoluteUri != "http://localhost:14312/api/v1/Auth/Login")
|
||||
throw new Exception("登录路由错误");
|
||||
var body = System.Text.Json.JsonDocument.Parse(await request.Content!.ReadAsStringAsync(cancellationToken));
|
||||
|
||||
@@ -54,7 +54,16 @@ internal static class Program
|
||||
window = new Window { Content = view, Width = 1280, Height = 760, Left = -10000, Top = -10000, ShowInTaskbar = false };
|
||||
window.Show();
|
||||
Pump();
|
||||
Check(grid.BorderThickness.Left == 1 && grid.BorderThickness.Right == 1, "continuous table side borders");
|
||||
Border toolbar = (Border)grid.Template.FindName("PART_ToolbarArea", grid);
|
||||
Border pagination = (Border)grid.Template.FindName("PART_PaginationArea", grid);
|
||||
Check(grid.BorderThickness == new Thickness(0) && toolbar.BorderThickness == grid.BorderThickness && pagination.BorderThickness == grid.BorderThickness, "borderless demo includes toolbar and pagination");
|
||||
grid.BorderThickness = new Thickness(2, 3, 4, 5);
|
||||
grid.BorderBrush = Brushes.Transparent;
|
||||
Pump();
|
||||
Check(toolbar.BorderThickness == new Thickness(2, 3, 4, 0) && pagination.BorderThickness == new Thickness(2, 0, 4, 5), "toolbar and pagination follow outer border widths");
|
||||
Check(toolbar.BorderBrush == grid.BorderBrush && pagination.BorderBrush == grid.BorderBrush, "toolbar and pagination follow grid border brush");
|
||||
grid.BorderThickness = new Thickness(0);
|
||||
grid.ClearValue(Control.BorderBrushProperty);
|
||||
Check(vm.TotalCount == 237 && grid.Items.Count == 50 && grid.PageCount == 5, "Bogus data and page count");
|
||||
Check(grid.Columns.Count == 10 && Equals(grid.Columns[0].Header, "序号"), "separate serial column");
|
||||
DataGridRow row = (DataGridRow)grid.ItemContainerGenerator.ContainerFromIndex(0);
|
||||
|
||||
Reference in new issue
Block a user