using System.Text.Json; using System.Text.Json.Nodes; using System.Text.RegularExpressions; using System.Windows; using YY.Admin.Core.Entity; using YY.Admin.Services.Service.Print; namespace YY.Admin.Views.Print; public partial class PrintPreviewWindow : HandyControl.Controls.Window { private readonly string _templateJson; public PrintPreviewWindow(PrintTemplate template, string? templateJson) { InitializeComponent(); _templateJson = templateJson ?? string.Empty; TbTemplateName.Text = template.TemplateName ?? "(未命名)"; TbTemplateCode.Text = $"编码:{template.TemplateCode} " + $"尺寸:{template.PaperWidthMm ?? 210}×{template.PaperHeightMm ?? 297} mm " + $"方向:{template.PaperOrientation ?? "纵向"}"; TbParamJson.Text = BuildMockParamJson(_templateJson); Loaded += async (_, _) => await LoadPreviewAsync(); } private async Task LoadPreviewAsync() { try { TbStatus.Text = "加载中…"; await WebView.EnsureCoreWebView2Async(); if (string.IsNullOrWhiteSpace(_templateJson) || _templateJson == "{}") { WebView.NavigateToString(BuildEmptyHtml()); TbStatus.Text = "尚未设计模板内容"; return; } await RenderCurrentParamJsonAsync(); } catch (Exception ex) { TbStatus.Text = $"预览失败:{ex.Message}"; } } private static string BuildEmptyHtml() => """
📄
该模板尚未设计内容
"""; /// /// 左侧参数 JSON 重新渲染预览(与后端预览一致:用参数 JSON 驱动模板绑定字段)。 /// private async Task RenderCurrentParamJsonAsync() { try { TbStatus.Text = "渲染中…"; JsonObject dataObj; var text = TbParamJson.Text?.Trim(); if (string.IsNullOrWhiteSpace(text)) { dataObj = new JsonObject(); } else { var node = JsonNode.Parse(text); if (node is not JsonObject obj) { WebView.NavigateToString(BuildErrorHtml("参数JSON必须是对象(JSON Object)")); TbStatus.Text = "参数JSON格式错误"; return; } dataObj = obj; } var html = NativePrintRenderService.RenderToHtml(_templateJson, dataObj); WebView.NavigateToString(html); TbStatus.Text = string.Empty; } catch (Exception ex) { WebView.NavigateToString(BuildErrorHtml(ex.Message)); TbStatus.Text = $"渲染失败:{ex.Message}"; } await Task.CompletedTask; } /// /// 根据模板绑定字段生成参数 JSON(便于用户直接编辑并预览)。 /// private static string BuildMockParamJson(string templateJson) { if (string.IsNullOrWhiteSpace(templateJson) || templateJson == "{}") return "{}"; try { var root = JsonNode.Parse(templateJson); var obj = new JsonObject(); var elements = root?["elements"]?.AsArray() ?? new JsonArray(); var fields = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (var el in elements.OfType()) { var type = (el["type"]?.ToString() ?? string.Empty).Trim(); if (type is "table" or "detailTable") { var source = (el["source"]?.ToString() ?? "mainTable").Trim(); if (!obj.ContainsKey(source)) { var rows = new JsonArray(); var columns = el["columns"]?.AsArray() ?? new JsonArray(); for (var i = 1; i <= 8; i++) { var row = new JsonObject(); foreach (var col in columns.OfType()) { var field = (col["bindField"]?.ToString() ?? col["field"]?.ToString() ?? string.Empty).Trim(); if (string.IsNullOrWhiteSpace(field)) continue; var contentType = (col["contentType"]?.ToString() ?? "text").Trim().ToLowerInvariant(); row[field] = contentType switch { "number" => i * 123.45, "amount" => i * 24567.89, "qrcode" => $"QR_{field}_{i}", "barcode" => $"BAR_{field}_{i}", "image" => $"https://picsum.photos/seed/{Uri.EscapeDataString(field + "_" + i)}/260/120", _ => $"{field}_示例值_{i}" }; fields.Add(field); } rows.Add(row); } obj[source] = rows; } } var bind = (el["bindField"]?.ToString() ?? string.Empty).Trim(); if (!string.IsNullOrWhiteSpace(bind)) fields.Add(bind); // 提取 text 中的 {{field}} 占位符(支持内嵌) var text = el["text"]?.ToString() ?? string.Empty; foreach (Match m in Regex.Matches(text, @"\{\{\s*([\w\.]+)\s*\}\}")) { var key = m.Groups[1].Value.Trim(); if (!string.IsNullOrWhiteSpace(key)) fields.Add(key); } CollectBindFields(el["cells"], fields); } foreach (var f in fields.OrderBy(x => x, StringComparer.OrdinalIgnoreCase)) { if (f.Equals("pageNo", StringComparison.OrdinalIgnoreCase) || f.Equals("totalPages", StringComparison.OrdinalIgnoreCase)) continue; if (!obj.ContainsKey(f)) obj[f] = $"{f}_示例值"; } return obj.ToJsonString(new JsonSerializerOptions { WriteIndented = true }); } catch { return "{}"; } } private static void CollectBindFields(JsonNode? node, ISet fields) { if (node == null) return; if (node is JsonObject o) { if (o.TryGetPropertyValue("bindField", out var bindNode)) { var bind = bindNode?.ToString()?.Trim(); if (!string.IsNullOrWhiteSpace(bind)) fields.Add(bind); } foreach (var kv in o) CollectBindFields(kv.Value, fields); return; } if (node is JsonArray arr) { foreach (var it in arr) CollectBindFields(it, fields); } } private static string BuildErrorHtml(string message) { var esc = message.Replace("&", "&").Replace("<", "<").Replace(">", ">"); return "
" + "
⚠️
" + "
渲染失败:" + esc + "
" + "
"; } private async void RenderByParamJson_Click(object sender, RoutedEventArgs e) { await RenderCurrentParamJsonAsync(); } private async void GenerateMockJson_Click(object sender, RoutedEventArgs e) { TbParamJson.Text = BuildMockParamJson(_templateJson); await RenderCurrentParamJsonAsync(); } private void CloseButton_Click(object sender, RoutedEventArgs e) => Close(); }