更新下拉列表组件,支持lambda表达式解析

This commit is contained in:
何镇汐
2018-01-02 21:34:01 +08:00
parent 462084d31a
commit b25cd87605
21 changed files with 272 additions and 42 deletions
+3 -1
View File
@@ -395,6 +395,7 @@ https://github.com/dotnetcore/util/
- 组件基类 [已发布]
- TagHelper基类 [已发布]
- 图标 - 集成了Material Design和Font Awesome图标集 [已发布]
- 下拉列表 - 基于Material Select组件 [已发布]
- 按钮 [开发中]
- 文本框 [开发中]
- 权限 [待启动]
@@ -438,4 +439,5 @@ https://github.com/dotnetcore/util/
- 2017年10月29日,更新了加密操作(Util.Helpers.Encrypt,集成了MD5,DES,AES,RSA加密操作)及单元测试。
- 2017年10月30日,更新了Url参数生成器(Util.Parameters)及单元测试。
- 2017年10月31日,更新了支付宝条码支付操作(Util.Biz.Payments/Alipay)及单元测试。
- 2017年11月15日,更新了EF Core调试日志(Util.Datas/Logs)及单元测试。
- 2017年11月15日,更新了EF Core调试日志(Util.Datas/Logs)及单元测试。
- 2018年1月2日,更新了下拉列表组件(Util.Ui.Angular/Material/Forms)及单元测试。
@@ -1,7 +1,10 @@
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;
using Util.Applications.Dtos;
using Util.Biz.Enums;
using Util.Ui.Attributes;
namespace Donau.Services.Dtos.Customers {
/// <summary>
@@ -29,17 +32,16 @@ namespace Donau.Services.Dtos.Customers {
/// <summary>
/// 余额
/// </summary>
[Required(ErrorMessage = "余额不能为空")]
[Display( Name = "余额" )]
[DataMember]
public decimal Balance { get; set; }
public string Balance { get; set; }
/// <summary>
/// 性别
/// </summary>
[Display( Name = "性别" )]
[DataMember]
public int? Gender { get; set; }
[Model(Ignore = true)]
public Gender Gender { get; set; }
/// <summary>
/// 联系电话
@@ -1,4 +1,5 @@
@{
@model Donau.Services.Dtos.Customers.CustomersDto
@{
ViewData["Title"] = "Tile";
}
<div class="toolbar toolbar-detail search-menu" fxLayout="row" fxLayoutAlign="start center">
@@ -23,9 +24,9 @@
@(Html.Ui().Select().Placeholder( "民族" ).Enum<Util.Biz.Enums.Nation>().Multiple())
@Html.Ui().Select(t => t.Gender )
@@ -5,11 +5,11 @@ namespace Util.Ui.Services {
/// <summary>
/// Ui上下文
/// </summary>
public interface IContext {
public interface IContext<TModel> {
/// <summary>
/// HtmlHelper
/// </summary>
IHtmlHelper Helper { get; }
IHtmlHelper<TModel> Helper { get; }
/// <summary>
/// Html编码
/// </summary>
@@ -1,7 +0,0 @@
namespace Util.Ui.Services {
/// <summary>
/// 组件服务
/// </summary>
public interface IUiService {
}
}
@@ -1,7 +1,7 @@
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Mvc.Rendering;
using Util.Helpers;
using Util.Ui.Services;
using Util.Ui.Material.Services;
namespace Util.Ui.Extensions {
/// <summary>
@@ -12,8 +12,8 @@ namespace Util.Ui.Extensions {
/// Angular组件服务
/// </summary>
/// <param name="helper">HtmlHelper</param>
public static IUiService Ui( this IHtmlHelper helper ) {
return new UiService( helper,Ioc.Create<HtmlEncoder>() );
public static IUiService<TModel> Ui<TModel>( this IHtmlHelper<TModel> helper ) {
return new UiService<TModel>( helper, Ioc.Create<HtmlEncoder>() );
}
}
}
@@ -3,6 +3,7 @@ using Util.Ui.Components;
using Util.Ui.Material.Buttons;
using Util.Ui.Material.Forms;
using Util.Ui.Material.Icons;
using Util.Ui.Material.Services;
using Util.Ui.Services;
namespace Util.Ui.Extensions {
@@ -14,7 +15,7 @@ namespace Util.Ui.Extensions {
/// 图标
/// </summary>
/// <param name="service">组件服务</param>
public static IIcon Icon( this IUiService service ) {
public static IIcon Icon<TModel>( this IUiService<TModel> service ) {
return new Icon();
}
@@ -22,7 +23,7 @@ namespace Util.Ui.Extensions {
/// 下拉列表
/// </summary>
/// <param name="service">组件服务</param>
public static ISelect Select( this IUiService service ) {
public static ISelect Select<TModel>( this IUiService<TModel> service ) {
return new Select();
}
@@ -31,7 +32,7 @@ namespace Util.Ui.Extensions {
/// </summary>
/// <param name="service">组件服务</param>
/// <param name="text">文本</param>
public static Util.Ui.Material.Buttons.IButton Button( this IUiService service,string text ) {
public static Util.Ui.Material.Buttons.IButton Button<TModel>( this IUiService<TModel> service, string text ) {
return new Button().Text( text );
}
@@ -39,7 +40,7 @@ namespace Util.Ui.Extensions {
/// 文本框
/// </summary>
/// <param name="service">组件服务</param>
public static ITextBox TextBox( this IUiService service ) {
public static ITextBox TextBox<TModel>( this IUiService<TModel> service ) {
return new TextBox();
}
@@ -47,8 +48,8 @@ namespace Util.Ui.Extensions {
/// 表单
/// </summary>
/// <param name="service">组件服务</param>
public static IForm Form( this IUiService service ) {
if( !( service is IContext context ))
public static IForm Form<TModel>( this IUiService<TModel> service ) {
if( !( service is IContext<TModel> context ) )
throw new NotImplementedException( "组件服务必须实现Util.Ui.Services.IContext" );
return new Form( context.Helper.ViewContext.Writer, context.Encoder );
}
@@ -11,7 +11,7 @@ namespace Util.Ui.Material.Extensions {
/// </summary>
public static partial class Extensions {
/// <summary>
/// 设置占位符
/// 设置占位提示符
/// </summary>
/// <typeparam name="TComponent">组件类型</typeparam>
/// <param name="component">组件实例</param>
@@ -2,7 +2,7 @@
using Util.Ui.Components.Internal;
using Util.Ui.Configs;
namespace Util.Ui.Extensions {
namespace Util.Ui.Material.Extensions {
/// <summary>
/// 文本框扩展
/// </summary>
@@ -0,0 +1,84 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq.Expressions;
using System.Reflection;
using Util.Helpers;
using Util.Ui.Extensions;
using Util.Ui.Internal;
using Util.Ui.Material.Extensions;
namespace Util.Ui.Material.Forms.Models {
/// <summary>
/// 模型下拉列表
/// </summary>
/// <typeparam name="TModel">模型类型</typeparam>
/// <typeparam name="TProperty">属性类型</typeparam>
public class ModelSelect<TModel, TProperty> : Select {
/// <summary>
/// 初始化模型下拉列表
/// </summary>
/// <param name="expression">属性表达式</param>
public ModelSelect( Expression<Func<TModel, TProperty>> expression ) {
if( expression == null )
return;
_expression = expression;
_memberInfo = Lambda.GetMember( _expression );
Init();
}
/// <summary>
/// 属性表达式
/// </summary>
private readonly Expression<Func<TModel, TProperty>> _expression;
/// <summary>
/// 成员
/// </summary>
private readonly MemberInfo _memberInfo;
/// <summary>
/// 初始化
/// </summary>
private void Init() {
InitModel();
InitPlaceholder();
InitType();
InitRequired();
}
/// <summary>
/// 初始化模型绑定
/// </summary>
private void InitModel() {
var model = Helper.GetModel( _expression );
if ( string.IsNullOrWhiteSpace( model ) )
return;
this.Model( model );
}
/// <summary>
/// 初始化占位提示符
/// </summary>
private void InitPlaceholder() {
this.Placeholder( Reflection.GetDisplayNameOrDescription( _memberInfo ) );
}
/// <summary>
/// 根据类型初始化
/// </summary>
private void InitType() {
if( Reflection.IsBool( _memberInfo ) )
this.Bool();
else if( Reflection.IsEnum( _memberInfo ) )
Enum<TProperty>();
}
/// <summary>
/// 初始化必填项验证
/// </summary>
private void InitRequired() {
var attribute = Lambda.GetAttribute<RequiredAttribute>( _expression );
if( attribute != null )
this.Required( attribute.ErrorMessage );
}
}
}
@@ -91,7 +91,7 @@ namespace Util.Ui.Material.Forms.Renders {
/// 配置模型绑定
/// </summary>
private void ConfigModel( SelectWrapperBuilder builder ) {
builder.AddAttribute( "[(model)]", _config.GetBoolValue( UiConst.Model ) );
builder.AddAttribute( "[(model)]", _config.GetValue( UiConst.Model ) );
}
/// <summary>
@@ -0,0 +1,17 @@
using System;
using System.Linq.Expressions;
using Util.Ui.Components;
namespace Util.Ui.Material.Services {
/// <summary>
/// 组件服务
/// </summary>
public interface IUiService<TModel> {
/// <summary>
/// 下拉列表
/// </summary>
/// <typeparam name="TProperty">属性类型</typeparam>
/// <param name="expression">属性表达式</param>
ISelect Select<TProperty>( Expression<Func<TModel, TProperty>> expression );
}
}
@@ -0,0 +1,32 @@
using System;
using System.Linq.Expressions;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Mvc.Rendering;
using Util.Ui.Components;
using Util.Ui.Material.Forms.Models;
using Util.Ui.Services;
namespace Util.Ui.Material.Services {
/// <summary>
/// 组件服务
/// </summary>
/// <typeparam name="TModel">模型类型</typeparam>
public class UiService<TModel> : UiServiceBase<TModel>, IUiService<TModel> {
/// <summary>
/// 初始化组件服务
/// </summary>
/// <param name="helper">HtmlHelper</param>
/// <param name="encoder">Html编码</param>
public UiService( IHtmlHelper<TModel> helper, HtmlEncoder encoder ) : base( helper, encoder ) {
}
/// <summary>
/// 下拉列表
/// </summary>
/// <typeparam name="TProperty">属性类型</typeparam>
/// <param name="expression">属性表达式</param>
public ISelect Select<TProperty>( Expression<Func<TModel, TProperty>> expression ) {
return new ModelSelect<TModel, TProperty>( expression );
}
}
}
@@ -0,0 +1,27 @@
using System;
namespace Util.Ui.Attributes {
/// <summary>
/// 模型绑定
/// </summary>
[AttributeUsage( AttributeTargets.Class | AttributeTargets.Property )]
public class ModelAttribute : Attribute {
/// <summary>
/// 初始化模型绑定
/// </summary>
/// <param name="model">模型</param>
public ModelAttribute( string model = "" ) {
Model = model;
}
/// <summary>
/// 模型
/// </summary>
public string Model { get; set; }
/// <summary>
/// 忽略模型绑定,设置为true则不设置模型绑定
/// </summary>
public bool Ignore { get; set; }
}
}
@@ -9,7 +9,7 @@ namespace Util.Ui.Extensions {
/// </summary>
public static partial class Extensions {
/// <summary>
/// 设置占位符
/// 设置占位提示符
/// </summary>
/// <typeparam name="TComponent">组件类型</typeparam>
/// <param name="component">组件实例</param>
+44
View File
@@ -0,0 +1,44 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
using Util.Helpers;
using Util.Ui.Attributes;
namespace Util.Ui.Internal {
/// <summary>
/// 辅助操作
/// </summary>
public static class Helper {
/// <summary>
/// 获取模型
/// </summary>
/// <typeparam name="TModel">模型类型</typeparam>
/// <typeparam name="TProperty">属性类型</typeparam>
public static string GetModel<TModel, TProperty>( Expression<Func<TModel, TProperty>> expression ) {
var modelName = Util.Helpers.String.FirstLowerCase( GetModelName<TModel>() );
var propertyName = Util.Helpers.String.FirstLowerCase( GetPropertyName( expression ) );
if ( string.IsNullOrWhiteSpace( modelName ) || string.IsNullOrWhiteSpace( propertyName ) )
return string.Empty;
return $"{modelName}.{propertyName}";
}
/// <summary>
/// 获取模型名称
/// </summary>
private static string GetModelName<TModel>() {
Type type = Common.GetType<TModel>();
if ( type.GetCustomAttribute<ModelAttribute>() is ModelAttribute attribute ) {
return attribute.Ignore ? string.Empty : attribute.Model;
}
return typeof( TModel ).Name;
}
/// <summary>
/// 获取属性名称
/// </summary>
private static string GetPropertyName<TModel, TProperty>( Expression<Func<TModel, TProperty>> expression ) {
var attribute = Lambda.GetAttribute<ModelAttribute>( expression );
return attribute == null ? Lambda.GetName( expression ) : attribute.Ignore ? string.Empty : attribute.Model; ;
}
}
}
@@ -5,13 +5,13 @@ namespace Util.Ui.Services {
/// <summary>
/// 组件服务
/// </summary>
public class UiService : IUiService, IContext {
public class UiServiceBase<TModel> : IContext<TModel> {
/// <summary>
/// 初始化组件服务
/// </summary>
/// <param name="helper">HtmlHelper</param>
/// <param name="encoder">Html编码</param>
public UiService( IHtmlHelper helper, HtmlEncoder encoder) {
public UiServiceBase( IHtmlHelper<TModel> helper, HtmlEncoder encoder ) {
Helper = helper;
Encoder = encoder;
}
@@ -19,7 +19,7 @@ namespace Util.Ui.Services {
/// <summary>
/// HtmlHelper
/// </summary>
public IHtmlHelper Helper { get; }
public IHtmlHelper<TModel> Helper { get; }
/// <summary>
/// Html编码
+3 -7
View File
@@ -81,7 +81,7 @@ namespace Util.Helpers {
/// <summary>
/// 获取属性显示名称或描述,使用DisplayNameAttribute或DisplayAttribute设置显示名称,使用DescriptionAttribute设置描述
/// </summary>
public static string GetDisplayNameOrDescription( PropertyInfo member ) {
public static string GetDisplayNameOrDescription( MemberInfo member ) {
var result = GetDisplayName( member );
return string.IsNullOrWhiteSpace( result ) ? GetDescription( member ) : result;
}
@@ -89,7 +89,7 @@ namespace Util.Helpers {
/// <summary>
/// 获取类型成员显示名称,使用DisplayNameAttribute或DisplayAttribute设置显示名称
/// </summary>
private static string GetDisplayName( PropertyInfo member ) {
private static string GetDisplayName( MemberInfo member ) {
if( member == null )
return string.Empty;
if( member.GetCustomAttribute( typeof( DisplayNameAttribute ) ) is DisplayNameAttribute displayNameAttribute )
@@ -147,11 +147,7 @@ namespace Util.Helpers {
/// 是否布尔类型
/// </summary>
private static bool IsBool( PropertyInfo property ) {
if( property.PropertyType == typeof( bool ) )
return true;
if( property.PropertyType == typeof( bool? ) )
return true;
return false;
return property.PropertyType == typeof( bool ) || property.PropertyType == typeof( bool? );
}
/// <summary>
+14
View File
@@ -123,5 +123,19 @@ namespace Util.Helpers {
}
#endregion
#region FirstLowerCase(首字母小写)
/// <summary>
/// 首字母小写
/// </summary>
/// <param name="value">值</param>
public static string FirstLowerCase( string value ) {
if ( string.IsNullOrWhiteSpace( value ) )
return string.Empty;
return $"{value.Substring( 0, 1 ).ToLower()}{value.Substring( 1 )}";
}
#endregion
}
}
+18 -2
View File
@@ -25,8 +25,8 @@ namespace Util.Tests.Helpers {
new Guid( "83B0233C-A24F-49FD-8083-1337209EBC9A" ),
new Guid( "EAB523C6-2FE7-47BE-89D5-C6D440C3033A" )
};
Assert.Equal( "83B0233C-A24F-49FD-8083-1337209EBC9A,EAB523C6-2FE7-47BE-89D5-C6D440C3033A".ToLower(),Util.Helpers.String.Join( list ) );
Assert.Equal( "'83B0233C-A24F-49FD-8083-1337209EBC9A','EAB523C6-2FE7-47BE-89D5-C6D440C3033A'".ToLower(),Util.Helpers.String.Join( list, "'" ) );
Assert.Equal( "83B0233C-A24F-49FD-8083-1337209EBC9A,EAB523C6-2FE7-47BE-89D5-C6D440C3033A".ToLower(), Util.Helpers.String.Join( list ) );
Assert.Equal( "'83B0233C-A24F-49FD-8083-1337209EBC9A','EAB523C6-2FE7-47BE-89D5-C6D440C3033A'".ToLower(), Util.Helpers.String.Join( list, "'" ) );
}
/// <summary>
@@ -42,5 +42,21 @@ namespace Util.Tests.Helpers {
public void TestPinYin( string input, string result ) {
Assert.Equal( result, Util.Helpers.String.PinYin( input ) );
}
/// <summary>
/// 首字母小写
/// </summary>
[Theory]
[InlineData( null, "" )]
[InlineData( "", "" )]
[InlineData( " ", "" )]
[InlineData( "a", "a" )]
[InlineData( "A", "a" )]
[InlineData( "Ab", "ab" )]
[InlineData( "AB", "aB" )]
[InlineData( "Abc", "abc" )]
public void TestFirstLowerCase( string value, string result ) {
Assert.Equal( result, Util.Helpers.String.FirstLowerCase( value ) );
}
}
}
@@ -3,6 +3,7 @@ using System.IO;
using System.Text.Encodings.Web;
using Util.Helpers;
using Util.Ui.Extensions;
using Util.Ui.Material.Extensions;
using Util.Ui.Material.Forms;
using Util.Webs;
using Xunit;