mirror of
https://gitee.com/ant-design-blazor/ant-design-blazor.git
synced 2024-12-03 12:37:40 +08:00
feat(module: table): Allow custom Field filters (#3279)
* feat(module: table): Refactor FilterExpression to FieldFilterType, allow custom Field filters * feat(module: table): Add DefaultFilters property * fix(module: table): Calculate _hasFilterSelected when DefaultFilters is set * Remove Resharper settings file * Fix generate columns example * Add CustomFieldFilter demo, filtering by a color's brightness * add Chinese description * fix docs * Change IFieldFilterType.SupportsCompareOperator to SupportedCompareOperators, returning an IEnumerable Small fixes * fix locale * fix the docs * refactor SupportedCompareOperators * fix build error --------- Co-authored-by: Rhodon <rhodonvantilburg@gmail.com> Co-authored-by: James Yeung <shunjiey@hotmail.com> Co-authored-by: James Yeung <shunjiev5@hotmail.com>
This commit is contained in:
parent
050822297d
commit
6eb2f2e558
@ -1,6 +1,7 @@
|
||||
using System.Globalization;
|
||||
using System.Text.Encodings.Web;
|
||||
using AntDesign;
|
||||
using AntDesign.Filters;
|
||||
using AntDesign.JsInterop;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@ -36,6 +37,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
services.TryAddScoped<ImageService>();
|
||||
services.TryAddScoped<ConfigService>();
|
||||
services.TryAddSingleton<ReuseTabsService>();
|
||||
services.TryAddSingleton<IFieldFilterTypeResolver, DefaultFieldFilterTypeResolver>();
|
||||
|
||||
CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.CurrentCulture;
|
||||
|
||||
|
@ -34,7 +34,12 @@ namespace AntDesign
|
||||
|
||||
public static bool IsTypeNullable<T>()
|
||||
{
|
||||
return Nullable.GetUnderlyingType(typeof(T)) != null;
|
||||
return IsTypeNullable(typeof(T));
|
||||
}
|
||||
|
||||
public static bool IsTypeNullable(Type type)
|
||||
{
|
||||
return Nullable.GetUnderlyingType(type) != null;
|
||||
}
|
||||
|
||||
public static Type GetNullableType<T>()
|
||||
@ -76,29 +81,21 @@ namespace AntDesign
|
||||
return targetType;
|
||||
}
|
||||
|
||||
public static bool IsNumericType<T>()
|
||||
public static bool IsNumericType(this Type type)
|
||||
{
|
||||
Type type = GetUnderlyingType<T>();
|
||||
if (type == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Type.GetTypeCode(type) switch
|
||||
{
|
||||
TypeCode.Byte
|
||||
or TypeCode.Decimal
|
||||
or TypeCode.Double
|
||||
or TypeCode.Int16
|
||||
or TypeCode.Int32
|
||||
or TypeCode.Int64
|
||||
or TypeCode.SByte
|
||||
or TypeCode.Single
|
||||
or TypeCode.UInt16
|
||||
or TypeCode.UInt32
|
||||
or TypeCode.UInt64 => true,
|
||||
_ => false,
|
||||
};
|
||||
return type != null
|
||||
&& Type.GetTypeCode(type)
|
||||
is TypeCode.Byte
|
||||
or TypeCode.Decimal
|
||||
or TypeCode.Double
|
||||
or TypeCode.Int16
|
||||
or TypeCode.Int32
|
||||
or TypeCode.Int64
|
||||
or TypeCode.SByte
|
||||
or TypeCode.Single
|
||||
or TypeCode.UInt16
|
||||
or TypeCode.UInt32
|
||||
or TypeCode.UInt64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
@namespace AntDesign
|
||||
@inherits ColumnBase
|
||||
@using AntDesign.Core.Helpers
|
||||
@using AntDesign.Filters
|
||||
@using AntDesign.TableModels
|
||||
@using Microsoft.AspNetCore.Components.Rendering;
|
||||
@typeparam TData
|
||||
@ -243,10 +244,9 @@ else if (IsBody && RowSpan != 0 && ColSpan != 0)
|
||||
</Template>
|
||||
;
|
||||
|
||||
|
||||
private void RenderDefaultFilterDropdown(RenderTreeBuilder __builder)
|
||||
{
|
||||
@if (_filters?.Any() == true && _columnFilterType == TableFilterType.List)
|
||||
@if (_filters.Any() == true && _columnFilterType == TableFilterType.List)
|
||||
{
|
||||
<Menu AutoCloseDropdown="false" SelectedKeys="_selectedFilterValues">
|
||||
@foreach (var filter in _filters)
|
||||
@ -267,11 +267,11 @@ else if (IsBody && RowSpan != 0 && ColSpan != 0)
|
||||
else
|
||||
{
|
||||
<div id="@("popup-container-for-" + Id)" style="position:relative!important"></div>
|
||||
int index = 0;
|
||||
int filterCount = _filters.Count();
|
||||
@for (int index = 0; index < filterCount; index++)
|
||||
@foreach (TableFilter filter in _filters)
|
||||
{
|
||||
var filter = _filters.ElementAt(index);
|
||||
var noInput = filter.FilterCompareOperator == TableFilterCompareOperator.IsNull || filter.FilterCompareOperator == TableFilterCompareOperator.IsNotNull;
|
||||
var noInput = filter.FilterCompareOperator is TableFilterCompareOperator.IsNull or TableFilterCompareOperator.IsNotNull;
|
||||
<div @key="filter" style="padding:10px;min-width:150px;@(index > 0 ? "border-top:1px solid #f0f0f0" : "")">
|
||||
@if (index > 0)
|
||||
{
|
||||
@ -292,39 +292,13 @@ else if (IsBody && RowSpan != 0 && ColSpan != 0)
|
||||
<SpaceItem Style="flex:auto">
|
||||
<Select Value="filter.FilterCompareOperator" TItemValue="TableFilterCompareOperator" TItem="TableFilterCompareOperator" Style="width: 100%; overflow: visible" ValueChanged="value => SetFilterCompareOperator(filter,value)" PopupContainerSelector="@("#popup-container-for-" + Id)" BoundaryAdjustMode="@TriggerBoundaryAdjustMode.None" DropdownMatchSelectWidth="false">
|
||||
<SelectOptions>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.Equals" Label="@Table?.Locale.FilterOptions.Equals"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.NotEquals" Label="@Table?.Locale.FilterOptions.NotEquals"></SelectOption>
|
||||
@if (_columnDataType == typeof(string))
|
||||
{
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.Contains" Label="@Table?.Locale.FilterOptions.Contains"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.StartsWith" Label="@Table?.Locale.FilterOptions.StartsWith"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.EndsWith" Label="@Table?.Locale.FilterOptions.EndsWith"></SelectOption>
|
||||
}
|
||||
else if (_columnDataType.IsEnum)
|
||||
{
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.Contains" Label="@Table?.Locale.FilterOptions.Contains"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.NotContains" Label="@Table?.Locale.FilterOptions.NotContains"></SelectOption>
|
||||
}
|
||||
else if (_columnDataType == typeof(DateTime))
|
||||
{
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.GreaterThan" Label="@Table?.Locale.FilterOptions.GreaterThan"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.LessThan" Label="@Table?.Locale.FilterOptions.LessThan"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.GreaterThanOrEquals" Label="@Table?.Locale.FilterOptions.GreaterThanOrEquals"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.LessThanOrEquals" Label="@Table?.Locale.FilterOptions.LessThanOrEquals"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.TheSameDateWith" Label="@Table?.Locale.FilterOptions.TheSameDateWith"></SelectOption>
|
||||
}
|
||||
else if (_columnDataType == typeof(Guid)) { }
|
||||
else
|
||||
{
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.GreaterThan" Label="@Table?.Locale.FilterOptions.GreaterThan"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.LessThan" Label="@Table?.Locale.FilterOptions.LessThan"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.GreaterThanOrEquals" Label="@Table?.Locale.FilterOptions.GreaterThanOrEquals"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.LessThanOrEquals" Label="@Table?.Locale.FilterOptions.LessThanOrEquals"></SelectOption>
|
||||
}
|
||||
@if (THelper.IsTypeNullable<TData>() || _columnDataType == typeof(string))
|
||||
{
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.IsNull" Label="@Table?.Locale.FilterOptions.IsNull"></SelectOption>
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator" Value="@TableFilterCompareOperator.IsNotNull" Label="@Table?.Locale.FilterOptions.IsNotNull"></SelectOption>
|
||||
@foreach (TableFilterCompareOperator compareOperator in _fieldFilterType.SupportedCompareOperators) {
|
||||
if (compareOperator is TableFilterCompareOperator.IsNull or TableFilterCompareOperator.IsNotNull
|
||||
&& !(THelper.IsTypeNullable<TData>() || _columnDataType == typeof(string)))
|
||||
continue;
|
||||
|
||||
<SelectOption TItem="TableFilterCompareOperator" TItemValue="TableFilterCompareOperator"
|
||||
Value="@compareOperator" Label="@Table?.Locale.FilterOptions.Operator(compareOperator)"/>
|
||||
}
|
||||
</SelectOptions>
|
||||
</Select>
|
||||
@ -332,7 +306,7 @@ else if (IsBody && RowSpan != 0 && ColSpan != 0)
|
||||
@if (!noInput)
|
||||
{
|
||||
<SpaceItem>
|
||||
@FilterInput(filter)
|
||||
@_fieldFilterType.FilterInput(new TableFilterInputRenderOptions(filter, "#popup-container-for-" + Id, Format))
|
||||
</SpaceItem>
|
||||
}
|
||||
@if (filterCount > 1)
|
||||
@ -344,6 +318,7 @@ else if (IsBody && RowSpan != 0 && ColSpan != 0)
|
||||
}
|
||||
</Space>
|
||||
</div>
|
||||
index++;
|
||||
}
|
||||
}
|
||||
<div class="ant-table-filter-dropdown-btns">
|
||||
@ -360,86 +335,4 @@ else if (IsBody && RowSpan != 0 && ColSpan != 0)
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
|
||||
RenderFragment<TableFilter> FilterInput => filter =>
|
||||
@<Template>
|
||||
@if (_columnDataType == typeof(DateTime))
|
||||
{
|
||||
if (filter.FilterCompareOperator != TableFilterCompareOperator.TheSameDateWith)
|
||||
{
|
||||
<DatePicker Value="(DateTime?)filter.Value" ShowTime="@true"
|
||||
TValue="DateTime?" ValueChanged="value => SetFilterValue(filter, value?.AddMilliseconds(-value.Value.Millisecond))"
|
||||
PopupContainerSelector="@("#popup-container-for-" + Id)"
|
||||
BoundaryAdjustMode="@TriggerBoundaryAdjustMode.None"/>
|
||||
}
|
||||
else
|
||||
{
|
||||
<DatePicker Value="(DateTime?)filter.Value" TValue="DateTime?" ValueChanged="value => SetFilterValue(filter, value)"
|
||||
PopupContainerSelector="@("#popup-container-for-" + Id)"
|
||||
BoundaryAdjustMode="@TriggerBoundaryAdjustMode.None"/>
|
||||
}
|
||||
}
|
||||
else if (_columnDataType.IsEnum)
|
||||
{
|
||||
<EnumSelect TEnum="TData" Mode="multiple"
|
||||
@*Values="EnumHelper<TData>.Split(filter.Value)" workaround for https://github.com/ant-design-blazor/ant-design-blazor/issues/3006 *@
|
||||
PopupContainerSelector="@("#popup-container-for-" + Id)"
|
||||
Style="width:180px;"
|
||||
ValuesChanged="value => SetFilterValue(filter, EnumHelper<TData>.Combine(value))"
|
||||
BoundaryAdjustMode="@TriggerBoundaryAdjustMode.None"/>
|
||||
}
|
||||
else if (_columnDataType == typeof(byte))
|
||||
{
|
||||
<InputNumber Value="(byte?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="byte?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(decimal))
|
||||
{
|
||||
<InputNumber Value="(decimal?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="decimal?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(double))
|
||||
{
|
||||
<InputNumber Value="(double?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="double?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(short))
|
||||
{
|
||||
<InputNumber Value="(short?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="short?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(int))
|
||||
{
|
||||
<InputNumber Value="(int?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="int?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(long))
|
||||
{
|
||||
<InputNumber Value="(long?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="long?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(sbyte))
|
||||
{
|
||||
<InputNumber Value="(sbyte?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="sbyte?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(float))
|
||||
{
|
||||
<InputNumber Value="(float?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="float?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(ushort))
|
||||
{
|
||||
<InputNumber Value="(ushort?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="ushort?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(uint))
|
||||
{
|
||||
<InputNumber Value="(uint?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="uint?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(ulong))
|
||||
{
|
||||
<InputNumber Value="(ulong?)filter.Value" Formatter="number => NumberFormatter(number)" TValue="ulong?" ValueChanged="value => SetFilterValue(filter, value)"></InputNumber>
|
||||
}
|
||||
else if (_columnDataType == typeof(Guid))
|
||||
{
|
||||
<Input Value="(Guid?)filter.Value" TValue="Guid?" ValueChanged="value => SetFilterValue(filter, value)" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<Input TValue="TData" Value="(TData)filter.Value" ValueChanged="value => SetFilterValue(filter, value)" />
|
||||
}
|
||||
</Template>
|
||||
;
|
||||
}
|
@ -10,6 +10,7 @@ using AntDesign.TableModels;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using System.Text.Json;
|
||||
using AntDesign.Core.Helpers;
|
||||
using AntDesign.Filters;
|
||||
|
||||
namespace AntDesign
|
||||
{
|
||||
@ -110,9 +111,15 @@ namespace AntDesign
|
||||
}
|
||||
}
|
||||
|
||||
[Parameter]
|
||||
public IEnumerable<TableFilter> DefaultFilters { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool FilterMultiple { get; set; } = true;
|
||||
|
||||
[Parameter]
|
||||
public IFieldFilterType FieldFilterType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Function that determines if the row is displayed when filtered
|
||||
/// <para>
|
||||
@ -129,6 +136,7 @@ namespace AntDesign
|
||||
public virtual RenderFragment<CellData<TData>> CellRender { get; set; }
|
||||
|
||||
private TableFilterType _columnFilterType;
|
||||
private IFieldFilterType _fieldFilterType;
|
||||
|
||||
private Type _columnDataType;
|
||||
|
||||
@ -235,43 +243,43 @@ namespace AntDesign
|
||||
else if (_hasFilterableAttribute)
|
||||
{
|
||||
_columnDataType = THelper.GetUnderlyingType<TData>();
|
||||
if (_columnDataType == typeof(bool))
|
||||
_columnFilterType = TableFilterType.FieldType;
|
||||
|
||||
if (FieldFilterType is null)
|
||||
{
|
||||
_columnFilterType = TableFilterType.List;
|
||||
|
||||
_filters = new List<TableFilter>();
|
||||
|
||||
var trueFilterOption = GetNewFilter();
|
||||
trueFilterOption.Text = Table.Locale.FilterOptions.True;
|
||||
trueFilterOption.Value = true;
|
||||
((List<TableFilter>)_filters).Add(trueFilterOption);
|
||||
var falseFilterOption = GetNewFilter();
|
||||
falseFilterOption.Text = Table.Locale.FilterOptions.False;
|
||||
falseFilterOption.Value = false;
|
||||
((List<TableFilter>)_filters).Add(falseFilterOption);
|
||||
}
|
||||
else if (_columnDataType.IsEnum && _columnDataType.GetCustomAttribute<FlagsAttribute>() == null)
|
||||
{
|
||||
_columnFilterType = TableFilterType.List;
|
||||
|
||||
_filters = EnumHelper<TData>.GetValueLabelList().Select(item =>
|
||||
if (_columnDataType == typeof(bool))
|
||||
{
|
||||
var filterOption = GetNewFilter();
|
||||
filterOption.Text = item.Label;
|
||||
filterOption.Value = item.Value;
|
||||
return filterOption;
|
||||
}).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
_columnFilterType = TableFilterType.FieldType;
|
||||
InitFilters();
|
||||
_columnFilterType = TableFilterType.List;
|
||||
|
||||
_filters = new List<TableFilter>();
|
||||
|
||||
var trueFilterOption = GetNewFilter();
|
||||
trueFilterOption.Text = Table.Locale.FilterOptions.True;
|
||||
trueFilterOption.Value = true;
|
||||
((List<TableFilter>)_filters).Add(trueFilterOption);
|
||||
var falseFilterOption = GetNewFilter();
|
||||
falseFilterOption.Text = Table.Locale.FilterOptions.False;
|
||||
falseFilterOption.Value = false;
|
||||
((List<TableFilter>)_filters).Add(falseFilterOption);
|
||||
}
|
||||
else if (_columnDataType.IsEnum && _columnDataType.GetCustomAttribute<FlagsAttribute>() == null)
|
||||
{
|
||||
_columnFilterType = TableFilterType.List;
|
||||
|
||||
_filters = EnumHelper<TData>.GetValueLabelList().Select(item =>
|
||||
{
|
||||
var filterOption = GetNewFilter();
|
||||
filterOption.Text = item.Label;
|
||||
filterOption.Value = item.Value;
|
||||
return filterOption;
|
||||
}).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
if (_columnFilterType == TableFilterType.List && THelper.IsTypeNullable<TData>())
|
||||
{
|
||||
var nullFilterOption = GetNewFilter();
|
||||
nullFilterOption.Text = Table.Locale.FilterOptions.IsNull;
|
||||
nullFilterOption.Text = Table.Locale.FilterOptions.Operator(TableFilterCompareOperator.IsNull);
|
||||
nullFilterOption.Value = null;
|
||||
((List<TableFilter>)_filters).Add(nullFilterOption);
|
||||
}
|
||||
@ -293,8 +301,20 @@ namespace AntDesign
|
||||
|
||||
if (IsHeader)
|
||||
{
|
||||
if (_columnFilterType == TableFilterType.FieldType && _fieldFilterType == null && Filterable)
|
||||
{
|
||||
_fieldFilterType = FieldFilterType ?? Table.FieldFilterTypeResolver.Resolve<TData>();
|
||||
if (DefaultFilters is null)
|
||||
ResetFieldFilters();
|
||||
else
|
||||
{
|
||||
_filters = DefaultFilters;
|
||||
_hasFilterSelected = DefaultFilters.Any(f => f.Selected);
|
||||
}
|
||||
}
|
||||
|
||||
FilterModel = _filterable && _filters?.Any(x => x.Selected) == true ?
|
||||
new FilterModel<TData>(this, GetFieldExpression, FieldName, OnFilter, _filters.Where(x => x.Selected).ToList(), _columnFilterType) :
|
||||
new FilterModel<TData>(this, GetFieldExpression, FieldName, OnFilter, _filters.Where(x => x.Selected).ToList(), _columnFilterType, _fieldFilterType) :
|
||||
null;
|
||||
}
|
||||
}
|
||||
@ -305,12 +325,6 @@ namespace AntDesign
|
||||
return true;
|
||||
}
|
||||
|
||||
private string NumberFormatter(object value)
|
||||
{
|
||||
if (value == null) return null;
|
||||
return Convert.ToDouble(value).ToString(Format);
|
||||
}
|
||||
|
||||
private void HandleSort()
|
||||
{
|
||||
if (Sortable)
|
||||
@ -369,11 +383,6 @@ namespace AntDesign
|
||||
filter.FilterCondition = filterCondition;
|
||||
}
|
||||
|
||||
private void SetFilterValue(TableFilter filter, object value)
|
||||
{
|
||||
filter.Value = value;
|
||||
}
|
||||
|
||||
private void FilterSelected(TableFilter filter)
|
||||
{
|
||||
if (_columnFilterType == TableFilterType.FieldType) return;
|
||||
@ -405,7 +414,10 @@ namespace AntDesign
|
||||
});
|
||||
}
|
||||
_hasFilterSelected = _filters?.Any(x => x.Selected) == true;
|
||||
FilterModel = _hasFilterSelected ? new FilterModel<TData>(this, GetFieldExpression, FieldName, OnFilter, _filters.Where(x => x.Selected).ToList(), _columnFilterType) : null;
|
||||
FilterModel = _hasFilterSelected
|
||||
? new FilterModel<TData>(this, GetFieldExpression, FieldName, OnFilter,
|
||||
_filters.Where(x => x.Selected).ToList(), _columnFilterType, _fieldFilterType)
|
||||
: null;
|
||||
|
||||
Table?.ColumnFilterChange();
|
||||
}
|
||||
@ -418,7 +430,7 @@ namespace AntDesign
|
||||
}
|
||||
else
|
||||
{
|
||||
InitFilters();
|
||||
ResetFieldFilters();
|
||||
}
|
||||
FilterConfirm(true);
|
||||
}
|
||||
@ -440,7 +452,7 @@ namespace AntDesign
|
||||
return new TableFilter()
|
||||
{
|
||||
FilterCondition = TableFilterCondition.And,
|
||||
FilterCompareOperator = _columnDataType == typeof(string) ? TableFilterCompareOperator.Contains : TableFilterCompareOperator.Equals
|
||||
FilterCompareOperator = _fieldFilterType.DefaultCompareOperator
|
||||
};
|
||||
}
|
||||
else
|
||||
@ -453,7 +465,7 @@ namespace AntDesign
|
||||
}
|
||||
}
|
||||
|
||||
private void InitFilters()
|
||||
private void ResetFieldFilters()
|
||||
{
|
||||
_filters = new List<TableFilter>() { GetNewFilter() };
|
||||
}
|
||||
@ -482,7 +494,8 @@ namespace AntDesign
|
||||
_filters = filterModel.Filters;
|
||||
}
|
||||
|
||||
FilterModel = new FilterModel<TData>(this, GetFieldExpression, FieldName, OnFilter, _filters.Where(x => x.Selected).ToList(), _columnFilterType);
|
||||
FilterModel = new FilterModel<TData>(this, GetFieldExpression, FieldName, OnFilter,
|
||||
_filters.Where(x => x.Selected).ToList(), _columnFilterType, _fieldFilterType);
|
||||
|
||||
_hasFilterSelected = true;
|
||||
}
|
||||
|
@ -1,97 +0,0 @@
|
||||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace AntDesign.FilterExpression
|
||||
{
|
||||
public class DateFilterExpression : IFilterExpression
|
||||
{
|
||||
public TableFilterCompareOperator GetDefaultCompareOperator()
|
||||
{
|
||||
return TableFilterCompareOperator.Equals;
|
||||
}
|
||||
public Expression GetFilterExpression(TableFilterCompareOperator compareOperator, Expression leftExpr, Expression rightExpr)
|
||||
{
|
||||
switch (compareOperator)
|
||||
{
|
||||
case TableFilterCompareOperator.IsNull:
|
||||
return Expression.Equal(leftExpr, rightExpr);
|
||||
case TableFilterCompareOperator.IsNotNull:
|
||||
return Expression.NotEqual(leftExpr, rightExpr);
|
||||
}
|
||||
|
||||
if (leftExpr.Type.IsGenericType)
|
||||
{
|
||||
Expression notNull = Expression.NotEqual(leftExpr, Expression.Constant(null));
|
||||
Expression isNull = Expression.Equal(leftExpr, Expression.Constant(null));
|
||||
leftExpr = Expression.Property(leftExpr, "Value");
|
||||
rightExpr = Expression.Property(rightExpr, "Value");
|
||||
if (compareOperator != TableFilterCompareOperator.TheSameDateWith)
|
||||
{
|
||||
leftExpr = RemoveMilliseconds(leftExpr);
|
||||
}
|
||||
|
||||
switch (compareOperator)
|
||||
{
|
||||
case TableFilterCompareOperator.Equals:
|
||||
return Expression.AndAlso(notNull, Expression.Equal(leftExpr, rightExpr));
|
||||
case TableFilterCompareOperator.NotEquals:
|
||||
return Expression.OrElse(isNull, Expression.NotEqual(leftExpr, rightExpr));
|
||||
case TableFilterCompareOperator.GreaterThan:
|
||||
return Expression.AndAlso(notNull, Expression.GreaterThan(leftExpr, rightExpr));
|
||||
case TableFilterCompareOperator.GreaterThanOrEquals:
|
||||
return Expression.AndAlso(notNull, Expression.GreaterThanOrEqual(leftExpr, rightExpr));
|
||||
case TableFilterCompareOperator.LessThan:
|
||||
return Expression.AndAlso(notNull, Expression.LessThan(leftExpr, rightExpr));
|
||||
case TableFilterCompareOperator.LessThanOrEquals:
|
||||
return Expression.AndAlso(notNull, Expression.LessThanOrEqual(leftExpr, rightExpr));
|
||||
case TableFilterCompareOperator.TheSameDateWith:
|
||||
return Expression.AndAlso(notNull,
|
||||
Expression.Equal(
|
||||
Expression.Property(leftExpr, "Date"),
|
||||
Expression.Property(rightExpr, "Date")));
|
||||
}
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
if (compareOperator != TableFilterCompareOperator.TheSameDateWith)
|
||||
{
|
||||
leftExpr = RemoveMilliseconds(leftExpr);
|
||||
}
|
||||
|
||||
switch (compareOperator)
|
||||
{
|
||||
case TableFilterCompareOperator.Equals:
|
||||
return Expression.Equal(leftExpr, rightExpr);
|
||||
case TableFilterCompareOperator.NotEquals:
|
||||
return Expression.NotEqual(leftExpr, rightExpr);
|
||||
case TableFilterCompareOperator.GreaterThan:
|
||||
return Expression.GreaterThan(leftExpr, rightExpr);
|
||||
case TableFilterCompareOperator.GreaterThanOrEquals:
|
||||
return Expression.GreaterThanOrEqual(leftExpr, rightExpr);
|
||||
case TableFilterCompareOperator.LessThan:
|
||||
return Expression.LessThan(leftExpr, rightExpr);
|
||||
case TableFilterCompareOperator.LessThanOrEquals:
|
||||
return Expression.LessThanOrEqual(leftExpr, rightExpr);
|
||||
case TableFilterCompareOperator.TheSameDateWith:
|
||||
return Expression.Equal(
|
||||
Expression.Property(leftExpr, "Date"),
|
||||
Expression.Property(rightExpr, "Date"));
|
||||
}
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
private static Expression RemoveMilliseconds(Expression dateTimeExpression)
|
||||
{
|
||||
return Expression.Call(dateTimeExpression, typeof(DateTime).GetMethod("AddMilliseconds")!,
|
||||
Expression.Convert(
|
||||
Expression.Subtract(Expression.Constant(0),
|
||||
Expression.MakeMemberAccess(dateTimeExpression,
|
||||
typeof(DateTime).GetMember("Millisecond").First())), typeof(double)));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace AntDesign.FilterExpression
|
||||
{
|
||||
public class EnumFilterExpression : IFilterExpression
|
||||
{
|
||||
public TableFilterCompareOperator GetDefaultCompareOperator()
|
||||
{
|
||||
return TableFilterCompareOperator.Equals;
|
||||
}
|
||||
|
||||
public Expression GetFilterExpression(TableFilterCompareOperator compareOperator, Expression leftExpr, Expression rightExpr)
|
||||
{
|
||||
switch (compareOperator)
|
||||
{
|
||||
case TableFilterCompareOperator.IsNull:
|
||||
case TableFilterCompareOperator.Equals:
|
||||
return Expression.Equal(leftExpr, rightExpr);
|
||||
case TableFilterCompareOperator.IsNotNull:
|
||||
case TableFilterCompareOperator.NotEquals:
|
||||
return Expression.NotEqual(leftExpr, rightExpr);
|
||||
case TableFilterCompareOperator.Contains:
|
||||
{
|
||||
MethodInfo mi = typeof(Enum).GetMethod(nameof(Enum.HasFlag));
|
||||
if (leftExpr.Type.IsGenericType)
|
||||
{
|
||||
Expression notNull = Expression.NotEqual(leftExpr, Expression.Constant(null));
|
||||
leftExpr = Expression.MakeMemberAccess(leftExpr, leftExpr.Type.GetMember("Value")[0]);
|
||||
rightExpr = Expression.MakeMemberAccess(rightExpr, rightExpr.Type.GetMember("Value")[0]);
|
||||
rightExpr = Expression.Convert(rightExpr, typeof(Enum));
|
||||
return Expression.AndAlso(notNull, Expression.Call(leftExpr, mi, rightExpr));
|
||||
}
|
||||
rightExpr = Expression.Convert(rightExpr, typeof(Enum));
|
||||
return Expression.Call(leftExpr, mi, rightExpr);
|
||||
}
|
||||
case TableFilterCompareOperator.NotContains:
|
||||
{
|
||||
MethodInfo mi = typeof(Enum).GetMethod(nameof(Enum.HasFlag));
|
||||
if (leftExpr.Type.IsGenericType)
|
||||
{
|
||||
Expression notNull = Expression.NotEqual(leftExpr, Expression.Constant(null));
|
||||
leftExpr = Expression.MakeMemberAccess(leftExpr, leftExpr.Type.GetMember("Value")[0]);
|
||||
rightExpr = Expression.MakeMemberAccess(rightExpr, rightExpr.Type.GetMember("Value")[0]);
|
||||
rightExpr = Expression.Convert(rightExpr, typeof(Enum));
|
||||
return Expression.Equal(Expression.AndAlso(notNull, Expression.Call(leftExpr, mi, rightExpr)), Expression.Constant(false, typeof(bool)));
|
||||
}
|
||||
rightExpr = Expression.Convert(rightExpr, typeof(Enum));
|
||||
return Expression.Equal(Expression.Call(leftExpr, mi, rightExpr), Expression.Constant(false, typeof(bool)));
|
||||
}
|
||||
}
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
// 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.
|
||||
|
||||
using System;
|
||||
|
||||
namespace AntDesign.FilterExpression
|
||||
{
|
||||
public class FilterExpressionResolver<T>
|
||||
{
|
||||
private readonly StringFilterExpression _stringFilter = new StringFilterExpression();
|
||||
private readonly NumberFilterExpression _numberFilter = new NumberFilterExpression(typeof(T));
|
||||
private readonly DateFilterExpression _dateFilter = new DateFilterExpression();
|
||||
private readonly EnumFilterExpression _enumFilter = new EnumFilterExpression();
|
||||
private readonly GuidFilterExpression _guidFilter = new GuidFilterExpression();
|
||||
|
||||
public FilterExpressionResolver()
|
||||
{
|
||||
}
|
||||
|
||||
public IFilterExpression GetFilterExpression()
|
||||
{
|
||||
if (THelper.GetUnderlyingType<T>().IsEnum)
|
||||
{
|
||||
return _enumFilter;
|
||||
}
|
||||
if (THelper.IsNumericType<T>())
|
||||
{
|
||||
return _numberFilter;
|
||||
}
|
||||
var underlyingType = THelper.GetUnderlyingType<T>();
|
||||
if (underlyingType == typeof(DateTime))
|
||||
{
|
||||
return _dateFilter;
|
||||
}
|
||||
if (underlyingType == typeof(string))
|
||||
{
|
||||
return _stringFilter;
|
||||
}
|
||||
if (underlyingType == typeof(Guid))
|
||||
{
|
||||
return _guidFilter;
|
||||
}
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace AntDesign.FilterExpression
|
||||
{
|
||||
public class GuidFilterExpression : IFilterExpression
|
||||
{
|
||||
public TableFilterCompareOperator GetDefaultCompareOperator()
|
||||
{
|
||||
return TableFilterCompareOperator.Equals;
|
||||
}
|
||||
|
||||
public Expression GetFilterExpression(TableFilterCompareOperator compareOperator, Expression leftExpr, Expression rightExpr)
|
||||
{
|
||||
switch (compareOperator)
|
||||
{
|
||||
case TableFilterCompareOperator.IsNull:
|
||||
case TableFilterCompareOperator.Equals:
|
||||
return Expression.Equal(leftExpr, rightExpr);
|
||||
case TableFilterCompareOperator.IsNotNull:
|
||||
case TableFilterCompareOperator.NotEquals:
|
||||
return Expression.NotEqual(leftExpr, rightExpr);
|
||||
default:
|
||||
throw new InvalidOperationException($"The compare operator {compareOperator} is not supported for Guid type.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
|
||||
namespace AntDesign.FilterExpression
|
||||
{
|
||||
public class NumberFilterExpression : IFilterExpression
|
||||
{
|
||||
private readonly Type _type;
|
||||
public NumberFilterExpression(Type type)
|
||||
{
|
||||
_type = type;
|
||||
}
|
||||
|
||||
public TableFilterCompareOperator GetDefaultCompareOperator()
|
||||
{
|
||||
return TableFilterCompareOperator.Equals;
|
||||
}
|
||||
|
||||
public Expression GetFilterExpression(TableFilterCompareOperator compareOperator, Expression leftExpr, Expression rightExpr)
|
||||
{
|
||||
rightExpr = Expression.Convert(rightExpr, _type);
|
||||
switch (compareOperator)
|
||||
{
|
||||
case TableFilterCompareOperator.IsNull:
|
||||
case TableFilterCompareOperator.Equals:
|
||||
return Expression.Equal(leftExpr, rightExpr);
|
||||
case TableFilterCompareOperator.IsNotNull:
|
||||
case TableFilterCompareOperator.NotEquals:
|
||||
return Expression.NotEqual(leftExpr, rightExpr);
|
||||
case TableFilterCompareOperator.GreaterThan:
|
||||
return Expression.GreaterThan(leftExpr, rightExpr);
|
||||
case TableFilterCompareOperator.GreaterThanOrEquals:
|
||||
return Expression.GreaterThanOrEqual(leftExpr, rightExpr);
|
||||
case TableFilterCompareOperator.LessThan:
|
||||
return Expression.LessThan(leftExpr, rightExpr);
|
||||
case TableFilterCompareOperator.LessThanOrEquals:
|
||||
return Expression.LessThanOrEqual(leftExpr, rightExpr);
|
||||
}
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace AntDesign.FilterExpression
|
||||
{
|
||||
public class StringFilterExpression : IFilterExpression
|
||||
{
|
||||
public TableFilterCompareOperator GetDefaultCompareOperator()
|
||||
{
|
||||
return TableFilterCompareOperator.Contains;
|
||||
}
|
||||
|
||||
public Expression GetFilterExpression(TableFilterCompareOperator compareOperator, Expression leftExpr, Expression rightExpr)
|
||||
{
|
||||
return GetCaseInsensitiveComparation(compareOperator, leftExpr, rightExpr);
|
||||
}
|
||||
|
||||
private Expression GetCaseInsensitiveComparation(TableFilterCompareOperator compareOperator, Expression leftExpr, Expression rightExpr)
|
||||
{
|
||||
MethodInfo miLower = typeof(string).GetMethod("ToLower", Array.Empty<Type>());
|
||||
Expression notNull = Expression.NotEqual(leftExpr, Expression.Constant(null));
|
||||
MethodCallExpression lowerLeftExpr = Expression.Call(leftExpr, miLower);
|
||||
MethodCallExpression lowerRightExpr = Expression.Call(rightExpr, miLower);
|
||||
|
||||
switch (compareOperator)
|
||||
{
|
||||
case TableFilterCompareOperator.IsNull:
|
||||
return Expression.Equal(leftExpr, rightExpr);
|
||||
case TableFilterCompareOperator.Equals:
|
||||
return Expression.AndAlso(notNull, Expression.Equal(lowerLeftExpr, lowerRightExpr));
|
||||
case TableFilterCompareOperator.IsNotNull:
|
||||
return Expression.NotEqual(leftExpr, rightExpr);
|
||||
case TableFilterCompareOperator.NotEquals:
|
||||
return Expression.AndAlso(notNull, Expression.NotEqual(lowerLeftExpr, lowerRightExpr));
|
||||
default:
|
||||
string methodName = Enum.GetName(typeof(TableFilterCompareOperator), compareOperator);
|
||||
MethodInfo mi = typeof(string).GetMethod(methodName, new[] { typeof(string) });
|
||||
if (mi == null)
|
||||
throw new MissingMethodException("There is no method - " + methodName);
|
||||
return Expression.AndAlso(notNull, Expression.Call(lowerLeftExpr, mi, lowerRightExpr));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
40
components/table/Filters/BaseFieldFilterType.cs
Normal file
40
components/table/Filters/BaseFieldFilterType.cs
Normal file
@ -0,0 +1,40 @@
|
||||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AntDesign.Filters;
|
||||
|
||||
public abstract class BaseFieldFilterType : IFieldFilterType
|
||||
{
|
||||
public virtual TableFilterCompareOperator DefaultCompareOperator => TableFilterCompareOperator.Equals;
|
||||
|
||||
public abstract RenderFragment<TableFilterInputRenderOptions> FilterInput { get; }
|
||||
|
||||
public virtual IEnumerable<TableFilterCompareOperator> SupportedCompareOperators { get; set; } = _supportedCompareOperators;
|
||||
|
||||
private static IEnumerable<TableFilterCompareOperator> _supportedCompareOperators = new[]
|
||||
{
|
||||
TableFilterCompareOperator.Equals,
|
||||
TableFilterCompareOperator.NotEquals,
|
||||
};
|
||||
|
||||
public virtual Expression GetFilterExpression(TableFilterCompareOperator compareOperator, Expression leftExpr,
|
||||
Expression rightExpr)
|
||||
=> compareOperator switch
|
||||
{
|
||||
TableFilterCompareOperator.Equals => Expression.Equal(leftExpr, rightExpr),
|
||||
TableFilterCompareOperator.NotEquals => Expression.NotEqual(leftExpr, rightExpr),
|
||||
TableFilterCompareOperator.GreaterThan => Expression.GreaterThan(leftExpr, rightExpr),
|
||||
TableFilterCompareOperator.LessThan => Expression.LessThan(leftExpr, rightExpr),
|
||||
TableFilterCompareOperator.GreaterThanOrEquals => Expression.GreaterThanOrEqual(leftExpr, rightExpr),
|
||||
TableFilterCompareOperator.LessThanOrEquals => Expression.LessThanOrEqual(leftExpr, rightExpr),
|
||||
TableFilterCompareOperator.IsNull => Expression.Equal(leftExpr, rightExpr),
|
||||
TableFilterCompareOperator.IsNotNull => Expression.NotEqual(leftExpr, rightExpr),
|
||||
_ => throw new NotSupportedException($"{nameof(TableFilterCompareOperator)} {compareOperator} is not supported by {GetType().Name}!")
|
||||
};
|
||||
}
|
93
components/table/Filters/DateFieldFilterType.cs
Normal file
93
components/table/Filters/DateFieldFilterType.cs
Normal file
@ -0,0 +1,93 @@
|
||||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AntDesign.Filters
|
||||
{
|
||||
public abstract class DateFieldFilterType : BaseFieldFilterType
|
||||
{
|
||||
private static IEnumerable<TableFilterCompareOperator> _supportedCompareOperators = new[]
|
||||
{
|
||||
TableFilterCompareOperator.Equals,
|
||||
TableFilterCompareOperator.NotEquals,
|
||||
TableFilterCompareOperator.GreaterThan,
|
||||
TableFilterCompareOperator.LessThan,
|
||||
TableFilterCompareOperator.GreaterThanOrEquals,
|
||||
TableFilterCompareOperator.LessThanOrEquals
|
||||
};
|
||||
|
||||
public DateFieldFilterType()
|
||||
{
|
||||
SupportedCompareOperators = _supportedCompareOperators;
|
||||
}
|
||||
|
||||
protected virtual Expression GetNonNullFilterExpression(TableFilterCompareOperator compareOperator,
|
||||
Expression leftExpr, Expression rightExpr)
|
||||
=> base.GetFilterExpression(compareOperator, leftExpr, rightExpr);
|
||||
|
||||
public override sealed Expression GetFilterExpression(TableFilterCompareOperator compareOperator, Expression leftExpr, Expression rightExpr)
|
||||
{
|
||||
switch (compareOperator)
|
||||
{
|
||||
case TableFilterCompareOperator.IsNull:
|
||||
return Expression.Equal(leftExpr, rightExpr);
|
||||
|
||||
case TableFilterCompareOperator.IsNotNull:
|
||||
return Expression.NotEqual(leftExpr, rightExpr);
|
||||
}
|
||||
|
||||
if (THelper.IsTypeNullable(leftExpr.Type))
|
||||
{
|
||||
Expression notNull = Expression.NotEqual(leftExpr, Expression.Constant(null));
|
||||
Expression isNull = Expression.Equal(leftExpr, Expression.Constant(null));
|
||||
leftExpr = Expression.Property(leftExpr, nameof(Nullable<DateTime>.Value));
|
||||
rightExpr = Expression.Property(rightExpr, nameof(Nullable<DateTime>.Value));
|
||||
|
||||
return compareOperator switch
|
||||
{
|
||||
TableFilterCompareOperator.NotEquals => Expression.OrElse(isNull, GetNonNullFilterExpression(compareOperator, leftExpr, rightExpr)),
|
||||
_ => Expression.AndAlso(notNull, GetNonNullFilterExpression(compareOperator, leftExpr, rightExpr))
|
||||
};
|
||||
}
|
||||
|
||||
return GetNonNullFilterExpression(compareOperator, leftExpr, rightExpr);
|
||||
}
|
||||
}
|
||||
|
||||
public class DateTimeFieldFilterType : DateFieldFilterType
|
||||
{
|
||||
public override RenderFragment<TableFilterInputRenderOptions> FilterInput => FilterInputs.Instance.DateTimeInput;
|
||||
|
||||
protected override Expression GetNonNullFilterExpression(TableFilterCompareOperator compareOperator,
|
||||
Expression leftExpr, Expression rightExpr)
|
||||
{
|
||||
if (compareOperator != TableFilterCompareOperator.TheSameDateWith)
|
||||
{
|
||||
leftExpr = RemoveMilliseconds(leftExpr);
|
||||
}
|
||||
|
||||
return compareOperator switch
|
||||
{
|
||||
TableFilterCompareOperator.TheSameDateWith => Expression.Equal(
|
||||
Expression.Property(leftExpr, nameof(DateTime.Date)),
|
||||
Expression.Property(rightExpr, nameof(DateTime.Date))),
|
||||
_ => base.GetNonNullFilterExpression(compareOperator, leftExpr, rightExpr)
|
||||
};
|
||||
}
|
||||
|
||||
private static Expression RemoveMilliseconds(Expression dateTimeExpression)
|
||||
{
|
||||
return Expression.Call(dateTimeExpression, typeof(DateTime).GetMethod(nameof(DateTime.AddMilliseconds))!,
|
||||
Expression.Convert(
|
||||
Expression.Subtract(Expression.Constant(0),
|
||||
Expression.MakeMemberAccess(dateTimeExpression,
|
||||
typeof(DateTime).GetMember(nameof(DateTime.Millisecond)).First())), typeof(double)));
|
||||
}
|
||||
}
|
||||
}
|
61
components/table/Filters/EnumFieldFilterType.cs
Normal file
61
components/table/Filters/EnumFieldFilterType.cs
Normal file
@ -0,0 +1,61 @@
|
||||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AntDesign.Filters
|
||||
{
|
||||
public class EnumFieldFilterType<T> : BaseFieldFilterType
|
||||
{
|
||||
private static readonly MethodInfo _enumHasFlag = typeof(Enum).GetMethod(nameof(Enum.HasFlag));
|
||||
|
||||
public override RenderFragment<TableFilterInputRenderOptions> FilterInput { get; } = FilterInputs.Instance.GetEnumInput<T>();
|
||||
|
||||
private static IEnumerable<TableFilterCompareOperator> _supportedCompareOperators = new[]
|
||||
{
|
||||
TableFilterCompareOperator.Equals,
|
||||
TableFilterCompareOperator.NotEquals,
|
||||
TableFilterCompareOperator.Contains,
|
||||
TableFilterCompareOperator.NotContains,
|
||||
};
|
||||
|
||||
public EnumFieldFilterType()
|
||||
{
|
||||
SupportedCompareOperators = _supportedCompareOperators;
|
||||
}
|
||||
|
||||
public override Expression GetFilterExpression(TableFilterCompareOperator compareOperator, Expression leftExpr, Expression rightExpr)
|
||||
{
|
||||
return compareOperator switch
|
||||
{
|
||||
TableFilterCompareOperator.Contains => ContainsExpression(),
|
||||
TableFilterCompareOperator.NotContains => Expression.Not(ContainsExpression()),
|
||||
_ => base.GetFilterExpression(compareOperator, leftExpr, rightExpr)
|
||||
};
|
||||
|
||||
Expression ContainsExpression()
|
||||
{
|
||||
if (THelper.IsTypeNullable(leftExpr.Type))
|
||||
{
|
||||
Expression notNull = Expression.NotEqual(leftExpr, Expression.Constant(null));
|
||||
leftExpr = Expression.MakeMemberAccess(leftExpr, leftExpr.Type.GetMember(nameof(Nullable<int>.Value))[0]);
|
||||
rightExpr = Expression.MakeMemberAccess(rightExpr, rightExpr.Type.GetMember(nameof(Nullable<int>.Value))[0]);
|
||||
return Expression.AndAlso(notNull, CallContains());
|
||||
}
|
||||
|
||||
return CallContains();
|
||||
|
||||
Expression CallContains()
|
||||
{
|
||||
rightExpr = Expression.Convert(rightExpr, typeof(Enum));
|
||||
return Expression.Call(leftExpr, _enumHasFlag, rightExpr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
64
components/table/Filters/FilterInputs.razor
Normal file
64
components/table/Filters/FilterInputs.razor
Normal file
@ -0,0 +1,64 @@
|
||||
@namespace AntDesign.Filters
|
||||
|
||||
@using AntDesign
|
||||
|
||||
@implements IHandleEvent
|
||||
|
||||
@{
|
||||
// This is a utility .razor page, and it should not be used as a component.
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
@code {
|
||||
|
||||
// Unfortunately, the razor compiler currently doesn't allow setting EventCallbacks (e.g. ValueChanged) in static fields/properties
|
||||
// (see https://github.com/dotnet/aspnetcore/issues/24655 and https://github.com/dotnet/aspnetcore/issues/18919)
|
||||
// Therefore, we need to implement them as instance properties and use a singleton pattern
|
||||
public static FilterInputs Instance { get; } = new();
|
||||
|
||||
public readonly RenderFragment<TableFilterInputRenderOptions> DateTimeInput, GuidInput;
|
||||
|
||||
private FilterInputs() {
|
||||
DateTimeInput = filter =>
|
||||
@<text>
|
||||
@if (filter.FilterCompareOperator != TableFilterCompareOperator.TheSameDateWith) {
|
||||
<DatePicker Value="(DateTime?)filter.Value" ShowTime="true"
|
||||
TValue="DateTime?" ValueChanged="value => filter.Value = value?.AddMilliseconds(-value.Value.Millisecond)"
|
||||
PopupContainerSelector="@filter.PopupContainerSelector"
|
||||
BoundaryAdjustMode="@TriggerBoundaryAdjustMode.None"/>
|
||||
}
|
||||
else {
|
||||
<DatePicker Value="(DateTime?)filter.Value" TValue="DateTime?" ValueChanged="value => filter.Value = value"
|
||||
PopupContainerSelector="@filter.PopupContainerSelector"
|
||||
BoundaryAdjustMode="@TriggerBoundaryAdjustMode.None"/>
|
||||
}
|
||||
</text>;
|
||||
GuidInput = filter =>
|
||||
@<Input Value="(Guid?)filter.Value" TValue="Guid?" ValueChanged="value => filter.Value = value"/>;
|
||||
}
|
||||
|
||||
public RenderFragment<TableFilterInputRenderOptions> GetEnumInput<TData>() => filter =>
|
||||
@<text>
|
||||
<EnumSelect TEnum="TData" Mode="multiple"
|
||||
@*Values="EnumHelper<TData>.Split(filter.Value)" workaround for https: //github.com/ant-design-blazor/ant-design-blazor/issues/3006 *@
|
||||
PopupContainerSelector="@filter.PopupContainerSelector"
|
||||
Style="width:180px;"
|
||||
ValuesChanged="value => filter.Value = EnumHelper<TData>.Combine(value)"
|
||||
BoundaryAdjustMode="@TriggerBoundaryAdjustMode.None" />
|
||||
</text>;
|
||||
|
||||
public RenderFragment<TableFilterInputRenderOptions> GetNumberInput<TData>() where TData : struct => filter =>
|
||||
@<InputNumber Value="(TData?)filter.Value" Formatter="number => NumberFormatter(number, filter.Format)" TValue="TData?" ValueChanged="value => filter.Value = value"/>;
|
||||
|
||||
private string NumberFormatter(object value, string format)
|
||||
{
|
||||
if (value == null) return null;
|
||||
return Convert.ToDouble(value).ToString(format);
|
||||
}
|
||||
|
||||
public RenderFragment<TableFilterInputRenderOptions> GetInput<TData>() => filter =>
|
||||
@<Input TValue="TData" Value="(TData)filter.Value" ValueChanged="value => filter.Value = value" />;
|
||||
|
||||
// https://github.com/dotnet/aspnetcore/issues/24655
|
||||
Task IHandleEvent.HandleEventAsync(EventCallbackWorkItem callback, object arg) => callback.InvokeAsync(arg);
|
||||
}
|
42
components/table/Filters/FilterTypeResolver.cs
Normal file
42
components/table/Filters/FilterTypeResolver.cs
Normal file
@ -0,0 +1,42 @@
|
||||
// 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.
|
||||
|
||||
using System;
|
||||
|
||||
namespace AntDesign.Filters;
|
||||
|
||||
public interface IFieldFilterTypeResolver
|
||||
{
|
||||
public IFieldFilterType Resolve<T>();
|
||||
}
|
||||
|
||||
public class DefaultFieldFilterTypeResolver : IFieldFilterTypeResolver
|
||||
{
|
||||
public IFieldFilterType Resolve<T>()
|
||||
{
|
||||
var underlyingType = THelper.GetUnderlyingType<T>();
|
||||
|
||||
#pragma warning disable format
|
||||
return underlyingType switch
|
||||
{
|
||||
_ when underlyingType.IsEnum => new EnumFieldFilterType<T>(),
|
||||
_ when underlyingType == typeof(byte) => new NumberFieldFilterType<byte>(),
|
||||
_ when underlyingType == typeof(decimal) => new NumberFieldFilterType<decimal>(),
|
||||
_ when underlyingType == typeof(double) => new NumberFieldFilterType<double>(),
|
||||
_ when underlyingType == typeof(short) => new NumberFieldFilterType<short>(),
|
||||
_ when underlyingType == typeof(int) => new NumberFieldFilterType<int>(),
|
||||
_ when underlyingType == typeof(long) => new NumberFieldFilterType<long>(),
|
||||
_ when underlyingType == typeof(sbyte) => new NumberFieldFilterType<sbyte>(),
|
||||
_ when underlyingType == typeof(float) => new NumberFieldFilterType<float>(),
|
||||
_ when underlyingType == typeof(ushort) => new NumberFieldFilterType<ushort>(),
|
||||
_ when underlyingType == typeof(uint) => new NumberFieldFilterType<uint>(),
|
||||
_ when underlyingType == typeof(ulong) => new NumberFieldFilterType<ulong>(),
|
||||
_ when underlyingType == typeof(DateTime) => new DateTimeFieldFilterType(),
|
||||
_ when underlyingType == typeof(string) => new StringFieldFilterType(),
|
||||
_ when underlyingType == typeof(Guid) => new GuidFieldFilterType(),
|
||||
_ => throw new NotSupportedException()
|
||||
};
|
||||
#pragma warning restore format
|
||||
}
|
||||
}
|
15
components/table/Filters/GuidFieldFilterType.cs
Normal file
15
components/table/Filters/GuidFieldFilterType.cs
Normal file
@ -0,0 +1,15 @@
|
||||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AntDesign.Filters
|
||||
{
|
||||
public class GuidFieldFilterType : BaseFieldFilterType
|
||||
{
|
||||
public override RenderFragment<TableFilterInputRenderOptions> FilterInput => FilterInputs.Instance.GuidInput;
|
||||
}
|
||||
}
|
@ -2,16 +2,18 @@
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AntDesign.FilterExpression
|
||||
namespace AntDesign.Filters
|
||||
{
|
||||
public interface IFilterExpression
|
||||
public interface IFieldFilterType
|
||||
{
|
||||
TableFilterCompareOperator GetDefaultCompareOperator();
|
||||
TableFilterCompareOperator DefaultCompareOperator { get; }
|
||||
RenderFragment<TableFilterInputRenderOptions> FilterInput { get; }
|
||||
IEnumerable<TableFilterCompareOperator> SupportedCompareOperators { get; set; }
|
||||
|
||||
Expression GetFilterExpression(TableFilterCompareOperator compareOperator, Expression leftExpr, Expression rightExpr);
|
||||
}
|
||||
}
|
37
components/table/Filters/NumberFieldFilterType.cs
Normal file
37
components/table/Filters/NumberFieldFilterType.cs
Normal file
@ -0,0 +1,37 @@
|
||||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AntDesign.Filters
|
||||
{
|
||||
public class NumberFieldFilterType<T> : BaseFieldFilterType where T : struct
|
||||
{
|
||||
public override RenderFragment<TableFilterInputRenderOptions> FilterInput { get; } =
|
||||
FilterInputs.Instance.GetNumberInput<T>();
|
||||
|
||||
private static IEnumerable<TableFilterCompareOperator> _supportedCompareOperators = new[]
|
||||
{
|
||||
TableFilterCompareOperator.Equals,
|
||||
TableFilterCompareOperator.NotEquals,
|
||||
TableFilterCompareOperator.GreaterThan,
|
||||
TableFilterCompareOperator.LessThan,
|
||||
TableFilterCompareOperator.GreaterThanOrEquals,
|
||||
TableFilterCompareOperator.LessThanOrEquals
|
||||
};
|
||||
|
||||
public NumberFieldFilterType()
|
||||
{
|
||||
SupportedCompareOperators = _supportedCompareOperators;
|
||||
}
|
||||
|
||||
public override Expression GetFilterExpression(TableFilterCompareOperator compareOperator, Expression leftExpr, Expression rightExpr)
|
||||
{
|
||||
return base.GetFilterExpression(compareOperator, leftExpr, Expression.Convert(rightExpr, leftExpr.Type));
|
||||
}
|
||||
}
|
||||
}
|
69
components/table/Filters/StringFieldFilterType.cs
Normal file
69
components/table/Filters/StringFieldFilterType.cs
Normal file
@ -0,0 +1,69 @@
|
||||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AntDesign.Filters
|
||||
{
|
||||
public class StringFieldFilterType : BaseFieldFilterType
|
||||
{
|
||||
private static readonly MethodInfo _stringToLower = typeof(string).GetMethod(nameof(string.ToLower), Array.Empty<Type>());
|
||||
|
||||
public override TableFilterCompareOperator DefaultCompareOperator => TableFilterCompareOperator.Contains;
|
||||
|
||||
public override RenderFragment<TableFilterInputRenderOptions> FilterInput { get; } =
|
||||
FilterInputs.Instance.GetInput<string>();
|
||||
|
||||
private static IEnumerable<TableFilterCompareOperator> _supportedCompareOperators = new[]
|
||||
{
|
||||
TableFilterCompareOperator.Equals,
|
||||
TableFilterCompareOperator.NotEquals,
|
||||
TableFilterCompareOperator.Contains,
|
||||
TableFilterCompareOperator.StartsWith,
|
||||
TableFilterCompareOperator.EndsWith,
|
||||
};
|
||||
|
||||
public StringFieldFilterType()
|
||||
{
|
||||
SupportedCompareOperators = _supportedCompareOperators;
|
||||
}
|
||||
|
||||
public override Expression GetFilterExpression(TableFilterCompareOperator compareOperator, Expression leftExpr, Expression rightExpr)
|
||||
{
|
||||
MethodCallExpression lowerLeftExpr = Expression.Call(leftExpr, _stringToLower);
|
||||
MethodCallExpression lowerRightExpr = Expression.Call(rightExpr, _stringToLower);
|
||||
|
||||
return compareOperator switch
|
||||
{
|
||||
TableFilterCompareOperator.IsNull or TableFilterCompareOperator.IsNotNull => base.GetFilterExpression(
|
||||
compareOperator, leftExpr, rightExpr),
|
||||
TableFilterCompareOperator.Contains => NotNullAnd(GetMethodExpression(nameof(string.Contains),
|
||||
lowerLeftExpr, lowerRightExpr)),
|
||||
TableFilterCompareOperator.StartsWith => NotNullAnd(GetMethodExpression(nameof(string.StartsWith),
|
||||
lowerLeftExpr, lowerRightExpr)),
|
||||
TableFilterCompareOperator.EndsWith => NotNullAnd(GetMethodExpression(nameof(string.EndsWith),
|
||||
lowerLeftExpr, lowerRightExpr)),
|
||||
TableFilterCompareOperator.NotEquals => Expression.OrElse(
|
||||
Expression.Equal(leftExpr, Expression.Constant(null)),
|
||||
base.GetFilterExpression(compareOperator, lowerLeftExpr, lowerRightExpr)),
|
||||
_ => NotNullAnd(base.GetFilterExpression(compareOperator, lowerLeftExpr, lowerRightExpr))
|
||||
};
|
||||
|
||||
Expression NotNullAnd(Expression innerExpression)
|
||||
=> Expression.AndAlso(Expression.NotEqual(leftExpr, Expression.Constant(null)), innerExpression);
|
||||
}
|
||||
|
||||
private static Expression GetMethodExpression(string methodName, Expression leftExpr, Expression rightExpr)
|
||||
{
|
||||
MethodInfo mi = typeof(string).GetMethod(methodName, new[] { typeof(string) });
|
||||
if (mi == null)
|
||||
throw new MissingMethodException("There is no method - " + methodName);
|
||||
return Expression.Call(leftExpr, mi, rightExpr);
|
||||
}
|
||||
}
|
||||
}
|
13
components/table/Filters/TableFilterInputRenderOptions.cs
Normal file
13
components/table/Filters/TableFilterInputRenderOptions.cs
Normal file
@ -0,0 +1,13 @@
|
||||
// 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.
|
||||
|
||||
using System;
|
||||
|
||||
namespace AntDesign.Filters;
|
||||
|
||||
public readonly record struct TableFilterInputRenderOptions(TableFilter Filter, string PopupContainerSelector, string Format)
|
||||
{
|
||||
public object Value { get => Filter.Value; set => Filter.Value = value; }
|
||||
public TableFilterCompareOperator FilterCompareOperator => Filter.FilterCompareOperator;
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using AntDesign.Filters;
|
||||
using AntDesign.TableModels;
|
||||
|
||||
namespace AntDesign
|
||||
@ -72,5 +73,7 @@ namespace AntDesign
|
||||
internal void AddSummaryRow(SummaryRow summaryRow);
|
||||
|
||||
internal void OnColumnInitialized();
|
||||
|
||||
IFieldFilterTypeResolver FieldFilterTypeResolver { get; }
|
||||
}
|
||||
}
|
||||
|
@ -207,13 +207,13 @@ RenderFragment<(Table<TItem> table, bool header)> colGroup = ctx =>
|
||||
@if (table._tableWidth>0)
|
||||
{
|
||||
<div class="ant-table-expanded-row-fixed" style="width: @(table._tableWidth)px; position: sticky; left: 0px; overflow: hidden;">
|
||||
@if (table.EmptyTemplate!=null) @table.EmptyTemplate else { <Empty Simple /> }
|
||||
@if (table.EmptyTemplate!=null) { @table.EmptyTemplate } else { <Empty Simple /> }
|
||||
</div>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (table.EmptyTemplate!=null) @table.EmptyTemplate else { <Empty Simple /> }
|
||||
@if (table.EmptyTemplate!=null) { @table.EmptyTemplate } else { <Empty Simple /> }
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -5,6 +5,7 @@ using System.Runtime.CompilerServices;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using AntDesign.Core.HashCodes;
|
||||
using AntDesign.Filters;
|
||||
using AntDesign.JsInterop;
|
||||
using AntDesign.TableModels;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
@ -175,6 +176,9 @@ namespace AntDesign
|
||||
/// </summary>
|
||||
[Parameter] public bool Resizable { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public IFieldFilterTypeResolver FieldFilterTypeResolver { get; set; }
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
/// <summary>
|
||||
/// Whether to enable virtualization feature or not, only works for .NET 5 and higher
|
||||
@ -190,6 +194,9 @@ namespace AntDesign
|
||||
[Inject]
|
||||
private ILogger<Table<TItem>> Logger { get; set; }
|
||||
|
||||
[Inject]
|
||||
private IFieldFilterTypeResolver InjectedFieldFilterTypeResolver { get; set; }
|
||||
|
||||
public ColumnContext ColumnContext { get; set; }
|
||||
|
||||
private IEnumerable<TItem> _showItems;
|
||||
@ -594,6 +601,8 @@ namespace AntDesign
|
||||
InitializePagination();
|
||||
|
||||
FlushCache();
|
||||
|
||||
FieldFilterTypeResolver ??= InjectedFieldFilterTypeResolver;
|
||||
}
|
||||
|
||||
private IEnumerable<TItem> GetAllItemsByTopLevelItems(IEnumerable<TItem> items, bool onlySelectable = false)
|
||||
@ -819,4 +828,4 @@ namespace AntDesign
|
||||
_isReloading = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -32,10 +32,10 @@ namespace AntDesign
|
||||
|
||||
public string CancelSort { get; set; } = "Click to cancel sort";
|
||||
|
||||
public FilterOptions FilterOptions { get; set; } = new();
|
||||
public FilterOptionsLocale FilterOptions { get; set; } = new();
|
||||
}
|
||||
|
||||
public class FilterOptions
|
||||
public class FilterOptionsLocale
|
||||
{
|
||||
public string True { get; set; } = "True";
|
||||
|
||||
@ -70,5 +70,24 @@ namespace AntDesign
|
||||
public string IsNotNull { get; set; } = "Is Not Null";
|
||||
|
||||
public string TheSameDateWith { get; set; } = "The Same Date With";
|
||||
|
||||
public string Operator(TableFilterCompareOperator compareOperator)
|
||||
=> compareOperator switch
|
||||
{
|
||||
TableFilterCompareOperator.Equals => Equals,
|
||||
TableFilterCompareOperator.Contains => Contains,
|
||||
TableFilterCompareOperator.StartsWith => StartsWith,
|
||||
TableFilterCompareOperator.EndsWith => EndsWith,
|
||||
TableFilterCompareOperator.GreaterThan => GreaterThan,
|
||||
TableFilterCompareOperator.LessThan => LessThan,
|
||||
TableFilterCompareOperator.GreaterThanOrEquals => GreaterThanOrEquals,
|
||||
TableFilterCompareOperator.LessThanOrEquals => LessThanOrEquals,
|
||||
TableFilterCompareOperator.NotEquals => NotEquals,
|
||||
TableFilterCompareOperator.IsNull => IsNull,
|
||||
TableFilterCompareOperator.IsNotNull => IsNotNull,
|
||||
TableFilterCompareOperator.NotContains => NotContains,
|
||||
TableFilterCompareOperator.TheSameDateWith => TheSameDateWith,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(compareOperator), compareOperator, null)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Text.Json.Serialization;
|
||||
using AntDesign.FilterExpression;
|
||||
using AntDesign.Filters;
|
||||
|
||||
namespace AntDesign.TableModels
|
||||
{
|
||||
@ -24,12 +24,11 @@ namespace AntDesign.TableModels
|
||||
|
||||
public int ColumnIndex => _columnIndex;
|
||||
|
||||
private readonly FilterExpressionResolver<TField> _filterExpressionResolver = new FilterExpressionResolver<TField>();
|
||||
|
||||
private TableFilterType FilterType { get; set; } = TableFilterType.List;
|
||||
|
||||
private LambdaExpression _getFieldExpression;
|
||||
private int _columnIndex;
|
||||
private readonly LambdaExpression _getFieldExpression;
|
||||
private readonly int _columnIndex;
|
||||
private readonly IFieldFilterType _fieldFilterType;
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
[JsonConstructor]
|
||||
@ -42,7 +41,8 @@ namespace AntDesign.TableModels
|
||||
this._columnIndex = columnIndex;
|
||||
}
|
||||
|
||||
public FilterModel(IFieldColumn column, LambdaExpression getFieldExpression, string fieldName, Expression<Func<TField, TField, bool>> onFilter, IList<TableFilter> filters, TableFilterType filterType)
|
||||
public FilterModel(IFieldColumn column, LambdaExpression getFieldExpression, string fieldName,
|
||||
Expression<Func<TField, TField, bool>> onFilter, IList<TableFilter> filters, TableFilterType filterType, IFieldFilterType fieldFilterType)
|
||||
{
|
||||
this._getFieldExpression = getFieldExpression;
|
||||
this.FieldName = fieldName;
|
||||
@ -54,13 +54,15 @@ namespace AntDesign.TableModels
|
||||
{
|
||||
this.OnFilter = onFilter;
|
||||
}
|
||||
|
||||
this.SelectedValues = filters.Select(x => x.Value?.ToString());
|
||||
this.Filters = filters;
|
||||
this.FilterType = filterType;
|
||||
this._columnIndex = column.ColIndex;
|
||||
this._fieldFilterType = fieldFilterType;
|
||||
}
|
||||
|
||||
public IQueryable<TItem> FilterList<TItem>(IQueryable<TItem> source)
|
||||
public IQueryable<TItem> FilterList<TItem>(IQueryable<TItem> source)
|
||||
{
|
||||
if (Filters?.Any() != true)
|
||||
{
|
||||
@ -74,12 +76,7 @@ namespace AntDesign.TableModels
|
||||
{
|
||||
lambda = Expression.Constant(false, typeof(bool));
|
||||
}
|
||||
|
||||
IFilterExpression filterExpression = null;
|
||||
if (FilterType == TableFilterType.FieldType)
|
||||
{
|
||||
filterExpression = _filterExpressionResolver.GetFilterExpression();
|
||||
}
|
||||
|
||||
foreach (var filter in Filters)
|
||||
{
|
||||
if (this.FilterType == TableFilterType.List)
|
||||
@ -88,17 +85,16 @@ namespace AntDesign.TableModels
|
||||
}
|
||||
else // TableFilterType.FieldType
|
||||
{
|
||||
if (filter.Value == null && (filter.FilterCompareOperator != TableFilterCompareOperator.IsNull && filter.FilterCompareOperator != TableFilterCompareOperator.IsNotNull)) continue;
|
||||
Expression constantExpression = null;
|
||||
if (filter.FilterCompareOperator == TableFilterCompareOperator.IsNull || filter.FilterCompareOperator == TableFilterCompareOperator.IsNotNull)
|
||||
{
|
||||
constantExpression = Expression.Constant(null, typeof(TField));
|
||||
}
|
||||
else
|
||||
{
|
||||
constantExpression = Expression.Constant(filter.Value, typeof(TField));
|
||||
}
|
||||
var expression = filterExpression!.GetFilterExpression(filter.FilterCompareOperator, _getFieldExpression.Body, constantExpression);
|
||||
if (filter.Value == null
|
||||
&& filter.FilterCompareOperator is not (TableFilterCompareOperator.IsNull or TableFilterCompareOperator.IsNotNull))
|
||||
continue;
|
||||
|
||||
Expression constantExpression = Expression.Constant(
|
||||
filter.FilterCompareOperator is TableFilterCompareOperator.IsNull
|
||||
or TableFilterCompareOperator.IsNotNull
|
||||
? null
|
||||
: filter.Value);
|
||||
var expression = _fieldFilterType.GetFilterExpression(filter.FilterCompareOperator, _getFieldExpression.Body, constantExpression);
|
||||
if (lambda == null)
|
||||
{
|
||||
lambda = expression;
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using AntDesign.Filters;
|
||||
|
||||
namespace AntDesign.TableModels
|
||||
{
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
using AntDesign.Filters;
|
||||
|
||||
namespace AntDesign.TableModels
|
||||
{
|
||||
|
@ -27,5 +27,13 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\components\AntDesign.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<UpToDateCheckInput Remove="Demos\Components\Table\demo\CustomFieldFilter.razor" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<_ContentIncludedByDefault Remove="Demos\Components\Table\demo\CustomFieldFilter.razor" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -0,0 +1,65 @@
|
||||
@using Color = System.Drawing.Color
|
||||
@using System.Drawing
|
||||
@using System.Linq.Expressions
|
||||
@using System.Reflection
|
||||
@using AntDesign.Filters
|
||||
|
||||
<Table DataSource="data" TItem="KnownColor">
|
||||
<PropertyColumn Property="c=>c.ToString()" Title="Name" Sortable Filterable FieldFilterType="simpleStringFiler" />
|
||||
|
||||
<PropertyColumn Property="c=>Color.FromKnownColor(c)" Title="Color"
|
||||
SorterCompare="(x, y) => x.GetHue().CompareTo(y.GetHue())"
|
||||
FieldFilterType="colorFilterOptions" Filterable>
|
||||
<CellRender Context="cell">
|
||||
<span style="background-color: @(ColorTranslator.ToHtml(cell.FieldValue))"> </span>
|
||||
</CellRender>
|
||||
</PropertyColumn>
|
||||
|
||||
<PropertyColumn Property="c=>Color.FromKnownColor(c).R" Title="Red" Sortable Filterable />
|
||||
<PropertyColumn Property="c=>Color.FromKnownColor(c).G" Title="Green" Sortable Filterable />
|
||||
<PropertyColumn Property="c=>Color.FromKnownColor(c).B" Title="Blue" Sortable Filterable />
|
||||
</Table>
|
||||
|
||||
@code {
|
||||
|
||||
readonly KnownColor[] data = Enum.GetValues<KnownColor>();
|
||||
readonly ColorFieldFilterType colorFilterOptions = new();
|
||||
readonly StringFieldFilterType simpleStringFiler = new() { SupportedCompareOperators = new[] { TableFilterCompareOperator.Contains } };
|
||||
class ColorFieldFilterType : BaseFieldFilterType
|
||||
{
|
||||
private static readonly MethodInfo colorGetBrightness = typeof(Color).GetMethod(nameof(Color.GetBrightness), Array.Empty<Type>());
|
||||
|
||||
public override RenderFragment<TableFilterInputRenderOptions> FilterInput { get; }
|
||||
|
||||
public override TableFilterCompareOperator DefaultCompareOperator => TableFilterCompareOperator.GreaterThan;
|
||||
|
||||
private static IEnumerable<TableFilterCompareOperator> _supportedCompareOperators = new[]
|
||||
{
|
||||
TableFilterCompareOperator.Equals,
|
||||
TableFilterCompareOperator.NotEquals,
|
||||
TableFilterCompareOperator.GreaterThan,
|
||||
TableFilterCompareOperator.LessThan,
|
||||
TableFilterCompareOperator.GreaterThanOrEquals,
|
||||
TableFilterCompareOperator.LessThanOrEquals
|
||||
};
|
||||
|
||||
public ColorFieldFilterType()
|
||||
{
|
||||
FilterInput = GetFilterInput;
|
||||
SupportedCompareOperators = _supportedCompareOperators;
|
||||
}
|
||||
|
||||
public RenderFragment GetFilterInput(TableFilterInputRenderOptions filter)
|
||||
{
|
||||
return @<Slider TValue="double" Value="(float?)filter.Value ?? 0" ValueChanged="value => filter.Value = (float?)value" Style="width: 70px" Min="0" Max="1" Step=".01" />;
|
||||
}
|
||||
|
||||
public override Expression GetFilterExpression(TableFilterCompareOperator compareOperator, Expression leftExpr, Expression rightExpr)
|
||||
{
|
||||
// Compare the brightness of the color to the selected value
|
||||
leftExpr = Expression.Call(leftExpr, colorGetBrightness);
|
||||
|
||||
return base.GetFilterExpression(compareOperator, leftExpr, rightExpr);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
---
|
||||
order: 6.55
|
||||
title:
|
||||
zh-CN: 自定义筛选器
|
||||
en-US: Custom field filter
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
当列类型不是内置筛选器支持的类型时,或者您想修改筛选器的比较操作,可以使用自定义筛选器 `FieldFilterType`。
|
||||
|
||||
在本示例中,Color 列就是用自定义筛选器,可以按"亮度"筛选 (或按HUE值排序)。
|
||||
|
||||
自定义筛选器需要实现 `IFieldFilterType`,或者继承 `BaseFieldFilterType` 或 `DateFieldFilterType` 来按需重写相关方法。
|
||||
|
||||
另外,还可以通过设置 `FieldFilterTypeResolver` 属性为整个表格组件配置自定义筛选器,或者可以通过在依赖注入服务中注册不同的 `IFilterTypeResolver` 实现来为整个应用程序中的表格组件设置自定义筛选器(`DefaultFieldFilterTypeResolver` 是默认实现。)。
|
||||
|
||||
## en-US
|
||||
|
||||
For types that are not supported by the built-in filters, or when you want different semantics like customizing the default operator, you can specify a custom `FieldFilterType` on the column.
|
||||
|
||||
In this example, the Color column can be filtered by the colors brightness (or sorted by its hue).
|
||||
|
||||
The custom Filter Types should implement `IFieldFilterType`, but may use the provided `BaseFieldFilterType` or `DateFieldFilterType` as a base implementation.
|
||||
|
||||
Your custom filter types can also be applied for a whole table, by setting its `FieldFilterTypeResolver` property, or globally by registering a different `IFilterTypeResolver` service. (See `DefaultFieldFilterTypeResolver` as a reference/fallback implementation.)
|
@ -1,4 +1,4 @@
|
||||
---
|
||||
---
|
||||
order: 6.6
|
||||
title:
|
||||
en-US: Custom Filter Dropdown
|
||||
|
@ -1,4 +1,4 @@
|
||||
---
|
||||
---
|
||||
category: Components
|
||||
cols: 1
|
||||
type: Data Display
|
||||
@ -61,6 +61,7 @@ Specify `dataSource` of Table as an array of data, the `OnChange` event and its
|
||||
| OnRowClick | Row click event (deprecated in antd v3) | EventCallback<RowData<TItem>> | - |
|
||||
| HidePagination| To hide the pager, PageSize would equals the number of rows in the data source | bool | false |
|
||||
| Resizable | Enable resizable column | bool | false |
|
||||
| FieldFilterTypeResolver | Used to resolve filter types for columns | `IFilterTypeResolver` | Injected |
|
||||
|
||||
### Column
|
||||
|
||||
@ -84,6 +85,7 @@ The Column definition of the previous version, For .NET 6 and above, `PropertyCo
|
||||
| FilterMultiple | Specify filter multiple selection and single selection | bool | true |
|
||||
| OnFilter | Filter current data | Expression<Func<TData, TData, bool>> | - |
|
||||
| FilterDropdown | Custom Filter Dropdown Template | RenderFragment | - |
|
||||
| FieldFilterType | Specifies what filter options to display and how to filter the data | `IFieldFilterType` | Resolved using Table's `FieldFilterTypeResolver` |
|
||||
|
||||
### PropertyColumn
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
---
|
||||
---
|
||||
category: Components
|
||||
cols: 1
|
||||
type: 数据展示
|
||||
@ -63,6 +63,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/f-SbcX2Lx/Table.svg
|
||||
| OnRowClick | 行点击事件(于antd v3中已废弃) | EventCallback<RowData<TItem>> | - |
|
||||
| HidePagination| 隐藏分页器,PageSize 等于数据源的行数 | bool | false |
|
||||
| Resizable | 启用可伸缩列 | bool | false |
|
||||
| FieldFilterTypeResolver | 用于解析列的筛选器类型 | `IFilterTypeResolver` | 默认由全局注入 |
|
||||
|
||||
### Column
|
||||
|
||||
@ -85,6 +86,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/f-SbcX2Lx/Table.svg
|
||||
| Filters | 指定需要筛选菜单的列 | IEnumerable<TableFilter<TData>> | - |
|
||||
| FilterMultiple | 指定筛选器多选和单选 | bool | true |
|
||||
| FilterDropdown | 自定义列筛选器模板 | RenderFragment | - |
|
||||
| FieldFilterType | 筛选器配置 ,可用于自定义额外的筛选器 | `IFieldFilterType` | 由 `FieldFilterTypeResolver` 根据类型解析内置筛选器 |
|
||||
| OnFilter | 筛选当前数据 | Expression<Func<TData, TData, bool>> | - |
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user