增强打印预览功能,新增离线打印功能,新增缩放控制按钮以提升用户体验。优化打印数据准备逻辑,支持实时预览缩放,确保打印效果的一致性。同时,重构相关视图和服务以增强系统的可维护性和扩展性。

This commit is contained in:
geht
2026-05-14 11:25:17 +08:00
parent 8bcc34aee0
commit 296bc2a4b2
7 changed files with 702 additions and 109 deletions

View File

@@ -5,6 +5,7 @@ using Microsoft.Extensions.Configuration;
using YY.Admin.Core.Helper;
using YY.Admin.Core.Session;
using YY.Admin.FluentValidation;
using YY.Admin.Helper;
using YY.Admin.Services;
using YY.Admin.Services.Service.Auth;
using YY.Admin.Services.Service.Jeecg;
@@ -220,6 +221,31 @@ namespace YY.Admin.ViewModels
while (!cancellationToken.IsCancellationRequested)
{
bool connected = false;
var settings = ServerSettingsStore.Load();
if (settings.DisconnectConnection)
{
try
{
await Application.Current.Dispatcher.InvokeAsync(() =>
{
IsBackendConnected = false;
});
}
catch
{
// 忽略窗口关闭后的调度异常
}
try
{
await Task.Delay(TimeSpan.FromSeconds(ConnectivityCheckIntervalSeconds), cancellationToken);
}
catch (OperationCanceledException)
{
break;
}
continue;
}
try
{
// 每轮都重新读取配置,保存服务器设置后可即时生效
@@ -269,6 +295,11 @@ namespace YY.Admin.ViewModels
{
if (r.Result == ButtonResult.OK)
{
var settings = ServerSettingsStore.Load();
if (settings.DisconnectConnection)
{
IsBackendConnected = false;
}
LoginMessage = "服务器配置已保存";
}
});

View File

@@ -108,6 +108,9 @@ public class RawMaterialEntryOperationViewModel : RawMaterialEntryEditDialogView
public DelegateCommand ResplitCommand { get; }
public DelegateCommand SaveAndPrintCommand { get; }
public DelegateCommand RefreshPrintersCommand { get; }
public DelegateCommand ZoomOutPrintPreviewCommand { get; }
public DelegateCommand ZoomInPrintPreviewCommand { get; }
public DelegateCommand ResetPrintPreviewZoomCommand { get; }
/// <summary>PrintDot 桥接器返回的打印机列表(与打印模板页一致)。</summary>
public ObservableCollection<PrintDotPrinter> Printers { get; } = new();
@@ -169,6 +172,27 @@ public class RawMaterialEntryOperationViewModel : RawMaterialEntryEditDialogView
private set => SetProperty(ref _printPreviewStatus, value);
}
private const double PrintPreviewMinZoom = 0.5d;
private const double PrintPreviewMaxZoom = 2.0d;
private const double PrintPreviewDefaultZoom = 0.7d;
private const double PrintPreviewZoomStep = 0.1d;
private double _printPreviewZoomFactor = PrintPreviewDefaultZoom;
/// <summary>打印预览缩放倍率WebView2 ZoomFactor。</summary>
public double PrintPreviewZoomFactor
{
get => _printPreviewZoomFactor;
set
{
var clamped = Math.Round(Math.Clamp(value, PrintPreviewMinZoom, PrintPreviewMaxZoom), 2);
if (!SetProperty(ref _printPreviewZoomFactor, clamped)) return;
RaisePropertyChanged(nameof(PrintPreviewZoomText));
}
}
/// <summary>打印预览缩放显示文本(百分比)。</summary>
public string PrintPreviewZoomText => $"{Math.Round(PrintPreviewZoomFactor * 100):0}%";
/// <summary>由 View 订阅,在 UI 线程将 HTML 交给 WebView2。</summary>
public event EventHandler<string>? PrintPreviewHtmlReady;
@@ -220,6 +244,9 @@ public class RawMaterialEntryOperationViewModel : RawMaterialEntryEditDialogView
.ObservesProperty(() => Entry);
SaveAndPrintCommand = new DelegateCommand(async () => await SaveAndPrintAsync());
RefreshPrintersCommand = new DelegateCommand(async () => await RefreshPrintersAsync(verbose: true));
ZoomOutPrintPreviewCommand = new DelegateCommand(() => PrintPreviewZoomFactor -= PrintPreviewZoomStep);
ZoomInPrintPreviewCommand = new DelegateCommand(() => PrintPreviewZoomFactor += PrintPreviewZoomStep);
ResetPrintPreviewZoomCommand = new DelegateCommand(() => PrintPreviewZoomFactor = PrintPreviewDefaultZoom);
// 集合变化:批量重订阅 item.PropertyChanged 监听 HasCard/Portions并同步刷新两个 Can*。
SplitCodeDetails.CollectionChanged += OnSplitCodeDetailsCollectionChangedForCanFlags;
_ = RefreshPrintersAsync(verbose: false);
@@ -808,7 +835,8 @@ public class RawMaterialEntryOperationViewModel : RawMaterialEntryEditDialogView
string html;
try
{
html = NativePrintRenderService.RenderToHtml(_previewTemplateJson, dataObj);
// 实时预览关闭模板内部 fitPage 自适应,避免与 WebView2 外层缩放叠加后出现“越放大越小”。
html = NativePrintRenderService.RenderToHtml(_previewTemplateJson, dataObj, enableScreenAutoFit: false);
}
catch (Exception ex)
{

View File

@@ -1057,42 +1057,76 @@
Margin="8,0,10,0"
VerticalAlignment="Center"
Foreground="{DynamicResource SecondaryTextBrush}"/>
<ToggleButton Grid.Column="2"
IsChecked="{Binding IsPrintPreviewExpanded, Mode=TwoWay}"
MinWidth="56"
Height="24"
Background="Transparent"
BorderThickness="1"
Padding="8,0"
FocusVisualStyle="{x:Null}"
Cursor="Hand"
VerticalAlignment="Center">
<ToggleButton.Template>
<ControlTemplate TargetType="ToggleButton">
<Border Background="{TemplateBinding Background}"
BorderBrush="{DynamicResource BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="2"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</ToggleButton.Template>
<TextBlock FontSize="12"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="展开"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, RelativeSource={RelativeSource AncestorType=ToggleButton}}" Value="True">
<Setter Property="Text" Value="折叠"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</ToggleButton>
<StackPanel Grid.Column="2"
Orientation="Horizontal"
VerticalAlignment="Center"
HorizontalAlignment="Right">
<StackPanel Orientation="Horizontal"
Margin="0,0,8,0"
VerticalAlignment="Center"
Visibility="{Binding IsPrintPreviewExpanded, Converter={StaticResource Boolean2VisibilityConverter}}">
<Button Content="-"
Width="24"
Height="24"
Padding="0"
FontSize="12"
Command="{Binding ZoomOutPrintPreviewCommand}"
Style="{StaticResource ButtonDefault}"
ToolTip="缩小预览"/>
<Button Content="{Binding PrintPreviewZoomText}"
MinWidth="56"
Height="24"
Margin="4,0,4,0"
Padding="10,0"
FontSize="11"
Command="{Binding ResetPrintPreviewZoomCommand}"
Style="{StaticResource ButtonDefault}"
ToolTip="重置为 70%"/>
<Button Content="+"
Width="24"
Height="24"
Padding="0"
FontSize="12"
Command="{Binding ZoomInPrintPreviewCommand}"
Style="{StaticResource ButtonDefault}"
ToolTip="放大预览"/>
</StackPanel>
<ToggleButton IsChecked="{Binding IsPrintPreviewExpanded, Mode=TwoWay}"
MinWidth="56"
Height="24"
Background="Transparent"
BorderThickness="1"
Padding="8,0"
FocusVisualStyle="{x:Null}"
Cursor="Hand"
VerticalAlignment="Center">
<ToggleButton.Template>
<ControlTemplate TargetType="ToggleButton">
<Border Background="{TemplateBinding Background}"
BorderBrush="{DynamicResource BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="2"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</ToggleButton.Template>
<TextBlock FontSize="12"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="展开"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, RelativeSource={RelativeSource AncestorType=ToggleButton}}" Value="True">
<Setter Property="Text" Value="折叠"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</ToggleButton>
</StackPanel>
</Grid>
</Border>
<Border BorderBrush="{DynamicResource BorderBrush}"

