Refactor: centralize grid band layout logic in service
Introduce BandLayoutService and shared models to manage grid band layouts across components. Refactor CatalogsGrid and MassDataGrid to use the new service, removing duplicated layout logic. Update _Imports.razor and register the service in Program.cs for improved maintainability and code reuse.
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
@inject CatalogApiClient Api
|
@inject CatalogApiClient Api
|
||||||
@inject LayoutApiClient LayoutApi
|
@inject BandLayoutService BandLayoutService
|
||||||
@inject IJSRuntime JsRuntime
|
|
||||||
|
|
||||||
@if (!string.IsNullOrWhiteSpace(errorMessage))
|
@if (!string.IsNullOrWhiteSpace(errorMessage))
|
||||||
{
|
{
|
||||||
@@ -101,7 +100,9 @@ else
|
|||||||
@RenderColumns()
|
@RenderColumns()
|
||||||
</Columns>
|
</Columns>
|
||||||
<EditFormTemplate Context="editFormContext">
|
<EditFormTemplate Context="editFormContext">
|
||||||
@{ SetEditContext(editFormContext.EditContext); var editModel = (CatalogEditModel)editFormContext.EditModel; SetPopupHeaderText(editModel.IsNew); }
|
@{
|
||||||
|
SetEditContext(editFormContext.EditContext); var editModel = (CatalogEditModel)editFormContext.EditModel; SetPopupHeaderText(editModel.IsNew);
|
||||||
|
}
|
||||||
<DxFormLayout ColCount="2">
|
<DxFormLayout ColCount="2">
|
||||||
<DxFormLayoutItem Caption="Titel">
|
<DxFormLayoutItem Caption="Titel">
|
||||||
<DxTextBox @bind-Text="editModel.CatTitle" Width="100%" />
|
<DxTextBox @bind-Text="editModel.CatTitle" Width="100%" />
|
||||||
@@ -143,13 +144,13 @@ else
|
|||||||
private string popupHeaderText = "Edit";
|
private string popupHeaderText = "Edit";
|
||||||
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 string? layoutUser;
|
private string? layoutUser;
|
||||||
private BandLayout bandLayout = new();
|
private BandLayout bandLayout = new();
|
||||||
private Dictionary<string, string> columnBandAssignments = new();
|
private Dictionary<string, string> columnBandAssignments = new();
|
||||||
private List<BandOption> bandOptions = new();
|
private List<BandOption> bandOptions = new();
|
||||||
private Dictionary<string, ColumnDefinition> columnLookup = new();
|
private Dictionary<string, ColumnDefinition> columnLookup = new();
|
||||||
private readonly JsonSerializerOptions jsonOptions = new(JsonSerializerDefaults.Web);
|
private bool gridLayoutApplied;
|
||||||
|
|
||||||
private List<ColumnDefinition> columnDefinitions = new()
|
private List<ColumnDefinition> columnDefinitions = new()
|
||||||
{
|
{
|
||||||
new() { FieldName = nameof(CatalogReadDto.Guid), Caption = "Id", Width = "140px", FilterType = ColumnFilterType.Text },
|
new() { FieldName = nameof(CatalogReadDto.Guid), Caption = "Id", Width = "140px", FilterType = ColumnFilterType.Text },
|
||||||
@@ -168,30 +169,32 @@ else
|
|||||||
};
|
};
|
||||||
|
|
||||||
private bool CanSaveBandLayout => !string.IsNullOrWhiteSpace(layoutUser);
|
private bool CanSaveBandLayout => !string.IsNullOrWhiteSpace(layoutUser);
|
||||||
private bool gridLayoutApplied;
|
|
||||||
|
|
||||||
private DevExpress.Blazor.SizeMode _sizeMode = DevExpress.Blazor.SizeMode.Medium;
|
private SizeMode _sizeMode = SizeMode.Medium;
|
||||||
private static readonly List<DevExpress.Blazor.SizeMode> _sizeModes =
|
private static readonly List<SizeMode> _sizeModes = Enum.GetValues<SizeMode>().ToList();
|
||||||
Enum.GetValues<DevExpress.Blazor.SizeMode>().ToList();
|
|
||||||
|
|
||||||
private string FormatSizeText(DevExpress.Blazor.SizeMode size) => size switch
|
private string FormatSizeText(SizeMode size) => size switch
|
||||||
{
|
{
|
||||||
DevExpress.Blazor.SizeMode.Small => "Klein",
|
SizeMode.Small => "Klein",
|
||||||
DevExpress.Blazor.SizeMode.Medium => "Mittel",
|
SizeMode.Medium => "Mittel",
|
||||||
DevExpress.Blazor.SizeMode.Large => "Groß",
|
SizeMode.Large => "Groß",
|
||||||
_ => size.ToString()
|
_ => size.ToString()
|
||||||
};
|
};
|
||||||
|
|
||||||
private void OnSizeChange(DropDownButtonItemClickEventArgs args)
|
private void OnSizeChange(DropDownButtonItemClickEventArgs args)
|
||||||
{
|
{
|
||||||
_sizeMode = Enum.Parse<DevExpress.Blazor.SizeMode>(args.ItemInfo.Id);
|
_sizeMode = Enum.Parse<SizeMode>(args.ItemInfo.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
columnLookup = columnDefinitions.ToDictionary(column => column.FieldName, StringComparer.OrdinalIgnoreCase);
|
columnLookup = columnDefinitions.ToDictionary(c => c.FieldName, StringComparer.OrdinalIgnoreCase);
|
||||||
await EnsureLayoutUserAsync();
|
layoutUser = await BandLayoutService.EnsureLayoutUserAsync();
|
||||||
await LoadBandLayoutAsync();
|
bandLayout = await BandLayoutService.LoadBandLayoutAsync(LayoutType, LayoutKey, layoutUser, columnLookup);
|
||||||
|
columnBandAssignments = BandLayoutService.BuildAssignmentsFromLayout(bandLayout);
|
||||||
|
ApplyColumnLayoutFromStorage();
|
||||||
|
_sizeMode = bandLayout.SizeMode;
|
||||||
|
UpdateBandOptions();
|
||||||
await LoadCatalogs();
|
await LoadCatalogs();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,71 +208,6 @@ else
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetEditContext(EditContext context)
|
|
||||||
{
|
|
||||||
if (editContext == context)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (editContext != null)
|
|
||||||
{
|
|
||||||
editContext.OnFieldChanged -= OnEditFieldChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
editContext = context;
|
|
||||||
validationMessageStore = new ValidationMessageStore(editContext);
|
|
||||||
editContext.OnFieldChanged += OnEditFieldChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnEditFieldChanged(object? sender, FieldChangedEventArgs e)
|
|
||||||
{
|
|
||||||
if (validationMessageStore == null || editContext == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.FieldIdentifier.FieldName == nameof(CatalogEditModel.UpdateProcedure))
|
|
||||||
{
|
|
||||||
validationMessageStore.Clear();
|
|
||||||
editContext.NotifyValidationStateChanged();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.FieldIdentifier.FieldName == nameof(CatalogEditModel.CatTitle))
|
|
||||||
{
|
|
||||||
var field = new FieldIdentifier(editContext.Model, nameof(CatalogEditModel.CatTitle));
|
|
||||||
validationMessageStore.Clear(field);
|
|
||||||
editContext.NotifyValidationStateChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetPopupHeaderText(bool isNew)
|
|
||||||
{
|
|
||||||
popupHeaderText = isNew ? "Neu" : "Edit";
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnCustomizeEditModel(GridCustomizeEditModelEventArgs e)
|
|
||||||
{
|
|
||||||
popupHeaderText = e.IsNew ? "Neu" : "Edit";
|
|
||||||
if (e.IsNew)
|
|
||||||
{
|
|
||||||
e.EditModel = new CatalogEditModel { IsNew = true };
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var item = (CatalogReadDto)e.DataItem;
|
|
||||||
e.EditModel = new CatalogEditModel
|
|
||||||
{
|
|
||||||
Guid = item.Guid,
|
|
||||||
CatTitle = item.CatTitle,
|
|
||||||
CatString = item.CatString,
|
|
||||||
UpdateProcedure = 0,
|
|
||||||
OriginalCatTitle = item.CatTitle,
|
|
||||||
IsNew = false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task LoadCatalogs()
|
private async Task LoadCatalogs()
|
||||||
{
|
{
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
@@ -290,11 +228,229 @@ else
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task SaveLayoutAsync()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(layoutUser))
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
CaptureColumnLayoutFromGrid();
|
||||||
|
await BandLayoutService.SaveBandLayoutAsync(LayoutType, LayoutKey, layoutUser, bandLayout);
|
||||||
|
infoMessage = "Layout gespeichert.";
|
||||||
|
errorMessage = null;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
errorMessage = $"Layout konnte nicht gespeichert werden: {ex.Message}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ResetLayoutAsync()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(layoutUser))
|
||||||
|
return;
|
||||||
|
|
||||||
|
await BandLayoutService.ResetBandLayoutAsync(LayoutType, LayoutKey, layoutUser);
|
||||||
|
|
||||||
|
bandLayout = new BandLayout();
|
||||||
|
columnBandAssignments.Clear();
|
||||||
|
UpdateBandOptions();
|
||||||
|
|
||||||
|
foreach (var column in columnDefinitions)
|
||||||
|
column.Width = null;
|
||||||
|
columnLookup = columnDefinitions.ToDictionary(c => c.FieldName, StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
_sizeMode = SizeMode.Medium;
|
||||||
|
|
||||||
|
if (gridRef != null)
|
||||||
|
gridRef.LoadLayout(new GridPersistentLayout());
|
||||||
|
gridLayoutApplied = false;
|
||||||
|
|
||||||
|
infoMessage = "Layout zurückgesetzt.";
|
||||||
|
errorMessage = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CaptureColumnLayoutFromGrid()
|
||||||
|
{
|
||||||
|
if (gridRef == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var layout = gridRef.SaveLayout();
|
||||||
|
bandLayout.GridLayout = layout;
|
||||||
|
bandLayout.SizeMode = _sizeMode;
|
||||||
|
|
||||||
|
var orderedColumns = layout.Columns
|
||||||
|
.Where(c => !string.IsNullOrWhiteSpace(c.FieldName))
|
||||||
|
.OrderBy(c => c.VisibleIndex)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
bandLayout.ColumnOrder = orderedColumns.Select(c => c.FieldName).ToList();
|
||||||
|
bandLayout.ColumnWidths = orderedColumns
|
||||||
|
.Where(c => !string.IsNullOrWhiteSpace(c.Width))
|
||||||
|
.ToDictionary(c => c.FieldName, c => c.Width, StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
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(c => c.FieldName, StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddBand()
|
||||||
|
{
|
||||||
|
bandLayout.Bands.Add(new BandDefinition { Id = Guid.NewGuid().ToString("N"), Caption = "Band" });
|
||||||
|
UpdateBandOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveBand(BandDefinition band)
|
||||||
|
{
|
||||||
|
bandLayout.Bands.Remove(band);
|
||||||
|
foreach (var key in columnBandAssignments.Where(p => p.Value == band.Id).Select(p => p.Key).ToList())
|
||||||
|
columnBandAssignments.Remove(key);
|
||||||
|
UpdateBandOptions();
|
||||||
|
SyncBandsFromAssignments();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateBandCaption(BandDefinition band, string value)
|
||||||
|
{
|
||||||
|
band.Caption = value;
|
||||||
|
UpdateBandOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateColumnBand(string fieldName, string? bandId)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(bandId))
|
||||||
|
columnBandAssignments.Remove(fieldName);
|
||||||
|
else
|
||||||
|
columnBandAssignments[fieldName] = bandId;
|
||||||
|
SyncBandsFromAssignments();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetColumnBand(string fieldName)
|
||||||
|
=> columnBandAssignments.TryGetValue(fieldName, out var bandId) ? bandId : string.Empty;
|
||||||
|
|
||||||
|
private void SyncBandsFromAssignments()
|
||||||
|
{
|
||||||
|
foreach (var band in bandLayout.Bands)
|
||||||
|
{
|
||||||
|
band.Columns = columnDefinitions
|
||||||
|
.Where(c => columnBandAssignments.TryGetValue(c.FieldName, out var id) && id == band.Id)
|
||||||
|
.Select(c => c.FieldName)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateBandOptions()
|
||||||
|
{
|
||||||
|
bandOptions = new List<BandOption> { new() { Id = string.Empty, Caption = "Ohne Band" } };
|
||||||
|
bandOptions.AddRange(bandLayout.Bands.Select(b => new BandOption { Id = b.Id, Caption = b.Caption }));
|
||||||
|
}
|
||||||
|
|
||||||
|
private RenderFragment RenderColumns() => builder =>
|
||||||
|
{
|
||||||
|
var seq = 0;
|
||||||
|
builder.OpenComponent<DxGridCommandColumn>(seq++);
|
||||||
|
builder.AddAttribute(seq++, "Width", "120px");
|
||||||
|
builder.CloseComponent();
|
||||||
|
|
||||||
|
var grouped = bandLayout.Bands.SelectMany(b => b.Columns).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (var column in columnDefinitions.Where(c => !grouped.Contains(c.FieldName)))
|
||||||
|
BuildDataColumn(builder, ref seq, column);
|
||||||
|
|
||||||
|
foreach (var band in bandLayout.Bands)
|
||||||
|
{
|
||||||
|
if (band.Columns.Count == 0) continue;
|
||||||
|
|
||||||
|
builder.OpenComponent<DxGridBandColumn>(seq++);
|
||||||
|
builder.AddAttribute(seq++, "Caption", band.Caption);
|
||||||
|
builder.AddAttribute(seq++, "Columns", (RenderFragment)(bandBuilder =>
|
||||||
|
{
|
||||||
|
var bandSeq = 0;
|
||||||
|
foreach (var columnName in band.Columns)
|
||||||
|
{
|
||||||
|
if (columnLookup.TryGetValue(columnName, out var column))
|
||||||
|
BuildDataColumn(bandBuilder, ref bandSeq, column);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
builder.CloseComponent();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private void BuildDataColumn(RenderTreeBuilder builder, ref int seq, ColumnDefinition column)
|
||||||
|
{
|
||||||
|
builder.OpenComponent<DxGridDataColumn>(seq++);
|
||||||
|
builder.AddAttribute(seq++, "FieldName", column.FieldName);
|
||||||
|
builder.AddAttribute(seq++, "Caption", column.Caption);
|
||||||
|
if (!string.IsNullOrWhiteSpace(column.Width))
|
||||||
|
builder.AddAttribute(seq++, "Width", column.Width);
|
||||||
|
if (!string.IsNullOrWhiteSpace(column.DisplayFormat))
|
||||||
|
builder.AddAttribute(seq++, "DisplayFormat", column.DisplayFormat);
|
||||||
|
if (column.ReadOnly)
|
||||||
|
builder.AddAttribute(seq++, "ReadOnly", true);
|
||||||
|
builder.CloseComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetEditContext(EditContext context)
|
||||||
|
{
|
||||||
|
if (editContext == context) return;
|
||||||
|
if (editContext != null)
|
||||||
|
editContext.OnFieldChanged -= OnEditFieldChanged;
|
||||||
|
editContext = context;
|
||||||
|
validationMessageStore = new ValidationMessageStore(editContext);
|
||||||
|
editContext.OnFieldChanged += OnEditFieldChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEditFieldChanged(object? sender, FieldChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (validationMessageStore == null || editContext == null) return;
|
||||||
|
|
||||||
|
if (e.FieldIdentifier.FieldName == nameof(CatalogEditModel.UpdateProcedure))
|
||||||
|
{
|
||||||
|
validationMessageStore.Clear();
|
||||||
|
editContext.NotifyValidationStateChanged();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.FieldIdentifier.FieldName == nameof(CatalogEditModel.CatTitle))
|
||||||
|
{
|
||||||
|
validationMessageStore.Clear(new FieldIdentifier(editContext.Model, nameof(CatalogEditModel.CatTitle)));
|
||||||
|
editContext.NotifyValidationStateChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetPopupHeaderText(bool isNew) => popupHeaderText = isNew ? "Neu" : "Edit";
|
||||||
|
|
||||||
|
private void OnCustomizeEditModel(GridCustomizeEditModelEventArgs e)
|
||||||
|
{
|
||||||
|
popupHeaderText = e.IsNew ? "Neu" : "Edit";
|
||||||
|
if (e.IsNew)
|
||||||
|
{
|
||||||
|
e.EditModel = new CatalogEditModel { IsNew = true };
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var item = (CatalogReadDto)e.DataItem;
|
||||||
|
e.EditModel = new CatalogEditModel
|
||||||
|
{
|
||||||
|
Guid = item.Guid,
|
||||||
|
CatTitle = item.CatTitle,
|
||||||
|
CatString = item.CatString,
|
||||||
|
UpdateProcedure = 0,
|
||||||
|
OriginalCatTitle = item.CatTitle,
|
||||||
|
IsNew = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private async Task OnEditModelSaving(GridEditModelSavingEventArgs e)
|
private async Task OnEditModelSaving(GridEditModelSavingEventArgs e)
|
||||||
{
|
{
|
||||||
errorMessage = null;
|
errorMessage = null;
|
||||||
infoMessage = null;
|
infoMessage = null;
|
||||||
|
|
||||||
validationMessageStore?.Clear();
|
validationMessageStore?.Clear();
|
||||||
editContext?.NotifyValidationStateChanged();
|
editContext?.NotifyValidationStateChanged();
|
||||||
|
|
||||||
@@ -320,18 +476,12 @@ else
|
|||||||
if (!created.Success || created.Value == null)
|
if (!created.Success || created.Value == null)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(created.Error))
|
if (!string.IsNullOrWhiteSpace(created.Error))
|
||||||
{
|
|
||||||
AddValidationError(editModel, nameof(CatalogEditModel.CatTitle), created.Error);
|
AddValidationError(editModel, nameof(CatalogEditModel.CatTitle), created.Error);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
errorMessage = "Anlegen fehlgeschlagen.";
|
errorMessage = "Anlegen fehlgeschlagen.";
|
||||||
}
|
|
||||||
|
|
||||||
e.Cancel = true;
|
e.Cancel = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
infoMessage = "Katalog angelegt.";
|
infoMessage = "Katalog angelegt.";
|
||||||
focusedRowKey = created.Value.Guid;
|
focusedRowKey = created.Value.Guid;
|
||||||
}
|
}
|
||||||
@@ -344,7 +494,6 @@ else
|
|||||||
e.Cancel = true;
|
e.Cancel = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
infoMessage = "Katalog aktualisiert.";
|
infoMessage = "Katalog aktualisiert.";
|
||||||
focusedRowKey = editModel.Guid;
|
focusedRowKey = editModel.Guid;
|
||||||
}
|
}
|
||||||
@@ -360,30 +509,20 @@ else
|
|||||||
|
|
||||||
private void AddValidationError(CatalogEditModel editModel, string fieldName, string message)
|
private void AddValidationError(CatalogEditModel editModel, string fieldName, string message)
|
||||||
{
|
{
|
||||||
if (editContext == null || validationMessageStore == null)
|
if (editContext == null || validationMessageStore == null) return;
|
||||||
{
|
validationMessageStore.Add(new FieldIdentifier(editModel, fieldName), message);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var field = new FieldIdentifier(editModel, fieldName);
|
|
||||||
validationMessageStore.Add(field, message);
|
|
||||||
editContext.NotifyValidationStateChanged();
|
editContext.NotifyValidationStateChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ValidateEditModel(CatalogEditModel editModel, bool isNew)
|
private bool ValidateEditModel(CatalogEditModel editModel, bool isNew)
|
||||||
{
|
{
|
||||||
if (isNew)
|
if (isNew) return true;
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (editModel.UpdateProcedure == 0 &&
|
if (editModel.UpdateProcedure == 0 &&
|
||||||
!string.Equals(editModel.CatTitle, editModel.OriginalCatTitle, StringComparison.OrdinalIgnoreCase))
|
!string.Equals(editModel.CatTitle, editModel.OriginalCatTitle, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
AddValidationError(editModel, nameof(CatalogEditModel.CatTitle), "Titel kann nicht geändert werden.");
|
AddValidationError(editModel, nameof(CatalogEditModel.CatTitle), "Titel kann nicht geändert werden.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,9 +530,7 @@ else
|
|||||||
{
|
{
|
||||||
errorMessage = null;
|
errorMessage = null;
|
||||||
infoMessage = null;
|
infoMessage = null;
|
||||||
|
|
||||||
var item = (CatalogReadDto)e.DataItem;
|
var item = (CatalogReadDto)e.DataItem;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var deleted = await Api.DeleteAsync(item.Guid);
|
var deleted = await Api.DeleteAsync(item.Guid);
|
||||||
@@ -403,350 +540,16 @@ else
|
|||||||
e.Cancel = true;
|
e.Cancel = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
infoMessage = "Katalog gelöscht.";
|
infoMessage = "Katalog gelöscht.";
|
||||||
await LoadCatalogs();
|
await LoadCatalogs();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
errorMessage = $"Fehler beim Löschen: {ex.Message}";
|
errorMessage = $"Fehler beim Löschen: {ex.Message}";
|
||||||
e.Cancel = true;
|
e.Cancel = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task EnsureLayoutUserAsync()
|
|
||||||
{
|
|
||||||
layoutUser = await JsRuntime.InvokeAsync<string?>("localStorage.getItem", LayoutUserStorageKey);
|
|
||||||
if (string.IsNullOrWhiteSpace(layoutUser))
|
|
||||||
{
|
|
||||||
layoutUser = Guid.NewGuid().ToString("N");
|
|
||||||
await JsRuntime.InvokeVoidAsync("localStorage.setItem", LayoutUserStorageKey, layoutUser);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SaveLayoutAsync()
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(layoutUser))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
CaptureColumnLayoutFromGrid();
|
|
||||||
|
|
||||||
var layoutData = JsonSerializer.Serialize(bandLayout, jsonOptions);
|
|
||||||
await LayoutApi.UpsertAsync(new LayoutDto
|
|
||||||
{
|
|
||||||
LayoutType = LayoutType,
|
|
||||||
LayoutKey = LayoutKey,
|
|
||||||
UserName = layoutUser,
|
|
||||||
LayoutData = layoutData
|
|
||||||
});
|
|
||||||
infoMessage = "Layout gespeichert.";
|
|
||||||
errorMessage = null;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
errorMessage = $"Layout konnte nicht gespeichert werden: {ex.Message}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CaptureColumnLayoutFromGrid()
|
|
||||||
{
|
|
||||||
if (gridRef == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var layout = gridRef.SaveLayout();
|
|
||||||
bandLayout.GridLayout = layout;
|
|
||||||
bandLayout.SizeMode = _sizeMode;
|
|
||||||
|
|
||||||
var orderedColumns = layout.Columns
|
|
||||||
.Where(column => !string.IsNullOrWhiteSpace(column.FieldName))
|
|
||||||
.OrderBy(column => column.VisibleIndex)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
bandLayout.ColumnOrder = orderedColumns
|
|
||||||
.Select(column => column.FieldName)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
bandLayout.ColumnWidths = orderedColumns
|
|
||||||
.Where(column => !string.IsNullOrWhiteSpace(column.Width))
|
|
||||||
.ToDictionary(column => column.FieldName, column => column.Width, StringComparer.OrdinalIgnoreCase);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task LoadBandLayoutAsync()
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(layoutUser))
|
|
||||||
{
|
|
||||||
bandLayout = new BandLayout();
|
|
||||||
UpdateBandOptions();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var stored = await LayoutApi.GetAsync(LayoutType, LayoutKey, layoutUser);
|
|
||||||
if (stored != null && !string.IsNullOrWhiteSpace(stored.LayoutData))
|
|
||||||
{
|
|
||||||
var parsed = JsonSerializer.Deserialize<BandLayout>(stored.LayoutData, jsonOptions);
|
|
||||||
bandLayout = NormalizeBandLayout(parsed);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bandLayout = new BandLayout();
|
|
||||||
}
|
|
||||||
|
|
||||||
columnBandAssignments = BuildAssignmentsFromLayout(bandLayout);
|
|
||||||
ApplyColumnLayoutFromStorage();
|
|
||||||
_sizeMode = bandLayout.SizeMode;
|
|
||||||
UpdateBandOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ResetLayoutAsync()
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(layoutUser))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await LayoutApi.DeleteAsync(LayoutType, LayoutKey, layoutUser);
|
|
||||||
|
|
||||||
bandLayout = new BandLayout();
|
|
||||||
columnBandAssignments.Clear();
|
|
||||||
UpdateBandOptions();
|
|
||||||
|
|
||||||
foreach (var column in columnDefinitions)
|
|
||||||
column.Width = null;
|
|
||||||
columnLookup = columnDefinitions.ToDictionary(c => c.FieldName, StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
_sizeMode = DevExpress.Blazor.SizeMode.Medium;
|
|
||||||
|
|
||||||
if (gridRef != null)
|
|
||||||
gridRef.LoadLayout(new GridPersistentLayout());
|
|
||||||
gridLayoutApplied = false;
|
|
||||||
|
|
||||||
infoMessage = "Layout zur\u00fcckgesetzt.";
|
|
||||||
errorMessage = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
|
||||||
{
|
|
||||||
bandLayout.Bands.Add(new BandDefinition
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid().ToString("N"),
|
|
||||||
Caption = "Band"
|
|
||||||
});
|
|
||||||
UpdateBandOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RemoveBand(BandDefinition band)
|
|
||||||
{
|
|
||||||
bandLayout.Bands.Remove(band);
|
|
||||||
var removedColumns = columnBandAssignments.Where(pair => pair.Value == band.Id)
|
|
||||||
.Select(pair => pair.Key)
|
|
||||||
.ToList();
|
|
||||||
foreach (var column in removedColumns)
|
|
||||||
{
|
|
||||||
columnBandAssignments.Remove(column);
|
|
||||||
}
|
|
||||||
UpdateBandOptions();
|
|
||||||
SyncBandsFromAssignments();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateBandCaption(BandDefinition band, string value)
|
|
||||||
{
|
|
||||||
band.Caption = value;
|
|
||||||
UpdateBandOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateColumnBand(string fieldName, string? bandId)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(bandId))
|
|
||||||
{
|
|
||||||
columnBandAssignments.Remove(fieldName);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
columnBandAssignments[fieldName] = bandId;
|
|
||||||
}
|
|
||||||
|
|
||||||
SyncBandsFromAssignments();
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetColumnBand(string fieldName)
|
|
||||||
{
|
|
||||||
return columnBandAssignments.TryGetValue(fieldName, out var bandId) ? bandId : string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SyncBandsFromAssignments()
|
|
||||||
{
|
|
||||||
foreach (var band in bandLayout.Bands)
|
|
||||||
{
|
|
||||||
band.Columns = columnDefinitions
|
|
||||||
.Where(column => columnBandAssignments.TryGetValue(column.FieldName, out var bandId) && bandId == band.Id)
|
|
||||||
.Select(column => column.FieldName)
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateBandOptions()
|
|
||||||
{
|
|
||||||
bandOptions = new List<BandOption> { new() { Id = string.Empty, Caption = "Ohne Band" } };
|
|
||||||
bandOptions.AddRange(bandLayout.Bands.Select(band => new BandOption { Id = band.Id, Caption = band.Caption }));
|
|
||||||
}
|
|
||||||
|
|
||||||
private BandLayout NormalizeBandLayout(BandLayout? layout)
|
|
||||||
{
|
|
||||||
layout ??= new BandLayout();
|
|
||||||
layout.Bands ??= new List<BandDefinition>();
|
|
||||||
layout.ColumnOrder ??= new List<string>();
|
|
||||||
layout.ColumnWidths ??= new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
foreach (var band in layout.Bands)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(band.Id))
|
|
||||||
{
|
|
||||||
band.Id = Guid.NewGuid().ToString("N");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(band.Caption))
|
|
||||||
{
|
|
||||||
band.Caption = "Band";
|
|
||||||
}
|
|
||||||
|
|
||||||
band.Columns = band.Columns?.Where(columnLookup.ContainsKey).ToList() ?? new List<string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Dictionary<string, string> BuildAssignmentsFromLayout(BandLayout layout)
|
|
||||||
{
|
|
||||||
var assignments = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
foreach (var band in layout.Bands)
|
|
||||||
{
|
|
||||||
foreach (var column in band.Columns)
|
|
||||||
{
|
|
||||||
assignments[column] = band.Id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return assignments;
|
|
||||||
}
|
|
||||||
|
|
||||||
private RenderFragment RenderColumns() => builder =>
|
|
||||||
{
|
|
||||||
var seq = 0;
|
|
||||||
builder.OpenComponent<DxGridCommandColumn>(seq++);
|
|
||||||
builder.AddAttribute(seq++, "Width", "120px");
|
|
||||||
builder.CloseComponent();
|
|
||||||
|
|
||||||
var grouped = bandLayout.Bands.SelectMany(band => band.Columns).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
|
||||||
foreach (var column in columnDefinitions.Where(column => !grouped.Contains(column.FieldName)))
|
|
||||||
{
|
|
||||||
BuildDataColumn(builder, ref seq, column);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var band in bandLayout.Bands)
|
|
||||||
{
|
|
||||||
if (band.Columns.Count == 0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.OpenComponent<DxGridBandColumn>(seq++);
|
|
||||||
builder.AddAttribute(seq++, "Caption", band.Caption);
|
|
||||||
builder.AddAttribute(seq++, "Columns", (RenderFragment)(bandBuilder =>
|
|
||||||
{
|
|
||||||
var bandSeq = 0;
|
|
||||||
foreach (var columnName in band.Columns)
|
|
||||||
{
|
|
||||||
if (columnLookup.TryGetValue(columnName, out var column))
|
|
||||||
{
|
|
||||||
BuildDataColumn(bandBuilder, ref bandSeq, column);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
builder.CloseComponent();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private void BuildDataColumn(RenderTreeBuilder builder, ref int seq, ColumnDefinition column)
|
|
||||||
{
|
|
||||||
builder.OpenComponent<DxGridDataColumn>(seq++);
|
|
||||||
builder.AddAttribute(seq++, "FieldName", column.FieldName);
|
|
||||||
builder.AddAttribute(seq++, "Caption", column.Caption);
|
|
||||||
if (!string.IsNullOrWhiteSpace(column.Width))
|
|
||||||
{
|
|
||||||
builder.AddAttribute(seq++, "Width", column.Width);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(column.DisplayFormat))
|
|
||||||
{
|
|
||||||
builder.AddAttribute(seq++, "DisplayFormat", column.DisplayFormat);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (column.ReadOnly)
|
|
||||||
{
|
|
||||||
builder.AddAttribute(seq++, "ReadOnly", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.CloseComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class BandLayout
|
|
||||||
{
|
|
||||||
public List<BandDefinition> Bands { get; set; } = new();
|
|
||||||
public List<string> ColumnOrder { get; set; } = new();
|
|
||||||
public Dictionary<string, string?> ColumnWidths { get; set; } = new(StringComparer.OrdinalIgnoreCase);
|
|
||||||
public GridPersistentLayout? GridLayout { get; set; }
|
|
||||||
public DevExpress.Blazor.SizeMode SizeMode { get; set; } = DevExpress.Blazor.SizeMode.Medium;
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class BandDefinition
|
|
||||||
{
|
|
||||||
public string Id { get; set; } = string.Empty;
|
|
||||||
public string Caption { get; set; } = string.Empty;
|
|
||||||
public List<string> Columns { get; set; } = new();
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class BandOption
|
|
||||||
{
|
|
||||||
public string Id { get; set; } = string.Empty;
|
|
||||||
public string Caption { get; set; } = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class ColumnDefinition
|
|
||||||
{
|
|
||||||
public string FieldName { get; init; } = string.Empty;
|
|
||||||
public string Caption { get; init; } = string.Empty;
|
|
||||||
public string? Width { get; set; }
|
|
||||||
public string? DisplayFormat { get; init; }
|
|
||||||
public bool ReadOnly { get; init; }
|
|
||||||
public ColumnFilterType FilterType { get; init; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum ColumnFilterType
|
|
||||||
{
|
|
||||||
Text,
|
|
||||||
Date
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class CatalogEditModel
|
private sealed class CatalogEditModel
|
||||||
{
|
{
|
||||||
public int Guid { get; set; }
|
public int Guid { get; set; }
|
||||||
@@ -762,10 +565,4 @@ 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
@inject MassDataApiClient Api
|
@inject MassDataApiClient Api
|
||||||
@inject LayoutApiClient LayoutApi
|
@inject BandLayoutService BandLayoutService
|
||||||
@inject IJSRuntime JsRuntime
|
|
||||||
|
|
||||||
@if (!string.IsNullOrWhiteSpace(errorMessage))
|
@if (!string.IsNullOrWhiteSpace(errorMessage))
|
||||||
{
|
{
|
||||||
@@ -113,7 +112,9 @@ else
|
|||||||
@RenderColumns()
|
@RenderColumns()
|
||||||
</Columns>
|
</Columns>
|
||||||
<EditFormTemplate Context="editFormContext">
|
<EditFormTemplate Context="editFormContext">
|
||||||
@{ SetEditContext(editFormContext.EditContext); var editModel = (MassDataEditModel)editFormContext.EditModel; SetPopupHeaderText(editModel.IsNew); }
|
@{
|
||||||
|
SetEditContext(editFormContext.EditContext); var editModel = (MassDataEditModel)editFormContext.EditModel; SetPopupHeaderText(editModel.IsNew);
|
||||||
|
}
|
||||||
<DxFormLayout ColCount="2">
|
<DxFormLayout ColCount="2">
|
||||||
<DxFormLayoutItem Caption="CustomerName">
|
<DxFormLayoutItem Caption="CustomerName">
|
||||||
<DxTextBox @bind-Text="editModel.CustomerName" Width="100%" />
|
<DxTextBox @bind-Text="editModel.CustomerName" Width="100%" />
|
||||||
@@ -171,13 +172,13 @@ else
|
|||||||
private int? focusedRowKey;
|
private int? focusedRowKey;
|
||||||
private const string LayoutType = "GRID_BANDS";
|
private const string LayoutType = "GRID_BANDS";
|
||||||
private const string LayoutKey = "MassDataGrid";
|
private const string LayoutKey = "MassDataGrid";
|
||||||
private const string LayoutUserStorageKey = "layoutUser";
|
|
||||||
private string? layoutUser;
|
private string? layoutUser;
|
||||||
private BandLayout bandLayout = new();
|
private BandLayout bandLayout = new();
|
||||||
private Dictionary<string, string> columnBandAssignments = new();
|
private Dictionary<string, string> columnBandAssignments = new();
|
||||||
private List<BandOption> bandOptions = new();
|
private List<BandOption> bandOptions = new();
|
||||||
private Dictionary<string, ColumnDefinition> columnLookup = new();
|
private Dictionary<string, ColumnDefinition> columnLookup = new();
|
||||||
private readonly JsonSerializerOptions jsonOptions = new(JsonSerializerDefaults.Web);
|
private bool gridLayoutApplied;
|
||||||
|
|
||||||
private List<ColumnDefinition> columnDefinitions = new()
|
private List<ColumnDefinition> columnDefinitions = new()
|
||||||
{
|
{
|
||||||
new() { FieldName = nameof(MassDataReadDto.Id), Caption = "Id", Width = "90px", ReadOnly = true, FilterType = ColumnFilterType.Text },
|
new() { FieldName = nameof(MassDataReadDto.Id), Caption = "Id", Width = "90px", ReadOnly = true, FilterType = ColumnFilterType.Text },
|
||||||
@@ -189,15 +190,13 @@ else
|
|||||||
new() { FieldName = nameof(MassDataReadDto.ChangedWhen), Caption = "Changed", ReadOnly = true, FilterType = ColumnFilterType.Date }
|
new() { FieldName = nameof(MassDataReadDto.ChangedWhen), Caption = "Changed", ReadOnly = true, FilterType = ColumnFilterType.Date }
|
||||||
};
|
};
|
||||||
|
|
||||||
private bool CanSaveBandLayout => !string.IsNullOrWhiteSpace(layoutUser);
|
|
||||||
|
|
||||||
private readonly List<PageSizeOption> pageSizeOptions = new()
|
private readonly List<PageSizeOption> pageSizeOptions = new()
|
||||||
{
|
{
|
||||||
new() { Value = 100, Text = "100" },
|
new() { Value = 100, Text = "100" },
|
||||||
new() { Value = 1000, Text = "1.000" },
|
new() { Value = 1000, Text = "1.000" },
|
||||||
new() { Value = 10000, Text = "10.000" },
|
new() { Value = 10000, Text = "10.000" },
|
||||||
new() { Value = 100000, Text = "100.000" },
|
new() { Value = 100000, Text = "100.000" },
|
||||||
new() { Value = null, Text = "Alle" }
|
new() { Value = null, Text = "Alle" }
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly List<ProcedureOption> procedureOptions = new()
|
private readonly List<ProcedureOption> procedureOptions = new()
|
||||||
@@ -205,33 +204,46 @@ else
|
|||||||
new() { Value = 0, Text = "PRMassdata_UpsertByCustomerName" }
|
new() { Value = 0, Text = "PRMassdata_UpsertByCustomerName" }
|
||||||
};
|
};
|
||||||
|
|
||||||
private bool gridLayoutApplied;
|
private bool CanSaveBandLayout => !string.IsNullOrWhiteSpace(layoutUser);
|
||||||
|
|
||||||
private DevExpress.Blazor.SizeMode _sizeMode = DevExpress.Blazor.SizeMode.Medium;
|
private SizeMode _sizeMode = SizeMode.Medium;
|
||||||
private static readonly List<DevExpress.Blazor.SizeMode> _sizeModes =
|
private static readonly List<SizeMode> _sizeModes = Enum.GetValues<SizeMode>().ToList();
|
||||||
Enum.GetValues<DevExpress.Blazor.SizeMode>().ToList();
|
|
||||||
|
|
||||||
private string FormatSizeText(DevExpress.Blazor.SizeMode size) => size switch
|
private string FormatSizeText(SizeMode size) => size switch
|
||||||
{
|
{
|
||||||
DevExpress.Blazor.SizeMode.Small => "Klein",
|
SizeMode.Small => "Klein",
|
||||||
DevExpress.Blazor.SizeMode.Medium => "Mittel",
|
SizeMode.Medium => "Mittel",
|
||||||
DevExpress.Blazor.SizeMode.Large => "Groß",
|
SizeMode.Large => "Groß",
|
||||||
_ => size.ToString()
|
_ => size.ToString()
|
||||||
};
|
};
|
||||||
|
|
||||||
private void OnSizeChange(DropDownButtonItemClickEventArgs args)
|
private void OnSizeChange(DropDownButtonItemClickEventArgs args)
|
||||||
{
|
{
|
||||||
_sizeMode = Enum.Parse<DevExpress.Blazor.SizeMode>(args.ItemInfo.Id);
|
_sizeMode = Enum.Parse<SizeMode>(args.ItemInfo.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
columnLookup = columnDefinitions.ToDictionary(column => column.FieldName, StringComparer.OrdinalIgnoreCase);
|
columnLookup = columnDefinitions.ToDictionary(c => c.FieldName, StringComparer.OrdinalIgnoreCase);
|
||||||
await EnsureLayoutUserAsync();
|
layoutUser = await BandLayoutService.EnsureLayoutUserAsync();
|
||||||
await LoadBandLayoutAsync();
|
bandLayout = await BandLayoutService.LoadBandLayoutAsync(LayoutType, LayoutKey, layoutUser, columnLookup);
|
||||||
|
columnBandAssignments = BandLayoutService.BuildAssignmentsFromLayout(bandLayout);
|
||||||
|
ApplyColumnLayoutFromStorage();
|
||||||
|
_sizeMode = bandLayout.SizeMode;
|
||||||
|
UpdateBandOptions();
|
||||||
await LoadPage(0);
|
await LoadPage(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if (!gridLayoutApplied && gridRef != null && bandLayout.GridLayout != null)
|
||||||
|
{
|
||||||
|
gridRef.LoadLayout(bandLayout.GridLayout);
|
||||||
|
gridLayoutApplied = true;
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task LoadPage(int page)
|
private async Task LoadPage(int page)
|
||||||
{
|
{
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
@@ -242,7 +254,6 @@ else
|
|||||||
var effectivePageSize = pageSize ?? (total == 0 ? 1 : total);
|
var effectivePageSize = pageSize ?? (total == 0 ? 1 : total);
|
||||||
pageCount = Math.Max(1, (int)Math.Ceiling(total / (double)effectivePageSize));
|
pageCount = Math.Max(1, (int)Math.Ceiling(total / (double)effectivePageSize));
|
||||||
pageIndex = Math.Clamp(page, 0, pageCount - 1);
|
pageIndex = Math.Clamp(page, 0, pageCount - 1);
|
||||||
|
|
||||||
var skip = pageSize.HasValue ? pageIndex * pageSize.Value : 0;
|
var skip = pageSize.HasValue ? pageIndex * pageSize.Value : 0;
|
||||||
items = await Api.GetAllAsync(skip, pageSize);
|
items = await Api.GetAllAsync(skip, pageSize);
|
||||||
}
|
}
|
||||||
@@ -258,10 +269,7 @@ else
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OnPageChanged(int index)
|
private async Task OnPageChanged(int index) => await LoadPage(index);
|
||||||
{
|
|
||||||
await LoadPage(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task OnPageSizeChanged(int? size)
|
private async Task OnPageSizeChanged(int? size)
|
||||||
{
|
{
|
||||||
@@ -269,61 +277,15 @@ else
|
|||||||
await LoadPage(0);
|
await LoadPage(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task EnsureLayoutUserAsync()
|
|
||||||
{
|
|
||||||
layoutUser = await JsRuntime.InvokeAsync<string?>("localStorage.getItem", LayoutUserStorageKey);
|
|
||||||
if (string.IsNullOrWhiteSpace(layoutUser))
|
|
||||||
{
|
|
||||||
layoutUser = Guid.NewGuid().ToString("N");
|
|
||||||
await JsRuntime.InvokeVoidAsync("localStorage.setItem", LayoutUserStorageKey, layoutUser);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task LoadBandLayoutAsync()
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(layoutUser))
|
|
||||||
{
|
|
||||||
bandLayout = new BandLayout();
|
|
||||||
UpdateBandOptions();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var stored = await LayoutApi.GetAsync(LayoutType, LayoutKey, layoutUser);
|
|
||||||
if (stored != null && !string.IsNullOrWhiteSpace(stored.LayoutData))
|
|
||||||
{
|
|
||||||
var parsed = JsonSerializer.Deserialize<BandLayout>(stored.LayoutData, jsonOptions);
|
|
||||||
bandLayout = NormalizeBandLayout(parsed);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bandLayout = new BandLayout();
|
|
||||||
}
|
|
||||||
|
|
||||||
columnBandAssignments = BuildAssignmentsFromLayout(bandLayout);
|
|
||||||
ApplyColumnLayoutFromStorage();
|
|
||||||
_sizeMode = bandLayout.SizeMode;
|
|
||||||
UpdateBandOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SaveLayoutAsync()
|
private async Task SaveLayoutAsync()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(layoutUser))
|
if (string.IsNullOrWhiteSpace(layoutUser))
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
CaptureColumnLayoutFromGrid();
|
CaptureColumnLayoutFromGrid();
|
||||||
|
await BandLayoutService.SaveBandLayoutAsync(LayoutType, LayoutKey, layoutUser, bandLayout);
|
||||||
var layoutData = JsonSerializer.Serialize(bandLayout, jsonOptions);
|
|
||||||
await LayoutApi.UpsertAsync(new LayoutDto
|
|
||||||
{
|
|
||||||
LayoutType = LayoutType,
|
|
||||||
LayoutKey = LayoutKey,
|
|
||||||
UserName = layoutUser,
|
|
||||||
LayoutData = layoutData
|
|
||||||
});
|
|
||||||
infoMessage = "Layout gespeichert.";
|
infoMessage = "Layout gespeichert.";
|
||||||
errorMessage = null;
|
errorMessage = null;
|
||||||
}
|
}
|
||||||
@@ -333,41 +295,12 @@ else
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CaptureColumnLayoutFromGrid()
|
|
||||||
{
|
|
||||||
if (gridRef == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var layout = gridRef.SaveLayout();
|
|
||||||
bandLayout.GridLayout = layout;
|
|
||||||
bandLayout.SizeMode = _sizeMode;
|
|
||||||
|
|
||||||
var orderedColumns = layout.Columns
|
|
||||||
.Where(column => !string.IsNullOrWhiteSpace(column.FieldName))
|
|
||||||
.OrderBy(column => column.VisibleIndex)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
bandLayout.ColumnOrder = orderedColumns
|
|
||||||
.Select(column => column.FieldName)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
bandLayout.ColumnWidths = orderedColumns
|
|
||||||
.Where(column => !string.IsNullOrWhiteSpace(column.Width))
|
|
||||||
.ToDictionary(column => column.FieldName, column => column.Width, StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
bandLayout.SizeMode = _sizeMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ResetLayoutAsync()
|
private async Task ResetLayoutAsync()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(layoutUser))
|
if (string.IsNullOrWhiteSpace(layoutUser))
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
await LayoutApi.DeleteAsync(LayoutType, LayoutKey, layoutUser);
|
await BandLayoutService.ResetBandLayoutAsync(LayoutType, LayoutKey, layoutUser);
|
||||||
|
|
||||||
bandLayout = new BandLayout();
|
bandLayout = new BandLayout();
|
||||||
columnBandAssignments.Clear();
|
columnBandAssignments.Clear();
|
||||||
@@ -377,49 +310,57 @@ else
|
|||||||
column.Width = null;
|
column.Width = null;
|
||||||
columnLookup = columnDefinitions.ToDictionary(c => c.FieldName, StringComparer.OrdinalIgnoreCase);
|
columnLookup = columnDefinitions.ToDictionary(c => c.FieldName, StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
_sizeMode = DevExpress.Blazor.SizeMode.Medium;
|
_sizeMode = SizeMode.Medium;
|
||||||
|
|
||||||
if (gridRef != null)
|
if (gridRef != null)
|
||||||
gridRef.LoadLayout(new GridPersistentLayout());
|
gridRef.LoadLayout(new GridPersistentLayout());
|
||||||
gridLayoutApplied = false;
|
gridLayoutApplied = false;
|
||||||
|
|
||||||
infoMessage = "Layout zur\u00fcckgesetzt.";
|
infoMessage = "Layout zurückgesetzt.";
|
||||||
errorMessage = null;
|
errorMessage = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CaptureColumnLayoutFromGrid()
|
||||||
|
{
|
||||||
|
if (gridRef == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var layout = gridRef.SaveLayout();
|
||||||
|
bandLayout.GridLayout = layout;
|
||||||
|
bandLayout.SizeMode = _sizeMode;
|
||||||
|
|
||||||
|
var orderedColumns = layout.Columns
|
||||||
|
.Where(c => !string.IsNullOrWhiteSpace(c.FieldName))
|
||||||
|
.OrderBy(c => c.VisibleIndex)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
bandLayout.ColumnOrder = orderedColumns.Select(c => c.FieldName).ToList();
|
||||||
|
bandLayout.ColumnWidths = orderedColumns
|
||||||
|
.Where(c => !string.IsNullOrWhiteSpace(c.Width))
|
||||||
|
.ToDictionary(c => c.FieldName, c => c.Width, StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
private void ApplyColumnLayoutFromStorage()
|
private void ApplyColumnLayoutFromStorage()
|
||||||
{
|
{
|
||||||
foreach (var column in columnDefinitions)
|
foreach (var column in columnDefinitions)
|
||||||
{
|
{
|
||||||
if (bandLayout.ColumnWidths.TryGetValue(column.FieldName, out var width) && !string.IsNullOrWhiteSpace(width))
|
if (bandLayout.ColumnWidths.TryGetValue(column.FieldName, out var width) && !string.IsNullOrWhiteSpace(width))
|
||||||
{
|
|
||||||
column.Width = width;
|
column.Width = width;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
columnLookup = columnDefinitions.ToDictionary(c => c.FieldName, StringComparer.OrdinalIgnoreCase);
|
||||||
columnLookup = columnDefinitions.ToDictionary(column => column.FieldName, StringComparer.OrdinalIgnoreCase);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddBand()
|
private void AddBand()
|
||||||
{
|
{
|
||||||
bandLayout.Bands.Add(new BandDefinition
|
bandLayout.Bands.Add(new BandDefinition { Id = Guid.NewGuid().ToString("N"), Caption = "Band" });
|
||||||
{
|
|
||||||
Id = Guid.NewGuid().ToString("N"),
|
|
||||||
Caption = "Band"
|
|
||||||
});
|
|
||||||
UpdateBandOptions();
|
UpdateBandOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RemoveBand(BandDefinition band)
|
private void RemoveBand(BandDefinition band)
|
||||||
{
|
{
|
||||||
bandLayout.Bands.Remove(band);
|
bandLayout.Bands.Remove(band);
|
||||||
var removedColumns = columnBandAssignments.Where(pair => pair.Value == band.Id)
|
foreach (var key in columnBandAssignments.Where(p => p.Value == band.Id).Select(p => p.Key).ToList())
|
||||||
.Select(pair => pair.Key)
|
columnBandAssignments.Remove(key);
|
||||||
.ToList();
|
|
||||||
foreach (var column in removedColumns)
|
|
||||||
{
|
|
||||||
columnBandAssignments.Remove(column);
|
|
||||||
}
|
|
||||||
UpdateBandOptions();
|
UpdateBandOptions();
|
||||||
SyncBandsFromAssignments();
|
SyncBandsFromAssignments();
|
||||||
}
|
}
|
||||||
@@ -433,77 +374,31 @@ else
|
|||||||
private void UpdateColumnBand(string fieldName, string? bandId)
|
private void UpdateColumnBand(string fieldName, string? bandId)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(bandId))
|
if (string.IsNullOrWhiteSpace(bandId))
|
||||||
{
|
|
||||||
columnBandAssignments.Remove(fieldName);
|
columnBandAssignments.Remove(fieldName);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
columnBandAssignments[fieldName] = bandId;
|
columnBandAssignments[fieldName] = bandId;
|
||||||
}
|
|
||||||
|
|
||||||
SyncBandsFromAssignments();
|
SyncBandsFromAssignments();
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetColumnBand(string fieldName)
|
private string GetColumnBand(string fieldName)
|
||||||
{
|
=> columnBandAssignments.TryGetValue(fieldName, out var bandId) ? bandId : string.Empty;
|
||||||
return columnBandAssignments.TryGetValue(fieldName, out var bandId) ? bandId : string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SyncBandsFromAssignments()
|
private void SyncBandsFromAssignments()
|
||||||
{
|
{
|
||||||
foreach (var band in bandLayout.Bands)
|
foreach (var band in bandLayout.Bands)
|
||||||
{
|
{
|
||||||
band.Columns = columnDefinitions
|
band.Columns = columnDefinitions
|
||||||
.Where(column => columnBandAssignments.TryGetValue(column.FieldName, out var bandId) && bandId == band.Id)
|
.Where(c => columnBandAssignments.TryGetValue(c.FieldName, out var id) && id == band.Id)
|
||||||
.Select(column => column.FieldName)
|
.Select(c => c.FieldName)
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateBandOptions()
|
private void UpdateBandOptions()
|
||||||
{
|
{
|
||||||
bandOptions = new List<BandOption> { new() { Id = string.Empty, Caption = "Ohne Band" } };
|
bandOptions = new List<BandOption> { new() { Id = string.Empty, Caption = "Ohne Band" } };
|
||||||
bandOptions.AddRange(bandLayout.Bands.Select(band => new BandOption { Id = band.Id, Caption = band.Caption }));
|
bandOptions.AddRange(bandLayout.Bands.Select(b => new BandOption { Id = b.Id, Caption = b.Caption }));
|
||||||
}
|
|
||||||
|
|
||||||
private Dictionary<string, string> BuildAssignmentsFromLayout(BandLayout layout)
|
|
||||||
{
|
|
||||||
var assignments = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
foreach (var band in layout.Bands)
|
|
||||||
{
|
|
||||||
foreach (var column in band.Columns)
|
|
||||||
{
|
|
||||||
assignments[column] = band.Id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return assignments;
|
|
||||||
}
|
|
||||||
|
|
||||||
private BandLayout NormalizeBandLayout(BandLayout? layout)
|
|
||||||
{
|
|
||||||
layout ??= new BandLayout();
|
|
||||||
layout.Bands ??= new List<BandDefinition>();
|
|
||||||
layout.ColumnOrder ??= new List<string>();
|
|
||||||
layout.ColumnWidths ??= new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
foreach (var band in layout.Bands)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(band.Id))
|
|
||||||
{
|
|
||||||
band.Id = Guid.NewGuid().ToString("N");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(band.Caption))
|
|
||||||
{
|
|
||||||
band.Caption = "Band";
|
|
||||||
}
|
|
||||||
|
|
||||||
band.Columns = band.Columns?.Where(columnLookup.ContainsKey).ToList() ?? new List<string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
return layout;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private RenderFragment RenderColumns() => builder =>
|
private RenderFragment RenderColumns() => builder =>
|
||||||
@@ -513,18 +408,13 @@ else
|
|||||||
builder.AddAttribute(seq++, "Width", "120px");
|
builder.AddAttribute(seq++, "Width", "120px");
|
||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
|
|
||||||
var grouped = bandLayout.Bands.SelectMany(band => band.Columns).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
var grouped = bandLayout.Bands.SelectMany(b => b.Columns).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||||
foreach (var column in columnDefinitions.Where(column => !grouped.Contains(column.FieldName)))
|
foreach (var column in columnDefinitions.Where(c => !grouped.Contains(c.FieldName)))
|
||||||
{
|
|
||||||
BuildDataColumn(builder, ref seq, column);
|
BuildDataColumn(builder, ref seq, column);
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var band in bandLayout.Bands)
|
foreach (var band in bandLayout.Bands)
|
||||||
{
|
{
|
||||||
if (band.Columns.Count == 0)
|
if (band.Columns.Count == 0) continue;
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.OpenComponent<DxGridBandColumn>(seq++);
|
builder.OpenComponent<DxGridBandColumn>(seq++);
|
||||||
builder.AddAttribute(seq++, "Caption", band.Caption);
|
builder.AddAttribute(seq++, "Caption", band.Caption);
|
||||||
@@ -534,9 +424,7 @@ else
|
|||||||
foreach (var columnName in band.Columns)
|
foreach (var columnName in band.Columns)
|
||||||
{
|
{
|
||||||
if (columnLookup.TryGetValue(columnName, out var column))
|
if (columnLookup.TryGetValue(columnName, out var column))
|
||||||
{
|
|
||||||
BuildDataColumn(bandBuilder, ref bandSeq, column);
|
BuildDataColumn(bandBuilder, ref bandSeq, column);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
@@ -549,35 +437,19 @@ else
|
|||||||
builder.AddAttribute(seq++, "FieldName", column.FieldName);
|
builder.AddAttribute(seq++, "FieldName", column.FieldName);
|
||||||
builder.AddAttribute(seq++, "Caption", column.Caption);
|
builder.AddAttribute(seq++, "Caption", column.Caption);
|
||||||
if (!string.IsNullOrWhiteSpace(column.Width))
|
if (!string.IsNullOrWhiteSpace(column.Width))
|
||||||
{
|
|
||||||
builder.AddAttribute(seq++, "Width", column.Width);
|
builder.AddAttribute(seq++, "Width", column.Width);
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(column.DisplayFormat))
|
if (!string.IsNullOrWhiteSpace(column.DisplayFormat))
|
||||||
{
|
|
||||||
builder.AddAttribute(seq++, "DisplayFormat", column.DisplayFormat);
|
builder.AddAttribute(seq++, "DisplayFormat", column.DisplayFormat);
|
||||||
}
|
|
||||||
|
|
||||||
if (column.ReadOnly)
|
if (column.ReadOnly)
|
||||||
{
|
|
||||||
builder.AddAttribute(seq++, "ReadOnly", true);
|
builder.AddAttribute(seq++, "ReadOnly", true);
|
||||||
}
|
|
||||||
|
|
||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetEditContext(EditContext context)
|
private void SetEditContext(EditContext context)
|
||||||
{
|
{
|
||||||
if (editContext == context)
|
if (editContext == context) return;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (editContext != null)
|
if (editContext != null)
|
||||||
{
|
|
||||||
editContext.OnFieldChanged -= OnEditFieldChanged;
|
editContext.OnFieldChanged -= OnEditFieldChanged;
|
||||||
}
|
|
||||||
|
|
||||||
editContext = context;
|
editContext = context;
|
||||||
validationMessageStore = new ValidationMessageStore(editContext);
|
validationMessageStore = new ValidationMessageStore(editContext);
|
||||||
editContext.OnFieldChanged += OnEditFieldChanged;
|
editContext.OnFieldChanged += OnEditFieldChanged;
|
||||||
@@ -585,10 +457,7 @@ else
|
|||||||
|
|
||||||
private void OnEditFieldChanged(object? sender, FieldChangedEventArgs e)
|
private void OnEditFieldChanged(object? sender, FieldChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (validationMessageStore == null || editContext == null)
|
if (validationMessageStore == null || editContext == null) return;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.FieldIdentifier.FieldName == nameof(MassDataEditModel.UpdateProcedure))
|
if (e.FieldIdentifier.FieldName == nameof(MassDataEditModel.UpdateProcedure))
|
||||||
{
|
{
|
||||||
@@ -599,16 +468,12 @@ else
|
|||||||
|
|
||||||
if (e.FieldIdentifier.FieldName == nameof(MassDataEditModel.CustomerName))
|
if (e.FieldIdentifier.FieldName == nameof(MassDataEditModel.CustomerName))
|
||||||
{
|
{
|
||||||
var field = new FieldIdentifier(editContext.Model, nameof(MassDataEditModel.CustomerName));
|
validationMessageStore.Clear(new FieldIdentifier(editContext.Model, nameof(MassDataEditModel.CustomerName)));
|
||||||
validationMessageStore.Clear(field);
|
|
||||||
editContext.NotifyValidationStateChanged();
|
editContext.NotifyValidationStateChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetPopupHeaderText(bool isNew)
|
private void SetPopupHeaderText(bool isNew) => popupHeaderText = isNew ? "Neu" : "Edit";
|
||||||
{
|
|
||||||
popupHeaderText = isNew ? "Neu" : "Edit";
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task OnCustomizeEditModel(GridCustomizeEditModelEventArgs e)
|
private async Task OnCustomizeEditModel(GridCustomizeEditModelEventArgs e)
|
||||||
{
|
{
|
||||||
@@ -638,7 +503,6 @@ else
|
|||||||
{
|
{
|
||||||
errorMessage = null;
|
errorMessage = null;
|
||||||
infoMessage = null;
|
infoMessage = null;
|
||||||
|
|
||||||
validationMessageStore?.Clear();
|
validationMessageStore?.Clear();
|
||||||
editContext?.NotifyValidationStateChanged();
|
editContext?.NotifyValidationStateChanged();
|
||||||
|
|
||||||
@@ -685,13 +549,8 @@ else
|
|||||||
|
|
||||||
private void AddValidationError(MassDataEditModel editModel, string fieldName, string message)
|
private void AddValidationError(MassDataEditModel editModel, string fieldName, string message)
|
||||||
{
|
{
|
||||||
if (editContext == null || validationMessageStore == null)
|
if (editContext == null || validationMessageStore == null) return;
|
||||||
{
|
validationMessageStore.Add(new FieldIdentifier(editModel, fieldName), message);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var field = new FieldIdentifier(editModel, fieldName);
|
|
||||||
validationMessageStore.Add(field, message);
|
|
||||||
editContext.NotifyValidationStateChanged();
|
editContext.NotifyValidationStateChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -703,55 +562,6 @@ else
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
||||||
{
|
|
||||||
if (!gridLayoutApplied && gridRef != null && bandLayout.GridLayout != null)
|
|
||||||
{
|
|
||||||
gridRef.LoadLayout(bandLayout.GridLayout);
|
|
||||||
gridLayoutApplied = true;
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class BandLayout
|
|
||||||
{
|
|
||||||
public List<BandDefinition> Bands { get; set; } = new();
|
|
||||||
public List<string> ColumnOrder { get; set; } = new();
|
|
||||||
public Dictionary<string, string?> ColumnWidths { get; set; } = new(StringComparer.OrdinalIgnoreCase);
|
|
||||||
public GridPersistentLayout? GridLayout { get; set; }
|
|
||||||
public DevExpress.Blazor.SizeMode SizeMode { get; set; } = DevExpress.Blazor.SizeMode.Medium;
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class BandDefinition
|
|
||||||
{
|
|
||||||
public string Id { get; set; } = string.Empty;
|
|
||||||
public string Caption { get; set; } = string.Empty;
|
|
||||||
public List<string> Columns { get; set; } = new();
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class BandOption
|
|
||||||
{
|
|
||||||
public string Id { get; set; } = string.Empty;
|
|
||||||
public string Caption { get; set; } = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class ColumnDefinition
|
|
||||||
{
|
|
||||||
public string FieldName { get; init; } = string.Empty;
|
|
||||||
public string Caption { get; init; } = string.Empty;
|
|
||||||
public string? Width { get; set; }
|
|
||||||
public string? DisplayFormat { get; init; }
|
|
||||||
public bool ReadOnly { get; init; }
|
|
||||||
public ColumnFilterType FilterType { get; init; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum ColumnFilterType
|
|
||||||
{
|
|
||||||
Text,
|
|
||||||
Bool,
|
|
||||||
Date
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class MassDataEditModel
|
private sealed class MassDataEditModel
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
@@ -770,21 +580,9 @@ else
|
|||||||
public string Text { get; set; } = string.Empty;
|
public string Text { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class BoolFilterOption
|
|
||||||
{
|
|
||||||
public bool? Value { get; set; }
|
|
||||||
public string Text { get; set; } = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class PageSizeOption
|
private sealed class PageSizeOption
|
||||||
{
|
{
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
@using System.Net.Http
|
@using System.Net.Http
|
||||||
@using System.Net.Http.Json
|
@using System.Net.Http.Json
|
||||||
|
@using System.Text.Json
|
||||||
@using Microsoft.AspNetCore.Components.Forms
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
@using Microsoft.AspNetCore.Components.Rendering
|
@using Microsoft.AspNetCore.Components.Rendering
|
||||||
@using Microsoft.AspNetCore.Components.Routing
|
@using Microsoft.AspNetCore.Components.Routing
|
||||||
@@ -11,10 +12,9 @@
|
|||||||
@using DbFirst.BlazorWebApp
|
@using DbFirst.BlazorWebApp
|
||||||
@using DbFirst.BlazorWebApp.Components
|
@using DbFirst.BlazorWebApp.Components
|
||||||
@using DbFirst.BlazorWebApp.Models
|
@using DbFirst.BlazorWebApp.Models
|
||||||
|
@using DbFirst.BlazorWebApp.Models.Grid
|
||||||
@using DbFirst.BlazorWebApp.Services
|
@using DbFirst.BlazorWebApp.Services
|
||||||
@using DevExpress.Blazor
|
@using DevExpress.Blazor
|
||||||
@using DevExpress.DashboardBlazor
|
@using DevExpress.DashboardBlazor
|
||||||
@using DevExpress.DashboardWeb
|
@using DevExpress.DashboardWeb
|
||||||
@using DevExpress.Data.Filtering
|
@using DevExpress.Data.Filtering
|
||||||
@using DbFirst.BlazorWebApp
|
|
||||||
@using System.Text.Json
|
|
||||||
43
DbFirst.BlazorWebApp/Models/Grid/BandLayoutModels.cs
Normal file
43
DbFirst.BlazorWebApp/Models/Grid/BandLayoutModels.cs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
using DevExpress.Blazor;
|
||||||
|
|
||||||
|
namespace DbFirst.BlazorWebApp.Models.Grid
|
||||||
|
{
|
||||||
|
public class BandLayout
|
||||||
|
{
|
||||||
|
public List<BandDefinition> Bands { get; set; } = new();
|
||||||
|
public List<string> ColumnOrder { get; set; } = new();
|
||||||
|
public Dictionary<string, string?> ColumnWidths { get; set; } = new(StringComparer.OrdinalIgnoreCase);
|
||||||
|
public GridPersistentLayout? GridLayout { get; set; }
|
||||||
|
public SizeMode SizeMode { get; set; } = SizeMode.Medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BandDefinition
|
||||||
|
{
|
||||||
|
public string Id { get; set; } = string.Empty;
|
||||||
|
public string Caption { get; set; } = string.Empty;
|
||||||
|
public List<string> Columns { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BandOption
|
||||||
|
{
|
||||||
|
public string Id { get; set; } = string.Empty;
|
||||||
|
public string Caption { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ColumnDefinition
|
||||||
|
{
|
||||||
|
public string FieldName { get; init; } = string.Empty;
|
||||||
|
public string Caption { get; init; } = string.Empty;
|
||||||
|
public string? Width { get; set; }
|
||||||
|
public string? DisplayFormat { get; init; }
|
||||||
|
public bool ReadOnly { get; init; }
|
||||||
|
public ColumnFilterType FilterType { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ColumnFilterType
|
||||||
|
{
|
||||||
|
Text,
|
||||||
|
Bool,
|
||||||
|
Date
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ builder.Services.AddRazorComponents()
|
|||||||
|
|
||||||
builder.Services.AddDevExpressBlazor(options => options.BootstrapVersion = BootstrapVersion.v5);
|
builder.Services.AddDevExpressBlazor(options => options.BootstrapVersion = BootstrapVersion.v5);
|
||||||
builder.Services.AddScoped<ThemeState>();
|
builder.Services.AddScoped<ThemeState>();
|
||||||
|
builder.Services.AddScoped<BandLayoutService>();
|
||||||
|
|
||||||
var apiBaseUrl = builder.Configuration["ApiBaseUrl"];
|
var apiBaseUrl = builder.Configuration["ApiBaseUrl"];
|
||||||
if (!string.IsNullOrWhiteSpace(apiBaseUrl))
|
if (!string.IsNullOrWhiteSpace(apiBaseUrl))
|
||||||
|
|||||||
103
DbFirst.BlazorWebApp/Services/BandLayoutService.cs
Normal file
103
DbFirst.BlazorWebApp/Services/BandLayoutService.cs
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
using DbFirst.BlazorWebApp.Models;
|
||||||
|
using DbFirst.BlazorWebApp.Models.Grid;
|
||||||
|
using Microsoft.JSInterop;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace DbFirst.BlazorWebApp.Services
|
||||||
|
{
|
||||||
|
public class BandLayoutService(LayoutApiClient layoutApi, IJSRuntime jsRuntime)
|
||||||
|
{
|
||||||
|
private const string LayoutUserStorageKey = "layoutUser";
|
||||||
|
private readonly JsonSerializerOptions _jsonOptions = new(JsonSerializerDefaults.Web);
|
||||||
|
|
||||||
|
public async Task<string> EnsureLayoutUserAsync()
|
||||||
|
{
|
||||||
|
var layoutUser = await jsRuntime.InvokeAsync<string?>("localStorage.getItem", LayoutUserStorageKey);
|
||||||
|
if (string.IsNullOrWhiteSpace(layoutUser))
|
||||||
|
{
|
||||||
|
layoutUser = Guid.NewGuid().ToString("N");
|
||||||
|
await jsRuntime.InvokeVoidAsync("localStorage.setItem", LayoutUserStorageKey, layoutUser);
|
||||||
|
}
|
||||||
|
return layoutUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<BandLayout> LoadBandLayoutAsync(
|
||||||
|
string layoutType,
|
||||||
|
string layoutKey,
|
||||||
|
string layoutUser,
|
||||||
|
Dictionary<string, ColumnDefinition> columnLookup)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(layoutUser))
|
||||||
|
return new BandLayout();
|
||||||
|
|
||||||
|
var stored = await layoutApi.GetAsync(layoutType, layoutKey, layoutUser);
|
||||||
|
if (stored != null && !string.IsNullOrWhiteSpace(stored.LayoutData))
|
||||||
|
{
|
||||||
|
var parsed = JsonSerializer.Deserialize<BandLayout>(stored.LayoutData, _jsonOptions);
|
||||||
|
return NormalizeBandLayout(parsed, columnLookup);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BandLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SaveBandLayoutAsync(
|
||||||
|
string layoutType,
|
||||||
|
string layoutKey,
|
||||||
|
string layoutUser,
|
||||||
|
BandLayout bandLayout)
|
||||||
|
{
|
||||||
|
var layoutData = JsonSerializer.Serialize(bandLayout, _jsonOptions);
|
||||||
|
await layoutApi.UpsertAsync(new LayoutDto
|
||||||
|
{
|
||||||
|
LayoutType = layoutType,
|
||||||
|
LayoutKey = layoutKey,
|
||||||
|
UserName = layoutUser,
|
||||||
|
LayoutData = layoutData
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ResetBandLayoutAsync(
|
||||||
|
string layoutType,
|
||||||
|
string layoutKey,
|
||||||
|
string layoutUser)
|
||||||
|
{
|
||||||
|
await layoutApi.DeleteAsync(layoutType, layoutKey, layoutUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<string, string> BuildAssignmentsFromLayout(BandLayout layout)
|
||||||
|
{
|
||||||
|
var assignments = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (var band in layout.Bands)
|
||||||
|
{
|
||||||
|
foreach (var column in band.Columns)
|
||||||
|
{
|
||||||
|
assignments[column] = band.Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return assignments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BandLayout NormalizeBandLayout(
|
||||||
|
BandLayout? layout,
|
||||||
|
Dictionary<string, ColumnDefinition> columnLookup)
|
||||||
|
{
|
||||||
|
layout ??= new BandLayout();
|
||||||
|
layout.Bands ??= new List<BandDefinition>();
|
||||||
|
layout.ColumnOrder ??= new List<string>();
|
||||||
|
layout.ColumnWidths ??= new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
foreach (var band in layout.Bands)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(band.Id))
|
||||||
|
band.Id = Guid.NewGuid().ToString("N");
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(band.Caption))
|
||||||
|
band.Caption = "Band";
|
||||||
|
|
||||||
|
band.Columns = band.Columns?.Where(columnLookup.ContainsKey).ToList() ?? new List<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user