diff --git a/AndroidPDACommand.csproj b/AndroidPDACommand.csproj
index 13b436e60b5038393f72f9fbb676989ddfefa3e8..fba84ea341ed0b7ffabbd4cd0d55f7e6892fb42d 100644
--- a/AndroidPDACommand.csproj
+++ b/AndroidPDACommand.csproj
@@ -50,6 +50,8 @@
+
+
@@ -166,6 +168,8 @@
+
+
diff --git a/OfflinePlusAddPattern_Async.cs b/OfflinePlusAddPattern_Async.cs
index fd5a6e7d6761807d12378e1d8391fa9d521dc5e8..8f7612d8c4a69afc31bd1faebb4cc5de4ad3fd1a 100644
--- a/OfflinePlusAddPattern_Async.cs
+++ b/OfflinePlusAddPattern_Async.cs
@@ -1,7 +1,5 @@
-using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
-using System.Linq;
using GrapeCity.Forguncy.Commands;
using GrapeCity.Forguncy.Plugin;
@@ -11,24 +9,11 @@ namespace AndroidPDACommand
[Category("活字格安卓容器(HAC)")]
public class OfflinePlusAddPattern_Async : BaseAsyncCommand
{
- [FormulaProperty]
- [DisplayName("项目编号")]
- [Description("项目的唯一标识,全局唯一")]
- public object PatternId { get; set; }
-
- [DisplayName("名称")]
- [FormulaProperty]
- public object Title { get; set; }
-
- [DisplayName("描述")]
- [FormulaProperty]
- public object Description { get; set; }
-
[Required]
[FormulaProperty]
- [DisplayName("版本号")]
- [Description("同一项目编号下的表单定义版本,用于区分表单结构变更。")]
- public string SchemaVersion { get; set; } = "1.0";
+ [DisplayName("离线表单JSON")]
+ [Description("由“构建离线表单”命令生成的表单JSON。")]
+ public object PatternJson { get; set; }
[FormulaProperty]
[DisplayName("手册附件单元格")]
@@ -40,299 +25,9 @@ namespace AndroidPDACommand
[Description("选择保存员工签名附件的单元格。下载离线表单时会把签名文件随表单一起保存到离线设备。")]
public object SignatureAttachmentCell { get; set; }
- [DisplayName("步骤")]
- [ObjectListProperty(ItemType = typeof(OfflineFormStep))]
- public List Steps { get; set; }
-
public override string ToString()
{
return "下载离线表单";
}
}
-
- public class OfflineFormStep : ObjectPropertyBase, INamedObject
- {
- public string Name { get; set; }
-
- [FormulaProperty]
- [DisplayName("步骤编号")]
- [Description("该步骤的唯一标识,同一个离线表单项目内必须唯一。")]
- public object StepId { get; set; }
-
- [FormulaProperty]
- [DisplayName("标题")]
- public object Title { get; set; }
-
- [TreeProperty(NodeType = typeof(OfflineFormNode), DefaultNodeName = "节点")]
- [DisplayName("页面内容")]
- public List Items { get; set; } = new List();
- }
-
- public class OfflineFormNode : ObjectPropertyBase, ITreeNode
- {
- private static readonly string[] CommonFieldVisibleProperties =
- {
- nameof(ItemId),
- nameof(ItemType),
- nameof(Title),
- nameof(Hint),
- nameof(Required)
- };
-
- private static readonly HashSet FieldTypeControlledProperties = new HashSet(CommonFieldVisibleProperties)
- {
- nameof(Value),
- nameof(CheckOptions),
- nameof(SelectOptionsList),
- nameof(IncludeSeconds),
- nameof(MaxCount),
- nameof(AllowImageUpload),
- nameof(CompressionOptions),
- nameof(WatermarkOptions),
- nameof(FileItemConfig)
- };
-
- private static readonly Dictionary> FieldVisiblePropertiesByType = new Dictionary>
- {
- { "textItem", CreateFieldVisibleProperties(nameof(Value), nameof(CheckOptions)) },
- { "passwordItem", CreateFieldVisibleProperties(nameof(Value), nameof(CheckOptions)) },
- { "selectItem", CreateFieldVisibleProperties(nameof(Value), nameof(SelectOptionsList)) },
- { "datePicker", CreateFieldVisibleProperties(nameof(Value)) },
- { "timePicker", CreateFieldVisibleProperties(nameof(Value), nameof(IncludeSeconds)) },
- { "imageItem", CreateFieldVisibleProperties(nameof(MaxCount), nameof(AllowImageUpload), nameof(CompressionOptions), nameof(WatermarkOptions)) },
- { "fileItem", CreateFieldVisibleProperties(nameof(FileItemConfig)) }
- };
-
- [DisplayName("节点名称")]
- public string Text { get; set; }
-
- [Browsable(false)]
- public IEnumerable Children { get; set; } = new List();
-
- [DisplayName("节点类型")]
- [ComboProperty(ValueList = "group|text|field", DisplayList = "分组|普通文本|表单项")]
- public string NodeType { get; set; } = "group";
-
- [FormulaProperty]
- [DisplayName("说明内容")]
- public object Content { get; set; }
-
- [DisplayName("默认折叠")]
- public bool DefaultCollapsed { get; set; }
-
- [FormulaProperty]
- [DisplayName("编号")]
- [Description("该表单项的唯一标识,全局唯一,无论是同一项目内,还是跨项目,都必须唯一,更建议使用项目编号作为前缀拼接;例如项目为Project01,则该表单项编号为Project01_Name")]
- public object ItemId { get; set; }
-
- [DisplayName("类型")]
- [ComboProperty(ValueList = "textItem|passwordItem|selectItem|datePicker|timePicker|imageItem|fileItem", DisplayList = "文本框|密码框|选择框|日期选择|时间选择|图片列表|文件上传")]
- public string ItemType { get; set; } = "textItem";
-
- [FormulaProperty]
- [DisplayName("标题")]
- public object Title { get; set; }
-
- [FormulaProperty]
- [DisplayName("水印")]
- public object Hint { get; set; }
-
- [DisplayName("必填")]
- public bool Required { get; set; }
-
- [FormulaProperty]
- [DisplayName("默认值")]
- public object Value { get; set; }
-
- [DisplayName("包含秒")]
- [Description("仅时间选择类型生效。启用后可选择时分秒;关闭时使用系统原生时分选择器。")]
- public bool IncludeSeconds { get; set; }
-
-
- [ObjectProperty(ObjType = typeof(CheckOptions))]
- [DisplayName("校验规则")]
- public CheckOptions CheckOptions { get; set; }
-
- [ListProperty]
- [DisplayName("选则项")]
- public List SelectOptionsList { get; set; }
-
- [FormulaProperty]
- [DisplayName("图片最大数量")]
- [Description("0表示不限制图片数量。")]
- public object MaxCount { get; set; } = 0;
-
- [DisplayName("允许图片上传")]
- [Description("仅图片列表类型生效。启用后填报时除拍照外还可以从本地选择图片。")]
- public bool AllowImageUpload { get; set; }
-
- [ObjectProperty(ObjType = typeof(ImageCompressionOptions))]
- [DisplayName("图片压缩设置")]
- public ImageCompressionOptions CompressionOptions { get; set; } = new ImageCompressionOptions();
-
- [ObjectProperty(ObjType = typeof(ImageWatermarkOptions))]
- [DisplayName("拍照水印配置")]
- public ImageWatermarkOptions WatermarkOptions { get; set; } = new ImageWatermarkOptions();
-
- [ObjectProperty(ObjType = typeof(FileItemConfig))]
- [DisplayName("文件上传配置")]
- public FileItemConfig FileItemConfig { get; set; } = new FileItemConfig();
-
- public override bool GetDesignerPropertyVisible(string propertyName)
- {
- var isField = NodeType == "field";
- var isText = NodeType == "text";
- var isGroup = NodeType == "group";
-
- switch (propertyName)
- {
- case nameof(Content):
- return isText;
- case nameof(DefaultCollapsed):
- return isGroup;
- default:
- if (FieldTypeControlledProperties.Contains(propertyName))
- {
- return isField && IsFieldPropertyVisible(ItemType, propertyName);
- }
- return base.GetDesignerPropertyVisible(propertyName);
- }
- }
-
- public override object Clone()
- {
- return new OfflineFormNode
- {
- Text = Text,
- Children = Children == null ? new List() : Children.ToList(),
- NodeType = NodeType,
- Content = Content,
- DefaultCollapsed = DefaultCollapsed,
- ItemId = ItemId,
- ItemType = ItemType,
- Title = Title,
- Hint = Hint,
- Required = Required,
- Value = Value,
- IncludeSeconds = IncludeSeconds,
- CheckOptions = CheckOptions,
- SelectOptionsList = SelectOptionsList,
- MaxCount = MaxCount,
- AllowImageUpload = AllowImageUpload,
- CompressionOptions = CompressionOptions,
- WatermarkOptions = WatermarkOptions,
- FileItemConfig = FileItemConfig
- };
- }
-
- private static HashSet CreateFieldVisibleProperties(params string[] propertyNames)
- {
- var visibleProperties = new HashSet(CommonFieldVisibleProperties);
- foreach (var propertyName in propertyNames)
- {
- visibleProperties.Add(propertyName);
- }
- return visibleProperties;
- }
-
- private static bool IsFieldPropertyVisible(string itemType, string propertyName)
- {
- HashSet visibleProperties;
- if (!FieldVisiblePropertiesByType.TryGetValue(itemType ?? "textItem", out visibleProperties))
- {
- visibleProperties = FieldVisiblePropertiesByType["textItem"];
- }
- return visibleProperties.Contains(propertyName);
- }
- }
-
- public class CheckOptions : ObjectPropertyBase
- {
- [FormulaProperty]
- [DisplayName("最小长度")]
- public object MinLength { get; set; }
-
- [FormulaProperty]
- [DisplayName("最大长度")]
- public object MaxLength { get; set; }
-
- [FormulaProperty]
- [DisplayName("正则表达式")]
- public object RegexPattern { get; set; }
- }
-
- public class SelectOptions : ObjectPropertyBase
- {
- [FormulaProperty]
- [DisplayName("值")]
- public object Value { get; set; }
-
- [FormulaProperty]
- [DisplayName("显示文本")]
- public object Label { get; set; }
- }
-
- public class ImageCompressionOptions : ObjectPropertyBase
- {
- [DisplayName("启用压缩")]
- [Description("默认关闭,关闭时按原图保存。启用后才使用下面的压缩参数。")]
- public bool EnableCompression { get; set; }
-
- [FormulaProperty]
- [DisplayName("最长边像素")]
- public object MaxLongEdge { get; set; } = 1600;
-
- [FormulaProperty]
- [DisplayName("JPEG质量")]
- public object JpegQuality { get; set; } = 80;
-
- [FormulaProperty]
- [DisplayName("目标大小KB")]
- [Description("0表示不按文件大小继续压缩。")]
- public object MaxFileSizeKb { get; set; } = 0;
-
- [FormulaProperty]
- [DisplayName("最低JPEG质量")]
- public object MinQuality { get; set; } = 60;
- }
-
- public class ImageWatermarkOptions : ObjectPropertyBase
- {
- [DisplayName("时间戳水印")]
- public bool EnableTimestamp { get; set; } = false;
-
- [ListProperty]
- [DisplayName("自定义字段")]
- public List Fields { get; set; } = new List
- {
- };
- }
-
- public class ImageWatermarkField : ObjectPropertyBase
- {
- [DisplayName("Key")]
- public string Key { get; set; }
-
- [FormulaProperty]
- [DisplayName("Value")]
- public object Value { get; set; }
- }
-
- public class FileItemConfig : ObjectPropertyBase
- {
- [FormulaProperty]
- [DisplayName("最大数量")]
- [Description("0表示不限制上传文件数量。")]
- public object MaxCount { get; set; } = 0;
-
- [FormulaProperty]
- [DisplayName("允许扩展名")]
- [Description("逗号分隔,每个扩展名前带点;留空表示不限制,例如:.pdf,.doc,.docx。")]
- public object AllowedExtensions { get; set; }
-
- [FormulaProperty]
- [DisplayName("单文件最大大小KB")]
- [Description("0表示不限制单文件大小。")]
- public object MaxFileSizeKb { get; set; } = 0;
- }
}
diff --git a/OfflinePlusBuildFormItem.cs b/OfflinePlusBuildFormItem.cs
new file mode 100644
index 0000000000000000000000000000000000000000..2cf5b47d46f3e5be256bd651c33add4845f4601f
--- /dev/null
+++ b/OfflinePlusBuildFormItem.cs
@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.ComponentModel;
+using GrapeCity.Forguncy.Commands;
+using GrapeCity.Forguncy.Plugin;
+
+namespace AndroidPDACommand
+{
+ [Icon("pack://application:,,,/AndroidPDACommand;component/Resources/Icon_OfflineFormItem.png")]
+ [Category("活字格安卓容器(HAC)")]
+ public class OfflinePlusBuildFormItem : BaseCommand
+ {
+ [TreeProperty(NodeType = typeof(OfflineFormNode), DefaultNodeName = "节点")]
+ [DisplayName("页面内容")]
+ public List Items { get; set; } = new List();
+
+ [DisplayName("将页面内容返回到变量")]
+ [ResultToProperty]
+ public string ItemResult { get; set; } = "OfflineFormItem";
+
+ public override string ToString()
+ {
+ return "构建离线页面内容";
+ }
+ }
+}
diff --git a/OfflinePlusBuildPattern.cs b/OfflinePlusBuildPattern.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7be725adf0e3bd00bb963cca7466f87ac4276954
--- /dev/null
+++ b/OfflinePlusBuildPattern.cs
@@ -0,0 +1,352 @@
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using GrapeCity.Forguncy.Commands;
+using GrapeCity.Forguncy.Plugin;
+
+namespace AndroidPDACommand
+{
+ [Icon("pack://application:,,,/AndroidPDACommand;component/Resources/Icon_OfflineBuild.png")]
+ [Category("活字格安卓容器(HAC)")]
+ public class OfflinePlusBuildPattern : BaseCommand
+ {
+ [FormulaProperty]
+ [DisplayName("项目编号")]
+ [Description("项目的唯一标识,全局唯一")]
+ public object PatternId { get; set; }
+
+ [DisplayName("名称")]
+ [FormulaProperty]
+ public object Title { get; set; }
+
+ [DisplayName("描述")]
+ [FormulaProperty]
+ public object Description { get; set; }
+
+ [Required]
+ [FormulaProperty]
+ [DisplayName("版本号")]
+ [Description("同一项目编号下的表单定义版本,用于区分表单结构变更。")]
+ public string SchemaVersion { get; set; } = "1.0";
+
+ [DisplayName("步骤")]
+ [ObjectListProperty(ItemType = typeof(OfflineFormStep))]
+ public List Steps { get; set; }
+
+ [DisplayName("将离线表单JSON返回到变量")]
+ [ResultToProperty]
+ public string PatternJson { get; set; } = "OfflinePatternJson";
+
+ public override string ToString()
+ {
+ return "构建离线表单";
+ }
+ }
+
+ public class OfflineFormStep : ObjectPropertyBase, INamedObject
+ {
+ public string Name { get; set; }
+
+ [FormulaProperty]
+ [DisplayName("步骤编号")]
+ [Description("该步骤的唯一标识,同一个离线表单项目内必须唯一。")]
+ public object StepId { get; set; }
+
+ [FormulaProperty]
+ [DisplayName("标题")]
+ public object Title { get; set; }
+
+ [DisplayName("使用页面内容公式")]
+ public bool UseItemsFormula { get; set; }
+
+ [TreeProperty(NodeType = typeof(OfflineFormNode), DefaultNodeName = "节点")]
+ [DisplayName("页面内容")]
+ public List Items { get; set; } = new List();
+
+ [FormulaProperty]
+ [DisplayName("页面内容")]
+ public object ItemsFormula { get; set; }
+
+ public override bool GetDesignerPropertyVisible(string propertyName)
+ {
+ switch (propertyName)
+ {
+ case nameof(Items):
+ return !UseItemsFormula;
+ case nameof(ItemsFormula):
+ return UseItemsFormula;
+ default:
+ return base.GetDesignerPropertyVisible(propertyName);
+ }
+ }
+ }
+
+ public class OfflineFormNode : ObjectPropertyBase, ITreeNode
+ {
+ private static readonly string[] CommonFieldVisibleProperties =
+ {
+ nameof(ItemId),
+ nameof(ItemType),
+ nameof(Title),
+ nameof(Hint),
+ nameof(Required)
+ };
+
+ private static readonly HashSet FieldTypeControlledProperties = new HashSet(CommonFieldVisibleProperties)
+ {
+ nameof(Value),
+ nameof(CheckOptions),
+ nameof(SelectOptionsList),
+ nameof(IncludeSeconds),
+ nameof(MaxCount),
+ nameof(AllowImageUpload),
+ nameof(CompressionOptions),
+ nameof(WatermarkOptions),
+ nameof(FileItemConfig)
+ };
+
+ private static readonly Dictionary> FieldVisiblePropertiesByType = new Dictionary>
+ {
+ { "textItem", CreateFieldVisibleProperties(nameof(Value), nameof(CheckOptions)) },
+ { "passwordItem", CreateFieldVisibleProperties(nameof(Value), nameof(CheckOptions)) },
+ { "selectItem", CreateFieldVisibleProperties(nameof(Value), nameof(SelectOptionsList)) },
+ { "datePicker", CreateFieldVisibleProperties(nameof(Value)) },
+ { "timePicker", CreateFieldVisibleProperties(nameof(Value), nameof(IncludeSeconds)) },
+ { "imageItem", CreateFieldVisibleProperties(nameof(MaxCount), nameof(AllowImageUpload), nameof(CompressionOptions), nameof(WatermarkOptions)) },
+ { "fileItem", CreateFieldVisibleProperties(nameof(FileItemConfig)) }
+ };
+
+ [DisplayName("节点名称")]
+ public string Text { get; set; }
+
+ [Browsable(false)]
+ public IEnumerable Children { get; set; } = new List();
+
+ [DisplayName("节点类型")]
+ [ComboProperty(ValueList = "group|text|field", DisplayList = "分组|普通文本|表单项")]
+ public string NodeType { get; set; } = "group";
+
+ [FormulaProperty]
+ [DisplayName("说明内容")]
+ public object Content { get; set; }
+
+ [DisplayName("默认折叠")]
+ public bool DefaultCollapsed { get; set; }
+
+ [FormulaProperty]
+ [DisplayName("编号")]
+ [Description("该表单项的唯一标识,全局唯一,无论是同一项目内,还是跨项目,都必须唯一,更建议使用项目编号作为前缀拼接;例如项目为Project01,则该表单项编号为Project01_Name")]
+ public object ItemId { get; set; }
+
+ [DisplayName("类型")]
+ [ComboProperty(ValueList = "textItem|passwordItem|selectItem|datePicker|timePicker|imageItem|fileItem", DisplayList = "文本框|密码框|选择框|日期选择|时间选择|图片列表|文件上传")]
+ public string ItemType { get; set; } = "textItem";
+
+ [FormulaProperty]
+ [DisplayName("标题")]
+ public object Title { get; set; }
+
+ [FormulaProperty]
+ [DisplayName("水印")]
+ public object Hint { get; set; }
+
+ [DisplayName("必填")]
+ public bool Required { get; set; }
+
+ [FormulaProperty]
+ [DisplayName("默认值")]
+ public object Value { get; set; }
+
+ [DisplayName("包含秒")]
+ [Description("仅时间选择类型生效。启用后可选择时分秒;关闭时使用系统原生时分选择器。")]
+ public bool IncludeSeconds { get; set; }
+
+
+ [ObjectProperty(ObjType = typeof(CheckOptions))]
+ [DisplayName("校验规则")]
+ public CheckOptions CheckOptions { get; set; }
+
+ [ListProperty]
+ [DisplayName("选则项")]
+ public List SelectOptionsList { get; set; }
+
+ [FormulaProperty]
+ [DisplayName("图片最大数量")]
+ [Description("0表示不限制图片数量。")]
+ public object MaxCount { get; set; } = 0;
+
+ [DisplayName("允许图片上传")]
+ [Description("仅图片列表类型生效。启用后填报时除拍照外还可以从本地选择图片。")]
+ public bool AllowImageUpload { get; set; }
+
+ [ObjectProperty(ObjType = typeof(ImageCompressionOptions))]
+ [DisplayName("图片压缩设置")]
+ public ImageCompressionOptions CompressionOptions { get; set; } = new ImageCompressionOptions();
+
+ [ObjectProperty(ObjType = typeof(ImageWatermarkOptions))]
+ [DisplayName("拍照水印配置")]
+ public ImageWatermarkOptions WatermarkOptions { get; set; } = new ImageWatermarkOptions();
+
+ [ObjectProperty(ObjType = typeof(FileItemConfig))]
+ [DisplayName("文件上传配置")]
+ public FileItemConfig FileItemConfig { get; set; } = new FileItemConfig();
+
+ public override bool GetDesignerPropertyVisible(string propertyName)
+ {
+ var isField = NodeType == "field";
+ var isText = NodeType == "text";
+ var isGroup = NodeType == "group";
+
+ switch (propertyName)
+ {
+ case nameof(Content):
+ return isText;
+ case nameof(DefaultCollapsed):
+ return isGroup;
+ default:
+ if (FieldTypeControlledProperties.Contains(propertyName))
+ {
+ return isField && IsFieldPropertyVisible(ItemType, propertyName);
+ }
+ return base.GetDesignerPropertyVisible(propertyName);
+ }
+ }
+
+ public override object Clone()
+ {
+ return new OfflineFormNode
+ {
+ Text = Text,
+ Children = Children == null ? new List() : Children.ToList(),
+ NodeType = NodeType,
+ Content = Content,
+ DefaultCollapsed = DefaultCollapsed,
+ ItemId = ItemId,
+ ItemType = ItemType,
+ Title = Title,
+ Hint = Hint,
+ Required = Required,
+ Value = Value,
+ IncludeSeconds = IncludeSeconds,
+ CheckOptions = CheckOptions,
+ SelectOptionsList = SelectOptionsList,
+ MaxCount = MaxCount,
+ AllowImageUpload = AllowImageUpload,
+ CompressionOptions = CompressionOptions,
+ WatermarkOptions = WatermarkOptions,
+ FileItemConfig = FileItemConfig
+ };
+ }
+
+ private static HashSet CreateFieldVisibleProperties(params string[] propertyNames)
+ {
+ var visibleProperties = new HashSet(CommonFieldVisibleProperties);
+ foreach (var propertyName in propertyNames)
+ {
+ visibleProperties.Add(propertyName);
+ }
+ return visibleProperties;
+ }
+
+ private static bool IsFieldPropertyVisible(string itemType, string propertyName)
+ {
+ HashSet visibleProperties;
+ if (!FieldVisiblePropertiesByType.TryGetValue(itemType ?? "textItem", out visibleProperties))
+ {
+ visibleProperties = FieldVisiblePropertiesByType["textItem"];
+ }
+ return visibleProperties.Contains(propertyName);
+ }
+ }
+
+ public class CheckOptions : ObjectPropertyBase
+ {
+ [FormulaProperty]
+ [DisplayName("最小长度")]
+ public object MinLength { get; set; }
+
+ [FormulaProperty]
+ [DisplayName("最大长度")]
+ public object MaxLength { get; set; }
+
+ [FormulaProperty]
+ [DisplayName("正则表达式")]
+ public object RegexPattern { get; set; }
+ }
+
+ public class SelectOptions : ObjectPropertyBase
+ {
+ [FormulaProperty]
+ [DisplayName("值")]
+ public object Value { get; set; }
+
+ [FormulaProperty]
+ [DisplayName("显示文本")]
+ public object Label { get; set; }
+ }
+
+ public class ImageCompressionOptions : ObjectPropertyBase
+ {
+ [DisplayName("启用压缩")]
+ [Description("默认关闭,关闭时按原图保存。启用后才使用下面的压缩参数。")]
+ public bool EnableCompression { get; set; }
+
+ [FormulaProperty]
+ [DisplayName("最长边像素")]
+ public object MaxLongEdge { get; set; } = 1600;
+
+ [FormulaProperty]
+ [DisplayName("JPEG质量")]
+ public object JpegQuality { get; set; } = 80;
+
+ [FormulaProperty]
+ [DisplayName("目标大小KB")]
+ [Description("0表示不按文件大小继续压缩。")]
+ public object MaxFileSizeKb { get; set; } = 0;
+
+ [FormulaProperty]
+ [DisplayName("最低JPEG质量")]
+ public object MinQuality { get; set; } = 60;
+ }
+
+ public class ImageWatermarkOptions : ObjectPropertyBase
+ {
+ [DisplayName("时间戳水印")]
+ public bool EnableTimestamp { get; set; } = false;
+
+ [ListProperty]
+ [DisplayName("自定义字段")]
+ public List Fields { get; set; } = new List
+ {
+ };
+ }
+
+ public class ImageWatermarkField : ObjectPropertyBase
+ {
+ [DisplayName("Key")]
+ public string Key { get; set; }
+
+ [FormulaProperty]
+ [DisplayName("Value")]
+ public object Value { get; set; }
+ }
+
+ public class FileItemConfig : ObjectPropertyBase
+ {
+ [FormulaProperty]
+ [DisplayName("最大数量")]
+ [Description("0表示不限制上传文件数量。")]
+ public object MaxCount { get; set; } = 0;
+
+ [FormulaProperty]
+ [DisplayName("允许扩展名")]
+ [Description("逗号分隔,每个扩展名前带点;留空表示不限制,例如:.pdf,.doc,.docx。")]
+ public object AllowedExtensions { get; set; }
+
+ [FormulaProperty]
+ [DisplayName("单文件最大大小KB")]
+ [Description("0表示不限制单文件大小。")]
+ public object MaxFileSizeKb { get; set; } = 0;
+ }
+}
diff --git a/Resources/AndroidPDACommand.js b/Resources/AndroidPDACommand.js
index 6022923ef98baf229b43acc585119fc80a6f77d3..e827326cdacd2208cb5a87c82dbf9ed3744754fb 100644
--- a/Resources/AndroidPDACommand.js
+++ b/Resources/AndroidPDACommand.js
@@ -2840,293 +2840,397 @@ var Biometric_Available_Async_Command = (function (_super) {
Forguncy.CommandFactory.registerCommand("AndroidPDACommand.Biometric_Available_Async, AndroidPDACommand", Biometric_Available_Async_Command);
-var OfflinePlusAddPattern_Async_Command = (function (_super) {
- __extends(OfflinePlusAddPattern_Async_Command, _super);
- function OfflinePlusAddPattern_Async_Command() {
- return _super !== null && _super.apply(this, arguments) || this;
+function HAC_OfflinePlusCreateFormNodeBuilder(context) {
+ let me = context;
+
+ function evaluate(value) {
+ if (value === undefined || value === null) {
+ return value;
+ }
+ return me.evaluateFormula(value);
}
- OfflinePlusAddPattern_Async_Command.prototype.execute = async function () {
+ function getList(value) {
+ return Array.isArray(value) ? value : [];
+ }
- let me = this;
- let params = this.CommandParam;
- let patternParamJson = "";
+ function buildSelectOptions(options) {
+ return getList(options).map(function (option) {
+ return {
+ value: evaluate(option.Value),
+ label: evaluate(option.Label)
+ };
+ });
+ }
- function evaluate(value) {
- if (value === undefined || value === null) {
- return value;
- }
- return me.evaluateFormula(value);
+ function buildCheckOptions(options) {
+ if (!options) {
+ return undefined;
}
- function getList(value) {
- return Array.isArray(value) ? value : [];
+ var checkOptions = {};
+ var minLength = evaluate(options.MinLength);
+ var maxLength = evaluate(options.MaxLength);
+ var regexPattern = evaluate(options.RegexPattern);
+ if (minLength !== undefined && minLength !== null && minLength !== "") {
+ checkOptions.minLength = minLength;
}
-
- function buildSelectOptions(options) {
- return getList(options).map(function (option) {
- return {
- value: evaluate(option.Value),
- label: evaluate(option.Label)
- };
- });
+ if (maxLength !== undefined && maxLength !== null && maxLength !== "") {
+ checkOptions.maxLength = maxLength;
}
+ if (regexPattern !== undefined && regexPattern !== null && regexPattern !== "") {
+ checkOptions.regexPattern = regexPattern;
+ }
+ return Object.keys(checkOptions).length > 0 ? checkOptions : undefined;
+ }
- function buildCheckOptions(options) {
- if (!options) {
- return undefined;
- }
+ function buildImageCompression(options) {
+ if (!options) {
+ return undefined;
+ }
- var checkOptions = {};
- var minLength = evaluate(options.MinLength);
- var maxLength = evaluate(options.MaxLength);
- var regexPattern = evaluate(options.RegexPattern);
- if (minLength !== undefined && minLength !== null && minLength !== "") {
- checkOptions.minLength = minLength;
- }
- if (maxLength !== undefined && maxLength !== null && maxLength !== "") {
- checkOptions.maxLength = maxLength;
- }
- if (regexPattern !== undefined && regexPattern !== null && regexPattern !== "") {
- checkOptions.regexPattern = regexPattern;
- }
- return Object.keys(checkOptions).length > 0 ? checkOptions : undefined;
+ var compression = {};
+ var maxLongEdge = evaluate(options.MaxLongEdge);
+ var jpegQuality = evaluate(options.JpegQuality);
+ var maxFileSizeKb = evaluate(options.MaxFileSizeKb);
+ var minQuality = evaluate(options.MinQuality);
+ compression.enableCompression = !!options.EnableCompression;
+ if (maxLongEdge !== undefined && maxLongEdge !== null && maxLongEdge !== "") {
+ compression.maxLongEdge = maxLongEdge;
+ }
+ if (jpegQuality !== undefined && jpegQuality !== null && jpegQuality !== "") {
+ compression.jpegQuality = jpegQuality;
+ }
+ if (maxFileSizeKb !== undefined && maxFileSizeKb !== null && maxFileSizeKb !== "") {
+ compression.maxFileSizeKb = maxFileSizeKb;
}
+ if (minQuality !== undefined && minQuality !== null && minQuality !== "") {
+ compression.minQuality = minQuality;
+ }
+ return Object.keys(compression).length > 0 ? compression : undefined;
+ }
- function buildImageCompression(options) {
- if (!options) {
- return undefined;
- }
+ function buildImageWatermark(options) {
+ if (!options) {
+ return undefined;
+ }
- var compression = {};
- var maxLongEdge = evaluate(options.MaxLongEdge);
- var jpegQuality = evaluate(options.JpegQuality);
- var maxFileSizeKb = evaluate(options.MaxFileSizeKb);
- var minQuality = evaluate(options.MinQuality);
- compression.enableCompression = !!options.EnableCompression;
- if (maxLongEdge !== undefined && maxLongEdge !== null && maxLongEdge !== "") {
- compression.maxLongEdge = maxLongEdge;
- }
- if (jpegQuality !== undefined && jpegQuality !== null && jpegQuality !== "") {
- compression.jpegQuality = jpegQuality;
- }
- if (maxFileSizeKb !== undefined && maxFileSizeKb !== null && maxFileSizeKb !== "") {
- compression.maxFileSizeKb = maxFileSizeKb;
- }
- if (minQuality !== undefined && minQuality !== null && minQuality !== "") {
- compression.minQuality = minQuality;
- }
- return Object.keys(compression).length > 0 ? compression : undefined;
+ var fields = getList(options.Fields);
+ if (!options.EnableTimestamp && fields.length === 0) {
+ return undefined;
}
- function buildImageWatermark(options) {
- if (!options) {
- return undefined;
- }
+ var watermark = {};
+ var items = fields.map(function (field) {
+ return {
+ key: field.Key,
+ value: evaluate(field.Value)
+ };
+ });
- var fields = getList(options.Fields);
- if (!options.EnableTimestamp && fields.length === 0) {
- return undefined;
- }
+ if (options.EnableTimestamp) {
+ watermark.enableTimestamp = true;
+ }
+ if (items.length > 0) {
+ watermark.items = items;
+ }
+ return Object.keys(watermark).length > 0 ? watermark : undefined;
+ }
- var watermark = {};
- var items = fields.map(function (field) {
- return {
- key: field.Key,
- value: evaluate(field.Value)
- };
- });
+ function buildFileItemConfig(options) {
+ if (!options) {
+ return undefined;
+ }
- if (options.EnableTimestamp) {
- watermark.enableTimestamp = true;
- }
- if (items.length > 0) {
- watermark.items = items;
- }
- return Object.keys(watermark).length > 0 ? watermark : undefined;
+ var config = {};
+ var maxCount = evaluate(options.MaxCount);
+ var allowedExtensions = evaluate(options.AllowedExtensions);
+ var maxFileSizeKb = evaluate(options.MaxFileSizeKb);
+ if (maxCount !== undefined && maxCount !== null && maxCount !== "") {
+ config.maxCount = maxCount;
+ }
+ if (allowedExtensions !== undefined && allowedExtensions !== null && allowedExtensions !== "") {
+ config.allowedExtensions = allowedExtensions;
+ }
+ if (maxFileSizeKb !== undefined && maxFileSizeKb !== null && maxFileSizeKb !== "") {
+ config.maxFileSizeKb = maxFileSizeKb;
}
+ return Object.keys(config).length > 0 ? config : undefined;
+ }
- function buildFileItemConfig(options) {
- if (!options) {
- return undefined;
+ var fieldTypeSpecs = {
+ textItem: {
+ apply: function (field, node) {
+ var checkOptions = buildCheckOptions(node.CheckOptions);
+ if (checkOptions) {
+ field.options = checkOptions;
+ }
}
-
- var config = {};
- var maxCount = evaluate(options.MaxCount);
- var allowedExtensions = evaluate(options.AllowedExtensions);
- var maxFileSizeKb = evaluate(options.MaxFileSizeKb);
- if (maxCount !== undefined && maxCount !== null && maxCount !== "") {
- config.maxCount = maxCount;
+ },
+ passwordItem: {
+ apply: function (field, node) {
+ var checkOptions = buildCheckOptions(node.CheckOptions);
+ if (checkOptions) {
+ field.options = checkOptions;
+ }
}
- if (allowedExtensions !== undefined && allowedExtensions !== null && allowedExtensions !== "") {
- config.allowedExtensions = allowedExtensions;
+ },
+ selectItem: {
+ apply: function (field, node) {
+ var selectOptions = buildSelectOptions(node.SelectOptionsList);
+ if (selectOptions.length > 0) {
+ field.options = {
+ selectOptions: selectOptions
+ };
+ }
}
- if (maxFileSizeKb !== undefined && maxFileSizeKb !== null && maxFileSizeKb !== "") {
- config.maxFileSizeKb = maxFileSizeKb;
+ },
+ timePicker: {
+ apply: function (field, node) {
+ if (node.IncludeSeconds) {
+ field.options = {
+ includeSeconds: true
+ };
+ }
}
- return Object.keys(config).length > 0 ? config : undefined;
- }
-
- var fieldTypeSpecs = {
- textItem: {
- apply: function (field, node) {
- var checkOptions = buildCheckOptions(node.CheckOptions);
- if (checkOptions) {
- field.options = checkOptions;
- }
+ },
+ imageItem: {
+ apply: function (field, node) {
+ var options = {};
+ var maxCount = evaluate(node.MaxCount);
+ var compression = buildImageCompression(node.CompressionOptions);
+ var watermark = buildImageWatermark(node.WatermarkOptions);
+ if (maxCount !== undefined && maxCount !== null && maxCount !== "") {
+ options.maxCount = maxCount;
}
- },
- passwordItem: {
- apply: function (field, node) {
- var checkOptions = buildCheckOptions(node.CheckOptions);
- if (checkOptions) {
- field.options = checkOptions;
- }
+ if (node.AllowImageUpload) {
+ options.allowImageUpload = true;
}
- },
- selectItem: {
- apply: function (field, node) {
- var selectOptions = buildSelectOptions(node.SelectOptionsList);
- if (selectOptions.length > 0) {
- field.options = {
- selectOptions: selectOptions
- };
- }
+ if (compression) {
+ options.compression = compression;
}
- },
- timePicker: {
- apply: function (field, node) {
- if (node.IncludeSeconds) {
- field.options = {
- includeSeconds: true
- };
- }
+ if (watermark) {
+ options.watermark = watermark;
}
- },
- imageItem: {
- apply: function (field, node) {
- var options = {};
- var maxCount = evaluate(node.MaxCount);
- var compression = buildImageCompression(node.CompressionOptions);
- var watermark = buildImageWatermark(node.WatermarkOptions);
- if (maxCount !== undefined && maxCount !== null && maxCount !== "") {
- options.maxCount = maxCount;
- }
- if (node.AllowImageUpload) {
- options.allowImageUpload = true;
- }
- if (compression) {
- options.compression = compression;
- }
- if (watermark) {
- options.watermark = watermark;
- }
- if (Object.keys(options).length > 0) {
- field.options = options;
- }
+ if (Object.keys(options).length > 0) {
+ field.options = options;
}
- },
- fileItem: {
- apply: function (field, node) {
- var config = buildFileItemConfig(node.FileItemConfig);
- if (config) {
- field.options = {
- fileItemConfig: config
- };
- }
+ }
+ },
+ fileItem: {
+ apply: function (field, node) {
+ var config = buildFileItemConfig(node.FileItemConfig);
+ if (config) {
+ field.options = {
+ fileItemConfig: config
+ };
}
}
+ }
+ };
+
+ function applyFieldTypeSpec(field, node) {
+ var spec = fieldTypeSpecs[node.ItemType];
+ if (spec && spec.apply) {
+ spec.apply(field, node);
+ }
+ }
+
+ function buildField(node) {
+ var field = {
+ itemId: evaluate(node.ItemId),
+ itemType: node.ItemType,
+ title: evaluate(node.Title),
+ hint: evaluate(node.Hint),
+ required: !!node.Required
};
- function applyFieldTypeSpec(field, node) {
- var spec = fieldTypeSpecs[node.ItemType];
- if (spec && spec.apply) {
- spec.apply(field, node);
- }
+ var value = evaluate(node.Value);
+ if (value !== undefined && value !== null && value !== "") {
+ field.value = value;
}
+ applyFieldTypeSpec(field, node);
+ return field;
+ }
- function buildField(node) {
- var field = {
- itemId: evaluate(node.ItemId),
- itemType: node.ItemType,
- title: evaluate(node.Title),
- hint: evaluate(node.Hint),
- required: !!node.Required
- };
+ function buildNode(node) {
+ var nodeType = node.NodeType || "group";
+ var children = getList(node.Children);
- var value = evaluate(node.Value);
- if (value !== undefined && value !== null && value !== "") {
- field.value = value;
- }
- applyFieldTypeSpec(field, node);
- return field;
+ if (nodeType !== "group" && children.length > 0) {
+ console.warn("离线表单节点不是分组,但包含子节点。", node);
}
- function buildNodes(nodes) {
- return getList(nodes).map(function (node) {
- var nodeType = node.NodeType || "group";
- var children = getList(node.Children);
+ if (nodeType === "field") {
+ return {
+ nodeType: "field",
+ title: node.Text,
+ field: buildField(node)
+ };
+ }
- if (nodeType !== "group" && children.length > 0) {
- console.warn("离线表单节点不是分组,但包含子节点。", node);
- }
+ if (nodeType === "text") {
+ return {
+ nodeType: "text",
+ title: node.Text,
+ content: evaluate(node.Content)
+ };
+ }
- if (nodeType === "field") {
- return {
- nodeType: "field",
- title: node.Text,
- field: buildField(node)
- };
- }
+ return {
+ nodeType: "group",
+ title: node.Text,
+ defaultCollapsed: !!node.DefaultCollapsed,
+ children: buildNodes(children)
+ };
+ }
- if (nodeType === "text") {
- return {
- nodeType: "text",
- title: node.Text,
- content: evaluate(node.Content)
- };
- }
+ function buildNodes(nodes) {
+ return getList(nodes).map(function (node) {
+ return buildNode(node);
+ });
+ }
- return {
- nodeType: "group",
- title: node.Text,
- defaultCollapsed: !!node.DefaultCollapsed,
- children: buildNodes(children)
- };
- });
+ function buildFormItem(nodes) {
+ var items = buildNodes(nodes);
+ if (items.length <= 1) {
+ return items[0];
}
+ var timestamp = new Date().getTime();
+ return {
+ nodeType: "group",
+ key: "offlineFormItemGroup_" + timestamp,
+ title: "页面内容分组_" + timestamp,
+ defaultCollapsed: false,
+ children: items
+ };
+ }
+
+ return {
+ evaluate: evaluate,
+ getList: getList,
+ buildNode: buildNode,
+ buildNodes: buildNodes,
+ buildFormItem: buildFormItem
+ };
+}
+
+var OfflinePlusBuildFormItem_Command = (function (_super) {
+ __extends(OfflinePlusBuildFormItem_Command, _super);
+ function OfflinePlusBuildFormItem_Command() {
+ return _super !== null && _super.apply(this, arguments) || this;
+ }
+
+ OfflinePlusBuildFormItem_Command.prototype.execute = function () {
+ let params = this.CommandParam;
+ var formNodeBuilder = HAC_OfflinePlusCreateFormNodeBuilder(this);
+ var item = formNodeBuilder.buildFormItem(params.Items);
+ HAC_ReturnToParam(params.ItemResult, item);
+ };
+
+ return OfflinePlusBuildFormItem_Command;
+}(Forguncy.CommandBase));
+
+Forguncy.CommandFactory.registerCommand("AndroidPDACommand.OfflinePlusBuildFormItem, AndroidPDACommand", OfflinePlusBuildFormItem_Command);
+
+var OfflinePlusBuildPattern_Command = (function (_super) {
+ __extends(OfflinePlusBuildPattern_Command, _super);
+ function OfflinePlusBuildPattern_Command() {
+ return _super !== null && _super.apply(this, arguments) || this;
+ }
+
+ OfflinePlusBuildPattern_Command.prototype.execute = function () {
+
+ let params = this.CommandParam;
+ var formNodeBuilder = HAC_OfflinePlusCreateFormNodeBuilder(this);
+
function buildSteps(steps) {
- return getList(steps).map(function (step) {
+ return formNodeBuilder.getList(steps).map(function (step) {
return {
- stepId: evaluate(step.StepId),
- title: evaluate(step.Title),
- items: buildNodes(step.Items)
+ stepId: formNodeBuilder.evaluate(step.StepId),
+ title: formNodeBuilder.evaluate(step.Title),
+ items: step.UseItemsFormula ? formNodeBuilder.evaluate(step.ItemsFormula) : formNodeBuilder.buildNodes(step.Items)
};
});
}
- function getManualPdfUrl() {
- if (params.ManualAttachmentCell === undefined || params.ManualAttachmentCell === null || params.ManualAttachmentCell === "") {
- return undefined;
+ const steps = buildSteps(params.Steps);
+ let patternParam = {
+ patternId: formNodeBuilder.evaluate(params.PatternId),
+ schemaVersion: formNodeBuilder.evaluate(params.SchemaVersion),
+ title: formNodeBuilder.evaluate(params.Title),
+ description: formNodeBuilder.evaluate(params.Description),
+ steps: steps
+ };
+
+ var patternParamJson = JSON.stringify(patternParam, null, 2);
+ console.log(patternParam);
+ HAC_ReturnToParam(params.PatternJson, patternParamJson);
+ };
+
+
+ return OfflinePlusBuildPattern_Command;
+}(Forguncy.CommandBase));
+
+Forguncy.CommandFactory.registerCommand("AndroidPDACommand.OfflinePlusBuildPattern, AndroidPDACommand", OfflinePlusBuildPattern_Command);
+
+var OfflinePlusAddPattern_Async_Command = (function (_super) {
+ __extends(OfflinePlusAddPattern_Async_Command, _super);
+ function OfflinePlusAddPattern_Async_Command() {
+ return _super !== null && _super.apply(this, arguments) || this;
+ }
+
+ OfflinePlusAddPattern_Async_Command.prototype.execute = async function () {
+
+ let me = this;
+ let params = this.CommandParam;
+ let finalPatternParamJson = "";
+
+ function evaluate(value) {
+ if (value === undefined || value === null) {
+ return value;
}
+ return me.evaluateFormula(value);
+ }
- var manualValue = evaluate(params.ManualAttachmentCell);
+ function getManualPdfUrl(manualValue) {
if (!manualValue) {
- return undefined;
+ return "";
}
var fileId = (manualValue + "").split("|")[0];
if (!fileId) {
console.warn("手册附件单元格没有文件标识。", manualValue);
- return undefined;
+ return "";
}
return Forguncy.Helper.SpecialPath.getFileDownloadUrl(fileId);
}
- let _ticket = HAC_GenerateCallbackTicket(me, function (payload, payload2) {
- alert("离线表单项目设置成功\r\n\r\nJSON Schema:\r\n" + patternParamJson);
+ var patternJson = evaluate(params.PatternJson);
+ if (!patternJson) {
+ alert("离线表单JSON不能为空。");
+ return;
+ }
+
+ var patternParam;
+ try {
+ patternParam = typeof patternJson === "string" ? JSON.parse(patternJson) : patternJson;
+ } catch (e) {
+ alert("离线表单JSON格式不正确:" + (e ? e.message : ""));
+ return;
+ }
+
+ var manualAttachmentValue = params.ManualAttachmentCell === undefined || params.ManualAttachmentCell === null || params.ManualAttachmentCell === ""
+ ? ""
+ : evaluate(params.ManualAttachmentCell);
+ var signatureBase64 = params.SignatureAttachmentCell === undefined || params.SignatureAttachmentCell === null || params.SignatureAttachmentCell === ""
+ ? ""
+ : evaluate(params.SignatureAttachmentCell);
+
+ let ticket = HAC_GenerateCallbackTicket(me, function (payload, payload2) {
+ alert("离线表单项目设置成功\r\n\r\nJSON Schema:\r\n" + finalPatternParamJson);
me.CommandExecutor.excuteCommand(me.CommandParam.CommandList, {
runTimePageName: me.CommandExecutingInfo.runTimePageName,
commandID: new Date().valueOf().toString(),
@@ -3136,27 +3240,21 @@ var OfflinePlusAddPattern_Async_Command = (function (_super) {
},
locationString: "执行window.offlinePlus.offlinePlusAddPattern"
});
- }, HAC_GetOfflinePlusDebugInfo("添加离线表单项目", "生成回调ticket"));
- const steps = buildSteps(params.Steps);
- let patternParam = {
- ticket: _ticket,
- patternId: this.evaluateFormula(params.PatternId),
- schemaVersion: this.evaluateFormula(params.SchemaVersion),
- title: this.evaluateFormula(params.Title),
- description: this.evaluateFormula(params.Description),
- steps: steps
- };
- const signatureBase64 = params.SignatureAttachmentCell === undefined || params.SignatureAttachmentCell === null || params.SignatureAttachmentCell === ""
- ? ""
- : this.evaluateFormula(params.SignatureAttachmentCell);
- var manualPdfUrl = getManualPdfUrl();
+ }, HAC_GetOfflinePlusDebugInfo("下载离线表单", "生成回调ticket"));
+ if (!ticket) {
+ console.error(HAC_GetOfflinePlusDebugInfo("下载离线表单", "生成回调ticket失败"));
+ return;
+ }
+
+ var manualPdfUrl = getManualPdfUrl(manualAttachmentValue);
+ patternParam.ticket = ticket;
+ finalPatternParamJson = JSON.stringify(patternParam, null, 2);
- patternParamJson = JSON.stringify(patternParam, null, 2);
console.log(patternParam);
if (window.offlinePlus && window.offlinePlus.offlinePlusAddPatternAsync) {
- window.offlinePlus.offlinePlusAddPatternAsync(patternParamJson, manualPdfUrl || "", signatureBase64 || "");
+ window.offlinePlus.offlinePlusAddPatternAsync(finalPatternParamJson, manualPdfUrl || "", signatureBase64 || "");
} else {
- HAC_CallFunctionOutOfHAC(HAC_GetOfflinePlusDebugInfo("添加离线表单项目", "调用offlinePlusAddPatternAsync"));
+ HAC_CallFunctionOutOfHAC(HAC_GetOfflinePlusDebugInfo("下载离线表单", "调用offlinePlusAddPatternAsync"));
}
};
diff --git a/Resources/Icon_OfflineBuild.png b/Resources/Icon_OfflineBuild.png
new file mode 100644
index 0000000000000000000000000000000000000000..9430bea9db1a5a7f715d3ae3259bade60d8e00cb
Binary files /dev/null and b/Resources/Icon_OfflineBuild.png differ
diff --git a/Resources/Icon_OfflineFormItem.png b/Resources/Icon_OfflineFormItem.png
new file mode 100644
index 0000000000000000000000000000000000000000..26e42e6d02f98faf21b2d29902644cb786662b0b
Binary files /dev/null and b/Resources/Icon_OfflineFormItem.png differ