Add operator selection to grid filter rows for dates/numbers
Introduce custom filter row cell templates for date and numeric columns in CatalogsGrid and MassDataGrid. Users can now select a filter operator (e.g., =, <, >, <=, >=) alongside the filter value for columns like AddedWhen, ChangedWhen, and Amount. Updated filter criteria logic to support these operators using DevExpress expressions. Added new CSS for filter row alignment and appearance. Removed obsolete methods and improved layout logic. This enhances filtering flexibility and user experience in both grids.
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
@using Microsoft.AspNetCore.Components.Rendering
|
@using Microsoft.AspNetCore.Components.Rendering
|
||||||
@using Microsoft.AspNetCore.Components.Forms
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
@using DevExpress.Blazor
|
@using DevExpress.Blazor
|
||||||
|
@using DevExpress.Data.Filtering
|
||||||
@inject CatalogApiClient Api
|
@inject CatalogApiClient Api
|
||||||
@inject LayoutApiClient LayoutApi
|
@inject LayoutApiClient LayoutApi
|
||||||
@inject IJSRuntime JsRuntime
|
@inject IJSRuntime JsRuntime
|
||||||
@@ -73,6 +74,21 @@
|
|||||||
.band-columns {
|
.band-columns {
|
||||||
max-width: 720px;
|
max-width: 720px;
|
||||||
}
|
}
|
||||||
|
.filter-row-cell {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.filter-operator {
|
||||||
|
width: 52px;
|
||||||
|
min-width: 52px;
|
||||||
|
flex: 0 0 52px;
|
||||||
|
}
|
||||||
|
.filter-value {
|
||||||
|
min-width: 140px;
|
||||||
|
flex: 1 1 140px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@if (!string.IsNullOrWhiteSpace(errorMessage))
|
@if (!string.IsNullOrWhiteSpace(errorMessage))
|
||||||
@@ -186,6 +202,10 @@ else
|
|||||||
private ValidationMessageStore? validationMessageStore;
|
private ValidationMessageStore? validationMessageStore;
|
||||||
private IGrid? gridRef;
|
private IGrid? gridRef;
|
||||||
private string popupHeaderText = "Edit";
|
private string popupHeaderText = "Edit";
|
||||||
|
private DateTime? addedWhenFilterValue;
|
||||||
|
private string addedWhenFilterOperator = "=";
|
||||||
|
private DateTime? changedWhenFilterValue;
|
||||||
|
private string changedWhenFilterOperator = "=";
|
||||||
private const string LayoutType = "GRID_BANDS";
|
private const string LayoutType = "GRID_BANDS";
|
||||||
private const string LayoutKey = "CatalogsGrid";
|
private const string LayoutKey = "CatalogsGrid";
|
||||||
private const string LayoutUserStorageKey = "layoutUser";
|
private const string LayoutUserStorageKey = "layoutUser";
|
||||||
@@ -212,8 +232,16 @@ else
|
|||||||
new() { Value = 1, Text = "PRTBMY_CATALOG_SAVE" }
|
new() { Value = 1, Text = "PRTBMY_CATALOG_SAVE" }
|
||||||
};
|
};
|
||||||
|
|
||||||
private bool CanSaveBandLayout => !string.IsNullOrWhiteSpace(layoutUser);
|
private readonly List<FilterOperatorOption> filterOperators = new()
|
||||||
|
{
|
||||||
|
new() { Value = "=", Text = "=" },
|
||||||
|
new() { Value = "<", Text = "<" },
|
||||||
|
new() { Value = ">", Text = ">" },
|
||||||
|
new() { Value = "<=", Text = "<=" },
|
||||||
|
new() { Value = ">=", Text = ">=" }
|
||||||
|
};
|
||||||
|
|
||||||
|
private bool CanSaveBandLayout => !string.IsNullOrWhiteSpace(layoutUser);
|
||||||
private bool gridLayoutApplied;
|
private bool gridLayoutApplied;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
@@ -554,49 +582,6 @@ else
|
|||||||
columnLookup = columnDefinitions.ToDictionary(column => column.FieldName, StringComparer.OrdinalIgnoreCase);
|
columnLookup = columnDefinitions.ToDictionary(column => column.FieldName, StringComparer.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyBandOrderingFromColumnOrder()
|
|
||||||
{
|
|
||||||
if (bandLayout.ColumnOrder.Count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var bandById = bandLayout.Bands.ToDictionary(band => band.Id, StringComparer.OrdinalIgnoreCase);
|
|
||||||
var orderedBandIds = new List<string>();
|
|
||||||
var orderedColumnsByBand = bandLayout.Bands.ToDictionary(
|
|
||||||
band => band.Id,
|
|
||||||
_ => new List<string>(),
|
|
||||||
StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
foreach (var field in bandLayout.ColumnOrder)
|
|
||||||
{
|
|
||||||
if (columnBandAssignments.TryGetValue(field, out var bandId) && bandById.ContainsKey(bandId))
|
|
||||||
{
|
|
||||||
if (!orderedBandIds.Contains(bandId, StringComparer.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
orderedBandIds.Add(bandId);
|
|
||||||
}
|
|
||||||
|
|
||||||
orderedColumnsByBand[bandId].Add(field);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var band in bandLayout.Bands)
|
|
||||||
{
|
|
||||||
var orderedColumns = orderedColumnsByBand[band.Id];
|
|
||||||
orderedColumns.AddRange(band.Columns.Where(column => !orderedColumns.Contains(column, StringComparer.OrdinalIgnoreCase)));
|
|
||||||
band.Columns = orderedColumns;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (orderedBandIds.Count > 0)
|
|
||||||
{
|
|
||||||
bandLayout.Bands = orderedBandIds
|
|
||||||
.Select(id => bandById[id])
|
|
||||||
.Concat(bandLayout.Bands.Where(band => !orderedBandIds.Contains(band.Id, StringComparer.OrdinalIgnoreCase)))
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddBand()
|
private void AddBand()
|
||||||
{
|
{
|
||||||
bandLayout.Bands.Add(new BandDefinition
|
bandLayout.Bands.Add(new BandDefinition
|
||||||
@@ -760,22 +745,95 @@ else
|
|||||||
builder.AddAttribute(seq++, "ReadOnly", true);
|
builder.AddAttribute(seq++, "ReadOnly", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (string.Equals(column.FieldName, nameof(CatalogReadDto.AddedWhen), StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
builder.AddAttribute(seq++, "FilterRowCellTemplate", BuildAddedWhenFilterTemplate());
|
||||||
|
}
|
||||||
|
else if (string.Equals(column.FieldName, nameof(CatalogReadDto.ChangedWhen), StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
builder.AddAttribute(seq++, "FilterRowCellTemplate", BuildChangedWhenFilterTemplate());
|
||||||
|
}
|
||||||
|
|
||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private RenderFragment? BuildFilterTemplate(ColumnDefinition column)
|
private RenderFragment<GridDataColumnFilterRowCellTemplateContext> BuildAddedWhenFilterTemplate()
|
||||||
{
|
{
|
||||||
return null;
|
return BuildDateFilterTemplate(
|
||||||
|
() => addedWhenFilterValue,
|
||||||
|
value => addedWhenFilterValue = value,
|
||||||
|
() => addedWhenFilterOperator,
|
||||||
|
value => addedWhenFilterOperator = value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private RenderFragment? BuildTextFilterTemplate()
|
private RenderFragment<GridDataColumnFilterRowCellTemplateContext> BuildChangedWhenFilterTemplate()
|
||||||
{
|
{
|
||||||
return null;
|
return BuildDateFilterTemplate(
|
||||||
|
() => changedWhenFilterValue,
|
||||||
|
value => changedWhenFilterValue = value,
|
||||||
|
() => changedWhenFilterOperator,
|
||||||
|
value => changedWhenFilterOperator = value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private RenderFragment? BuildDateFilterTemplate()
|
private RenderFragment<GridDataColumnFilterRowCellTemplateContext> BuildDateFilterTemplate(
|
||||||
|
Func<DateTime?> getValue,
|
||||||
|
Action<DateTime?> setValue,
|
||||||
|
Func<string> getOperator,
|
||||||
|
Action<string> setOperator)
|
||||||
{
|
{
|
||||||
return null;
|
return context => builder =>
|
||||||
|
{
|
||||||
|
var seq = 0;
|
||||||
|
builder.OpenElement(seq++, "div");
|
||||||
|
builder.AddAttribute(seq++, "class", "filter-row-cell");
|
||||||
|
|
||||||
|
builder.OpenComponent<DxComboBox<FilterOperatorOption, string>>(seq++);
|
||||||
|
builder.AddAttribute(seq++, "Data", filterOperators);
|
||||||
|
builder.AddAttribute(seq++, "TextFieldName", "Text");
|
||||||
|
builder.AddAttribute(seq++, "ValueFieldName", "Value");
|
||||||
|
builder.AddAttribute(seq++, "Value", getOperator());
|
||||||
|
builder.AddAttribute(seq++, "ValueChanged", EventCallback.Factory.Create<string>(this, value =>
|
||||||
|
{
|
||||||
|
setOperator(value);
|
||||||
|
UpdateFilterCriteria(context, value, getValue());
|
||||||
|
}));
|
||||||
|
builder.AddAttribute(seq++, "CssClass", "filter-operator");
|
||||||
|
builder.CloseComponent();
|
||||||
|
|
||||||
|
builder.OpenComponent<DxDateEdit<DateTime?>>(seq++);
|
||||||
|
builder.AddAttribute(seq++, "Date", getValue());
|
||||||
|
builder.AddAttribute(seq++, "DateChanged", EventCallback.Factory.Create<DateTime?>(this, value =>
|
||||||
|
{
|
||||||
|
setValue(value);
|
||||||
|
UpdateFilterCriteria(context, getOperator(), value);
|
||||||
|
}));
|
||||||
|
builder.AddAttribute(seq++, "ClearButtonDisplayMode", DataEditorClearButtonDisplayMode.Auto);
|
||||||
|
builder.AddAttribute(seq++, "CssClass", "filter-value");
|
||||||
|
builder.CloseComponent();
|
||||||
|
|
||||||
|
builder.CloseElement();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateFilterCriteria(GridDataColumnFilterRowCellTemplateContext context, string op, DateTime? value)
|
||||||
|
{
|
||||||
|
if (!value.HasValue)
|
||||||
|
{
|
||||||
|
context.FilterCriteria = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var prop = new OperandProperty(context.DataColumn.FieldName);
|
||||||
|
var date = value.Value.Date;
|
||||||
|
context.FilterCriteria = op switch
|
||||||
|
{
|
||||||
|
"=" => new GroupOperator(GroupOperatorType.And, prop >= date, prop < date.AddDays(1)),
|
||||||
|
"<" => prop < date,
|
||||||
|
"<=" => prop < date.AddDays(1),
|
||||||
|
">" => prop >= date.AddDays(1),
|
||||||
|
">=" => prop >= date,
|
||||||
|
_ => prop == date
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class BandLayout
|
private sealed class BandLayout
|
||||||
@@ -830,4 +888,10 @@ else
|
|||||||
public int Value { get; set; }
|
public int Value { get; set; }
|
||||||
public string Text { get; set; } = string.Empty;
|
public string Text { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sealed class FilterOperatorOption
|
||||||
|
{
|
||||||
|
public string Value { get; set; } = string.Empty;
|
||||||
|
public string Text { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
@using Microsoft.AspNetCore.Components.Rendering
|
@using Microsoft.AspNetCore.Components.Rendering
|
||||||
@using Microsoft.AspNetCore.Components.Forms
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
@using DevExpress.Blazor
|
@using DevExpress.Blazor
|
||||||
|
@using DevExpress.Data.Filtering
|
||||||
@inject MassDataApiClient Api
|
@inject MassDataApiClient Api
|
||||||
@inject LayoutApiClient LayoutApi
|
@inject LayoutApiClient LayoutApi
|
||||||
@inject IJSRuntime JsRuntime
|
@inject IJSRuntime JsRuntime
|
||||||
@@ -96,6 +97,25 @@
|
|||||||
.band-columns {
|
.band-columns {
|
||||||
max-width: 720px;
|
max-width: 720px;
|
||||||
}
|
}
|
||||||
|
.filter-row-cell {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.filter-operator {
|
||||||
|
width: 52px;
|
||||||
|
min-width: 52px;
|
||||||
|
flex: 0 0 52px;
|
||||||
|
}
|
||||||
|
.filter-value {
|
||||||
|
min-width: 140px;
|
||||||
|
flex: 1 1 140px;
|
||||||
|
}
|
||||||
|
.filter-value-amount {
|
||||||
|
min-width: 110px;
|
||||||
|
flex-basis: 110px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@if (!string.IsNullOrWhiteSpace(errorMessage))
|
@if (!string.IsNullOrWhiteSpace(errorMessage))
|
||||||
@@ -234,6 +254,12 @@ else
|
|||||||
private int pageCount = 1;
|
private int pageCount = 1;
|
||||||
private int? pageSize = 100;
|
private int? pageSize = 100;
|
||||||
private string popupHeaderText = "Edit";
|
private string popupHeaderText = "Edit";
|
||||||
|
private decimal? amountFilterValue;
|
||||||
|
private string amountFilterOperator = "=";
|
||||||
|
private DateTime? addedWhenFilterValue;
|
||||||
|
private string addedWhenFilterOperator = "=";
|
||||||
|
private DateTime? changedWhenFilterValue;
|
||||||
|
private string changedWhenFilterOperator = "=";
|
||||||
private EditContext? editContext;
|
private EditContext? editContext;
|
||||||
private ValidationMessageStore? validationMessageStore;
|
private ValidationMessageStore? validationMessageStore;
|
||||||
private IGrid? gridRef;
|
private IGrid? gridRef;
|
||||||
@@ -280,6 +306,17 @@ else
|
|||||||
new() { Value = 0, Text = "PRMassdata_UpsertByCustomerName" }
|
new() { Value = 0, Text = "PRMassdata_UpsertByCustomerName" }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private readonly List<FilterOperatorOption> filterOperators = new()
|
||||||
|
{
|
||||||
|
new() { Value = "=", Text = "=" },
|
||||||
|
new() { Value = "<", Text = "<" },
|
||||||
|
new() { Value = ">", Text = ">" },
|
||||||
|
new() { Value = "<=", Text = "<=" },
|
||||||
|
new() { Value = ">=", Text = ">=" }
|
||||||
|
};
|
||||||
|
|
||||||
|
private bool gridLayoutApplied;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
columnLookup = columnDefinitions.ToDictionary(column => column.FieldName, StringComparer.OrdinalIgnoreCase);
|
columnLookup = columnDefinitions.ToDictionary(column => column.FieldName, StringComparer.OrdinalIgnoreCase);
|
||||||
@@ -601,9 +638,163 @@ else
|
|||||||
builder.AddAttribute(seq++, "ReadOnly", true);
|
builder.AddAttribute(seq++, "ReadOnly", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (string.Equals(column.FieldName, nameof(MassDataReadDto.Amount), StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
builder.AddAttribute(seq++, "FilterRowCellTemplate", BuildAmountFilterTemplate());
|
||||||
|
}
|
||||||
|
else if (string.Equals(column.FieldName, nameof(MassDataReadDto.AddedWhen), StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
builder.AddAttribute(seq++, "FilterRowCellTemplate", BuildAddedWhenFilterTemplate());
|
||||||
|
}
|
||||||
|
else if (string.Equals(column.FieldName, nameof(MassDataReadDto.ChangedWhen), StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
builder.AddAttribute(seq++, "FilterRowCellTemplate", BuildChangedWhenFilterTemplate());
|
||||||
|
}
|
||||||
|
|
||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private RenderFragment<GridDataColumnFilterRowCellTemplateContext> BuildAmountFilterTemplate()
|
||||||
|
{
|
||||||
|
return BuildNumberFilterTemplate(
|
||||||
|
() => amountFilterValue,
|
||||||
|
value => amountFilterValue = value,
|
||||||
|
() => amountFilterOperator,
|
||||||
|
value => amountFilterOperator = value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RenderFragment<GridDataColumnFilterRowCellTemplateContext> BuildAddedWhenFilterTemplate()
|
||||||
|
{
|
||||||
|
return BuildDateFilterTemplate(
|
||||||
|
() => addedWhenFilterValue,
|
||||||
|
value => addedWhenFilterValue = value,
|
||||||
|
() => addedWhenFilterOperator,
|
||||||
|
value => addedWhenFilterOperator = value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RenderFragment<GridDataColumnFilterRowCellTemplateContext> BuildChangedWhenFilterTemplate()
|
||||||
|
{
|
||||||
|
return BuildDateFilterTemplate(
|
||||||
|
() => changedWhenFilterValue,
|
||||||
|
value => changedWhenFilterValue = value,
|
||||||
|
() => changedWhenFilterOperator,
|
||||||
|
value => changedWhenFilterOperator = value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RenderFragment<GridDataColumnFilterRowCellTemplateContext> BuildNumberFilterTemplate(
|
||||||
|
Func<decimal?> getValue,
|
||||||
|
Action<decimal?> setValue,
|
||||||
|
Func<string> getOperator,
|
||||||
|
Action<string> setOperator)
|
||||||
|
{
|
||||||
|
return context => builder =>
|
||||||
|
{
|
||||||
|
var seq = 0;
|
||||||
|
builder.OpenElement(seq++, "div");
|
||||||
|
builder.AddAttribute(seq++, "class", "filter-row-cell");
|
||||||
|
|
||||||
|
builder.OpenComponent<DxComboBox<FilterOperatorOption, string>>(seq++);
|
||||||
|
builder.AddAttribute(seq++, "Data", filterOperators);
|
||||||
|
builder.AddAttribute(seq++, "TextFieldName", "Text");
|
||||||
|
builder.AddAttribute(seq++, "ValueFieldName", "Value");
|
||||||
|
builder.AddAttribute(seq++, "Value", getOperator());
|
||||||
|
builder.AddAttribute(seq++, "ValueChanged", EventCallback.Factory.Create<string>(this, value =>
|
||||||
|
{
|
||||||
|
setOperator(value);
|
||||||
|
UpdateFilterCriteria(context, value, getValue());
|
||||||
|
}));
|
||||||
|
builder.AddAttribute(seq++, "CssClass", "filter-operator");
|
||||||
|
builder.CloseComponent();
|
||||||
|
|
||||||
|
builder.OpenComponent<DxSpinEdit<decimal?>>(seq++);
|
||||||
|
builder.AddAttribute(seq++, "Value", getValue());
|
||||||
|
builder.AddAttribute(seq++, "ValueChanged", EventCallback.Factory.Create<decimal?>(this, value =>
|
||||||
|
{
|
||||||
|
setValue(value);
|
||||||
|
UpdateFilterCriteria(context, getOperator(), value);
|
||||||
|
}));
|
||||||
|
builder.AddAttribute(seq++, "ClearButtonDisplayMode", DataEditorClearButtonDisplayMode.Auto);
|
||||||
|
builder.AddAttribute(seq++, "CssClass", "filter-value filter-value-amount");
|
||||||
|
builder.CloseComponent();
|
||||||
|
|
||||||
|
builder.CloseElement();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private RenderFragment<GridDataColumnFilterRowCellTemplateContext> BuildDateFilterTemplate(
|
||||||
|
Func<DateTime?> getValue,
|
||||||
|
Action<DateTime?> setValue,
|
||||||
|
Func<string> getOperator,
|
||||||
|
Action<string> setOperator)
|
||||||
|
{
|
||||||
|
return context => builder =>
|
||||||
|
{
|
||||||
|
var seq = 0;
|
||||||
|
builder.OpenElement(seq++, "div");
|
||||||
|
builder.AddAttribute(seq++, "class", "filter-row-cell");
|
||||||
|
|
||||||
|
builder.OpenComponent<DxComboBox<FilterOperatorOption, string>>(seq++);
|
||||||
|
builder.AddAttribute(seq++, "Data", filterOperators);
|
||||||
|
builder.AddAttribute(seq++, "TextFieldName", "Text");
|
||||||
|
builder.AddAttribute(seq++, "ValueFieldName", "Value");
|
||||||
|
builder.AddAttribute(seq++, "Value", getOperator());
|
||||||
|
builder.AddAttribute(seq++, "ValueChanged", EventCallback.Factory.Create<string>(this, value =>
|
||||||
|
{
|
||||||
|
setOperator(value);
|
||||||
|
UpdateFilterCriteria(context, value, getValue());
|
||||||
|
}));
|
||||||
|
builder.AddAttribute(seq++, "CssClass", "filter-operator");
|
||||||
|
builder.CloseComponent();
|
||||||
|
|
||||||
|
builder.OpenComponent<DxDateEdit<DateTime?>>(seq++);
|
||||||
|
builder.AddAttribute(seq++, "Date", getValue());
|
||||||
|
builder.AddAttribute(seq++, "DateChanged", EventCallback.Factory.Create<DateTime?>(this, value =>
|
||||||
|
{
|
||||||
|
setValue(value);
|
||||||
|
UpdateFilterCriteria(context, getOperator(), value);
|
||||||
|
}));
|
||||||
|
builder.AddAttribute(seq++, "ClearButtonDisplayMode", DataEditorClearButtonDisplayMode.Auto);
|
||||||
|
builder.AddAttribute(seq++, "CssClass", "filter-value");
|
||||||
|
builder.CloseComponent();
|
||||||
|
|
||||||
|
builder.CloseElement();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateFilterCriteria(GridDataColumnFilterRowCellTemplateContext context, string op, object? value)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
context.FilterCriteria = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var prop = new OperandProperty(context.DataColumn.FieldName);
|
||||||
|
if (value is DateTime dateValue)
|
||||||
|
{
|
||||||
|
var date = dateValue.Date;
|
||||||
|
context.FilterCriteria = op switch
|
||||||
|
{
|
||||||
|
"=" => new GroupOperator(GroupOperatorType.And, prop >= date, prop < date.AddDays(1)),
|
||||||
|
"<" => prop < date,
|
||||||
|
"<=" => prop < date.AddDays(1),
|
||||||
|
">" => prop >= date.AddDays(1),
|
||||||
|
">=" => prop >= date,
|
||||||
|
_ => prop == date
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.FilterCriteria = op switch
|
||||||
|
{
|
||||||
|
"<" => new BinaryOperator(prop, new OperandValue(value), BinaryOperatorType.Less),
|
||||||
|
">" => new BinaryOperator(prop, new OperandValue(value), BinaryOperatorType.Greater),
|
||||||
|
"<=" => new BinaryOperator(prop, new OperandValue(value), BinaryOperatorType.LessOrEqual),
|
||||||
|
">=" => new BinaryOperator(prop, new OperandValue(value), BinaryOperatorType.GreaterOrEqual),
|
||||||
|
_ => new BinaryOperator(prop, new OperandValue(value), BinaryOperatorType.Equal)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private void SetEditContext(EditContext context)
|
private void SetEditContext(EditContext context)
|
||||||
{
|
{
|
||||||
if (editContext == context)
|
if (editContext == context)
|
||||||
@@ -808,7 +999,11 @@ else
|
|||||||
public string Text { get; set; } = string.Empty;
|
public string Text { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool gridLayoutApplied;
|
private sealed class FilterOperatorOption
|
||||||
|
{
|
||||||
|
public string Value { get; set; } = string.Empty;
|
||||||
|
public string Text { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
@using Microsoft.AspNetCore.Components.Rendering
|
@using Microsoft.AspNetCore.Components.Rendering
|
||||||
@using Microsoft.AspNetCore.Components.Forms
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
@using DevExpress.Blazor
|
@using DevExpress.Blazor
|
||||||
|
@using DevExpress.Data.Filtering
|
||||||
@inject CatalogApiClient Api
|
@inject CatalogApiClient Api
|
||||||
@inject LayoutApiClient LayoutApi
|
@inject LayoutApiClient LayoutApi
|
||||||
@inject IJSRuntime JsRuntime
|
@inject IJSRuntime JsRuntime
|
||||||
@@ -32,6 +33,21 @@
|
|||||||
.band-columns {
|
.band-columns {
|
||||||
max-width: 720px;
|
max-width: 720px;
|
||||||
}
|
}
|
||||||
|
.filter-row-cell {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.filter-operator {
|
||||||
|
width: 52px;
|
||||||
|
min-width: 52px;
|
||||||
|
flex: 0 0 52px;
|
||||||
|
}
|
||||||
|
.filter-value {
|
||||||
|
min-width: 140px;
|
||||||
|
flex: 1 1 140px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@if (!string.IsNullOrWhiteSpace(errorMessage))
|
@if (!string.IsNullOrWhiteSpace(errorMessage))
|
||||||
@@ -145,6 +161,10 @@ else
|
|||||||
private ValidationMessageStore? validationMessageStore;
|
private ValidationMessageStore? validationMessageStore;
|
||||||
private IGrid? gridRef;
|
private IGrid? gridRef;
|
||||||
private string popupHeaderText = "Edit";
|
private string popupHeaderText = "Edit";
|
||||||
|
private DateTime? addedWhenFilterValue;
|
||||||
|
private string addedWhenFilterOperator = "=";
|
||||||
|
private DateTime? changedWhenFilterValue;
|
||||||
|
private string changedWhenFilterOperator = "=";
|
||||||
private const string LayoutType = "GRID_BANDS";
|
private const string LayoutType = "GRID_BANDS";
|
||||||
private const string LayoutKey = "CatalogsGrid";
|
private const string LayoutKey = "CatalogsGrid";
|
||||||
private const string LayoutUserStorageKey = "layoutUser";
|
private const string LayoutUserStorageKey = "layoutUser";
|
||||||
@@ -171,7 +191,17 @@ else
|
|||||||
new() { Value = 1, Text = "PRTBMY_CATALOG_SAVE" }
|
new() { Value = 1, Text = "PRTBMY_CATALOG_SAVE" }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private readonly List<FilterOperatorOption> filterOperators = new()
|
||||||
|
{
|
||||||
|
new() { Value = "=", Text = "=" },
|
||||||
|
new() { Value = "<", Text = "<" },
|
||||||
|
new() { Value = ">", Text = ">" },
|
||||||
|
new() { Value = "<=", Text = "<=" },
|
||||||
|
new() { Value = ">=", Text = ">=" }
|
||||||
|
};
|
||||||
|
|
||||||
private bool CanSaveBandLayout => !string.IsNullOrWhiteSpace(layoutUser);
|
private bool CanSaveBandLayout => !string.IsNullOrWhiteSpace(layoutUser);
|
||||||
|
private bool gridLayoutApplied;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
@@ -181,6 +211,16 @@ else
|
|||||||
await LoadCatalogs();
|
await LoadCatalogs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if (!gridLayoutApplied && gridRef != null && bandLayout.GridLayout != null)
|
||||||
|
{
|
||||||
|
gridRef.LoadLayout(bandLayout.GridLayout);
|
||||||
|
gridLayoutApplied = true;
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void SetEditContext(EditContext context)
|
private void SetEditContext(EditContext context)
|
||||||
{
|
{
|
||||||
if (editContext == context)
|
if (editContext == context)
|
||||||
@@ -474,6 +514,33 @@ else
|
|||||||
UpdateBandOptions();
|
UpdateBandOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task ResetBandLayoutAsync()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(layoutUser))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await LayoutApi.DeleteAsync(LayoutType, LayoutKey, layoutUser);
|
||||||
|
bandLayout = new BandLayout();
|
||||||
|
columnBandAssignments.Clear();
|
||||||
|
UpdateBandOptions();
|
||||||
|
infoMessage = "Band-Layout zurückgesetzt.";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyColumnLayoutFromStorage()
|
||||||
|
{
|
||||||
|
foreach (var column in columnDefinitions)
|
||||||
|
{
|
||||||
|
if (bandLayout.ColumnWidths.TryGetValue(column.FieldName, out var width) && !string.IsNullOrWhiteSpace(width))
|
||||||
|
{
|
||||||
|
column.Width = width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
columnLookup = columnDefinitions.ToDictionary(column => column.FieldName, StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
private void AddBand()
|
private void AddBand()
|
||||||
{
|
{
|
||||||
bandLayout.Bands.Add(new BandDefinition
|
bandLayout.Bands.Add(new BandDefinition
|
||||||
@@ -637,9 +704,97 @@ else
|
|||||||
builder.AddAttribute(seq++, "ReadOnly", true);
|
builder.AddAttribute(seq++, "ReadOnly", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (string.Equals(column.FieldName, nameof(CatalogReadDto.AddedWhen), StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
builder.AddAttribute(seq++, "FilterRowCellTemplate", BuildAddedWhenFilterTemplate());
|
||||||
|
}
|
||||||
|
else if (string.Equals(column.FieldName, nameof(CatalogReadDto.ChangedWhen), StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
builder.AddAttribute(seq++, "FilterRowCellTemplate", BuildChangedWhenFilterTemplate());
|
||||||
|
}
|
||||||
|
|
||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private RenderFragment<GridDataColumnFilterRowCellTemplateContext> BuildAddedWhenFilterTemplate()
|
||||||
|
{
|
||||||
|
return BuildDateFilterTemplate(
|
||||||
|
() => addedWhenFilterValue,
|
||||||
|
value => addedWhenFilterValue = value,
|
||||||
|
() => addedWhenFilterOperator,
|
||||||
|
value => addedWhenFilterOperator = value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RenderFragment<GridDataColumnFilterRowCellTemplateContext> BuildChangedWhenFilterTemplate()
|
||||||
|
{
|
||||||
|
return BuildDateFilterTemplate(
|
||||||
|
() => changedWhenFilterValue,
|
||||||
|
value => changedWhenFilterValue = value,
|
||||||
|
() => changedWhenFilterOperator,
|
||||||
|
value => changedWhenFilterOperator = value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RenderFragment<GridDataColumnFilterRowCellTemplateContext> BuildDateFilterTemplate(
|
||||||
|
Func<DateTime?> getValue,
|
||||||
|
Action<DateTime?> setValue,
|
||||||
|
Func<string> getOperator,
|
||||||
|
Action<string> setOperator)
|
||||||
|
{
|
||||||
|
return context => builder =>
|
||||||
|
{
|
||||||
|
var seq = 0;
|
||||||
|
builder.OpenElement(seq++, "div");
|
||||||
|
builder.AddAttribute(seq++, "class", "filter-row-cell");
|
||||||
|
|
||||||
|
builder.OpenComponent<DxComboBox<FilterOperatorOption, string>>(seq++);
|
||||||
|
builder.AddAttribute(seq++, "Data", filterOperators);
|
||||||
|
builder.AddAttribute(seq++, "TextFieldName", "Text");
|
||||||
|
builder.AddAttribute(seq++, "ValueFieldName", "Value");
|
||||||
|
builder.AddAttribute(seq++, "Value", getOperator());
|
||||||
|
builder.AddAttribute(seq++, "ValueChanged", EventCallback.Factory.Create<string>(this, value =>
|
||||||
|
{
|
||||||
|
setOperator(value);
|
||||||
|
UpdateFilterCriteria(context, value, getValue());
|
||||||
|
}));
|
||||||
|
builder.AddAttribute(seq++, "CssClass", "filter-operator");
|
||||||
|
builder.CloseComponent();
|
||||||
|
|
||||||
|
builder.OpenComponent<DxDateEdit<DateTime?>>(seq++);
|
||||||
|
builder.AddAttribute(seq++, "Date", getValue());
|
||||||
|
builder.AddAttribute(seq++, "DateChanged", EventCallback.Factory.Create<DateTime?>(this, value =>
|
||||||
|
{
|
||||||
|
setValue(value);
|
||||||
|
UpdateFilterCriteria(context, getOperator(), value);
|
||||||
|
}));
|
||||||
|
builder.AddAttribute(seq++, "ClearButtonDisplayMode", DataEditorClearButtonDisplayMode.Auto);
|
||||||
|
builder.AddAttribute(seq++, "CssClass", "filter-value");
|
||||||
|
builder.CloseComponent();
|
||||||
|
|
||||||
|
builder.CloseElement();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateFilterCriteria(GridDataColumnFilterRowCellTemplateContext context, string op, DateTime? value)
|
||||||
|
{
|
||||||
|
if (!value.HasValue)
|
||||||
|
{
|
||||||
|
context.FilterCriteria = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var prop = new OperandProperty(context.DataColumn.FieldName);
|
||||||
|
var date = value.Value.Date;
|
||||||
|
context.FilterCriteria = op switch
|
||||||
|
{
|
||||||
|
"=" => new GroupOperator(GroupOperatorType.And, prop >= date, prop < date.AddDays(1)),
|
||||||
|
"<" => prop < date,
|
||||||
|
"<=" => prop < date.AddDays(1),
|
||||||
|
">" => prop >= date.AddDays(1),
|
||||||
|
">=" => prop >= date,
|
||||||
|
_ => prop == date
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private sealed class BandLayout
|
private sealed class BandLayout
|
||||||
{
|
{
|
||||||
public List<BandDefinition> Bands { get; set; } = new();
|
public List<BandDefinition> Bands { get; set; } = new();
|
||||||
@@ -693,42 +848,9 @@ else
|
|||||||
public string Text { get; set; } = string.Empty;
|
public string Text { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ResetBandLayoutAsync()
|
private sealed class FilterOperatorOption
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(layoutUser))
|
public string Value { get; set; } = string.Empty;
|
||||||
{
|
public string Text { get; set; } = string.Empty;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await LayoutApi.DeleteAsync(LayoutType, LayoutKey, layoutUser);
|
|
||||||
bandLayout = new BandLayout();
|
|
||||||
columnBandAssignments.Clear();
|
|
||||||
UpdateBandOptions();
|
|
||||||
infoMessage = "Band-Layout zurückgesetzt.";
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplyColumnLayoutFromStorage()
|
|
||||||
{
|
|
||||||
foreach (var column in columnDefinitions)
|
|
||||||
{
|
|
||||||
if (bandLayout.ColumnWidths.TryGetValue(column.FieldName, out var width) && !string.IsNullOrWhiteSpace(width))
|
|
||||||
{
|
|
||||||
column.Width = width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
columnLookup = columnDefinitions.ToDictionary(column => column.FieldName, StringComparer.OrdinalIgnoreCase);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool gridLayoutApplied;
|
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
||||||
{
|
|
||||||
if (!gridLayoutApplied && gridRef != null && bandLayout.GridLayout != null)
|
|
||||||
{
|
|
||||||
gridRef.LoadLayout(bandLayout.GridLayout);
|
|
||||||
gridLayoutApplied = true;
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
@using Microsoft.AspNetCore.Components.Rendering
|
@using Microsoft.AspNetCore.Components.Rendering
|
||||||
@using Microsoft.AspNetCore.Components.Forms
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
@using DevExpress.Blazor
|
@using DevExpress.Blazor
|
||||||
|
@using DevExpress.Data.Filtering
|
||||||
@inject MassDataApiClient Api
|
@inject MassDataApiClient Api
|
||||||
@inject LayoutApiClient LayoutApi
|
@inject LayoutApiClient LayoutApi
|
||||||
@inject IJSRuntime JsRuntime
|
@inject IJSRuntime JsRuntime
|
||||||
@@ -96,6 +97,25 @@
|
|||||||
.band-columns {
|
.band-columns {
|
||||||
max-width: 720px;
|
max-width: 720px;
|
||||||
}
|
}
|
||||||
|
.filter-row-cell {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.filter-operator {
|
||||||
|
width: 52px;
|
||||||
|
min-width: 52px;
|
||||||
|
flex: 0 0 52px;
|
||||||
|
}
|
||||||
|
.filter-value {
|
||||||
|
min-width: 140px;
|
||||||
|
flex: 1 1 140px;
|
||||||
|
}
|
||||||
|
.filter-value-amount {
|
||||||
|
min-width: 110px;
|
||||||
|
flex-basis: 110px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@if (!string.IsNullOrWhiteSpace(errorMessage))
|
@if (!string.IsNullOrWhiteSpace(errorMessage))
|
||||||
@@ -234,6 +254,12 @@ else
|
|||||||
private int pageCount = 1;
|
private int pageCount = 1;
|
||||||
private int? pageSize = 100;
|
private int? pageSize = 100;
|
||||||
private string popupHeaderText = "Edit";
|
private string popupHeaderText = "Edit";
|
||||||
|
private decimal? amountFilterValue;
|
||||||
|
private string amountFilterOperator = "=";
|
||||||
|
private DateTime? addedWhenFilterValue;
|
||||||
|
private string addedWhenFilterOperator = "=";
|
||||||
|
private DateTime? changedWhenFilterValue;
|
||||||
|
private string changedWhenFilterOperator = "=";
|
||||||
private EditContext? editContext;
|
private EditContext? editContext;
|
||||||
private ValidationMessageStore? validationMessageStore;
|
private ValidationMessageStore? validationMessageStore;
|
||||||
private IGrid? gridRef;
|
private IGrid? gridRef;
|
||||||
@@ -280,6 +306,17 @@ else
|
|||||||
new() { Value = 0, Text = "PRMassdata_UpsertByCustomerName" }
|
new() { Value = 0, Text = "PRMassdata_UpsertByCustomerName" }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private readonly List<FilterOperatorOption> filterOperators = new()
|
||||||
|
{
|
||||||
|
new() { Value = "=", Text = "=" },
|
||||||
|
new() { Value = "<", Text = "<" },
|
||||||
|
new() { Value = ">", Text = ">" },
|
||||||
|
new() { Value = "<=", Text = "<=" },
|
||||||
|
new() { Value = ">=", Text = ">=" }
|
||||||
|
};
|
||||||
|
|
||||||
|
private bool gridLayoutApplied;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
columnLookup = columnDefinitions.ToDictionary(column => column.FieldName, StringComparer.OrdinalIgnoreCase);
|
columnLookup = columnDefinitions.ToDictionary(column => column.FieldName, StringComparer.OrdinalIgnoreCase);
|
||||||
@@ -356,7 +393,6 @@ else
|
|||||||
|
|
||||||
columnBandAssignments = BuildAssignmentsFromLayout(bandLayout);
|
columnBandAssignments = BuildAssignmentsFromLayout(bandLayout);
|
||||||
ApplyColumnLayoutFromStorage();
|
ApplyColumnLayoutFromStorage();
|
||||||
//ApplyBandOrderingFromColumnOrder();
|
|
||||||
UpdateBandOptions();
|
UpdateBandOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -426,49 +462,6 @@ else
|
|||||||
infoMessage = "Band-Layout zurückgesetzt.";
|
infoMessage = "Band-Layout zurückgesetzt.";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyBandOrderingFromColumnOrder()
|
|
||||||
{
|
|
||||||
if (bandLayout.ColumnOrder.Count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var bandById = bandLayout.Bands.ToDictionary(band => band.Id, StringComparer.OrdinalIgnoreCase);
|
|
||||||
var orderedBandIds = new List<string>();
|
|
||||||
var orderedColumnsByBand = bandLayout.Bands.ToDictionary(
|
|
||||||
band => band.Id,
|
|
||||||
_ => new List<string>(),
|
|
||||||
StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
foreach (var field in bandLayout.ColumnOrder)
|
|
||||||
{
|
|
||||||
if (columnBandAssignments.TryGetValue(field, out var bandId) && bandById.ContainsKey(bandId))
|
|
||||||
{
|
|
||||||
if (!orderedBandIds.Contains(bandId, StringComparer.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
orderedBandIds.Add(bandId);
|
|
||||||
}
|
|
||||||
|
|
||||||
orderedColumnsByBand[bandId].Add(field);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var band in bandLayout.Bands)
|
|
||||||
{
|
|
||||||
var orderedColumns = orderedColumnsByBand[band.Id];
|
|
||||||
orderedColumns.AddRange(band.Columns.Where(column => !orderedColumns.Contains(column, StringComparer.OrdinalIgnoreCase)));
|
|
||||||
band.Columns = orderedColumns;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (orderedBandIds.Count > 0)
|
|
||||||
{
|
|
||||||
bandLayout.Bands = orderedBandIds
|
|
||||||
.Select(id => bandById[id])
|
|
||||||
.Concat(bandLayout.Bands.Where(band => !orderedBandIds.Contains(band.Id, StringComparer.OrdinalIgnoreCase)))
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplyColumnLayoutFromStorage()
|
private void ApplyColumnLayoutFromStorage()
|
||||||
{
|
{
|
||||||
foreach (var column in columnDefinitions)
|
foreach (var column in columnDefinitions)
|
||||||
@@ -645,9 +638,163 @@ else
|
|||||||
builder.AddAttribute(seq++, "ReadOnly", true);
|
builder.AddAttribute(seq++, "ReadOnly", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (string.Equals(column.FieldName, nameof(MassDataReadDto.Amount), StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
builder.AddAttribute(seq++, "FilterRowCellTemplate", BuildAmountFilterTemplate());
|
||||||
|
}
|
||||||
|
else if (string.Equals(column.FieldName, nameof(MassDataReadDto.AddedWhen), StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
builder.AddAttribute(seq++, "FilterRowCellTemplate", BuildAddedWhenFilterTemplate());
|
||||||
|
}
|
||||||
|
else if (string.Equals(column.FieldName, nameof(MassDataReadDto.ChangedWhen), StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
builder.AddAttribute(seq++, "FilterRowCellTemplate", BuildChangedWhenFilterTemplate());
|
||||||
|
}
|
||||||
|
|
||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private RenderFragment<GridDataColumnFilterRowCellTemplateContext> BuildAmountFilterTemplate()
|
||||||
|
{
|
||||||
|
return BuildNumberFilterTemplate(
|
||||||
|
() => amountFilterValue,
|
||||||
|
value => amountFilterValue = value,
|
||||||
|
() => amountFilterOperator,
|
||||||
|
value => amountFilterOperator = value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RenderFragment<GridDataColumnFilterRowCellTemplateContext> BuildAddedWhenFilterTemplate()
|
||||||
|
{
|
||||||
|
return BuildDateFilterTemplate(
|
||||||
|
() => addedWhenFilterValue,
|
||||||
|
value => addedWhenFilterValue = value,
|
||||||
|
() => addedWhenFilterOperator,
|
||||||
|
value => addedWhenFilterOperator = value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RenderFragment<GridDataColumnFilterRowCellTemplateContext> BuildChangedWhenFilterTemplate()
|
||||||
|
{
|
||||||
|
return BuildDateFilterTemplate(
|
||||||
|
() => changedWhenFilterValue,
|
||||||
|
value => changedWhenFilterValue = value,
|
||||||
|
() => changedWhenFilterOperator,
|
||||||
|
value => changedWhenFilterOperator = value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RenderFragment<GridDataColumnFilterRowCellTemplateContext> BuildNumberFilterTemplate(
|
||||||
|
Func<decimal?> getValue,
|
||||||
|
Action<decimal?> setValue,
|
||||||
|
Func<string> getOperator,
|
||||||
|
Action<string> setOperator)
|
||||||
|
{
|
||||||
|
return context => builder =>
|
||||||
|
{
|
||||||
|
var seq = 0;
|
||||||
|
builder.OpenElement(seq++, "div");
|
||||||
|
builder.AddAttribute(seq++, "class", "filter-row-cell");
|
||||||
|
|
||||||
|
builder.OpenComponent<DxComboBox<FilterOperatorOption, string>>(seq++);
|
||||||
|
builder.AddAttribute(seq++, "Data", filterOperators);
|
||||||
|
builder.AddAttribute(seq++, "TextFieldName", "Text");
|
||||||
|
builder.AddAttribute(seq++, "ValueFieldName", "Value");
|
||||||
|
builder.AddAttribute(seq++, "Value", getOperator());
|
||||||
|
builder.AddAttribute(seq++, "ValueChanged", EventCallback.Factory.Create<string>(this, value =>
|
||||||
|
{
|
||||||
|
setOperator(value);
|
||||||
|
UpdateFilterCriteria(context, value, getValue());
|
||||||
|
}));
|
||||||
|
builder.AddAttribute(seq++, "CssClass", "filter-operator");
|
||||||
|
builder.CloseComponent();
|
||||||
|
|
||||||
|
builder.OpenComponent<DxSpinEdit<decimal?>>(seq++);
|
||||||
|
builder.AddAttribute(seq++, "Value", getValue());
|
||||||
|
builder.AddAttribute(seq++, "ValueChanged", EventCallback.Factory.Create<decimal?>(this, value =>
|
||||||
|
{
|
||||||
|
setValue(value);
|
||||||
|
UpdateFilterCriteria(context, getOperator(), value);
|
||||||
|
}));
|
||||||
|
builder.AddAttribute(seq++, "ClearButtonDisplayMode", DataEditorClearButtonDisplayMode.Auto);
|
||||||
|
builder.AddAttribute(seq++, "CssClass", "filter-value filter-value-amount");
|
||||||
|
builder.CloseComponent();
|
||||||
|
|
||||||
|
builder.CloseElement();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private RenderFragment<GridDataColumnFilterRowCellTemplateContext> BuildDateFilterTemplate(
|
||||||
|
Func<DateTime?> getValue,
|
||||||
|
Action<DateTime?> setValue,
|
||||||
|
Func<string> getOperator,
|
||||||
|
Action<string> setOperator)
|
||||||
|
{
|
||||||
|
return context => builder =>
|
||||||
|
{
|
||||||
|
var seq = 0;
|
||||||
|
builder.OpenElement(seq++, "div");
|
||||||
|
builder.AddAttribute(seq++, "class", "filter-row-cell");
|
||||||
|
|
||||||
|
builder.OpenComponent<DxComboBox<FilterOperatorOption, string>>(seq++);
|
||||||
|
builder.AddAttribute(seq++, "Data", filterOperators);
|
||||||
|
builder.AddAttribute(seq++, "TextFieldName", "Text");
|
||||||
|
builder.AddAttribute(seq++, "ValueFieldName", "Value");
|
||||||
|
builder.AddAttribute(seq++, "Value", getOperator());
|
||||||
|
builder.AddAttribute(seq++, "ValueChanged", EventCallback.Factory.Create<string>(this, value =>
|
||||||
|
{
|
||||||
|
setOperator(value);
|
||||||
|
UpdateFilterCriteria(context, value, getValue());
|
||||||
|
}));
|
||||||
|
builder.AddAttribute(seq++, "CssClass", "filter-operator");
|
||||||
|
builder.CloseComponent();
|
||||||
|
|
||||||
|
builder.OpenComponent<DxDateEdit<DateTime?>>(seq++);
|
||||||
|
builder.AddAttribute(seq++, "Date", getValue());
|
||||||
|
builder.AddAttribute(seq++, "DateChanged", EventCallback.Factory.Create<DateTime?>(this, value =>
|
||||||
|
{
|
||||||
|
setValue(value);
|
||||||
|
UpdateFilterCriteria(context, getOperator(), value);
|
||||||
|
}));
|
||||||
|
builder.AddAttribute(seq++, "ClearButtonDisplayMode", DataEditorClearButtonDisplayMode.Auto);
|
||||||
|
builder.AddAttribute(seq++, "CssClass", "filter-value");
|
||||||
|
builder.CloseComponent();
|
||||||
|
|
||||||
|
builder.CloseElement();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateFilterCriteria(GridDataColumnFilterRowCellTemplateContext context, string op, object? value)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
context.FilterCriteria = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var prop = new OperandProperty(context.DataColumn.FieldName);
|
||||||
|
if (value is DateTime dateValue)
|
||||||
|
{
|
||||||
|
var date = dateValue.Date;
|
||||||
|
context.FilterCriteria = op switch
|
||||||
|
{
|
||||||
|
"=" => new GroupOperator(GroupOperatorType.And, prop >= date, prop < date.AddDays(1)),
|
||||||
|
"<" => prop < date,
|
||||||
|
"<=" => prop < date.AddDays(1),
|
||||||
|
">" => prop >= date.AddDays(1),
|
||||||
|
">=" => prop >= date,
|
||||||
|
_ => prop == date
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.FilterCriteria = op switch
|
||||||
|
{
|
||||||
|
"<" => new BinaryOperator(prop, new OperandValue(value), BinaryOperatorType.Less),
|
||||||
|
">" => new BinaryOperator(prop, new OperandValue(value), BinaryOperatorType.Greater),
|
||||||
|
"<=" => new BinaryOperator(prop, new OperandValue(value), BinaryOperatorType.LessOrEqual),
|
||||||
|
">=" => new BinaryOperator(prop, new OperandValue(value), BinaryOperatorType.GreaterOrEqual),
|
||||||
|
_ => new BinaryOperator(prop, new OperandValue(value), BinaryOperatorType.Equal)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private void SetEditContext(EditContext context)
|
private void SetEditContext(EditContext context)
|
||||||
{
|
{
|
||||||
if (editContext == context)
|
if (editContext == context)
|
||||||
@@ -783,7 +930,7 @@ else
|
|||||||
e.Cancel = true;
|
e.Cancel = true;
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class BandLayout
|
private sealed class BandLayout
|
||||||
{
|
{
|
||||||
public List<BandDefinition> Bands { get; set; } = new();
|
public List<BandDefinition> Bands { get; set; } = new();
|
||||||
@@ -852,7 +999,11 @@ else
|
|||||||
public string Text { get; set; } = string.Empty;
|
public string Text { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool gridLayoutApplied;
|
private sealed class FilterOperatorOption
|
||||||
|
{
|
||||||
|
public string Value { get; set; } = string.Empty;
|
||||||
|
public string Text { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user