mirror of
https://gitee.com/ant-design-blazor/ant-design-blazor.git
synced 2024-11-29 18:48:50 +08:00
feat(module: form): Add GenerateFormItem component for automatic generation basic FormItem (#3877)
* add HideColumnsByName parameter to handle GenerateColumns hide some column * add docs * Add GenerateFormItem component for automatic generation basic FormItem * update doc * update demo * [GenerateFormItem] remove parameter RulesRequireByType, add ValidateRules * [GenerateFormItem] delete the redundant variables. * [GenerateFormItem] Support automatic generation of nested types. * [GenerateFormItem] Support subfrom multi-style * [GenerateFormItem] update demo --------- Co-authored-by: James Yeung <shunjiey@hotmail.com>
This commit is contained in:
parent
45aae2b047
commit
54da103867
@ -3,6 +3,7 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
|
||||
namespace AntDesign
|
||||
@ -97,5 +98,10 @@ namespace AntDesign
|
||||
or TypeCode.UInt32
|
||||
or TypeCode.UInt64;
|
||||
}
|
||||
|
||||
public static bool IsArrayOrList(this Type that) => that != null && (that.IsArray || typeof(IList).IsAssignableFrom(that));
|
||||
|
||||
public static bool IsUserDefinedClass(this Type thta) =>
|
||||
thta.IsClass && thta.Namespace != null && !thta.Namespace.StartsWith("System");
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,9 @@ using OneOf;
|
||||
|
||||
namespace AntDesign
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
[CascadingTypeParameter(nameof(TModel))]
|
||||
#endif
|
||||
public partial class Form<TModel> : AntDomComponentBase, IForm
|
||||
{
|
||||
private readonly string _prefixCls = "ant-form";
|
||||
|
311
components/form/GenerateFormItem.razor.cs
Normal file
311
components/form/GenerateFormItem.razor.cs
Normal file
@ -0,0 +1,311 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Rendering;
|
||||
using AntDesign.Internal;
|
||||
|
||||
namespace AntDesign;
|
||||
|
||||
public partial class GenerateFormItem<TModel> : ComponentBase
|
||||
{
|
||||
private PropertyInfo[] _propertyInfos = { };
|
||||
|
||||
[CascadingParameter(Name = "Form")] private IForm Form { get; set; }
|
||||
|
||||
[Parameter] public Func<string, FormValidationRule[]?>? ValidateRules { get; set; }
|
||||
|
||||
[Parameter] public Func<string, RenderFragment?>? Definitions { get; set; }
|
||||
|
||||
[Parameter] public Func<string, bool>? NotGenerate { get; set; }
|
||||
|
||||
[Parameter] public string SubformStyle { get; set; } = "Collapse";
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_propertyInfos = typeof(TModel).GetProperties();
|
||||
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
protected override void BuildRenderTree(RenderTreeBuilder builder)
|
||||
{
|
||||
foreach (var property in _propertyInfos)
|
||||
{
|
||||
// Not Generate
|
||||
if (NotGenerate != null && NotGenerate.Invoke(property.Name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
GenerateByType(property, builder, Form.Model, property.Name);
|
||||
}
|
||||
}
|
||||
|
||||
private void MakeFormItemBlock(PropertyInfo property, FormValidationRule[]? validateRule,
|
||||
RenderFragment? formItemFragment,
|
||||
RenderTreeBuilder builder, int sequenceIndex)
|
||||
{
|
||||
var displayName = property.GetCustomAttribute<DisplayNameAttribute>()?.DisplayName ?? property.Name;
|
||||
if (validateRule != null)
|
||||
{
|
||||
builder.OpenComponent(sequenceIndex++, typeof(FormItem));
|
||||
builder.AddAttribute(sequenceIndex++, "Label", displayName);
|
||||
builder.AddAttribute(sequenceIndex++, "Rules", validateRule);
|
||||
builder.AddAttribute(sequenceIndex++, "ChildContent", formItemFragment);
|
||||
builder.CloseComponent();
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.OpenComponent(sequenceIndex++, typeof(FormItem));
|
||||
builder.AddAttribute(sequenceIndex++, "Label", displayName);
|
||||
builder.AddAttribute(sequenceIndex++, "ChildContent", formItemFragment);
|
||||
builder.CloseComponent();
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateByType(PropertyInfo property, RenderTreeBuilder builder, object model, string structurePath)
|
||||
{
|
||||
var sequenceIndex = 0;
|
||||
|
||||
FormValidationRule[]? validateRule = null;
|
||||
if (ValidateRules != null)
|
||||
{
|
||||
validateRule = ValidateRules.Invoke(structurePath);
|
||||
}
|
||||
|
||||
var definitionsFormItem = Definitions?.Invoke(structurePath);
|
||||
if (definitionsFormItem != null)
|
||||
{
|
||||
MakeFormItemBlock(property, validateRule, definitionsFormItem, builder, sequenceIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
var underlyingType = THelper.GetUnderlyingType(property.PropertyType);
|
||||
if (underlyingType == typeof(string))
|
||||
{
|
||||
var formItemFragment = MakeInputString(property, model);
|
||||
MakeFormItemBlock(property, validateRule, formItemFragment, builder, sequenceIndex);
|
||||
}
|
||||
|
||||
if (THelper.IsNumericType(underlyingType) && !underlyingType.IsEnum)
|
||||
{
|
||||
var formItemFragment = MakeInputNumeric(property, model);
|
||||
MakeFormItemBlock(property, validateRule, formItemFragment, builder, sequenceIndex);
|
||||
}
|
||||
|
||||
if (underlyingType == typeof(DateTime))
|
||||
{
|
||||
var formItemFragment = MakeDatePicker(property, model);
|
||||
MakeFormItemBlock(property, validateRule, formItemFragment, builder, sequenceIndex);
|
||||
}
|
||||
|
||||
if (underlyingType.IsEnum)
|
||||
{
|
||||
var formItemFragment = MakeEnumSelect(property, model);
|
||||
MakeFormItemBlock(property, validateRule, formItemFragment, builder, sequenceIndex);
|
||||
}
|
||||
|
||||
if (underlyingType.IsUserDefinedClass() && !underlyingType.IsArrayOrList())
|
||||
{
|
||||
if (SubformStyle == "Block")
|
||||
{
|
||||
MakeSubformWithBlockStyle(property, builder, model, structurePath, underlyingType, sequenceIndex);
|
||||
}
|
||||
else if (SubformStyle == "Collapse")
|
||||
{
|
||||
MakeSubformWithCollapseStyle(property, builder, model, structurePath, underlyingType, sequenceIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private RenderFragment GenerateByTypeResult(PropertyInfo property, RenderTreeBuilder builder, object model,
|
||||
string structurePath)
|
||||
{
|
||||
return builder =>
|
||||
{
|
||||
var sequenceIndex = 0;
|
||||
|
||||
FormValidationRule[]? validateRule = null;
|
||||
if (ValidateRules != null)
|
||||
{
|
||||
validateRule = ValidateRules.Invoke(structurePath);
|
||||
}
|
||||
|
||||
var definitionsFormItem = Definitions?.Invoke(structurePath);
|
||||
if (definitionsFormItem != null)
|
||||
{
|
||||
MakeFormItemBlock(property, validateRule, definitionsFormItem, builder, sequenceIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
var underlyingType = THelper.GetUnderlyingType(property.PropertyType);
|
||||
if (underlyingType == typeof(string))
|
||||
{
|
||||
var formItemFragment = MakeInputString(property, model);
|
||||
MakeFormItemBlock(property, validateRule, formItemFragment, builder, sequenceIndex);
|
||||
}
|
||||
|
||||
if (THelper.IsNumericType(underlyingType) && !underlyingType.IsEnum)
|
||||
{
|
||||
var formItemFragment = MakeInputNumeric(property, model);
|
||||
MakeFormItemBlock(property, validateRule, formItemFragment, builder, sequenceIndex);
|
||||
}
|
||||
|
||||
if (underlyingType == typeof(DateTime))
|
||||
{
|
||||
var formItemFragment = MakeDatePicker(property, model);
|
||||
MakeFormItemBlock(property, validateRule, formItemFragment, builder, sequenceIndex);
|
||||
}
|
||||
|
||||
if (underlyingType.IsEnum)
|
||||
{
|
||||
var formItemFragment = MakeEnumSelect(property, model);
|
||||
MakeFormItemBlock(property, validateRule, formItemFragment, builder, sequenceIndex);
|
||||
}
|
||||
|
||||
if (underlyingType.IsUserDefinedClass() && !underlyingType.IsArrayOrList())
|
||||
{
|
||||
if (SubformStyle == "Block")
|
||||
{
|
||||
MakeSubformWithBlockStyle(property, builder, model, structurePath, underlyingType, sequenceIndex);
|
||||
}
|
||||
else if (SubformStyle == "Collapse")
|
||||
{
|
||||
MakeSubformWithCollapseStyle(property, builder, model, structurePath, underlyingType,
|
||||
sequenceIndex);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void MakeSubformWithBlockStyle(PropertyInfo property, RenderTreeBuilder builder, object model,
|
||||
string structurePath, Type underlyingType, int sequenceIndex)
|
||||
{
|
||||
var childModel = property.GetValue(model);
|
||||
if (childModel == null) return;
|
||||
var propertyInfos = underlyingType.GetProperties();
|
||||
var displayName = property.GetCustomAttribute<DisplayNameAttribute>()?.DisplayName ?? property.Name;
|
||||
builder.OpenElement(sequenceIndex++, "div");
|
||||
builder.AddAttribute(sequenceIndex++, "style",
|
||||
"border: 1px solid #9e9e9e;padding: 16px;position: relative;border-radius: 2px;");
|
||||
builder.OpenElement(sequenceIndex++, "div");
|
||||
builder.AddAttribute(sequenceIndex++, "style", "position: absolute;top: -12px;background: #fff;");
|
||||
builder.AddContent(sequenceIndex++, displayName);
|
||||
builder.CloseElement();
|
||||
foreach (var propertyInfo in propertyInfos)
|
||||
{
|
||||
// Not Generate
|
||||
var itemStructurePath = $"{structurePath}.{propertyInfo.Name}";
|
||||
if (NotGenerate != null && NotGenerate.Invoke(itemStructurePath))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
GenerateByType(propertyInfo, builder, childModel, itemStructurePath);
|
||||
}
|
||||
|
||||
builder.CloseElement();
|
||||
}
|
||||
|
||||
private void MakeSubformWithCollapseStyle(PropertyInfo property, RenderTreeBuilder builder, object model,
|
||||
string structurePath, Type underlyingType, int sequenceIndex)
|
||||
{
|
||||
var childModel = property.GetValue(model);
|
||||
if (childModel == null) return;
|
||||
var propertyInfos = underlyingType.GetProperties();
|
||||
var displayName = property.GetCustomAttribute<DisplayNameAttribute>()?.DisplayName ?? property.Name;
|
||||
builder.OpenComponent(sequenceIndex++, typeof(Collapse));
|
||||
RenderFragment panel = panelBuilder =>
|
||||
{
|
||||
var panelIndex = 0;
|
||||
panelBuilder.OpenComponent(panelIndex++, typeof(Panel));
|
||||
panelBuilder.AddAttribute(panelIndex++, "header", displayName);
|
||||
RenderFragment subform = subformBuilder =>
|
||||
{
|
||||
foreach (var propertyInfo in propertyInfos)
|
||||
{
|
||||
// Not Generate
|
||||
var itemStructurePath = $"{structurePath}.{propertyInfo.Name}";
|
||||
if (NotGenerate != null && NotGenerate.Invoke(itemStructurePath))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
GenerateByType(propertyInfo, subformBuilder, childModel, itemStructurePath);
|
||||
}
|
||||
};
|
||||
panelBuilder.AddAttribute(panelIndex++, "ChildContent", subform);
|
||||
panelBuilder.CloseComponent();
|
||||
};
|
||||
builder.AddAttribute(sequenceIndex++, "ChildContent", panel);
|
||||
|
||||
builder.CloseComponent();
|
||||
}
|
||||
|
||||
private object? CreateEventCallback(Type callbackType, object receiver, Action<object> callback)
|
||||
{
|
||||
var createMethod = typeof(EventCallbackFactory)
|
||||
.GetMethod(nameof(EventCallbackFactory.Create), new Type[] { typeof(object), typeof(Action<object>) });
|
||||
|
||||
if (createMethod == null)
|
||||
{
|
||||
throw new InvalidOperationException("Unable to find EventCallbackFactory.Create method.");
|
||||
}
|
||||
|
||||
var eventCallbackType = typeof(EventCallback<>).MakeGenericType(callbackType);
|
||||
|
||||
var eventCallback = Activator.CreateInstance(eventCallbackType, receiver, callback);
|
||||
|
||||
return eventCallback;
|
||||
}
|
||||
|
||||
private RenderFragment MakeInputString(PropertyInfo property, object model)
|
||||
{
|
||||
return MakeFormItem(property, typeof(Input<>), model);
|
||||
}
|
||||
|
||||
private RenderFragment MakeInputNumeric(PropertyInfo property, object model)
|
||||
{
|
||||
return MakeFormItem(property, typeof(InputNumber<>), model);
|
||||
}
|
||||
|
||||
private RenderFragment MakeDatePicker(PropertyInfo property, object model)
|
||||
{
|
||||
return MakeFormItem(property, typeof(DatePicker<>), model);
|
||||
}
|
||||
|
||||
private RenderFragment MakeEnumSelect(PropertyInfo property, object model)
|
||||
{
|
||||
return MakeFormItem(property, typeof(EnumSelect<>), model);
|
||||
}
|
||||
|
||||
private RenderFragment MakeFormItem(PropertyInfo property, Type formItemChildContent, object model)
|
||||
{
|
||||
var constant = Expression.Constant(model, model.GetType());
|
||||
var exp = Expression.Property(constant, property.Name);
|
||||
var genericType = formItemChildContent;
|
||||
var constructedType = genericType.MakeGenericType(property.PropertyType);
|
||||
|
||||
var funcType = typeof(Func<>);
|
||||
var constructedFuncType = funcType.MakeGenericType(property.PropertyType);
|
||||
|
||||
return builder =>
|
||||
{
|
||||
builder.OpenComponent(0, constructedType);
|
||||
builder.AddAttribute(1, "Value", property.GetValue(model));
|
||||
var eventCallback = CreateEventCallback(property.PropertyType, this,
|
||||
new Action<object>(o => property.SetValue(model, o)));
|
||||
builder.AddAttribute(2, "ValueChanged", eventCallback);
|
||||
builder.AddAttribute(3, "ValueExpression", Expression.Lambda(constructedFuncType, exp));
|
||||
builder.CloseComponent();
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,162 @@
|
||||
@using System.ComponentModel.DataAnnotations;
|
||||
@using System.ComponentModel
|
||||
|
||||
<Form Model="@model" TModel="Model"
|
||||
ValidateMode="@FormValidateMode.Complex"
|
||||
OnFinish="OnFinish"
|
||||
OnFinishFailed="OnFinishFailed"
|
||||
LabelColSpan="8"
|
||||
WrapperColSpan="16">
|
||||
<GenerateFormItem TModel="Model" Definitions="Definitions" ValidateRules="ValidateRules" NotGenerate="NotGenerate"/>
|
||||
<FormItem Label="ListTestOutOfGenerate">
|
||||
<Select @bind-Values="@context.I" Style="width: 120px;" TItemValue="string" TItem="string">
|
||||
<SelectOptions>
|
||||
<SelectOption Value="@("lucy")" Label="Lucy"/>
|
||||
</SelectOptions>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem WrapperColOffset="8" WrapperColSpan="16">
|
||||
<Button Type="@ButtonType.Primary" HtmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
|
||||
@code
|
||||
{
|
||||
public class Model
|
||||
{
|
||||
[DisplayName("StringTest")] public string A { get; set; }
|
||||
|
||||
[DisplayName("StringTest nullable")] public string? B { get; set; }
|
||||
|
||||
[DisplayName("DateTimeTest")]
|
||||
[Required]
|
||||
public DateTime C { get; set; }
|
||||
|
||||
[DisplayName("DateTimeTestNullable")] public DateTime? D { get; set; }
|
||||
|
||||
[DisplayName("NumericTest")]
|
||||
[Required]
|
||||
public decimal E { get; set; }
|
||||
|
||||
[DisplayName("NumericTestNullable")] public decimal? F { get; set; }
|
||||
|
||||
[DisplayName("EnumTest")] [Required] public TestEnum G { get; set; }
|
||||
|
||||
[DisplayName("EnumTestNullable")] public TestEnum? H { get; set; }
|
||||
|
||||
[DisplayName("ListTestOutOfGenerate")]
|
||||
[Required]
|
||||
public IEnumerable<string> I { get; set; }
|
||||
|
||||
[DisplayName("DefinitionsTest")] public string J { get; set; }
|
||||
|
||||
[ValidateComplexType]
|
||||
public ModelChild ModelChild { get; set; }
|
||||
}
|
||||
|
||||
public enum TestEnum
|
||||
{
|
||||
A,
|
||||
B,
|
||||
C
|
||||
}
|
||||
|
||||
public class ModelChild
|
||||
{
|
||||
[Required] public string AA { get; set; }
|
||||
|
||||
public string? BB { get; set; }
|
||||
|
||||
[Required] public DateTime CC { get; set; }
|
||||
|
||||
public DateTime? DD { get; set; }
|
||||
|
||||
[Required] public decimal EE { get; set; }
|
||||
|
||||
public decimal? FF { get; set; }
|
||||
|
||||
[Required] public TestEnum GG { get; set; }
|
||||
|
||||
public TestEnum? HH { get; set; }
|
||||
|
||||
[Required] public IEnumerable<string> II { get; set; }
|
||||
|
||||
public string JJ { get; set; }
|
||||
|
||||
[ValidateComplexType]
|
||||
public ModelChild2 ModelChild2 { get; set; }
|
||||
}
|
||||
|
||||
public class ModelChild2
|
||||
{
|
||||
[Required] public string AAA { get; set; }
|
||||
}
|
||||
|
||||
private Model model = new Model()
|
||||
{
|
||||
ModelChild = new ModelChild()
|
||||
{
|
||||
ModelChild2 = new ModelChild2()
|
||||
}
|
||||
};
|
||||
|
||||
public FormValidationRule[]? ValidateRules(string structurePath)
|
||||
{
|
||||
if (structurePath == "A")
|
||||
{
|
||||
return new FormValidationRule[]
|
||||
{
|
||||
new FormValidationRule { Required = true, Min = 5, Max = 10 }
|
||||
};
|
||||
}
|
||||
if (structurePath == "ModelChild.AA")
|
||||
{
|
||||
return new FormValidationRule[]
|
||||
{
|
||||
new FormValidationRule { Required = true, Max = 10 }
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public RenderFragment? Definitions(string structurePath)
|
||||
{
|
||||
if (structurePath == "J")
|
||||
{
|
||||
return @<Select @bind-Value="model.J" Style="width: 120px;" TItemValue="string" TItem="string">
|
||||
<SelectOptions>
|
||||
<SelectOption Value="@("lucy")" Label="Lucy"/>
|
||||
</SelectOptions>
|
||||
</Select>;
|
||||
}
|
||||
if (structurePath == "ModelChild.JJ")
|
||||
{
|
||||
return @<Select @bind-Value="model.ModelChild.JJ" Style="width: 120px;" TItemValue="string" TItem="string">
|
||||
<SelectOptions>
|
||||
<SelectOption Value="@("lucy")" Label="Lucy"/>
|
||||
</SelectOptions>
|
||||
</Select>;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool NotGenerate(string structurePath)
|
||||
{
|
||||
if (structurePath == "I") return true;
|
||||
if (structurePath == "ModelChild.II") return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void OnFinish(EditContext editContext)
|
||||
{
|
||||
Console.WriteLine($"Success:{JsonSerializer.Serialize(model)}");
|
||||
}
|
||||
|
||||
private void OnFinishFailed(EditContext editContext)
|
||||
{
|
||||
Console.WriteLine($"Failed:{JsonSerializer.Serialize(model)}");
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
---
|
||||
order: 18.2
|
||||
title:
|
||||
en-US: Generate Form Item
|
||||
zh-CN: 自动生成表单
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
可通过 `TModel` 类型自动生成表单元素。当前支持string、DateTime、number、enum类型成员自动生成,根据成员是否为可空控制是否为必填
|
||||
|
||||
> **注意:** 此功能正在迭代中,后续本版可能会存在不兼容的变更。
|
||||
|
||||
## en-US
|
||||
|
||||
FormItem can be automatically generated by the 'TModel' type. currently supports automatic generation for string, DateTime, number, and enum type members. Whether a member is required or not is controlled based on its nullability.
|
||||
|
||||
> **Notice:** This feature is in iteration, and incompatible changes may occur in subsequent releases.
|
@ -116,6 +116,19 @@ var formConfig = new FormConfig {
|
||||
</ConfigProvider>;
|
||||
```
|
||||
|
||||
### GenerateFormItem
|
||||
FormItem can be automatically generated by the 'TItem' type. currently supports automatic generation for string, DateTime, number, and enum type members, Support automatic generation of nested types.
|
||||
> **Notice:** This feature is in iteration, and incompatible changes may occur in subsequent releases.
|
||||
|
||||
| Name | Description | Type | Default Value |
|
||||
| --- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------|------|
|
||||
| ValidateRules | A generic delegate with one parameters, where the first parameter is of type PropertyInfo, and the return value is of type FormValidationRule[]?. | Func<PropertyInfo, FormValidationRule[]?> | null |
|
||||
| Definitions | A generic delegate with two parameters, where the first parameter is of type PropertyInfo, the second parameter is of type TItem, and the return value is of type RenderFragment. | Func<PropertyInfo, TItem, RenderFragment>? | null |
|
||||
| NotGenerate | A generic delegate with two parameters, where the first parameter is of type PropertyInfo, the second parameter is of type TItem, and the return value is of type bool. If the delegate returns true, automatic generation will be skipped. | Func<PropertyInfo, TItem, bool>? | null |
|
||||
| SubformStyle | Nested form style, default to Collapse style, optional Block style.. | string | Collapse |
|
||||
|
||||
|
||||
|
||||
<style>
|
||||
.code-box-demo .ant-form:not(.ant-form-inline):not(.ant-form-vertical) {
|
||||
max-width: 600px;
|
||||
|
@ -117,6 +117,18 @@ var formConfig = new FormConfig {
|
||||
</ConfigProvider>;
|
||||
```
|
||||
|
||||
### GenerateFormItem
|
||||
可通过 `TItem` 类型自动生成表单元素。当前支持string、DateTime、number、enum类型成员自动生成,支持嵌套类型自动生成。
|
||||
> **注意:** 此功能正在迭代中,后续本版可能会存在不兼容的变更。
|
||||
|
||||
| 名称 | 说明 | 类型 | 默认值 |
|
||||
| --- |-------------------------------------------------------------------|-------------------------------------------|--|
|
||||
| ValidateRules | 一个单参数范型委托,第一个参数是熟悉PropertyInfo,返回值为FormValidationRule[]? | Func<PropertyInfo, FormValidationRule[]?> | null |
|
||||
| Definitions | 一个两参数范型委托,第一个参数是熟悉PropertyInfo,第二个参数是TItem, 返回值为RenderFragment | Func<PropertyInfo, TItem, RenderFragment>? | null |
|
||||
| NotGenerate | 一个两参数范型委托,第一个参数是熟悉PropertyInfo,第二个参数是TItem, 返回值为bool,返回true则不自动生成 | Func<PropertyInfo, TItem, bool>? | null |
|
||||
| SubformStyle | 嵌套表单风格,默认为Collapse风格,可选Block风格 | string | Collapse |
|
||||
|
||||
|
||||
<style>
|
||||
.code-box-demo .ant-form:not(.ant-form-inline):not(.ant-form-vertical) {
|
||||
max-width: 600px;
|
||||
|
Loading…
Reference in New Issue
Block a user