2026-05-14 15:52:00 +08:00
|
|
|
|
using System.Collections.ObjectModel;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
|
using System.Globalization;
|
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Text.Json;
|
|
|
|
|
|
using System.Threading;
|
2026-05-15 15:46:25 +08:00
|
|
|
|
using System.Threading.Tasks;
|
2026-05-14 15:52:00 +08:00
|
|
|
|
using System.Windows;
|
|
|
|
|
|
using System.Windows.Controls.Primitives;
|
|
|
|
|
|
using YY.Admin.Core.Services;
|
|
|
|
|
|
using YY.Admin.ViewModels.RawMaterialEntry;
|
|
|
|
|
|
|
|
|
|
|
|
namespace YY.Admin.Views.RawMaterialEntry;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 需显式实现 <see cref="INotifyPropertyChanged"/>,否则 WPF 绑定不会订阅 PropertyChanged,
|
|
|
|
|
|
/// 界面会一直停留在属性初始值(例如 PrinterStatus 的「加载打印机中…」)。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public partial class RawMaterialCardGenerateConfirmWindow : HandyControl.Controls.Window, INotifyPropertyChanged
|
|
|
|
|
|
{
|
|
|
|
|
|
private const int PrinterLoadTimeoutMs = 8000;
|
|
|
|
|
|
private const double DefaultLeftRatio = 0.7d;
|
|
|
|
|
|
private const double MinLeftRatio = 0.2d;
|
|
|
|
|
|
private const double MaxLeftRatio = 0.8d;
|
|
|
|
|
|
private static readonly JsonSerializerOptions LayoutJsonOpts = new() { WriteIndented = true };
|
|
|
|
|
|
private static string LayoutFilePath => Path.Combine(
|
|
|
|
|
|
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
|
|
|
|
|
"YY.Admin",
|
|
|
|
|
|
"raw-material-card-generate-confirm-layout.json");
|
|
|
|
|
|
|
|
|
|
|
|
public ObservableCollection<RawMaterialCardGeneratePlanRow> PlanItems { get; } = new();
|
|
|
|
|
|
|
|
|
|
|
|
public string HeaderText { get; }
|
|
|
|
|
|
public string TemplateText { get; }
|
|
|
|
|
|
public ObservableCollection<PrintDotPrinter> Printers { get; } = new();
|
|
|
|
|
|
private readonly Func<RawMaterialCardGeneratePlanRow, string> _previewHtmlBuilder;
|
|
|
|
|
|
private readonly IPrintDotService _printDotService;
|
|
|
|
|
|
private bool _webViewReady;
|
2026-05-15 15:46:25 +08:00
|
|
|
|
/// <summary>增大表示又有新的预览请求,旧的后台渲染结果应丢弃。</summary>
|
|
|
|
|
|
private int _previewVersion;
|
2026-05-14 15:52:00 +08:00
|
|
|
|
private bool _isRefreshingPrinters;
|
|
|
|
|
|
private bool _suppressPrinterSave;
|
2026-05-15 15:46:25 +08:00
|
|
|
|
/// <summary>Loaded 中批量赋值选中行时跳过 Setter 内的去抖预览,再由 Loaded 单次立即刷新。</summary>
|
|
|
|
|
|
private bool _suppressPreviewSchedule;
|
2026-05-14 15:52:00 +08:00
|
|
|
|
private string? _preferredPrinterNameOnLoad;
|
|
|
|
|
|
|
|
|
|
|
|
private string _printerStatus = "加载打印机中...";
|
|
|
|
|
|
public string PrinterStatus
|
|
|
|
|
|
{
|
|
|
|
|
|
get => _printerStatus;
|
|
|
|
|
|
set
|
|
|
|
|
|
{
|
|
|
|
|
|
if (string.Equals(_printerStatus, value, StringComparison.Ordinal)) return;
|
|
|
|
|
|
_printerStatus = value;
|
|
|
|
|
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(PrinterStatus)));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private PrintDotPrinter? _selectedPrinter;
|
|
|
|
|
|
public PrintDotPrinter? SelectedPrinter
|
|
|
|
|
|
{
|
|
|
|
|
|
get => _selectedPrinter;
|
|
|
|
|
|
set
|
|
|
|
|
|
{
|
|
|
|
|
|
if (ReferenceEquals(_selectedPrinter, value)) return;
|
|
|
|
|
|
_selectedPrinter = value;
|
|
|
|
|
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedPrinter)));
|
|
|
|
|
|
if (_suppressPrinterSave) return;
|
|
|
|
|
|
SaveLayout(dto => dto.SelectedPrinterName = value?.Name ?? string.Empty);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public string SelectedPrinterName => SelectedPrinter?.Name?.Trim() ?? string.Empty;
|
|
|
|
|
|
|
|
|
|
|
|
private RawMaterialCardGeneratePlanRow? _selectedPlanItem;
|
|
|
|
|
|
public RawMaterialCardGeneratePlanRow? SelectedPlanItem
|
|
|
|
|
|
{
|
|
|
|
|
|
get => _selectedPlanItem;
|
|
|
|
|
|
set
|
|
|
|
|
|
{
|
|
|
|
|
|
if (ReferenceEquals(_selectedPlanItem, value)) return;
|
|
|
|
|
|
_selectedPlanItem = value;
|
|
|
|
|
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedPlanItem)));
|
2026-05-15 15:46:25 +08:00
|
|
|
|
// 仅用属性变更触发预览刷新,避免 SelectionChanged + 绑定 双重 Navigate 造成卡顿
|
|
|
|
|
|
if (!_suppressPreviewSchedule)
|
|
|
|
|
|
{
|
|
|
|
|
|
SchedulePreviewNavigate(skipDebounce: false);
|
|
|
|
|
|
}
|
2026-05-14 15:52:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public event PropertyChangedEventHandler? PropertyChanged;
|
|
|
|
|
|
|
|
|
|
|
|
public RawMaterialCardGenerateConfirmWindow(
|
|
|
|
|
|
IReadOnlyList<RawMaterialEntryOperationViewModel.RawMaterialCardGeneratePlanItem> planItems,
|
|
|
|
|
|
string templateName,
|
|
|
|
|
|
string templateCode,
|
|
|
|
|
|
IPrintDotService printDotService,
|
|
|
|
|
|
Func<RawMaterialCardGeneratePlanRow, string> previewHtmlBuilder)
|
|
|
|
|
|
{
|
|
|
|
|
|
InitializeComponent();
|
|
|
|
|
|
_printDotService = printDotService;
|
|
|
|
|
|
_previewHtmlBuilder = previewHtmlBuilder;
|
|
|
|
|
|
HeaderText = $"共 {planItems.Count} 张,左侧展示即将生成的原材料卡片,右侧展示业务关联打印模板预览。";
|
|
|
|
|
|
TemplateText = $"模板:{templateName}({templateCode})";
|
|
|
|
|
|
var idx = 0;
|
|
|
|
|
|
foreach (var item in planItems)
|
|
|
|
|
|
{
|
|
|
|
|
|
idx++;
|
|
|
|
|
|
PlanItems.Add(new RawMaterialCardGeneratePlanRow
|
|
|
|
|
|
{
|
|
|
|
|
|
Index = idx,
|
|
|
|
|
|
SourceRowNo = item.SourceRowNo,
|
|
|
|
|
|
DetailId = item.DetailId,
|
|
|
|
|
|
Card = item.Card
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DataContext = this;
|
|
|
|
|
|
Loaded += OnLoadedAsync;
|
|
|
|
|
|
Closing += OnClosing;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private async void OnLoadedAsync(object? sender, RoutedEventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
var layout = LoadSavedLayout();
|
|
|
|
|
|
ApplyPaneRatio(layout.LeftPaneRatio ?? DefaultLeftRatio);
|
|
|
|
|
|
ApplySavedColumnWidths(layout.ColumnWidths);
|
|
|
|
|
|
_preferredPrinterNameOnLoad = layout.SelectedPrinterName;
|
|
|
|
|
|
await RefreshPrintersAsync(verbose: false);
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
await PreviewWebView.EnsureCoreWebView2Async();
|
|
|
|
|
|
_webViewReady = true;
|
2026-05-15 15:46:25 +08:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
_suppressPreviewSchedule = true;
|
|
|
|
|
|
if (SelectedPlanItem == null && PlanItems.Count > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
SelectedPlanItem = PlanItems[0];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
finally
|
2026-05-14 15:52:00 +08:00
|
|
|
|
{
|
2026-05-15 15:46:25 +08:00
|
|
|
|
_suppressPreviewSchedule = false;
|
2026-05-14 15:52:00 +08:00
|
|
|
|
}
|
2026-05-15 15:46:25 +08:00
|
|
|
|
|
|
|
|
|
|
SchedulePreviewNavigate(skipDebounce: true);
|
2026-05-14 15:52:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
2026-05-15 15:46:25 +08:00
|
|
|
|
PreviewWebView.NavigateToString("<html><body style='font-family:Microsoft YaHei;padding:24px;color:#eee;background:#525659;'>模板预览加载失败</body></html>");
|
2026-05-14 15:52:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private async void RefreshPrintersButton_OnClick(object sender, RoutedEventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
await RefreshPrintersAsync(verbose: true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void MainSplitter_OnDragCompleted(object sender, DragCompletedEventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
SavePaneRatio(GetCurrentLeftRatio());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private double GetCurrentLeftRatio()
|
|
|
|
|
|
{
|
|
|
|
|
|
var left = LeftPaneCol.ActualWidth;
|
|
|
|
|
|
var right = RightPaneCol.ActualWidth;
|
|
|
|
|
|
var total = left + right;
|
|
|
|
|
|
if (total <= 0.1d) return DefaultLeftRatio;
|
|
|
|
|
|
return Math.Clamp(left / total, MinLeftRatio, MaxLeftRatio);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void ApplyPaneRatio(double ratio)
|
|
|
|
|
|
{
|
|
|
|
|
|
var leftRatio = Math.Clamp(ratio, MinLeftRatio, MaxLeftRatio);
|
|
|
|
|
|
LeftPaneCol.Width = new GridLength(leftRatio, GridUnitType.Star);
|
|
|
|
|
|
RightPaneCol.Width = new GridLength(1d - leftRatio, GridUnitType.Star);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static ConfirmWindowLayoutDto LoadSavedLayout()
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!File.Exists(LayoutFilePath)) return new ConfirmWindowLayoutDto();
|
|
|
|
|
|
var json = File.ReadAllText(LayoutFilePath);
|
|
|
|
|
|
var dto = JsonSerializer.Deserialize<ConfirmWindowLayoutDto>(json, LayoutJsonOpts);
|
|
|
|
|
|
if (dto is null) return new ConfirmWindowLayoutDto();
|
|
|
|
|
|
if (dto.LeftPaneRatio is > 0 and < 1)
|
|
|
|
|
|
dto.LeftPaneRatio = Math.Clamp(dto.LeftPaneRatio.Value, MinLeftRatio, MaxLeftRatio);
|
|
|
|
|
|
return dto;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
|
|
|
|
|
// 布局缓存异常时使用默认比例,不影响主流程
|
|
|
|
|
|
}
|
|
|
|
|
|
return new ConfirmWindowLayoutDto();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static void SavePaneRatio(double leftRatio)
|
|
|
|
|
|
{
|
|
|
|
|
|
SaveLayout(dto => { dto.LeftPaneRatio = Math.Clamp(leftRatio, MinLeftRatio, MaxLeftRatio); });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static void SaveLayout(Action<ConfirmWindowLayoutDto> mutator)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var dir = Path.GetDirectoryName(LayoutFilePath);
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(dir))
|
|
|
|
|
|
{
|
|
|
|
|
|
Directory.CreateDirectory(dir);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var dto = LoadSavedLayout();
|
|
|
|
|
|
mutator(dto);
|
|
|
|
|
|
File.WriteAllText(LayoutFilePath, JsonSerializer.Serialize(dto, LayoutJsonOpts));
|
|
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
|
|
|
|
|
// 写入失败不阻断页面交互
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void ApplySavedColumnWidths(Dictionary<string, double>? columnWidths)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (columnWidths is null || columnWidths.Count == 0) return;
|
|
|
|
|
|
for (var index = 0; index < PlanGrid.Columns.Count; index++)
|
|
|
|
|
|
{
|
|
|
|
|
|
var column = PlanGrid.Columns[index];
|
|
|
|
|
|
var key = GetColumnWidthKey(column, index);
|
|
|
|
|
|
if (!columnWidths.TryGetValue(key, out var cachedWidth) || cachedWidth <= 0) continue;
|
|
|
|
|
|
var minWidth = column.MinWidth > 0 ? column.MinWidth : 40d;
|
|
|
|
|
|
column.Width = new System.Windows.Controls.DataGridLength(
|
|
|
|
|
|
Math.Max(cachedWidth, minWidth),
|
|
|
|
|
|
System.Windows.Controls.DataGridLengthUnitType.Pixel);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void SaveCurrentColumnWidths()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (PlanGrid.Columns.Count == 0) return;
|
|
|
|
|
|
var widths = new Dictionary<string, double>(StringComparer.Ordinal);
|
|
|
|
|
|
for (var index = 0; index < PlanGrid.Columns.Count; index++)
|
|
|
|
|
|
{
|
|
|
|
|
|
var column = PlanGrid.Columns[index];
|
|
|
|
|
|
var width = column.ActualWidth;
|
|
|
|
|
|
if (double.IsNaN(width) || double.IsInfinity(width) || width <= 0) continue;
|
|
|
|
|
|
widths[GetColumnWidthKey(column, index)] = width;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (widths.Count == 0) return;
|
|
|
|
|
|
SaveLayout(dto => dto.ColumnWidths = widths);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static string GetColumnWidthKey(System.Windows.Controls.DataGridColumn column, int index)
|
|
|
|
|
|
{
|
|
|
|
|
|
var header = column.Header?.ToString();
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(header))
|
|
|
|
|
|
return $"col-{index.ToString(CultureInfo.InvariantCulture)}";
|
|
|
|
|
|
return $"col-{index.ToString(CultureInfo.InvariantCulture)}-{header.Trim()}";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OnClosing(object? sender, CancelEventArgs e)
|
|
|
|
|
|
{
|
2026-05-15 15:46:25 +08:00
|
|
|
|
Interlocked.Increment(ref _previewVersion);
|
2026-05-14 15:52:00 +08:00
|
|
|
|
SavePaneRatio(GetCurrentLeftRatio());
|
|
|
|
|
|
SaveCurrentColumnWidths();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private async Task RefreshPrintersAsync(bool verbose)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_isRefreshingPrinters) return;
|
|
|
|
|
|
_isRefreshingPrinters = true;
|
|
|
|
|
|
const string loadingText = "加载打印机中...";
|
|
|
|
|
|
PrinterStatus = verbose ? "刷新打印机中..." : loadingText;
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
using var cts = new CancellationTokenSource();
|
|
|
|
|
|
var fetchTask = _printDotService.GetPrintersAsync(cts.Token);
|
|
|
|
|
|
var timeoutTask = Task.Delay(PrinterLoadTimeoutMs);
|
|
|
|
|
|
var completedTask = await Task.WhenAny(fetchTask, timeoutTask);
|
|
|
|
|
|
if (!ReferenceEquals(completedTask, fetchTask))
|
|
|
|
|
|
{
|
|
|
|
|
|
cts.Cancel();
|
|
|
|
|
|
throw new TimeoutException("获取打印机列表超时");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var list = await fetchTask;
|
|
|
|
|
|
Printers.Clear();
|
|
|
|
|
|
foreach (var printer in list) Printers.Add(printer);
|
|
|
|
|
|
// 先更新状态,避免后续本地缓存逻辑异常导致“加载中”残留
|
|
|
|
|
|
PrinterStatus = list.Count > 0 ? $"共 {list.Count} 台打印机" : "未检测到打印机";
|
|
|
|
|
|
|
|
|
|
|
|
var preferred = _preferredPrinterNameOnLoad;
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(preferred))
|
|
|
|
|
|
{
|
|
|
|
|
|
preferred = SelectedPrinter?.Name;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var match = list.FirstOrDefault(p => string.Equals(p.Name, preferred, StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
|
?? list.FirstOrDefault(p => p.IsDefault)
|
|
|
|
|
|
?? list.FirstOrDefault();
|
|
|
|
|
|
|
|
|
|
|
|
_suppressPrinterSave = true;
|
|
|
|
|
|
SelectedPrinter = match;
|
|
|
|
|
|
_suppressPrinterSave = false;
|
|
|
|
|
|
|
|
|
|
|
|
if (match is not null)
|
|
|
|
|
|
{
|
|
|
|
|
|
SaveLayout(dto => dto.SelectedPrinterName = match.Name);
|
|
|
|
|
|
}
|
|
|
|
|
|
_preferredPrinterNameOnLoad = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (OperationCanceledException)
|
|
|
|
|
|
{
|
|
|
|
|
|
Printers.Clear();
|
|
|
|
|
|
_suppressPrinterSave = true;
|
|
|
|
|
|
SelectedPrinter = null;
|
|
|
|
|
|
_suppressPrinterSave = false;
|
|
|
|
|
|
PrinterStatus = "加载打印机超时,请检查 PrintDot 后重试";
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (TimeoutException)
|
|
|
|
|
|
{
|
|
|
|
|
|
Printers.Clear();
|
|
|
|
|
|
_suppressPrinterSave = true;
|
|
|
|
|
|
SelectedPrinter = null;
|
|
|
|
|
|
_suppressPrinterSave = false;
|
|
|
|
|
|
PrinterStatus = "加载打印机超时,请检查 PrintDot 后重试";
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Printers.Clear();
|
|
|
|
|
|
_suppressPrinterSave = true;
|
|
|
|
|
|
SelectedPrinter = null;
|
|
|
|
|
|
_suppressPrinterSave = false;
|
|
|
|
|
|
PrinterStatus = verbose ? $"打印机连接失败:{ex.Message}" : "打印机连接失败";
|
|
|
|
|
|
}
|
|
|
|
|
|
finally
|
|
|
|
|
|
{
|
|
|
|
|
|
// 兜底:如果实际有打印机但状态仍是“加载中”,强制纠正状态文本
|
|
|
|
|
|
if (string.Equals(PrinterStatus, loadingText, StringComparison.Ordinal) && Printers.Count > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
PrinterStatus = $"共 {Printers.Count} 台打印机";
|
|
|
|
|
|
}
|
|
|
|
|
|
_isRefreshingPrinters = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-15 15:46:25 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// RenderToHtml 在 UI 线程会阻塞鼠标/选中反馈;移至后台线程并做短去抖合并连点。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private async void SchedulePreviewNavigate(bool skipDebounce)
|
2026-05-14 15:52:00 +08:00
|
|
|
|
{
|
2026-05-15 15:46:25 +08:00
|
|
|
|
if (!_webViewReady)
|
2026-05-14 15:52:00 +08:00
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-15 15:46:25 +08:00
|
|
|
|
var token = Interlocked.Increment(ref _previewVersion);
|
|
|
|
|
|
|
2026-05-14 15:52:00 +08:00
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-05-15 15:46:25 +08:00
|
|
|
|
if (!skipDebounce)
|
|
|
|
|
|
{
|
|
|
|
|
|
await Task.Delay(45).ConfigureAwait(true);
|
|
|
|
|
|
if (token != _previewVersion)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var row = SelectedPlanItem;
|
|
|
|
|
|
if (row == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
PreviewWebView.NavigateToString(
|
|
|
|
|
|
"<html><body style='font-family:Microsoft YaHei;padding:24px;color:#eee;background:#525659;'>请先选择左侧卡片记录</body></html>");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
string html;
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var capturedRow = row;
|
|
|
|
|
|
html = await Task.Run(() => _previewHtmlBuilder(capturedRow)).ConfigureAwait(true);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
html =
|
|
|
|
|
|
"<html><body style='font-family:Microsoft YaHei;padding:24px;color:#eee;background:#525659;'>模板预览失败:"
|
|
|
|
|
|
+ System.Net.WebUtility.HtmlEncode(ex.Message)
|
|
|
|
|
|
+ "</body></html>";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (token != _previewVersion || !ReferenceEquals(SelectedPlanItem, row))
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-14 15:52:00 +08:00
|
|
|
|
PreviewWebView.NavigateToString(html);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
2026-05-15 15:46:25 +08:00
|
|
|
|
/* 窗口关闭或调度异常时忽略 */
|
2026-05-14 15:52:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void CancelButton_OnClick(object sender, RoutedEventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
DialogResult = false;
|
|
|
|
|
|
Close();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void ConfirmButton_OnClick(object sender, RoutedEventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(SelectedPrinterName))
|
|
|
|
|
|
{
|
|
|
|
|
|
HandyControl.Controls.MessageBox.Warning("请先在当前弹窗选择打印机,再执行“生成并打印”。");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
DialogResult = true;
|
|
|
|
|
|
Close();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
internal sealed class ConfirmWindowLayoutDto
|
|
|
|
|
|
{
|
|
|
|
|
|
public double? LeftPaneRatio { get; set; }
|
|
|
|
|
|
public Dictionary<string, double>? ColumnWidths { get; set; }
|
|
|
|
|
|
public string? SelectedPrinterName { get; set; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public sealed class RawMaterialCardGeneratePlanRow
|
|
|
|
|
|
{
|
|
|
|
|
|
public int Index { get; init; }
|
|
|
|
|
|
public int SourceRowNo { get; init; }
|
|
|
|
|
|
public required string DetailId { get; init; }
|
|
|
|
|
|
public required YY.Admin.Core.Entity.MesXslRawMaterialCard Card { get; init; }
|
|
|
|
|
|
}
|