diff --git a/src/Oms.Application/Labeling/Hiprint/HiprintModels.cs b/src/Oms.Application/Labeling/Hiprint/HiprintModels.cs index 50c401b..1411b1d 100644 --- a/src/Oms.Application/Labeling/Hiprint/HiprintModels.cs +++ b/src/Oms.Application/Labeling/Hiprint/HiprintModels.cs @@ -63,6 +63,10 @@ public sealed class HiprintElementOptions [JsonPropertyName("title")] public string? Title { get; set; } + /// 设计器「测试数据」;无 field 的纯文本有时只把文案写在这里。 + [JsonPropertyName("testData")] + public string? TestData { get; set; } + [JsonPropertyName("field")] public string? Field { get; set; } diff --git a/src/Oms.Application/Labeling/Hiprint/HiprintTemplateParser.cs b/src/Oms.Application/Labeling/Hiprint/HiprintTemplateParser.cs index a913556..db27950 100644 --- a/src/Oms.Application/Labeling/Hiprint/HiprintTemplateParser.cs +++ b/src/Oms.Application/Labeling/Hiprint/HiprintTemplateParser.cs @@ -105,14 +105,15 @@ public sealed class HiprintTemplateParser Src = ResolveValue(opt.Field, opt.Src ?? opt.Title, printData) }; case "longtext": - return BuildTextOp(opt, left, top, width, height, printData, wrap: true); + return BuildTextOp( + opt, left, top, width, height, printData, wrap: true, element.PrintElementType?.Title); case "text": default: { var textType = opt.TextType?.Trim().ToLowerInvariant(); if (textType is "barcode" or "qrcode") { - var content = ResolveValue(opt.Field, opt.Title, printData); + var content = ResolveValue(opt.Field, FirstNonEmpty(opt.Title, opt.TestData), printData); if (string.IsNullOrWhiteSpace(content)) { return null; @@ -129,7 +130,8 @@ public sealed class HiprintTemplateParser }; } - return BuildTextOp(opt, left, top, width, height, printData, wrap: false); + return BuildTextOp( + opt, left, top, width, height, printData, wrap: false, element.PrintElementType?.Title); } } } @@ -141,25 +143,38 @@ public sealed class HiprintTemplateParser float width, float height, IReadOnlyDictionary printData, - bool wrap) + bool wrap, + string? elementTypeTitle = null) { var value = ResolveValue(opt.Field, null, printData); + // 与 vg-print 一致:无 field 时打印 title;有 field 时按 hideTitle 决定是否带标题 + var title = FirstNonEmpty(opt.Title, elementTypeTitle); string text; if (!string.IsNullOrWhiteSpace(opt.Field)) { - // 有 field:默认只打字段值;hideTitle=false 且有 title 时拼「标题:值」 - if (opt.HideTitle == false && !string.IsNullOrWhiteSpace(opt.Title) && !string.IsNullOrWhiteSpace(value)) + var hideTitle = opt.HideTitle != false; // null/true → 隐藏标题(OMS 字段默认 hideTitle:true) + if (!hideTitle && !string.IsNullOrWhiteSpace(title) && !string.IsNullOrWhiteSpace(value)) { - text = $"{opt.Title}:{value}"; + text = $"{title}:{value}"; + } + else if (!string.IsNullOrWhiteSpace(value)) + { + text = value!; + } + else if (!hideTitle && !string.IsNullOrWhiteSpace(title)) + { + // 仅标题(值为空时仍保留自定义标题文案) + text = title!; } else { - text = value ?? string.Empty; + text = string.Empty; } } else { - text = opt.Title ?? string.Empty; + // 纯自定义文本:title → testData → 元素类型名 + text = FirstNonEmpty(opt.Title, opt.TestData, elementTypeTitle) ?? string.Empty; } if (string.IsNullOrWhiteSpace(text)) @@ -169,15 +184,18 @@ public sealed class HiprintTemplateParser var bold = string.Equals(opt.FontWeight, "bold", StringComparison.OrdinalIgnoreCase) || opt.FontWeight is "700" or "600"; + var fontSize = opt.FontSize is > 0 ? opt.FontSize.Value : 10f; + // 设计器文本框常只有十余 pt 高,行高略大于字号时会被裁成「空白」 + var boxHeight = Math.Max(height, fontSize * 1.4f); return new LabelTextOp { LeftPt = left, TopPt = top, WidthPt = width, - HeightPt = height, + HeightPt = boxHeight, Text = text, - FontSizePt = opt.FontSize is > 0 ? opt.FontSize.Value : 10f, + FontSizePt = fontSize, Bold = bold, ColorHex = opt.Color, Align = (opt.TextAlign ?? "left").ToLowerInvariant(), @@ -185,6 +203,9 @@ public sealed class HiprintTemplateParser }; } + private static string? FirstNonEmpty(params string?[] values) => + values.FirstOrDefault(x => !string.IsNullOrWhiteSpace(x)); + private static string? ResolveValue( string? field, string? fallback, diff --git a/src/Oms.Tests/Labeling/HiprintTemplateParserTests.cs b/src/Oms.Tests/Labeling/HiprintTemplateParserTests.cs index bcff18c..344dfbd 100644 --- a/src/Oms.Tests/Labeling/HiprintTemplateParserTests.cs +++ b/src/Oms.Tests/Labeling/HiprintTemplateParserTests.cs @@ -49,6 +49,59 @@ public class HiprintTemplateParserTests act.Should().Throw(); } + [Fact] + public void Parse_StaticCustomText_ShouldKeepTitleAndNotClipHeight() + { + const string json = """ + { + "panels": [{ + "width": 100, + "height": 50, + "printElements": [{ + "options": { + "left": 10, + "top": 10, + "width": 80, + "height": 13, + "title": "箱子数量", + "fontSize": 10, + "hideTitle": true + }, + "printElementType": { "type": "text", "title": "文本" } + }] + }] + } + """; + + var page = new HiprintTemplateParser().Parse(json, new Dictionary()); + var text = page.Ops.OfType().Should().ContainSingle().Subject; + text.Text.Should().Be("箱子数量"); + text.HeightPt.Should().BeGreaterThanOrEqualTo(14f); + } + + [Fact] + public void Parse_StaticTextFromTestData_WhenTitleEmpty() + { + const string json = """ + { + "panels": [{ + "width": 100, + "height": 50, + "printElements": [{ + "options": { + "left": 1, "top": 1, "width": 50, "height": 16, + "testData": "仅测试数据文案" + }, + "printElementType": { "type": "text" } + }] + }] + } + """; + + var page = new HiprintTemplateParser().Parse(json, new Dictionary()); + page.Ops.OfType().Should().ContainSingle(x => x.Text == "仅测试数据文案"); + } + internal static string LoadFixture(string fileName) { var dir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;