From d63e25653a6d813abb5e5f69d9ee2f6aef652f52 Mon Sep 17 00:00:00 2001 From: rqx110 Date: Mon, 13 Jun 2022 10:36:04 +0800 Subject: [PATCH] feat: implements property extension --- .../Pages/RoleManagement.razor | 4 +- .../Pages/RoleManagement.razor.cs | 20 +- .../Pages/UserManagement.razor | 37 +-- .../Pages/UserManagement.razor.cs | 17 +- .../_Imports.razor | 1 + .../Pages/TenantManagement.razor | 6 +- .../Pages/TenantManagement.razor.cs | 20 +- .../_Imports.razor | 1 + src/Lsw.Abp.AntDesignUI/AbpCrudPageBase.cs | 46 ++++ ...UiObjectExtensionPropertyInfoExtensions.cs | 259 ++++++++++++++++++ .../Components/AbpExtensibleDataGrid.razor | 46 +++- .../CheckExtensionProperty.razor | 13 + .../CheckExtensionProperty.razor.cs | 16 ++ .../DateTimeExtensionProperty.razor | 15 + .../DateTimeExtensionProperty.razor.cs | 17 ++ .../Components/ObjectExtending/EnumHelper.cs | 28 ++ .../ObjectExtending/ExtensionProperties.razor | 27 ++ .../ExtensionProperties.razor.cs | 30 ++ .../ExtensionPropertyComponentBase.cs | 76 +++++ .../LookupExtensionProperty.razor | 16 ++ .../LookupExtensionProperty.razor.cs | 91 ++++++ .../SelectExtensionProperty.razor | 12 + .../SelectExtensionProperty.razor.cs | 53 ++++ .../TextExtensionProperty.razor | 11 + .../TextExtensionProperty.razor.cs | 16 ++ .../TimeExtensionProperty.razor | 11 + .../TimeExtensionProperty.razor.cs | 17 ++ ...ctExtensionPropertyInfoBlazorExtensions.cs | 37 +++ 28 files changed, 893 insertions(+), 50 deletions(-) create mode 100644 src/Lsw.Abp.AntDesignUI/AntDesignUiObjectExtensionPropertyInfoExtensions.cs create mode 100644 src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/CheckExtensionProperty.razor create mode 100644 src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/CheckExtensionProperty.razor.cs create mode 100644 src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/DateTimeExtensionProperty.razor create mode 100644 src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/DateTimeExtensionProperty.razor.cs create mode 100644 src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/EnumHelper.cs create mode 100644 src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/ExtensionProperties.razor create mode 100644 src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/ExtensionProperties.razor.cs create mode 100644 src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/ExtensionPropertyComponentBase.cs create mode 100644 src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/LookupExtensionProperty.razor create mode 100644 src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/LookupExtensionProperty.razor.cs create mode 100644 src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/SelectExtensionProperty.razor create mode 100644 src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/SelectExtensionProperty.razor.cs create mode 100644 src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/TextExtensionProperty.razor create mode 100644 src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/TextExtensionProperty.razor.cs create mode 100644 src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/TimeExtensionProperty.razor create mode 100644 src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/TimeExtensionProperty.razor.cs create mode 100644 src/Lsw.Abp.AntDesignUI/ObjectExtensionPropertyInfoBlazorExtensions.cs diff --git a/modules/IdentityManagement/Lsw.Abp.IdentityManagement.Blazor.AntDesignUI/Pages/RoleManagement.razor b/modules/IdentityManagement/Lsw.Abp.IdentityManagement.Blazor.AntDesignUI/Pages/RoleManagement.razor index bd7d400..54385da 100644 --- a/modules/IdentityManagement/Lsw.Abp.IdentityManagement.Blazor.AntDesignUI/Pages/RoleManagement.razor +++ b/modules/IdentityManagement/Lsw.Abp.IdentityManagement.Blazor.AntDesignUI/Pages/RoleManagement.razor @@ -32,6 +32,7 @@ + @L["DisplayName:IsDefault"] @@ -53,6 +54,7 @@ + @L["DisplayName:IsDefault"] @@ -67,4 +69,4 @@ @if (HasManagePermissionsPermission) { -} +} \ No newline at end of file diff --git a/modules/IdentityManagement/Lsw.Abp.IdentityManagement.Blazor.AntDesignUI/Pages/RoleManagement.razor.cs b/modules/IdentityManagement/Lsw.Abp.IdentityManagement.Blazor.AntDesignUI/Pages/RoleManagement.razor.cs index 41ee96a..0c2047b 100644 --- a/modules/IdentityManagement/Lsw.Abp.IdentityManagement.Blazor.AntDesignUI/Pages/RoleManagement.razor.cs +++ b/modules/IdentityManagement/Lsw.Abp.IdentityManagement.Blazor.AntDesignUI/Pages/RoleManagement.razor.cs @@ -10,6 +10,7 @@ using Volo.Abp.AspNetCore.Components.Web.Extensibility.EntityActions; using Volo.Abp.AspNetCore.Components.Web.Extensibility.TableColumns; using Volo.Abp.Identity; using Volo.Abp.Identity.Localization; +using Volo.Abp.ObjectExtending; namespace Lsw.Abp.IdentityManagement.Blazor.AntDesignUI.Pages; @@ -90,19 +91,18 @@ public partial class RoleManagement Title = L["RoleName"], Data = nameof(IdentityRoleDto.Name), Component = typeof(RoleNameComponent) - }, - new TableColumn - { - Title = L["Actions"], - Actions = EntityActions.Get() - }, + } }); + RoleManagementTableColumns.AddRange(GetExtensionTableColumns(IdentityModuleExtensionConsts.ModuleName, + IdentityModuleExtensionConsts.EntityNames.Role)); + + RoleManagementTableColumns.Add(new TableColumn + { + Title = L["Actions"], + Actions = EntityActions.Get() + }); - //TODO Implement object extensions - // RoleManagementTableColumns.AddRange(GetExtensionTableColumns(IdentityModuleExtensionConsts.ModuleName, - // IdentityModuleExtensionConsts.EntityNames.Role)); - return base.SetTableColumnsAsync(); } diff --git a/modules/IdentityManagement/Lsw.Abp.IdentityManagement.Blazor.AntDesignUI/Pages/UserManagement.razor b/modules/IdentityManagement/Lsw.Abp.IdentityManagement.Blazor.AntDesignUI/Pages/UserManagement.razor index f731647..d76e5dd 100644 --- a/modules/IdentityManagement/Lsw.Abp.IdentityManagement.Blazor.AntDesignUI/Pages/UserManagement.razor +++ b/modules/IdentityManagement/Lsw.Abp.IdentityManagement.Blazor.AntDesignUI/Pages/UserManagement.razor @@ -41,7 +41,7 @@ - + @@ -55,15 +55,16 @@ @L["DisplayName:LockoutEnabled"] + - + - @foreach (var role in NewUserRoles) - { - - @role.Name - - } + @foreach (var role in NewUserRoles) + { + + @role.Name + + } } @@ -92,7 +93,7 @@ - + @@ -106,15 +107,17 @@ @L["DisplayName:LockoutEnabled"] + + - + - @foreach (var role in EditUserRoles) - { - - @role.Name - - } + @foreach (var role in EditUserRoles) + { + + @role.Name + + } } @@ -126,4 +129,4 @@ @if (HasManagePermissionsPermission) { -} +} \ No newline at end of file diff --git a/modules/IdentityManagement/Lsw.Abp.IdentityManagement.Blazor.AntDesignUI/Pages/UserManagement.razor.cs b/modules/IdentityManagement/Lsw.Abp.IdentityManagement.Blazor.AntDesignUI/Pages/UserManagement.razor.cs index e7523d0..2e91505 100644 --- a/modules/IdentityManagement/Lsw.Abp.IdentityManagement.Blazor.AntDesignUI/Pages/UserManagement.razor.cs +++ b/modules/IdentityManagement/Lsw.Abp.IdentityManagement.Blazor.AntDesignUI/Pages/UserManagement.razor.cs @@ -11,6 +11,7 @@ using Volo.Abp.AspNetCore.Components.Web.Extensibility.EntityActions; using Volo.Abp.AspNetCore.Components.Web.Extensibility.TableColumns; using Volo.Abp.Identity; using Volo.Abp.Identity.Localization; +using Volo.Abp.ObjectExtending; namespace Lsw.Abp.IdentityManagement.Blazor.AntDesignUI.Pages; @@ -177,14 +178,18 @@ public partial class UserManagement { Title = L["PhoneNumber"], Data = nameof(IdentityUserDto.PhoneNumber), - }, - new TableColumn - { - Title = L["Actions"], - Actions = EntityActions.Get() - }, + } }); + UserManagementTableColumns.AddRange(GetExtensionTableColumns(IdentityModuleExtensionConsts.ModuleName, + IdentityModuleExtensionConsts.EntityNames.User)); + + UserManagementTableColumns.Add(new TableColumn + { + Title = L["Actions"], + Actions = EntityActions.Get() + }); + return base.SetEntityActionsAsync(); } diff --git a/modules/IdentityManagement/Lsw.Abp.IdentityManagement.Blazor.AntDesignUI/_Imports.razor b/modules/IdentityManagement/Lsw.Abp.IdentityManagement.Blazor.AntDesignUI/_Imports.razor index 3996685..cc2925a 100644 --- a/modules/IdentityManagement/Lsw.Abp.IdentityManagement.Blazor.AntDesignUI/_Imports.razor +++ b/modules/IdentityManagement/Lsw.Abp.IdentityManagement.Blazor.AntDesignUI/_Imports.razor @@ -2,6 +2,7 @@ @using Volo.Abp.AspNetCore.Components.Web @using Lsw.Abp.AntDesignUI @using Lsw.Abp.AntDesignUI.Components +@using Lsw.Abp.AntDesignUI.Components.ObjectExtending @using AntDesign @using AntDesign.TableModels @using Lsw.Abp.AspnetCore.Components.Web.AntDesignTheme.Layout diff --git a/modules/TenantManagement/Lsw.Abp.TenantManagement.Blazor.AntDesignUI/Pages/TenantManagement.razor b/modules/TenantManagement/Lsw.Abp.TenantManagement.Blazor.AntDesignUI/Pages/TenantManagement.razor index 6eda37d..63cd59e 100644 --- a/modules/TenantManagement/Lsw.Abp.TenantManagement.Blazor.AntDesignUI/Pages/TenantManagement.razor +++ b/modules/TenantManagement/Lsw.Abp.TenantManagement.Blazor.AntDesignUI/Pages/TenantManagement.razor @@ -39,7 +39,7 @@ - + } @@ -55,7 +55,7 @@ - + } @@ -63,4 +63,4 @@ @if (HasManageFeaturesPermission) { -} +} \ No newline at end of file diff --git a/modules/TenantManagement/Lsw.Abp.TenantManagement.Blazor.AntDesignUI/Pages/TenantManagement.razor.cs b/modules/TenantManagement/Lsw.Abp.TenantManagement.Blazor.AntDesignUI/Pages/TenantManagement.razor.cs index 4284f5f..ebc6dd2 100644 --- a/modules/TenantManagement/Lsw.Abp.TenantManagement.Blazor.AntDesignUI/Pages/TenantManagement.razor.cs +++ b/modules/TenantManagement/Lsw.Abp.TenantManagement.Blazor.AntDesignUI/Pages/TenantManagement.razor.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Authorization; using Volo.Abp.AspNetCore.Components.Web.Extensibility.EntityActions; using Volo.Abp.AspNetCore.Components.Web.Extensibility.TableColumns; using Volo.Abp.FeatureManagement; +using Volo.Abp.ObjectExtending; using Volo.Abp.TenantManagement; using Volo.Abp.TenantManagement.Localization; @@ -117,18 +118,19 @@ public partial class TenantManagement { Title = L["TenantName"], Data = nameof(TenantDto.Name), - }, - new TableColumn - { - Title = L["Actions"], - Actions = EntityActions.Get() - }, + } }); - // TenantManagementTableColumns.AddRange(GetExtensionTableColumns( - // TenantManagementModuleExtensionConsts.ModuleName, - // TenantManagementModuleExtensionConsts.EntityNames.Tenant)); + TenantManagementTableColumns.AddRange(GetExtensionTableColumns( + TenantManagementModuleExtensionConsts.ModuleName, + TenantManagementModuleExtensionConsts.EntityNames.Tenant)); + TenantManagementTableColumns.Add(new TableColumn + { + Title = L["Actions"], + Actions = EntityActions.Get() + }); + return base.SetTableColumnsAsync(); } } diff --git a/modules/TenantManagement/Lsw.Abp.TenantManagement.Blazor.AntDesignUI/_Imports.razor b/modules/TenantManagement/Lsw.Abp.TenantManagement.Blazor.AntDesignUI/_Imports.razor index d5ec4b0..e2a7a78 100644 --- a/modules/TenantManagement/Lsw.Abp.TenantManagement.Blazor.AntDesignUI/_Imports.razor +++ b/modules/TenantManagement/Lsw.Abp.TenantManagement.Blazor.AntDesignUI/_Imports.razor @@ -3,5 +3,6 @@ @using AntDesign @using Lsw.Abp.AntDesignUI @using Lsw.Abp.AntDesignUI.Components +@using Lsw.Abp.AntDesignUI.Components.ObjectExtending @using Lsw.Abp.FeatureManagement.Blazor.AntDesignUI.Components @using Lsw.Abp.AspnetCore.Components.Web.AntDesignTheme.Layout diff --git a/src/Lsw.Abp.AntDesignUI/AbpCrudPageBase.cs b/src/Lsw.Abp.AntDesignUI/AbpCrudPageBase.cs index 8fa7e60..0141637 100644 --- a/src/Lsw.Abp.AntDesignUI/AbpCrudPageBase.cs +++ b/src/Lsw.Abp.AntDesignUI/AbpCrudPageBase.cs @@ -7,6 +7,7 @@ using AntDesign.TableModels; using JetBrains.Annotations; using Localization.Resources.AbpUi; using Lsw.Abp.AntDesignUI.Components; +using Lsw.Abp.AntDesignUI.Components.ObjectExtending; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; @@ -17,6 +18,9 @@ using Volo.Abp.AspNetCore.Components; using Volo.Abp.AspNetCore.Components.Web.Extensibility.EntityActions; using Volo.Abp.AspNetCore.Components.Web.Extensibility.TableColumns; using Volo.Abp.Authorization; +using Volo.Abp.Localization; +using Volo.Abp.ObjectExtending; +using Volo.Abp.ObjectExtending.Modularity; namespace Lsw.Abp.AntDesignUI; @@ -580,6 +584,48 @@ public abstract class AbpCrudPageBase< { return ValueTask.CompletedTask; } + + protected virtual IEnumerable GetExtensionTableColumns(string moduleName, string entityType) + { + var properties = ModuleExtensionConfigurationHelper.GetPropertyConfigurations(moduleName, entityType); + foreach (var propertyInfo in properties) + { + if (propertyInfo.IsAvailableToClients && propertyInfo.UI.OnTable.IsVisible) + { + if (propertyInfo.Name.EndsWith("_Text")) + { + var lookupPropertyName = propertyInfo.Name.RemovePostFix("_Text"); + var lookupPropertyDefinition = properties.SingleOrDefault(t => t.Name == lookupPropertyName); + yield return new TableColumn + { + Title = lookupPropertyDefinition.GetLocalizedDisplayName(StringLocalizerFactory), + Data = $"ExtraProperties[{propertyInfo.Name}]" + }; + } + else + { + var column = new TableColumn + { + Title = propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory), + Data = $"ExtraProperties[{propertyInfo.Name}]" + }; + + if (propertyInfo.IsDate() || propertyInfo.IsDateTime()) + { + column.DisplayFormat = propertyInfo.GetDateEditInputFormatOrNull(); + } + + if (propertyInfo.Type.IsEnum) + { + column.ValueConverter = (val) => + EnumHelper.GetLocalizedMemberName(propertyInfo.Type, val.As().ExtraProperties[propertyInfo.Name], StringLocalizerFactory); + } + + yield return column; + } + } + } + } } diff --git a/src/Lsw.Abp.AntDesignUI/AntDesignUiObjectExtensionPropertyInfoExtensions.cs b/src/Lsw.Abp.AntDesignUI/AntDesignUiObjectExtensionPropertyInfoExtensions.cs new file mode 100644 index 0000000..e75b2e8 --- /dev/null +++ b/src/Lsw.Abp.AntDesignUI/AntDesignUiObjectExtensionPropertyInfoExtensions.cs @@ -0,0 +1,259 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using Lsw.Abp.AntDesignUI.Components.ObjectExtending; +using Volo.Abp.ObjectExtending; +using Volo.Abp.Reflection; + +namespace Lsw.Abp.AntDesignUI; + +public static class AntDesignUiObjectExtensionPropertyInfoExtensions +{ + private static readonly HashSet NumberTypes = new HashSet { + typeof(int), + typeof(long), + typeof(byte), + typeof(sbyte), + typeof(short), + typeof(ushort), + typeof(uint), + typeof(long), + typeof(ulong), + typeof(float), + typeof(double), + typeof(decimal), + typeof(int?), + typeof(long?), + typeof(byte?), + typeof(sbyte?), + typeof(short?), + typeof(ushort?), + typeof(uint?), + typeof(long?), + typeof(ulong?), + typeof(float?), + typeof(double?), + typeof(decimal?) + }; + + private static readonly HashSet TextEditSupportedAttributeTypes = new HashSet { + typeof(EmailAddressAttribute), + typeof(UrlAttribute), + typeof(PhoneAttribute) + }; + + public static string GetDateEditInputFormatOrNull(this IBasicObjectExtensionPropertyInfo property) + { + if (property.IsDate()) + { + return "{0:yyyy-MM-dd}"; + } + + if (property.IsDateTime()) + { + return "{0:yyyy-MM-dd HH:mm}"; + } + + return null; + } + + public static string GetTextInputValueOrNull(this IBasicObjectExtensionPropertyInfo property, object value) + { + if (value == null) + { + return null; + } + + if (TypeHelper.IsFloatingType(property.Type)) + { + return value.ToString()?.Replace(',', '.'); + } + + return value.ToString(); + } + + public static T GetInputValueOrDefault(this IBasicObjectExtensionPropertyInfo property, object value) + { + if (value == null) + { + return default; + } + + return (T)value; + } + + public static string GetTextInputMode(this ObjectExtensionPropertyInfo propertyInfo) + { + foreach (var attribute in propertyInfo.Attributes) + { + var textRoleByAttribute = GetTextInputModeFromAttributeOrNull(attribute); + if (textRoleByAttribute != null) + { + return textRoleByAttribute; + } + } + + return GetTextInputModeFromTypeOrNull(propertyInfo.Type) + ?? "text"; //default + } + + private static string GetTextInputModeFromTypeOrNull(Type type) + { + // if (TypeHelper.IsFloatingType(type)) + // { + // return "number"; + // } + + if (NumberTypes.Contains(type)) + { + return "number"; + } + + return null; + } + + private static string GetTextInputModeFromAttributeOrNull(Attribute attribute) + { + if (attribute is EmailAddressAttribute) + { + return "email"; + } + + if (attribute is UrlAttribute) + { + return "url"; + } + + + if (attribute is PhoneAttribute) + { + return "tel"; + } + + if (attribute is DataTypeAttribute dataTypeAttribute) + { + switch (dataTypeAttribute.DataType) + { + case DataType.EmailAddress: + return "email"; + case DataType.Url: + return "url"; + case DataType.PhoneNumber: + return "tel"; + } + } + + return null; + } + + public static string GetTextRole(this ObjectExtensionPropertyInfo propertyInfo) + { + foreach (var attribute in propertyInfo.Attributes) + { + var textRoleByAttribute = GetTextRoleFromAttributeOrNull(attribute); + if (textRoleByAttribute != null) + { + return textRoleByAttribute; + } + } + + return "text"; //default + } + + private static string GetTextRoleFromAttributeOrNull(Attribute attribute) + { + if (attribute is EmailAddressAttribute) + { + return "email"; + } + + if (attribute is UrlAttribute) + { + return "url"; + } + + if (attribute is DataTypeAttribute dataTypeAttribute) + { + switch (dataTypeAttribute.DataType) + { + case DataType.Password: + return "password"; + case DataType.EmailAddress: + return "email"; + case DataType.Url: + return "url"; + } + } + + return null; + } + + public static Type GetInputType(this ObjectExtensionPropertyInfo propertyInfo) + { + foreach (var attribute in propertyInfo.Attributes) + { + var inputTypeByAttribute = GetInputTypeFromAttributeOrNull(attribute); + if (inputTypeByAttribute != null) + { + return inputTypeByAttribute; + } + } + return GetInputTypeFromTypeOrNull(propertyInfo.Type) + ?? typeof(TextExtensionProperty<,>); //default + } + + private static Type GetInputTypeFromAttributeOrNull(Attribute attribute) + { + var hasTextEditSupport = TextEditSupportedAttributeTypes.Any(t => t == attribute.GetType()); + + if (hasTextEditSupport) + { + return typeof(TextExtensionProperty<,>); + } + + + if (attribute is DataTypeAttribute dataTypeAttribute) + { + switch (dataTypeAttribute.DataType) + { + case DataType.Password: + return typeof(TextExtensionProperty<,>); + case DataType.Date: + return typeof(DateTimeExtensionProperty<,>); + case DataType.Time: + return typeof(TimeExtensionProperty<,>); + case DataType.EmailAddress: + return typeof(TextExtensionProperty<,>); + case DataType.Url: + return typeof(TextExtensionProperty<,>); + case DataType.PhoneNumber: + return typeof(TextExtensionProperty<,>); + case DataType.DateTime: + return typeof(DateTimeExtensionProperty<,>); + } + } + + return null; + } + + private static Type GetInputTypeFromTypeOrNull(Type type) + { + if (type == typeof(bool)) + { + return typeof(CheckExtensionProperty<,>); + } + + if (type == typeof(DateTime)) + { + return typeof(DateTimeExtensionProperty<,>); + } + + if (NumberTypes.Contains(type)) + { + return typeof(TextExtensionProperty<,>); + } + + return null; + } +} diff --git a/src/Lsw.Abp.AntDesignUI/Components/AbpExtensibleDataGrid.razor b/src/Lsw.Abp.AntDesignUI/Components/AbpExtensibleDataGrid.razor index 8e684e6..9ff6def 100644 --- a/src/Lsw.Abp.AntDesignUI/Components/AbpExtensibleDataGrid.razor +++ b/src/Lsw.Abp.AntDesignUI/Components/AbpExtensibleDataGrid.razor @@ -1,4 +1,5 @@ -@typeparam TItem +@using Volo.Abp.Data +@typeparam TItem + Responsive="@Responsive" + TableLayout="" + ScrollX="100%"> @if (Columns != null) { @@ -17,7 +20,7 @@ { if (column.Actions.Any()) { - + @foreach (var action in column.Actions) { @@ -75,7 +78,42 @@ } else { - + + @{ + var entity = context as IHasExtraProperties; + var propertyName = ExtensionPropertiesRegex.Match(column.Data).Groups[1].Value; + var propertyValue = entity.GetProperty(propertyName); + if (propertyValue is bool) + { + if ((bool) propertyValue) + { + + } + else + { + + } + } + else + { + if (column.ValueConverter != null) + { + @(GetConvertedFieldValue(context, column)) + } + else + { + if (column.DisplayFormat == null) + { + @propertyValue + } + else + { + @string.Format(column.DisplayFormatProvider, column.DisplayFormat, propertyValue) + } + } + } + } + } } } diff --git a/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/CheckExtensionProperty.razor b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/CheckExtensionProperty.razor new file mode 100644 index 0000000..4aef585 --- /dev/null +++ b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/CheckExtensionProperty.razor @@ -0,0 +1,13 @@ +@typeparam TEntity +@typeparam TResourceType +@using Volo.Abp.Localization +@inherits ExtensionPropertyComponentBase + +@if (PropertyInfo != null && Entity != null) +{ + + + @PropertyInfo.GetLocalizedDisplayName(StringLocalizerFactory) + + +} \ No newline at end of file diff --git a/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/CheckExtensionProperty.razor.cs b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/CheckExtensionProperty.razor.cs new file mode 100644 index 0000000..ac9ad4c --- /dev/null +++ b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/CheckExtensionProperty.razor.cs @@ -0,0 +1,16 @@ +using Volo.Abp.Data; + +namespace Lsw.Abp.AntDesignUI.Components.ObjectExtending; + +public partial class CheckExtensionProperty + where TEntity : IHasExtraProperties +{ + protected bool Value { + get { + return PropertyInfo.GetInputValueOrDefault(Entity.GetProperty(PropertyInfo.Name)); + } + set { + Entity.SetProperty(PropertyInfo.Name, value, false); + } + } +} diff --git a/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/DateTimeExtensionProperty.razor b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/DateTimeExtensionProperty.razor new file mode 100644 index 0000000..1e26bbe --- /dev/null +++ b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/DateTimeExtensionProperty.razor @@ -0,0 +1,15 @@ +@typeparam TEntity +@typeparam TResourceType +@using Volo.Abp.Localization +@inherits ExtensionPropertyComponentBase + +@if (PropertyInfo != null && Entity != null) +{ + + + +} \ No newline at end of file diff --git a/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/DateTimeExtensionProperty.razor.cs b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/DateTimeExtensionProperty.razor.cs new file mode 100644 index 0000000..23ea070 --- /dev/null +++ b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/DateTimeExtensionProperty.razor.cs @@ -0,0 +1,17 @@ +using System; +using Volo.Abp.Data; + +namespace Lsw.Abp.AntDesignUI.Components.ObjectExtending; + +public partial class DateTimeExtensionProperty + where TEntity : IHasExtraProperties +{ + protected DateTime? Value { + get { + return PropertyInfo.GetInputValueOrDefault(Entity.GetProperty(PropertyInfo.Name)); + } + set { + Entity.SetProperty(PropertyInfo.Name, value, false); + } + } +} diff --git a/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/EnumHelper.cs b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/EnumHelper.cs new file mode 100644 index 0000000..d1ea1db --- /dev/null +++ b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/EnumHelper.cs @@ -0,0 +1,28 @@ +using System; +using Microsoft.Extensions.Localization; +using Volo.Abp.Localization; + +namespace Lsw.Abp.AntDesignUI.Components.ObjectExtending; + +public static class EnumHelper +{ + public static string GetLocalizedMemberName(Type enumType, object value, IStringLocalizerFactory stringLocalizerFactory) + { + var memberName = enumType.GetEnumName(value); + var localizedMemberName = AbpInternalLocalizationHelper.LocalizeWithFallback( + new[] + { + stringLocalizerFactory.CreateDefaultOrNull() + }, + new[] + { + $"Enum:{enumType.Name}.{memberName}", + $"{enumType.Name}.{memberName}", + memberName + }, + memberName + ); + + return localizedMemberName; + } +} diff --git a/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/ExtensionProperties.razor b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/ExtensionProperties.razor new file mode 100644 index 0000000..138c88a --- /dev/null +++ b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/ExtensionProperties.razor @@ -0,0 +1,27 @@ +@typeparam TEntityType +@typeparam TResourceType +@using Volo.Abp.ObjectExtending +@using Volo.Abp.Data + +@{ + Entity.SetDefaultsForExtraProperties(); + + foreach (var propertyInfo in ObjectExtensionManager.Instance.GetProperties()) + { + if (!propertyInfo.Name.EndsWith("_Text")) + { + if (propertyInfo.Type.IsEnum) + { + + } + else if (!propertyInfo.Lookup.Url.IsNullOrEmpty()) + { + + } + else + { + @ExtensionPropertyRender(propertyInfo) + } + } + } +} diff --git a/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/ExtensionProperties.razor.cs b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/ExtensionProperties.razor.cs new file mode 100644 index 0000000..8725068 --- /dev/null +++ b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/ExtensionProperties.razor.cs @@ -0,0 +1,30 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.Extensions.Localization; +using Volo.Abp.AspNetCore.Components.Web; +using Volo.Abp.Data; +using Volo.Abp.ObjectExtending; + +namespace Lsw.Abp.AntDesignUI.Components.ObjectExtending; + +public partial class ExtensionProperties : ComponentBase + where TEntityType : IHasExtraProperties +{ + [Inject] + public IStringLocalizerFactory StringLocalizerFactory { get; set; } + + [Parameter] + public AbpBlazorMessageLocalizerHelper LH { get; set; } + + [Parameter] + public TEntityType Entity { get; set; } + + private RenderFragment ExtensionPropertyRender(ObjectExtensionPropertyInfo propertyInfo) => builder => + { + var inputType = propertyInfo.GetInputType(); + builder.OpenComponent(0, inputType.MakeGenericType(typeof(TEntityType), typeof(TResourceType))); + builder.AddAttribute(1, "PropertyInfo", propertyInfo); + builder.AddAttribute(2, "Entity", Entity); + builder.AddAttribute(3, "LH", LH); + builder.CloseComponent(); + }; +} diff --git a/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/ExtensionPropertyComponentBase.cs b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/ExtensionPropertyComponentBase.cs new file mode 100644 index 0000000..5229093 --- /dev/null +++ b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/ExtensionPropertyComponentBase.cs @@ -0,0 +1,76 @@ +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Components; +using Microsoft.Extensions.Localization; +using Volo.Abp.AspNetCore.Components.Web; +using Volo.Abp.Data; +using Volo.Abp.ObjectExtending; + +namespace Lsw.Abp.AntDesignUI.Components.ObjectExtending; + +public abstract class ExtensionPropertyComponentBase : OwningComponentBase + where TEntity : IHasExtraProperties +{ + [Inject] + public IStringLocalizerFactory StringLocalizerFactory { get; set; } + + [Parameter] + public TEntity Entity { get; set; } + + [Parameter] + public ObjectExtensionPropertyInfo PropertyInfo { get; set; } + + [Parameter] + public AbpBlazorMessageLocalizerHelper LH { get; set; } + // + // protected virtual void Validate(ValidatorEventArgs e) + // { + // e.Status = ValidationStatus.Success; + // + // var validationAttributes = PropertyInfo.GetValidationAttributes(); + // var validationContext = new ValidationContext(Entity) + // { + // DisplayName = PropertyInfo.Name, + // MemberName = PropertyInfo.Name + // }; + // + // foreach (var validationAttribute in validationAttributes) + // { + // var result = validationAttribute.GetValidationResult(e.Value, validationContext); + // if (result == ValidationResult.Success || result == null) + // { + // continue; + // } + // + // var errorMessage = result.ErrorMessage; + // if (LH != null) + // { + // var formattedErrorMessage = GetDefaultErrorMessage(validationAttribute); + // var errorMessageString = ValidationAttributeHelper.RevertErrorMessagePlaceholders(formattedErrorMessage); + // var errorMessageArguments = ValidationMessageLocalizerAttributeFinder.FindAll(errorMessage, errorMessageString) + // ?.OrderBy(x => x.Index) + // ?.Select(x => x.Argument); + // + // errorMessage = LH.Localize(errorMessageString, errorMessageArguments); + // } + // + // e.MemberNames = result.MemberNames; + // e.Status = ValidationStatus.Error; + // e.ErrorText = errorMessage; + // break; + // } + // } + // + // private static string GetDefaultErrorMessage(ValidationAttribute validationAttribute) + // { + // if (validationAttribute is StringLengthAttribute stringLengthAttribute && stringLengthAttribute.MinimumLength != 0) + // { + // var nullable = ValidationAttributeHelper.ValidationAttributeCustomErrorMessageSetProperty.GetValue((object) validationAttribute) as bool?; + // var flag = true; + // if (!(nullable.GetValueOrDefault() == flag & nullable.HasValue)) + // { + // return ValidationAttributeHelper.SetErrorMessagePlaceholders("The field {0} must be a string with a minimum length of {2} and a maximum length of {1}."); + // } + // } + // return ValidationAttributeHelper.SetErrorMessagePlaceholders(ValidationAttributeHelper.ValidationAttributeErrorMessageStringProperty.GetValue((object) validationAttribute) as string); + // } +} diff --git a/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/LookupExtensionProperty.razor b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/LookupExtensionProperty.razor new file mode 100644 index 0000000..5819bff --- /dev/null +++ b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/LookupExtensionProperty.razor @@ -0,0 +1,16 @@ +@typeparam TEntity +@typeparam TResourceType +@using Volo.Abp.Localization +@inherits ExtensionPropertyComponentBase + + + + + + + + + \ No newline at end of file diff --git a/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/LookupExtensionProperty.razor.cs b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/LookupExtensionProperty.razor.cs new file mode 100644 index 0000000..fb2555f --- /dev/null +++ b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/LookupExtensionProperty.razor.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using System.Threading.Tasks; +using AntDesign; +using Microsoft.AspNetCore.Components; +using Volo.Abp.AspNetCore.Components.Web.Extensibility; +using Volo.Abp.Data; + +namespace Lsw.Abp.AntDesignUI.Components.ObjectExtending; + +public partial class LookupExtensionProperty + where TEntity : IHasExtraProperties +{ + protected List> lookupItems; + + [Inject] public ILookupApiRequestService LookupApiService { get; set; } + + public string TextPropertyName => PropertyInfo.Name + "_Text"; + + private string SelectedValue { get; set; } + + private Func _compareWith = (a, b) => + { + if (a is SelectItem o1 && b is SelectItem o2) + { + return o1.Value == o2.Value; + } + + return false; + }; + + public LookupExtensionProperty() + { + lookupItems = new List>(); + } + + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync(); + await SearchFilterChangedAsync(string.Empty); + + SelectedValue = Entity.GetProperty(TextPropertyName)?.ToString(); + } + + protected virtual void UpdateLookupTextProperty(object value) + { + var selectedItemText = lookupItems.SingleOrDefault(t => t.Value.Equals(value))?.Text; + Entity.SetProperty(TextPropertyName, selectedItemText); + } + + protected virtual async Task>> GetLookupItemsAsync(string filter) + { + var selectItems = new List>(); + + var url = PropertyInfo.Lookup.Url; + if (!filter.IsNullOrEmpty()) + { + url += $"?{PropertyInfo.Lookup.FilterParamName}={filter.Trim()}"; + } + + var response = await LookupApiService.SendAsync(url); + + var document = JsonDocument.Parse(response); + var itemsArrayProp = document.RootElement.GetProperty(PropertyInfo.Lookup.ResultListPropertyName); + foreach (var item in itemsArrayProp.EnumerateArray()) + { + selectItems.Add(new SelectItem + { + Text = item.GetProperty(PropertyInfo.Lookup.DisplayPropertyName).GetString(), + Value = JsonSerializer.Deserialize(item.GetProperty(PropertyInfo.Lookup.ValuePropertyName).GetRawText(), + PropertyInfo.Type) + }); + } + + return selectItems; + } + + private void SelectedValueChanged(AutoCompleteOption selectedItem) + { + var selectedObject = (selectedItem.Value as SelectItem)?.Value; + Entity.SetProperty(PropertyInfo.Name, selectedObject, false); + UpdateLookupTextProperty(selectedObject); + } + + protected virtual async Task SearchFilterChangedAsync(string filter) + { + lookupItems = await GetLookupItemsAsync(filter); + } +} \ No newline at end of file diff --git a/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/SelectExtensionProperty.razor b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/SelectExtensionProperty.razor new file mode 100644 index 0000000..aca7690 --- /dev/null +++ b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/SelectExtensionProperty.razor @@ -0,0 +1,12 @@ +@typeparam TEntity +@typeparam TResourceType +@using Volo.Abp.Localization +@inherits ExtensionPropertyComponentBase + + + + \ No newline at end of file diff --git a/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/SelectExtensionProperty.razor.cs b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/SelectExtensionProperty.razor.cs new file mode 100644 index 0000000..3d92434 --- /dev/null +++ b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/SelectExtensionProperty.razor.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using Volo.Abp.Data; + +namespace Lsw.Abp.AntDesignUI.Components.ObjectExtending; + +public partial class SelectExtensionProperty + where TEntity : IHasExtraProperties +{ + protected List> SelectItems = new(); + + public int SelectedValue { + get { return Entity.GetProperty(PropertyInfo.Name); } + set { Entity.SetProperty(PropertyInfo.Name, value, false); } + } + + protected virtual List> GetSelectItemsFromEnum() + { + var selectItems = new List>(); + + foreach (var enumValue in PropertyInfo.Type.GetEnumValues()) + { + selectItems.Add(new SelectItem + { + Value = (int)enumValue, + Text = EnumHelper.GetLocalizedMemberName(PropertyInfo.Type, enumValue, StringLocalizerFactory) + }); + } + + return selectItems; + } + + protected override void OnParametersSet() + { + SelectItems = GetSelectItemsFromEnum(); + StateHasChanged(); + + if (!Entity.HasProperty(PropertyInfo.Name)) + { + SelectedValue = (int)PropertyInfo.Type.GetEnumValues().GetValue(0); + } + } +} + +public class SelectItem +{ + public string Text { get; set; } + public TValue Value { get; set; } + + public override string ToString() + { + return Text; + } +} diff --git a/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/TextExtensionProperty.razor b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/TextExtensionProperty.razor new file mode 100644 index 0000000..c03eee7 --- /dev/null +++ b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/TextExtensionProperty.razor @@ -0,0 +1,11 @@ +@typeparam TEntity +@typeparam TResourceType +@using Volo.Abp.Localization +@inherits ExtensionPropertyComponentBase + +@if (PropertyInfo != null && Entity != null) +{ + + + +} \ No newline at end of file diff --git a/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/TextExtensionProperty.razor.cs b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/TextExtensionProperty.razor.cs new file mode 100644 index 0000000..eab051c --- /dev/null +++ b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/TextExtensionProperty.razor.cs @@ -0,0 +1,16 @@ +using Volo.Abp.Data; + +namespace Lsw.Abp.AntDesignUI.Components.ObjectExtending; + +public partial class TextExtensionProperty + where TEntity : IHasExtraProperties +{ + protected string Value { + get { + return PropertyInfo.GetTextInputValueOrNull(Entity.GetProperty(PropertyInfo.Name)); + } + set { + Entity.SetProperty(PropertyInfo.Name, value, validate: false); + } + } +} diff --git a/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/TimeExtensionProperty.razor b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/TimeExtensionProperty.razor new file mode 100644 index 0000000..0a6af02 --- /dev/null +++ b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/TimeExtensionProperty.razor @@ -0,0 +1,11 @@ +@typeparam TEntity +@typeparam TResourceType +@using Volo.Abp.Localization +@inherits ExtensionPropertyComponentBase + +@if (PropertyInfo != null && Entity != null) +{ + + + +} diff --git a/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/TimeExtensionProperty.razor.cs b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/TimeExtensionProperty.razor.cs new file mode 100644 index 0000000..615458f --- /dev/null +++ b/src/Lsw.Abp.AntDesignUI/Components/ObjectExtending/TimeExtensionProperty.razor.cs @@ -0,0 +1,17 @@ +using System; +using Volo.Abp.Data; + +namespace Lsw.Abp.AntDesignUI.Components.ObjectExtending; + +public partial class TimeExtensionProperty + where TEntity : IHasExtraProperties +{ + protected TimeSpan? Value { + get { + return PropertyInfo.GetInputValueOrDefault(Entity.GetProperty(PropertyInfo.Name)); + } + set { + Entity.SetProperty(PropertyInfo.Name, value, false); + } + } +} diff --git a/src/Lsw.Abp.AntDesignUI/ObjectExtensionPropertyInfoBlazorExtensions.cs b/src/Lsw.Abp.AntDesignUI/ObjectExtensionPropertyInfoBlazorExtensions.cs new file mode 100644 index 0000000..8d34b43 --- /dev/null +++ b/src/Lsw.Abp.AntDesignUI/ObjectExtensionPropertyInfoBlazorExtensions.cs @@ -0,0 +1,37 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using Volo.Abp.ObjectExtending; + +namespace Lsw.Abp.AntDesignUI; + +public static class ObjectExtensionPropertyInfoBlazorExtensions +{ + private static readonly Type[] DateTimeTypes = + { + typeof(DateTime), + typeof(DateTime?), + typeof(DateTimeOffset), + typeof(DateTimeOffset?) + }; + + public static bool IsDate(this IBasicObjectExtensionPropertyInfo property) + { + return DateTimeTypes.Contains(property.Type) && + property.GetDataTypeOrNull() == DataType.Date; + } + + public static bool IsDateTime(this IBasicObjectExtensionPropertyInfo property) + { + return DateTimeTypes.Contains(property.Type) && + !property.IsDate(); + } + + public static DataType? GetDataTypeOrNull(this IBasicObjectExtensionPropertyInfo property) + { + return property + .Attributes + .OfType() + .FirstOrDefault()?.DataType; + } +}