View File

@@ -49,10 +49,22 @@ public partial class RawMaterialEntryOperationView : UserControl
// null/空 表示“所有属性”通知Prism/BindableBase 批量刷新时),需同步分割布局
if (!string.IsNullOrEmpty(e.PropertyName)
&& e.PropertyName is not nameof(RawMaterialEntryOperationViewModel.IsRightPanelExpanded)
&& e.PropertyName is not nameof(RawMaterialEntryOperationViewModel.ExpandedRightPanelWidth))
&& e.PropertyName is not nameof(RawMaterialEntryOperationViewModel.ExpandedRightPanelWidth)
&& e.PropertyName is not nameof(RawMaterialEntryOperationViewModel.PrintPreviewZoomFactor))
return;
_ = Dispatcher.InvokeAsync(ApplySplitLayout);
if (string.IsNullOrEmpty(e.PropertyName)
|| e.PropertyName is nameof(RawMaterialEntryOperationViewModel.IsRightPanelExpanded)
|| e.PropertyName is nameof(RawMaterialEntryOperationViewModel.ExpandedRightPanelWidth))
{
_ = Dispatcher.InvokeAsync(ApplySplitLayout);
}
if (string.IsNullOrEmpty(e.PropertyName)
|| e.PropertyName is nameof(RawMaterialEntryOperationViewModel.PrintPreviewZoomFactor))
{
_ = Dispatcher.InvokeAsync(ApplyPrintPreviewZoom);
}
}
/// <summary>按钮 Click 在 Command 之后执行,用于兜底刷新列宽(不重复切换状态)。</summary>
@@ -66,6 +78,7 @@ public partial class RawMaterialEntryOperationView : UserControl
EnsureVmAttached();
ApplySplitLayout();
ApplyPrintPreviewZoom();
if (DataContext is RawMaterialEntryOperationViewModel vm && !_initialized)
{
@@ -92,6 +105,7 @@ public partial class RawMaterialEntryOperationView : UserControl
try
{
await PrintPreviewWebView.EnsureCoreWebView2Async();
ApplyPrintPreviewZoom();
PrintPreviewWebView.NavigateToString(html ?? string.Empty);
}
catch
@@ -100,6 +114,15 @@ public partial class RawMaterialEntryOperationView : UserControl
}
}
private void ApplyPrintPreviewZoom()
{
var vm = _vm ?? DataContext as RawMaterialEntryOperationViewModel;
if (vm == null) return;
if (PrintPreviewWebView?.CoreWebView2 == null) return;
// 实时预览已关闭 HTML 内部 fitPage自定义缩放直接映射到 WebView2 即可(+ 放大,- 缩小)。
PrintPreviewWebView.ZoomFactor = vm.PrintPreviewZoomFactor;
}
/// <summary>
/// 根据 ViewModel 同步右侧栏与分割条:展开时使用持久化宽度;折叠时右栏与分割条占宽均为 0完全隐藏
/// </summary>