Compare commits
4 Commits
2a730ddfcc
...
8387b71676
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8387b71676 | ||
|
|
8824492057 | ||
|
|
59f22be405 | ||
|
|
23865aefb6 |
@@ -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))
|
||||||
@@ -128,6 +144,9 @@ else
|
|||||||
<DxGrid Data="@items"
|
<DxGrid Data="@items"
|
||||||
TItem="CatalogReadDto"
|
TItem="CatalogReadDto"
|
||||||
KeyFieldName="@nameof(CatalogReadDto.Guid)"
|
KeyFieldName="@nameof(CatalogReadDto.Guid)"
|
||||||
|
ShowGroupPanel="true"
|
||||||
|
ShowGroupedColumns="true"
|
||||||
|
AllowGroup="true"
|
||||||
ShowFilterRow="true"
|
ShowFilterRow="true"
|
||||||
AllowColumnResize="true"
|
AllowColumnResize="true"
|
||||||
ColumnResizeMode="GridColumnResizeMode.ColumnsContainer"
|
ColumnResizeMode="GridColumnResizeMode.ColumnsContainer"
|
||||||
@@ -183,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";
|
||||||
@@ -209,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()
|
||||||
@@ -551,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
|
||||||
@@ -757,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
|
||||||
@@ -827,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))
|
||||||
@@ -163,6 +183,9 @@ else
|
|||||||
<DxGrid Data="@items"
|
<DxGrid Data="@items"
|
||||||
TItem="MassDataReadDto"
|
TItem="MassDataReadDto"
|
||||||
KeyFieldName="@nameof(MassDataReadDto.Id)"
|
KeyFieldName="@nameof(MassDataReadDto.Id)"
|
||||||
|
ShowGroupPanel="true"
|
||||||
|
ShowGroupedColumns="true"
|
||||||
|
AllowGroup="true"
|
||||||
ShowFilterRow="true"
|
ShowFilterRow="true"
|
||||||
AllowColumnResize="true"
|
AllowColumnResize="true"
|
||||||
ColumnResizeMode="GridColumnResizeMode.ColumnsContainer"
|
ColumnResizeMode="GridColumnResizeMode.ColumnsContainer"
|
||||||
@@ -231,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;
|
||||||
@@ -277,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);
|
||||||
@@ -598,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)
|
||||||
@@ -805,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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,12 +23,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="nav-item px-3">
|
<div class="nav-item px-3">
|
||||||
<NavLink class="nav-link" href="dashboards">
|
<NavLink class="nav-link" href="dashboards">
|
||||||
<span class="oi oi-list-rich" aria-hidden="true"></span> Dashboards
|
<span class="bi bi-speedometer-nav-menu" aria-hidden="true"></span> Dashboards
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
<div class="nav-item px-3">
|
<div class="nav-item px-3">
|
||||||
<NavLink class="nav-link" href="massdata">
|
<NavLink class="nav-link" href="massdata">
|
||||||
<span class="bi bi-table" aria-hidden="true"></span> MassData
|
<span class="bi bi-table-nav-menu" aria-hidden="true"></span> MassData
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -33,6 +33,18 @@
|
|||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bi-collection-nav-menu {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' viewBox='0 0 16 16'%3E%3Cpath d='M2 3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 .5.5v1H2V3z'/%3E%3Cpath d='M2 5h12v8a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5z'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
|
||||||
|
.bi-speedometer-nav-menu {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' viewBox='0 0 16 16'%3E%3Cpath d='M1 11a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1v4zm5 0a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1v8zm5 0a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V9a1 1 0 0 0-1-1h-2a1 1 0 0 0-1 1v2z'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
|
||||||
|
.bi-table-nav-menu {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' viewBox='0 0 16 16'%3E%3Cpath d='M1 2a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2zm1 1v2h12V3H2zm12 3H2v2h12V6zm0 3H2v2h12V9zm0 3H2v1h12v-1z'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
|
||||||
.nav-item {
|
.nav-item {
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
padding-bottom: 0.5rem;
|
padding-bottom: 0.5rem;
|
||||||
|
|||||||
@@ -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))
|
||||||
@@ -87,6 +103,9 @@ else
|
|||||||
<DxGrid Data="@items"
|
<DxGrid Data="@items"
|
||||||
TItem="CatalogReadDto"
|
TItem="CatalogReadDto"
|
||||||
KeyFieldName="@nameof(CatalogReadDto.Guid)"
|
KeyFieldName="@nameof(CatalogReadDto.Guid)"
|
||||||
|
ShowGroupPanel="true"
|
||||||
|
ShowGroupedColumns="true"
|
||||||
|
AllowGroup="true"
|
||||||
ShowFilterRow="true"
|
ShowFilterRow="true"
|
||||||
AllowColumnResize="true"
|
AllowColumnResize="true"
|
||||||
ColumnResizeMode="GridColumnResizeMode.ColumnsContainer"
|
ColumnResizeMode="GridColumnResizeMode.ColumnsContainer"
|
||||||
@@ -142,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";
|
||||||
@@ -168,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()
|
||||||
{
|
{
|
||||||
@@ -178,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)
|
||||||
@@ -471,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
|
||||||
@@ -634,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();
|
||||||
@@ -690,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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,17 +14,6 @@
|
|||||||
</NavLink>
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="nav-item px-3">
|
|
||||||
<NavLink class="nav-link" href="counter">
|
|
||||||
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Counter
|
|
||||||
</NavLink>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="nav-item px-3">
|
|
||||||
<NavLink class="nav-link" href="weather">
|
|
||||||
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Weather
|
|
||||||
</NavLink>
|
|
||||||
</div>
|
|
||||||
<div class="nav-item px-3">
|
<div class="nav-item px-3">
|
||||||
<NavLink class="nav-link" href="catalogs">
|
<NavLink class="nav-link" href="catalogs">
|
||||||
<span class="bi bi-collection-nav-menu" aria-hidden="true"></span> Catalogs
|
<span class="bi bi-collection-nav-menu" aria-hidden="true"></span> Catalogs
|
||||||
@@ -33,13 +22,13 @@
|
|||||||
|
|
||||||
<div class="nav-item px-3">
|
<div class="nav-item px-3">
|
||||||
<NavLink class="nav-link" href="dashboards">
|
<NavLink class="nav-link" href="dashboards">
|
||||||
<span class="oi oi-list-rich" aria-hidden="true"></span> Dashboards
|
<span class="bi bi-speedometer-nav-menu" aria-hidden="true"></span> Dashboards
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="nav-item px-3">
|
<div class="nav-item px-3">
|
||||||
<NavLink class="nav-link" href="massdata">
|
<NavLink class="nav-link" href="massdata">
|
||||||
<span class="bi bi-table" aria-hidden="true"></span> MassData
|
<span class="bi bi-table-nav-menu" aria-hidden="true"></span> MassData
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -46,6 +46,18 @@
|
|||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bi-collection-nav-menu {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' viewBox='0 0 16 16'%3E%3Cpath d='M2 3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 .5.5v1H2V3z'/%3E%3Cpath d='M2 5h12v8a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5z'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
|
||||||
|
.bi-speedometer-nav-menu {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' viewBox='0 0 16 16'%3E%3Cpath d='M1 11a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1v4zm5 0a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1v8zm5 0a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V9a1 1 0 0 0-1-1h-2a1 1 0 0 0-1 1v2z'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
|
||||||
|
.bi-table-nav-menu {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' viewBox='0 0 16 16'%3E%3Cpath d='M1 2a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2zm1 1v2h12V3H2zm12 3H2v2h12V6zm0 3H2v2h12V9zm0 3H2v1h12v-1z'/%3E%3C/svg%3E");
|
||||||
|
}
|
||||||
|
|
||||||
.nav-item {
|
.nav-item {
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
padding-bottom: 0.5rem;
|
padding-bottom: 0.5rem;
|
||||||
|
|||||||
@@ -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))
|
||||||
@@ -163,6 +183,9 @@ else
|
|||||||
<DxGrid Data="@items"
|
<DxGrid Data="@items"
|
||||||
TItem="MassDataReadDto"
|
TItem="MassDataReadDto"
|
||||||
KeyFieldName="@nameof(MassDataReadDto.Id)"
|
KeyFieldName="@nameof(MassDataReadDto.Id)"
|
||||||
|
ShowGroupPanel="true"
|
||||||
|
ShowGroupedColumns="true"
|
||||||
|
AllowGroup="true"
|
||||||
ShowFilterRow="true"
|
ShowFilterRow="true"
|
||||||
AllowColumnResize="true"
|
AllowColumnResize="true"
|
||||||
ColumnResizeMode="GridColumnResizeMode.ColumnsContainer"
|
ColumnResizeMode="GridColumnResizeMode.ColumnsContainer"
|
||||||
@@ -231,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;
|
||||||
@@ -277,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);
|
||||||
@@ -353,7 +393,6 @@ else
|
|||||||
|
|
||||||
columnBandAssignments = BuildAssignmentsFromLayout(bandLayout);
|
columnBandAssignments = BuildAssignmentsFromLayout(bandLayout);
|
||||||
ApplyColumnLayoutFromStorage();
|
ApplyColumnLayoutFromStorage();
|
||||||
//ApplyBandOrderingFromColumnOrder();
|
|
||||||
UpdateBandOptions();
|
UpdateBandOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -423,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)
|
||||||
@@ -642,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)
|
||||||
@@ -849,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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
@page "/counter"
|
|
||||||
@rendermode InteractiveServer
|
|
||||||
|
|
||||||
<PageTitle>Counter</PageTitle>
|
|
||||||
|
|
||||||
<h1>Counter</h1>
|
|
||||||
|
|
||||||
<p role="status">Current count: @currentCount</p>
|
|
||||||
|
|
||||||
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
|
|
||||||
|
|
||||||
@code {
|
|
||||||
private int currentCount = 0;
|
|
||||||
|
|
||||||
private void IncrementCount()
|
|
||||||
{
|
|
||||||
currentCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
@page "/weather"
|
|
||||||
@attribute [StreamRendering]
|
|
||||||
|
|
||||||
<PageTitle>Weather</PageTitle>
|
|
||||||
|
|
||||||
<h1>Weather</h1>
|
|
||||||
|
|
||||||
<p>This component demonstrates showing data.</p>
|
|
||||||
|
|
||||||
@if (forecasts == null)
|
|
||||||
{
|
|
||||||
<p><em>Loading...</em></p>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Date</th>
|
|
||||||
<th>Temp. (C)</th>
|
|
||||||
<th>Temp. (F)</th>
|
|
||||||
<th>Summary</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
@foreach (var forecast in forecasts)
|
|
||||||
{
|
|
||||||
<tr>
|
|
||||||
<td>@forecast.Date.ToShortDateString()</td>
|
|
||||||
<td>@forecast.TemperatureC</td>
|
|
||||||
<td>@forecast.TemperatureF</td>
|
|
||||||
<td>@forecast.Summary</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
}
|
|
||||||
|
|
||||||
@code {
|
|
||||||
private WeatherForecast[]? forecasts;
|
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
|
||||||
{
|
|
||||||
// Simulate asynchronous loading to demonstrate streaming rendering
|
|
||||||
await Task.Delay(500);
|
|
||||||
|
|
||||||
var startDate = DateOnly.FromDateTime(DateTime.Now);
|
|
||||||
var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
|
|
||||||
forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
|
|
||||||
{
|
|
||||||
Date = startDate.AddDays(index),
|
|
||||||
TemperatureC = Random.Shared.Next(-20, 55),
|
|
||||||
Summary = summaries[Random.Shared.Next(summaries.Length)]
|
|
||||||
}).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class WeatherForecast
|
|
||||||
{
|
|
||||||
public DateOnly Date { get; set; }
|
|
||||||
public int TemperatureC { get; set; }
|
|
||||||
public string? Summary { get; set; }
|
|
||||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user