mirror of
https://gitee.com/ant-design-blazor/ant-design-blazor.git
synced 2024-11-30 02:58:13 +08:00
feat(module: table): add filters (#1178)
* feat: add table filter * add filter buttons * fix filter menu * make filter work * support multiple filters * delete the old files * support single select
This commit is contained in:
parent
08fec90cd3
commit
4065869806
@ -88,6 +88,7 @@ namespace AntDesign.Internal
|
||||
* Current placement, would change by overlay in some cases(via ChangePlacementForShow function)
|
||||
*/
|
||||
private PlacementType _placement = PlacementType.BottomLeft;
|
||||
|
||||
[Parameter]
|
||||
public PlacementType Placement
|
||||
{
|
||||
@ -127,6 +128,17 @@ namespace AntDesign.Internal
|
||||
[Parameter]
|
||||
public TriggerBoundaryAdjustMode BoundaryAdjustMode { get; set; } = TriggerBoundaryAdjustMode.InView;
|
||||
|
||||
[Parameter]
|
||||
public ElementReference TriggerReference
|
||||
{
|
||||
get => _triggerReference;
|
||||
set
|
||||
{
|
||||
_triggerReference = value;
|
||||
RefBack.Set(value);
|
||||
}
|
||||
}
|
||||
|
||||
[Inject]
|
||||
private DomEventService DomEventService { get; set; }
|
||||
|
||||
@ -134,6 +146,7 @@ namespace AntDesign.Internal
|
||||
private bool _mouseInOverlay = false;
|
||||
|
||||
protected Overlay _overlay = null;
|
||||
private ElementReference _triggerReference;
|
||||
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
@ -158,14 +171,16 @@ namespace AntDesign.Internal
|
||||
DomEventService.AddEventListener(Ref, "focusin", OnUnboundFocusIn, true);
|
||||
DomEventService.AddEventListener(Ref, "focusout", OnUnboundFocusOut, true);
|
||||
DomEventService.AddEventListener(Ref, "contextmenu", OnContextMenu, true, true);
|
||||
|
||||
}
|
||||
return base.OnAfterRenderAsync(firstRender);
|
||||
}
|
||||
|
||||
private void OnUnboundMouseEnter(JsonElement jsonElement) => OnTriggerMouseEnter();
|
||||
|
||||
private void OnUnboundMouseLeave(JsonElement jsonElement) => OnTriggerMouseLeave();
|
||||
|
||||
private void OnUnboundFocusIn(JsonElement jsonElement) => OnTriggerFocusIn();
|
||||
|
||||
private void OnUnboundFocusOut(JsonElement jsonElement) => OnTriggerFocusOut();
|
||||
|
||||
private async void OnUnboundClick(JsonElement jsonElement)
|
||||
@ -175,6 +190,7 @@ namespace AntDesign.Internal
|
||||
|
||||
await OnClickDiv(eventArgs);
|
||||
}
|
||||
|
||||
private async void OnContextMenu(JsonElement jsonElement)
|
||||
{
|
||||
var eventArgs = JsonSerializer.Deserialize<MouseEventArgs>(jsonElement.ToString(),
|
||||
@ -361,8 +377,13 @@ namespace AntDesign.Internal
|
||||
await OnOverlayHiding.InvokeAsync(visible);
|
||||
}
|
||||
|
||||
protected virtual void OnOverlayShow() { }
|
||||
protected virtual void OnOverlayHide() { }
|
||||
protected virtual void OnOverlayShow()
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnOverlayHide()
|
||||
{
|
||||
}
|
||||
|
||||
internal void ChangePlacementForShow(PlacementType placement)
|
||||
{
|
||||
|
@ -102,6 +102,7 @@ namespace AntDesign
|
||||
set
|
||||
{
|
||||
_selectedKeys = value;
|
||||
MenuItems.ForEach(x => x.UpdateStelected());
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,7 +141,7 @@ namespace AntDesign
|
||||
}
|
||||
else if (menuitem.IsSelected || menuitem.FirstRun)
|
||||
{
|
||||
if (!menuitem.FirstRun)
|
||||
if (!menuitem.FirstRun)
|
||||
skipParentSelection = item.ParentMenu?.Key == menuitem.ParentMenu?.Key;
|
||||
menuitem.Deselect(skipParentSelection);
|
||||
}
|
||||
|
@ -87,6 +87,18 @@ namespace AntDesign
|
||||
Select();
|
||||
}
|
||||
|
||||
internal void UpdateStelected()
|
||||
{
|
||||
if (RootMenu.SelectedKeys.Contains(Key))
|
||||
{
|
||||
if (!IsSelected) Select();
|
||||
}
|
||||
else if (IsSelected)
|
||||
{
|
||||
Deselect();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task HandleOnClick(MouseEventArgs args)
|
||||
{
|
||||
if (Disabled)
|
||||
|
@ -40,17 +40,10 @@ else if (IsHeader && HeaderColSpan != 0)
|
||||
<th class="ant-table-cell ant-table-row-expand-icon-cell"></th>
|
||||
}
|
||||
|
||||
<th class="@ClassMapper.Class" style="@FixedStyle @HeaderStyle" @onclick="HandelHeaderClick" colspan="@HeaderColSpan" title="@(Ellipsis?HeaderTitle:"")">
|
||||
@if (Sortable)
|
||||
<th class="@ClassMapper.Class" style="@FixedStyle @HeaderStyle" colspan="@HeaderColSpan" title="@(Ellipsis?HeaderTitle:"")">
|
||||
@if (Sortable || Filters?.Any() == true)
|
||||
{
|
||||
if (ShowSorterTooltip)
|
||||
{
|
||||
@ToolTipSorter(this)
|
||||
}
|
||||
else
|
||||
{
|
||||
@SortHeader(this)
|
||||
}
|
||||
@FilterToolTipSorter(this)
|
||||
}
|
||||
else if (TitleTemplate != null)
|
||||
{
|
||||
@ -120,17 +113,16 @@ else if (IsBody && RowSpan != 0 && ColSpan != 0)
|
||||
|
||||
string HeaderTitle => Title ?? DisplayName ?? FieldName ?? DataIndex;
|
||||
|
||||
|
||||
readonly RenderFragment<Column<TData>> SortHeader = col =>
|
||||
@<div class="ant-table-column-sorters">
|
||||
<span>
|
||||
@if (col.TitleTemplate != null)@col.TitleTemplate else @col.HeaderTitle
|
||||
</span>
|
||||
@{
|
||||
@{
|
||||
bool hasDescendingSorter = SortDirection.Descending.IsIn(col.SortDirections);
|
||||
bool hasAscendingSorter = SortDirection.Ascending.IsIn(col.SortDirections);
|
||||
}
|
||||
<span class="ant-table-column-sorter @((hasDescendingSorter && hasAscendingSorter)?"ant-table-column-sorter-full":"")">
|
||||
<span class="ant-table-column-sorter @(hasDescendingSorter && hasAscendingSorter?"ant-table-column-sorter-full":"")">
|
||||
<span class="ant-table-column-sorter-inner">
|
||||
@if (hasAscendingSorter)
|
||||
{
|
||||
@ -144,13 +136,89 @@ else if (IsBody && RowSpan != 0 && ColSpan != 0)
|
||||
</span>
|
||||
</div>;
|
||||
|
||||
readonly RenderFragment<Column<TData>> ToolTipSorter = col =>
|
||||
@<Tooltip Title="@col.SorterTooltip">
|
||||
<Unbound>
|
||||
<div class="ant-table-column-sorters-with-tooltip" @ref="context.Current">
|
||||
@col.SortHeader(col)
|
||||
</div>
|
||||
</Unbound>
|
||||
</Tooltip>;
|
||||
readonly RenderFragment<Column<TData>> ToolTipSorter = col =>
|
||||
@<Template>
|
||||
@if (col.ShowSorterTooltip)
|
||||
{
|
||||
<Tooltip Title="@col.SorterTooltip">
|
||||
<Unbound>
|
||||
<div class="ant-table-column-sorters-with-tooltip" @ref="context.Current">
|
||||
@col.SortHeader(col)
|
||||
</div>
|
||||
</Unbound>
|
||||
</Tooltip>
|
||||
}
|
||||
else
|
||||
{
|
||||
@col.SortHeader(col)
|
||||
}
|
||||
</Template>;
|
||||
|
||||
}
|
||||
readonly RenderFragment<Column<TData>> FilterToolTipSorter = col =>
|
||||
@<Template>
|
||||
@if (col.Filters?.Any() == true)
|
||||
{
|
||||
<div class="ant-table-filter-column">
|
||||
<span class="ant-table-filter-column-title" @attributes="@(new Dictionary<string, object>() { ["onclick"]=_callbackFactory.Create(col, col.HandelHeaderClick )})">
|
||||
@if (col.Sortable)
|
||||
{
|
||||
@col.ToolTipSorter(col)
|
||||
}
|
||||
</span>
|
||||
<Dropdown Trigger="new[] { TriggerType.Click }" Visible="col._filterOpened" Placement="PlacementType.BottomRight" TriggerReference="col._filterTriggerRef">
|
||||
<Unbound>
|
||||
<span @ref="@col._filterTriggerRef" class="ant-table-filter-trigger-container @(col._filterOpened?" ant-table-filter-trigger-container-open":"")">
|
||||
<span role="button" tabindex="-1" class="ant-dropdown-trigger ant-table-filter-trigger @(col._hasFilterSelected?"active":"")"
|
||||
@attributes="@(new Dictionary<string, object>() { ["onclick"] = _callbackFactory.Create<MouseEventArgs>(col, () => col._filterOpened = !col._filterOpened )})">
|
||||
<Icon Type="filter" Theme="fill" />
|
||||
</span>
|
||||
</span>
|
||||
</Unbound>
|
||||
<Overlay>
|
||||
<div class="ant-table-filter-dropdown">
|
||||
<Menu AutoCloseDropdown="false" SelectedKeys="col._selectedFilterValues">
|
||||
@foreach (var filter in col.Filters)
|
||||
{
|
||||
<MenuItem Key="@filter.Value.ToString()" @attributes="@(new Dictionary<string, object>() { ["OnClick"] = _callbackFactory.Create<MouseEventArgs>(col, () => col.FilterSelected(filter))})">
|
||||
@if (col.FilterMultiple)
|
||||
{
|
||||
<Checkbox Value="filter.Selected" @attributes="@(new Dictionary<string, object>() { ["ValueChanged"] = _callbackFactory.Create<bool>(col, value => col.FilterSelected(filter)) })">@filter.Text</Checkbox>
|
||||
}
|
||||
else
|
||||
{
|
||||
<Radio TValue="bool" Checked="filter.Selected" @attributes="@(new Dictionary<string, object>() { ["CheckedChanged"] = _callbackFactory.Create<bool>(col, value => col.FilterSelected(filter))})">@filter.Text</Radio>
|
||||
}
|
||||
</MenuItem>
|
||||
}
|
||||
</Menu>
|
||||
<div class="ant-table-filter-dropdown-btns">
|
||||
<Button Size="small" Type="link" @attributes="@(new Dictionary<string, object>() { ["OnClick"] = _callbackFactory.Create<MouseEventArgs>(col, ()=> col.FilterReset()) })">
|
||||
@col.Table?.Locale.FilterReset
|
||||
</Button>
|
||||
<Button Size="small" Type="primary" @attributes="@(new Dictionary<string, object>() { ["OnClick"] = _callbackFactory.Create<MouseEventArgs>(col, ()=> col.FilterConfirm()) })">
|
||||
@col.Table?.Locale.FilterConfirm
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Overlay>
|
||||
</Dropdown>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (col.Sortable)
|
||||
{
|
||||
@col.ToolTipSorter(col)
|
||||
}
|
||||
else if (col.TitleTemplate != null)
|
||||
{
|
||||
@col.TitleTemplate
|
||||
}
|
||||
else
|
||||
{
|
||||
@col.HeaderTitle
|
||||
}
|
||||
}
|
||||
</Template>;
|
||||
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using AntDesign.Core.Reflection;
|
||||
using AntDesign.Internal;
|
||||
@ -49,6 +50,24 @@ namespace AntDesign
|
||||
[Parameter]
|
||||
public SortDirection DefaultSortOrder { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public IEnumerable<TableFilter<TData>> Filters { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool FilterMultiple { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Function that determines if the row is displayed when filtered
|
||||
/// <para>
|
||||
/// Parameter 1: The value of the filter item
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Parameter 2: The value of the column
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Expression<Func<TData, TData, bool>> OnFilter { get; set; }
|
||||
|
||||
private PropertyReflector? _propertyReflector;
|
||||
|
||||
public string DisplayName => _propertyReflector?.DisplayName;
|
||||
@ -57,12 +76,22 @@ namespace AntDesign
|
||||
|
||||
public ITableSortModel SortModel { get; private set; }
|
||||
|
||||
public ITableFilterModel FilterModel { get; private set; }
|
||||
|
||||
private SortDirection _sortDirection;
|
||||
|
||||
public Func<RowData, TData> GetValue { get; private set; }
|
||||
|
||||
void IFieldColumn.ClearSorter() => SetSorter(SortDirection.None);
|
||||
|
||||
private static readonly EventCallbackFactory _callbackFactory = new EventCallbackFactory();
|
||||
|
||||
private bool _filterOpened;
|
||||
private bool _hasFilterSelected;
|
||||
private string[] _selectedFilterValues;
|
||||
|
||||
private ElementReference _filterTriggerRef;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
@ -158,5 +187,37 @@ namespace AntDesign
|
||||
RowData.Expanded = !RowData.Expanded;
|
||||
Table?.Refresh();
|
||||
}
|
||||
|
||||
private void FilterSelected(TableFilter<TData> filter)
|
||||
{
|
||||
if (!FilterMultiple)
|
||||
{
|
||||
Filters.ForEach(x => x.Selected = false);
|
||||
filter.Selected = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
filter.Selected = !filter.Selected;
|
||||
}
|
||||
|
||||
_selectedFilterValues = Filters.Where(x => x.Selected).Select(x => x.Value.ToString()).ToArray();
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private void FilterConfirm()
|
||||
{
|
||||
_filterOpened = false;
|
||||
_hasFilterSelected = Filters?.Any(x => x.Selected) == true;
|
||||
|
||||
FilterModel = _hasFilterSelected ? new FilterModel<TData>(_propertyReflector.Value.PropertyInfo, OnFilter, Filters.Where(x => x.Selected).ToList()) : null;
|
||||
|
||||
Table?.ReloadAndInvokeChange();
|
||||
}
|
||||
|
||||
private void FilterReset()
|
||||
{
|
||||
Filters.ForEach(x => x.Selected = false);
|
||||
FilterConfirm();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using AntDesign.TableModels;
|
||||
using System.Collections.Generic;
|
||||
using AntDesign.TableModels;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace AntDesign
|
||||
@ -17,6 +18,8 @@ namespace AntDesign
|
||||
|
||||
public ITableSortModel SortModel { get; }
|
||||
|
||||
public ITableFilterModel FilterModel { get; }
|
||||
|
||||
internal void ClearSorter();
|
||||
}
|
||||
}
|
||||
|
@ -189,9 +189,17 @@ namespace AntDesign
|
||||
|
||||
foreach (var col in ColumnContext.HeaderColumns)
|
||||
{
|
||||
if (col is IFieldColumn fieldColumn && fieldColumn.SortModel != null)
|
||||
if (col is IFieldColumn fieldColumn)
|
||||
{
|
||||
queryModel.AddSortModel(fieldColumn.SortModel);
|
||||
if (fieldColumn.SortModel != null)
|
||||
{
|
||||
queryModel.AddSortModel(fieldColumn.SortModel);
|
||||
}
|
||||
|
||||
if (fieldColumn.FilterModel != null)
|
||||
{
|
||||
queryModel.AddFilterModel(fieldColumn.FilterModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,12 +212,16 @@ namespace AntDesign
|
||||
if (_dataSource != null)
|
||||
{
|
||||
var query = _dataSource.AsQueryable();
|
||||
var orderedSortModels = queryModel.SortModel.OrderBy(x => x.Priority);
|
||||
foreach (var sort in orderedSortModels)
|
||||
foreach (var sort in queryModel.SortModel.OrderBy(x => x.Priority))
|
||||
{
|
||||
query = sort.SortList(query);
|
||||
}
|
||||
|
||||
foreach (var filter in queryModel.FilterModel)
|
||||
{
|
||||
query = filter.FilterList(query);
|
||||
}
|
||||
|
||||
query = query.Skip((PageIndex - 1) * PageSize).Take(PageSize);
|
||||
queryModel.SetQueryableLambda(query);
|
||||
|
||||
|
20
components/table/TableFilter.cs
Normal file
20
components/table/TableFilter.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace AntDesign
|
||||
{
|
||||
public class TableFilter<TValue>
|
||||
{
|
||||
public string Text { get; set; }
|
||||
|
||||
public TValue Value { get; set; }
|
||||
|
||||
public bool Selected { get; set; }
|
||||
|
||||
internal void SelectValue(bool selected)
|
||||
{
|
||||
this.Selected = selected;
|
||||
}
|
||||
}
|
||||
}
|
52
components/table/TableModels/FilterModel.cs
Normal file
52
components/table/TableModels/FilterModel.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
namespace AntDesign.TableModels
|
||||
{
|
||||
public class FilterModel<TField> : ITableFilterModel
|
||||
{
|
||||
public string FieldName { get; }
|
||||
|
||||
public IEnumerable<string> SelectedValues { get; set; }
|
||||
|
||||
public IList<TableFilter<TField>> Filters { get; }
|
||||
|
||||
public Expression<Func<TField, TField, bool>> OnFilter { get; set; }
|
||||
|
||||
private PropertyInfo _propertyInfo;
|
||||
|
||||
public FilterModel(PropertyInfo propertyInfo, Expression<Func<TField, TField, bool>> onFilter, IList<TableFilter<TField>> filters)
|
||||
{
|
||||
this._propertyInfo = propertyInfo;
|
||||
this.FieldName = _propertyInfo.Name;
|
||||
this.OnFilter = onFilter;
|
||||
this.SelectedValues = filters.Select(x => x.Value.ToString());
|
||||
this.Filters = filters;
|
||||
}
|
||||
|
||||
public IQueryable<TItem> FilterList<TItem>(IQueryable<TItem> source)
|
||||
{
|
||||
if (Filters?.Any() != true)
|
||||
{
|
||||
return source;
|
||||
}
|
||||
|
||||
var sourceExpression = Expression.Parameter(typeof(TItem));
|
||||
var propertyExpression = Expression.Property(sourceExpression, _propertyInfo);
|
||||
|
||||
Expression invocationExpression = Expression.Invoke((Expression<Func<bool>>)(() => false));
|
||||
|
||||
foreach (var filter in Filters)
|
||||
{
|
||||
invocationExpression = Expression.OrElse(invocationExpression, Expression.Invoke(OnFilter, Expression.Constant(filter.Value), propertyExpression));
|
||||
}
|
||||
|
||||
var lambda = Expression.Lambda<Func<TItem, bool>>(invocationExpression, sourceExpression);
|
||||
|
||||
return source.Where(lambda);
|
||||
}
|
||||
}
|
||||
}
|
16
components/table/TableModels/ITableFilterModel.cs
Normal file
16
components/table/TableModels/ITableFilterModel.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace AntDesign.TableModels
|
||||
{
|
||||
public interface ITableFilterModel
|
||||
{
|
||||
public string FieldName { get; }
|
||||
|
||||
public IEnumerable<string> SelectedValues { get; }
|
||||
|
||||
public IQueryable<TItem> FilterList<TItem>(IQueryable<TItem> source);
|
||||
}
|
||||
}
|
@ -12,6 +12,8 @@ namespace AntDesign.TableModels
|
||||
|
||||
public IList<ITableSortModel> SortModel { get; private set; }
|
||||
|
||||
public IList<ITableFilterModel> FilterModel { get; private set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public IQueryable<TItem> QueryableLambda { get; private set; }
|
||||
|
||||
@ -20,6 +22,7 @@ namespace AntDesign.TableModels
|
||||
this.PageSize = pageSize;
|
||||
this.PageIndex = pageIndex;
|
||||
this.SortModel = new List<ITableSortModel>();
|
||||
this.FilterModel = new List<ITableFilterModel>();
|
||||
}
|
||||
|
||||
internal void AddSortModel(ITableSortModel model)
|
||||
@ -27,6 +30,11 @@ namespace AntDesign.TableModels
|
||||
SortModel.Add(model);
|
||||
}
|
||||
|
||||
internal void AddFilterModel(ITableFilterModel model)
|
||||
{
|
||||
FilterModel.Add(model);
|
||||
}
|
||||
|
||||
internal void SetQueryableLambda(IQueryable<TItem> query)
|
||||
{
|
||||
this.QueryableLambda = query;
|
||||
|
@ -8,10 +8,6 @@ namespace AntDesign.TableModels
|
||||
{
|
||||
public class SortModel<TField> : ITableSortModel, IComparer<TField>
|
||||
{
|
||||
private PropertyInfo _propertyInfo;
|
||||
|
||||
private readonly Func<TField, TField, int> _comparer;
|
||||
|
||||
public int Priority { get; }
|
||||
|
||||
public string FieldName { get; }
|
||||
@ -20,14 +16,17 @@ namespace AntDesign.TableModels
|
||||
|
||||
SortDirection ITableSortModel.SortDirection => _sortDirection;
|
||||
|
||||
private readonly PropertyInfo _propertyInfo;
|
||||
private readonly Func<TField, TField, int> _comparer;
|
||||
|
||||
private SortDirection _sortDirection;
|
||||
|
||||
public SortModel(PropertyInfo propertyInfo, int priority, SortDirection defaultSortOrder, Func<TField, TField, int> comparer)
|
||||
{
|
||||
this._propertyInfo = propertyInfo;
|
||||
_comparer = comparer;
|
||||
this.Priority = priority;
|
||||
this.FieldName = propertyInfo?.Name;
|
||||
this._propertyInfo = propertyInfo;
|
||||
this._comparer = comparer;
|
||||
this._sortDirection = defaultSortOrder ?? SortDirection.None;
|
||||
}
|
||||
|
||||
|
@ -1,36 +1,66 @@
|
||||
<Table DataSource="data" OnChange="OnChange" TItem="Column">
|
||||
<Column TData="string"
|
||||
DataIndex="@nameof(context.Name)"
|
||||
SorterCompare="@((a,b)=> a.Length - b.Length)"
|
||||
SortDirections="new[] { SortDirection.Descending }"/>
|
||||
<Column TData="int"
|
||||
DataIndex="@nameof(context.Age)"
|
||||
DefaultSortOrder="@SortDirection.Descending"
|
||||
SorterCompare="@((a,b)=> a - b)" />
|
||||
<Column TData="string"
|
||||
DataIndex="@nameof(context.Address)"
|
||||
SorterCompare="@((a,b)=> a.Length - b.Length)"
|
||||
SortDirections="new[] { SortDirection.Descending, SortDirection.Ascending }"
|
||||
/>
|
||||
<Table DataSource="data" OnChange="OnChange" TItem="Data">
|
||||
<Column TData="string"
|
||||
@bind-Field="context.Name"
|
||||
SorterCompare="@((a,b)=> a.Length - b.Length)"
|
||||
SortDirections="new[] { SortDirection.Descending }"
|
||||
Filters="nameFilters"
|
||||
OnFilter="((value,name)=>name.StartsWith(value))" />
|
||||
<Column TData="int"
|
||||
DataIndex="@nameof(context.Age)"
|
||||
DefaultSortOrder="@SortDirection.Descending"
|
||||
SorterCompare="@((a,b)=> a - b)" />
|
||||
<Column TData="string"
|
||||
@bind-Field="context.Address"
|
||||
SorterCompare="@((a,b)=> a.Length - b.Length)"
|
||||
SortDirections="new[] { SortDirection.Descending, SortDirection.Ascending }"
|
||||
Filters="addressFilters"
|
||||
FilterMultiple="false"
|
||||
OnFilter="(value,address)=>address.StartsWith(value)" />
|
||||
</Table>
|
||||
|
||||
|
||||
@using AntDesign.TableModels;
|
||||
@using System.Text.Json;
|
||||
@code {
|
||||
|
||||
record Column(string Name, int Age, string Address);
|
||||
|
||||
Column[] data =
|
||||
class Data
|
||||
{
|
||||
public Data(string name, int age, string address)
|
||||
{
|
||||
Name = name;
|
||||
Age = age;
|
||||
Address = address;
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
public int Age { get; set; }
|
||||
public string Address { get; set; }
|
||||
}
|
||||
|
||||
Data[] data =
|
||||
{
|
||||
new("John Brown",32,"New York No. 1 Lake Park"),
|
||||
new("Jim Green",42, "London No. 1 Lake Park"),
|
||||
new("Joe Black", 32,"Sidney No. 1 Lake Park"),
|
||||
new("Jim Red",32,"London No. 2 Lake Park"),
|
||||
};
|
||||
|
||||
void OnChange(QueryModel<Column> query)
|
||||
public TableFilter<string>[] nameFilters = new TableFilter<string>[] {
|
||||
new() { Text = "Joe", Value = "Joe" },
|
||||
new() { Text = "Jim", Value = "Jim" },
|
||||
};
|
||||
|
||||
public TableFilter<string>[] addressFilters = new TableFilter<string>[] {
|
||||
new() { Text = "London", Value = "London" },
|
||||
new() { Text = "New York", Value = "New York" },
|
||||
};
|
||||
|
||||
void OnChange(QueryModel<Data> query)
|
||||
{
|
||||
Console.WriteLine(JsonSerializer.Serialize(query));
|
||||
}
|
||||
}
|
||||
|
||||
void OnRowClick(RowData<Data> row)
|
||||
{
|
||||
Console.WriteLine($"row {row.Data.Name} was clicked");
|
||||
}
|
||||
}
|
@ -7,8 +7,6 @@ title:
|
||||
|
||||
## zh-CN
|
||||
|
||||
*⚠注意: 筛选功能 将在下个版本中提供*
|
||||
|
||||
对某一列数据进行筛选,使用列的 `filters` 属性来指定需要筛选菜单的列,`onFilter` 用于筛选当前数据,`filterMultiple` 用于指定多选和单选。
|
||||
|
||||
对某一列数据进行排序,通过指定列的 `sorter` 函数即可启动排序按钮。`sorter: function(rowA, rowB) { ... }`, rowA、rowB 为比较的两个行数据。
|
||||
@ -19,8 +17,6 @@ title:
|
||||
|
||||
## en-US
|
||||
|
||||
*⚠Notice: `filters` will be provided in the next release.*
|
||||
|
||||
Use `filters` to generate filter menu in columns, `onFilter` to determine filtered result, and `filterMultiple` to indicate whether it's multiple or single selection.
|
||||
|
||||
Uses `defaultFilteredValue` to make a column filtered by default.
|
||||
|
Loading…
Reference in New Issue
Block a user