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)!;